Game of Life (naive Implementierung)
-
Naive Implementierung des Game of Life. Mags mir bitte jemand um die Ohren hauen?
#include <cstddef> #include <stdexcept> #include <algorithm> #include <chrono> #include <random> #include <array> #include <vector> #include <string> #include <sstream> #include <iostream> #include <tchar.h> #include <conio.h> #define _WIN32_WINNT 0x0500 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include <windows.h> class console_t { HWND window_handle; HANDLE output_handle; unsigned max_width; unsigned max_height; unsigned width; unsigned height; HANDLE original_buffer; HANDLE front_buffer; HANDLE back_buffer; public: console_t(); console_t( unsigned width, unsigned height ); console_t( console_t const & other ) = delete; console_t & operator=( console_t other ) = delete; ~console_t(); unsigned get_max_width() const { return max_width; } unsigned get_max_height() const { return max_height; } unsigned get_width() const { return width; } unsigned get_height() const { return height; } void set_title( std::string const & title ); void move_window( unsigned x, unsigned y ); void resize( unsigned new_width, unsigned new_height ); void set_font_size( unsigned size ); void gotoxy( unsigned x, unsigned y ); void toggle_double_buffering(); HANDLE get_front_buffer() const; HANDLE get_back_buffer() const; void flip(); }; console_t::console_t() : window_handle { GetConsoleWindow() }, output_handle {}, max_width {}, max_height {}, width {}, height {}, original_buffer {}, front_buffer {}, back_buffer {} { if( !window_handle ) { if( !AllocConsole() ) throw std::runtime_error{ "An attempt to allocate a console for this process failed!" }; window_handle = GetConsoleWindow(); } output_handle = GetStdHandle( STD_OUTPUT_HANDLE ); if( !output_handle || output_handle == INVALID_HANDLE_VALUE ) throw std::runtime_error{ "Couldn't get standard output handle!" }; COORD max_size( GetLargestConsoleWindowSize( output_handle ) ); max_width = max_size.X; max_height = max_size.Y; CONSOLE_SCREEN_BUFFER_INFOEX csbiex{ sizeof csbiex }; GetConsoleScreenBufferInfoEx( output_handle, &csbiex ); width = csbiex.srWindow.Right - csbiex.srWindow.Left + 1; height = csbiex.srWindow.Bottom - csbiex.srWindow.Top + 1; resize( width, height ); } console_t::console_t( unsigned width, unsigned height ) : console_t{} { resize( width, height ); } console_t::~console_t() { if( original_buffer ) toggle_double_buffering(); } void console_t::set_title( std::string const & title ) { SetConsoleTitleA( title.c_str() ); } void console_t::move_window( unsigned x, unsigned y ) { SetWindowPos( window_handle, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER ); } void console_t::resize( unsigned new_width, unsigned new_height ) { if( !new_width || !new_height || new_width > max_width || new_height > max_height ) throw std::runtime_error{ "New dimensions for console window exceed system limits!" }; CONSOLE_SCREEN_BUFFER_INFOEX csbiex{ sizeof csbiex }; GetConsoleScreenBufferInfoEx( output_handle, &csbiex ); csbiex.srWindow.Left = 0; csbiex.srWindow.Top = 0; csbiex.srWindow.Right = 1; csbiex.srWindow.Bottom = 1; SetConsoleWindowInfo( output_handle, TRUE, &csbiex.srWindow ); csbiex.dwSize.X = new_width; csbiex.dwSize.Y = new_height; SetConsoleScreenBufferInfoEx( output_handle, &csbiex ); csbiex.srWindow.Left = 0; csbiex.srWindow.Top = 0; csbiex.srWindow.Right = new_width - 1; csbiex.srWindow.Bottom = new_height - 1; SetConsoleWindowInfo( output_handle, TRUE, &csbiex.srWindow ); width = new_width; height = new_height; } void console_t::set_font_size( unsigned size ) { if( size < 2 ) throw std::runtime_error{ "Can't set a console font size smaller than 2!" }; CONSOLE_FONT_INFOEX cfiex{ sizeof cfiex }; GetCurrentConsoleFontEx( GetStdHandle( STD_OUTPUT_HANDLE ), FALSE, &cfiex ); cfiex.dwFontSize.Y = size; SetCurrentConsoleFontEx( GetStdHandle( STD_OUTPUT_HANDLE ), FALSE, &cfiex ); COORD max_size( GetLargestConsoleWindowSize( output_handle ) ); max_width = max_size.X; max_height = max_size.Y; } void console_t::gotoxy( unsigned x, unsigned y ) { if( !SetConsoleCursorPosition( output_handle, COORD{ x, y } ) ) throw std::runtime_error{ "gotoxy() failed!" }; } void console_t::toggle_double_buffering() { if( !front_buffer ) { front_buffer = original_buffer = CreateFile( _T( "CONOUT$" ), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ); back_buffer = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, 0, nullptr, CONSOLE_TEXTMODE_BUFFER, nullptr ); } else { SetConsoleActiveScreenBuffer( original_buffer ); CloseHandle( original_buffer == front_buffer ? back_buffer : front_buffer ); original_buffer = front_buffer = back_buffer = nullptr; } } HANDLE console_t::get_front_buffer() const { return front_buffer; } HANDLE console_t::get_back_buffer() const { return back_buffer; } void console_t::flip() { std::swap( front_buffer, back_buffer ); SetConsoleActiveScreenBuffer( front_buffer ); } class ruleset_t { std::array< std::size_t, 9 > birth; std::array< std::size_t, 9 > survival; public: ruleset_t(); ruleset_t( std::vector< unsigned > & birth_neighbours, std::vector< unsigned > & survival_neighbours ); bool dead_or_alive( bool cell, std::size_t neighbours_alive ) const; }; ruleset_t::ruleset_t() : birth ( { 0, 0, 0, 1, 0, 0, 0, 0, 0 } ), // { 3 } survival ( { 0, 0, 1, 1, 0, 0, 0, 0, 0 } ) // { 2, 3 } {} ruleset_t::ruleset_t( std::vector< unsigned > & birth_neighbours, std::vector< unsigned > & survival_neighbours ) : birth (), survival () { for( auto i : birth_neighbours ) birth[ 1 << i ] = 1; for( auto i : survival_neighbours ) survival[ 1 << i ] = 1; } bool ruleset_t::dead_or_alive( bool cell, std::size_t neighbours_alive ) const { return cell && survival[ neighbours_alive ] || birth[ neighbours_alive ]; } struct point_t { std::size_t x; std::size_t y; }; class life_t { private: typedef std::vector< char > state_t; std::size_t generation; std::size_t population; std::size_t width; std::size_t height; state_t curr; state_t next; ruleset_t ruleset; std::string static_title_part; std::size_t get_moore_neighbours( std::size_t i ) const; public: life_t( std::size_t width, std::size_t height, ruleset_t const & ruleset = ruleset_t() ); void reset(); template< typename Engine > void randomize( double probability_of_life, Engine & engine ) { std::bernoulli_distribution distribution( probability_of_life ); std::transform( curr.begin(), curr.end(), curr.begin(), [&]( char i ){ return distribution( engine ); } ); } void iterate(); void print( console_t & console ) const; }; life_t::life_t( std::size_t width, std::size_t height, ruleset_t const & ruleset ) : generation (), population (), width ( width ), height ( height ), curr ( width * height, 0 ), next ( width * height, 0 ), ruleset ( ruleset ) { std::stringstream ss{ "Game of Life [ " }; ss.seekp( 0, std::ios::end ); ss << width << " x " << height << " | Population: "; static_title_part = ss.str(); } std::size_t life_t::get_moore_neighbours( std::size_t i ) const { std::size_t neighbours{}; bool const has_top_neighbour { i >= width }; bool const has_bottom_neighbour { i < curr.size() - width }; bool const has_left_neighbour { i && i % width }; bool const has_right_neighbour { ( i + 1 ) % width != 0 }; if( has_top_neighbour ) { if( curr[ i - width ] ) ++neighbours; if( has_right_neighbour && curr[ i - width + 1 ] ) ++neighbours; if( has_left_neighbour && curr[ i - width - 1 ] ) ++neighbours; } if( has_left_neighbour && curr[ i - 1 ] ) ++neighbours; if( has_right_neighbour && curr[ i + 1 ] ) ++neighbours; if( has_bottom_neighbour ) { if( curr[ i + width ] ) ++neighbours; if( has_left_neighbour && curr[ i + width - 1 ] ) ++neighbours; if( has_right_neighbour && curr[ i + width + 1 ] ) ++neighbours; } return neighbours; } void life_t::reset() { generation = 0; population = 0; std::fill( curr.begin(), curr.end(), 0 ); std::fill( next.begin(), next.end(), 0 ); } void life_t::iterate() { population = 0; for( std::size_t i{}; i < width * height; ++i ) next[ i ] = ruleset.dead_or_alive( curr[ i ] != 0, get_moore_neighbours( i ) ) && ++population; std::swap( curr, next ); ++generation; } void life_t::print( console_t & console ) const { static std::stringstream ss{}; DWORD written; WriteConsoleOutputCharacterA( console.get_back_buffer(), &curr[ 0 ], static_cast< DWORD >( curr.size() ), { 0, 0 }, &written ); ss.str( static_title_part ); ss.seekp( 0, std::ios::end ); ss << population << " | Generation: " << generation << " ]"; console.set_title( ss.str() ); console.flip(); } int main() { try { console_t console{}; console.set_font_size( 5 ); console.resize( console.get_max_width() - 4, console.get_max_height() - 4 ); console.move_window( 0, 0 ); console.toggle_double_buffering(); life_t life{ console.get_width(), console.get_height() }; std::mt19937 random_engine{ static_cast< unsigned >( std::chrono::high_resolution_clock::now().time_since_epoch().count() ) }; double life_chances{ 0.1 }; life.randomize( life_chances, random_engine ); bool run{ true }; DWORD delay = 0; do { if( _kbhit() ) { switch( _getch() ) { case 'r': life.reset(); life.randomize( life_chances, random_engine ); break; case 0x20: // space _ungetch( _getch() ); break; case 0x1b: // esc run = false; break; case '+': if( delay ) delay -= 10; break; case '-': delay += 10; break; } } else { life.iterate(); life.print( console ); Sleep( delay ); } } while( run ); } catch( std::runtime_error & exception ) { std::cerr << exception.what() << "\n\n"; return EXIT_FAILURE; } catch( ... ) { std::cerr << "An unknown error occured!\n\n"; return EXIT_FAILURE; } }
-
Viel Kapselung ist da nicht. live_t ruft WriteConsoleOutputCharacterA auf, nimmt aber trotzdem eine allgemeine console_t entgegen. Ausserdem sollten Simulation und Ausgabe völlig getrennt sein, was hier irgendwie nicht der Fall ist.
-
Guter Punkt. Ich hab' mich da bis zum Ende noch mit mehreren Ausgabemöglichkeiten gespielt und es ist was halbgares übrig geblieben. Gehört überarbeitet.
-
Nur um sicherzugehen, kennste Golly nebst http://en.wikipedia.org/wiki/Hashlife ?
Ist einfach frech, wie er in 20 Sekunden den Status eines Brüters nach 568,369,413,039,756,162,463,293,997,882,080,960,114,807,029,482,683,105,646,244,545,020,753,710,871,466,714,894,912 Generationen berechnet hat.

-
Der ctor
ruleset_t::ruleset_t( std::vector< unsigned > & birth_neighbours, std::vector< unsigned > & survival_neighbours ) : birth (), survival () { for( auto i : birth_neighbours ) birth[ 1 << i ] = 1; for( auto i : survival_neighbours ) survival[ 1 << i ] = 1; }ist kaputt. Behaupte ich mal.
1 << imacht hier keinen Sinn. Und nachdem es nicht Performance-kritisch ist wäre mMn..at()statt[]angebracht.get_moore_neighbourskannst du deutlich vereinfachen, auf eine von zwei Arten:- Erweitere das Array auf allen vier Seiten um je eine Zelle die immer 0 bleibt. Das würde ich machen.
Oder - Bastel dir eine
at(x, y)Funktion die 0 für "out of bounds" Zellen zurückliefert und ruf diese in derget_moore_neighboursauf.
Der
&& ++population;Trick ist grauenhaft. Ich will lieber nicht genauer darauf eingehen was ich davon halte - will dich nicht beleidigen.Wenn du Output und Simulation wirklich trennen willst, dann gehört die
printFunktion aus derlife_tKlasse raus. Statt dessen wirst du dann irgend eine generische Möglichkeit brauchen in einlife_treinzugucken bzw. das aktuelle "Frame" zu extrahieren.Die Initialisierung von Skalaren mit
{...}finde ich ebenso furchtbar, aber das ist ne Stilfrage, darum geht's ja weniger.Und - nochmal Stil - ... was soll das
_tals Suffix bei den Klassennamen? Verwende statt dessen lieber vernünftige Namen.
life_t=>game_of_life
ruleset_t=>game_of_life::ruleset
point_t=>point
console_t=>console_utility/console_windowUnd dann die Variablennamen... alles sehr einsilbig. Das geht auch besser (klarer, eindeutiger, leichter zu verstehen).
- Erweitere das Array auf allen vier Seiten um je eine Zelle die immer 0 bleibt. Das würde ich machen.
-
@volkard: ja, kenn ich natürlich

hustbaer schrieb:
Der ctor [
ruleset()] ist kaputt. Behaupte ich mal.1 << imacht hier keinen Sinn. [...]Sorry, da steh' ich auf irgendwelchen Leitungen rum ... was ist da kaputt? Bitte sag noch ein bisschen was dazu, damit mir vielleicht ein Licht aufgeht.
hustbaer schrieb:
[...] Und nachdem es nicht Performance-kritisch ist wäre mMn.
.at()statt[]angebracht.Angenommen.

hustbaer schrieb:
get_moore_neighbourskannst du deutlich vereinfachen, auf eine von zwei Arten:- Erweitere das Array auf allen vier Seiten um je eine Zelle die immer 0 bleibt. Das würde ich machen.
Oder [...]
Erledigt. Sieht besser aus.

hustbaer schrieb:
Der
&& ++population;Trick ist grauenhaft. Ich will lieber nicht genauer darauf eingehen was ich davon halte - will dich nicht beleidigen.Keine Angst, dafür könntest mich mit keiner Aussage beleidigen. Ich tipp mal auf Fehlerträchtig und bzw. weil leicht zu übersehen?
In der jetzigen Version wirst mich halt für die Zuweisung imifhaun ...hustbaer schrieb:
Wenn du Output und Simulation wirklich trennen willst, dann gehört die
printFunktion aus derlife_tKlasse raus. Statt dessen wirst du dann irgend eine generische Möglichkeit brauchen in einlife_treinzugucken bzw. das aktuelle "Frame" zu extrahieren.Erledigt. Aber ob das nicht auch eleganter geht?
hustbaer schrieb:
[...] Und dann die Variablennamen... alles sehr einsilbig. Das geht auch besser (klarer, eindeutiger, leichter zu verstehen).
Hm. Welche sind denn besonders schlimm?
Aktuelle Version:
#include <cstddef> #include <stdexcept> #include <algorithm> #include <chrono> #include <random> #include <array> #include <vector> #include <string> #include <sstream> #include <iostream> #define _WIN32_WINNT 0x0500 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include <windows.h> #include <conio.h> class console_window { HWND window_handle; HANDLE output_handle; unsigned max_width; unsigned max_height; unsigned width; unsigned height; HANDLE original_buffer; HANDLE front_buffer; HANDLE back_buffer; public: console_window( console_window const & ) = delete; console_window & operator=( console_window ) = delete; console_window() : window_handle { GetConsoleWindow() }, output_handle {}, max_width {}, max_height {}, width {}, height {}, original_buffer {}, front_buffer {}, back_buffer {} { if( !window_handle ) { if( !AllocConsole() ) throw std::runtime_error{ "An attempt to allocate a console for this process failed!" }; window_handle = GetConsoleWindow(); } output_handle = GetStdHandle( STD_OUTPUT_HANDLE ); if( !output_handle || output_handle == INVALID_HANDLE_VALUE ) throw std::runtime_error{ "Couldn't get standard output handle!" }; COORD max_size( GetLargestConsoleWindowSize( output_handle ) ); max_width = max_size.X; max_height = max_size.Y; CONSOLE_SCREEN_BUFFER_INFOEX csbiex{ sizeof csbiex }; GetConsoleScreenBufferInfoEx( output_handle, &csbiex ); width = csbiex.srWindow.Right - csbiex.srWindow.Left + 1; height = csbiex.srWindow.Bottom - csbiex.srWindow.Top + 1; resize( width, height ); } console_window( unsigned width, unsigned height ) : console_window{} { resize( width, height ); } ~console_window() { if( original_buffer ) toggle_double_buffering(); } unsigned get_max_width() const { return max_width; } unsigned get_max_height() const { return max_height; } unsigned get_width() const { return width; } unsigned get_height() const { return height; } void set_title( std::string const & title ) { SetConsoleTitleA( title.c_str() ); } void move_window( unsigned x, unsigned y ) { SetWindowPos( window_handle, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER ); } void resize( unsigned new_width, unsigned new_height ) { if( !new_width || !new_height || new_width > max_width || new_height > max_height ) throw std::runtime_error{ "New dimensions for console window exceed system limits!" }; CONSOLE_SCREEN_BUFFER_INFOEX csbiex{ sizeof csbiex }; GetConsoleScreenBufferInfoEx( output_handle, &csbiex ); csbiex.srWindow = { 0, 0, 1, 1 }; SetConsoleWindowInfo( output_handle, TRUE, &csbiex.srWindow ); csbiex.dwSize = { new_width, new_height }; SetConsoleScreenBufferInfoEx( output_handle, &csbiex ); csbiex.srWindow = { 0, 0, new_width - 1, new_height - 1 }; SetConsoleWindowInfo( output_handle, TRUE, &csbiex.srWindow ); width = new_width; height = new_height; } void set_font_size( unsigned size ) { if( size < 2 ) throw std::runtime_error{ "Can't set a console font size smaller than 2!" }; CONSOLE_FONT_INFOEX cfiex{ sizeof cfiex }; GetCurrentConsoleFontEx( output_handle, FALSE, &cfiex ); cfiex.dwFontSize.Y = size; SetCurrentConsoleFontEx( output_handle, FALSE, &cfiex ); COORD max_size( GetLargestConsoleWindowSize( output_handle ) ); max_width = max_size.X; max_height = max_size.Y; } void gotoxy( unsigned x, unsigned y ) { if( !SetConsoleCursorPosition( output_handle, COORD{ x, y } ) ) throw std::runtime_error{ "gotoxy() failed!" }; } void write_buffer( char const * buffer, std::size_t length, std::size_t x, std::size_t y ) { DWORD written; WriteConsoleOutputCharacterA( front_buffer ? back_buffer : front_buffer, buffer, length, { x, y }, &written ); } bool kbhit () const { return _kbhit() != 0; } int getch () const { return _getch(); } int ungetch ( int key ) const { return _ungetch( key ); } void sleep ( DWORD ms ) const { Sleep( ms ); } void toggle_double_buffering() { if( !front_buffer ) { front_buffer = original_buffer = CreateFileA( "CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ); back_buffer = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, 0, nullptr, CONSOLE_TEXTMODE_BUFFER, nullptr ); } else { SetConsoleActiveScreenBuffer( original_buffer ); CloseHandle( original_buffer == front_buffer ? back_buffer : front_buffer ); original_buffer = front_buffer = back_buffer = nullptr; } } HANDLE get_front_buffer() const { return front_buffer; } HANDLE get_back_buffer() const { return back_buffer; } void flip() { std::swap( front_buffer, back_buffer ); SetConsoleActiveScreenBuffer( front_buffer ); } }; class game_of_life { public: class ruleset { std::array< std::size_t, 9 > birth; std::array< std::size_t, 9 > survival; public: ruleset() : birth ({ 0, 0, 0, 1, 0, 0, 0, 0, 0 }), // { 3 } survival ({ 0, 0, 1, 1, 0, 0, 0, 0, 0 }) // { 2, 3 } {} ruleset( std::vector< unsigned > & birth_neighbours, std::vector< unsigned > & survival_neighbours ) : birth {}, survival {} { for( auto i : birth_neighbours ) birth.at( 1 << i ) = 1; for( auto i : survival_neighbours ) survival.at( 1 << i ) = 1; } bool dead_or_alive( bool cell, std::size_t neighbours_alive ) const { return cell && survival[ neighbours_alive ] || birth[ neighbours_alive ]; } }; private: typedef std::vector< char > state; static char const * caption; static char const * caption_seperator; static char const * stats_title; static char const * stats_seperator; static char const * stats_generation_title; static char const * stats_population_title; std::size_t const width; std::size_t const height; std::size_t generation; std::size_t population; state current_state; state next_state; ruleset rules; std::size_t get_moore_neighbours( std::size_t i ) const { return current_state[ i - width - 1 ] + current_state[ i - width ] + current_state[ i - width + 1 ] + current_state[ i - 1 ] + current_state[ i + 1 ] + current_state[ i + width - 1 ] + current_state[ i + width ] + current_state[ i + width + 1 ]; } public: game_of_life( std::size_t field_width, std::size_t field_height, ruleset const & rules = game_of_life::ruleset{} ) : width { field_width + 2 }, height { field_height + 2 }, generation {}, population {}, current_state { width * height, 0 }, next_state { width * height, 0 }, rules { rules } {} std::size_t get_width() const { return width - 2; } std::size_t get_height() const { return height - 2; } state::const_pointer operator[]( std::size_t y ) const { if( y > height - 2 ) throw std::runtime_error{ "Line number out of range!" }; return ¤t_state[ ( y + 1 ) * width + 2 ]; } std::size_t get_generation() const { return generation; } std::size_t get_population() const { return population; } std::string generate_stats_line() const { std::stringstream ss{ caption }; ss.seekp( 0, std::ios::end ); ss << caption_seperator << stats_title << get_width() << " x " << get_height() << stats_seperator << stats_population_title << population << stats_seperator << stats_generation_title << generation; return ss.str(); } void reset() { generation = population = 0; std::fill( std::begin( current_state ), std::end( current_state ), 0 ); std::fill( std::begin( next_state ), std::end( next_state ), 0 ); } template< typename Engine > void randomize( double probability_of_life, Engine & engine ) { std::bernoulli_distribution distribution( probability_of_life ); std::transform( std::begin( current_state ) + width, std::end( current_state ) - width, std::begin( current_state ) + width, [&]( state::value_type ){ return distribution( engine ); } ); for( std::size_t i{ width }; i < current_state.size() - width; ++i ) { current_state[ i ] = 0; current_state[ i += width - 1 ] = 0; } } void iterate() { population = 0; for( std::size_t y{ 1 }; y < height - 1; ++y ) { for( std::size_t x{ 1 }; x < width - 1; ++x ) { std::size_t i{ y * width + x }; if( next_state[ i ] = rules.dead_or_alive( current_state[ i ] != 0, get_moore_neighbours( i ) ) ) ++population; } } std::swap( current_state, next_state ); ++generation; } }; char const * game_of_life::caption { "Game of Life" }; char const * game_of_life::caption_seperator { " - " }; char const * game_of_life::stats_title { "Stats: " }; char const * game_of_life::stats_seperator { " | " }; char const * game_of_life::stats_generation_title { "Generation: " }; char const * game_of_life::stats_population_title { "Population: " }; int main() { try { console_window console{}; console.set_font_size( 5 ); console.resize( console.get_max_width() - 4, console.get_max_height() - 4 ); console.move_window( 0, 0 ); console.toggle_double_buffering(); game_of_life life{ console.get_width(), console.get_height() }; std::mt19937 random_engine{ static_cast< unsigned >( std::chrono::high_resolution_clock::now().time_since_epoch().count() ) }; double const life_chances{ 0.1 }; life.randomize( life_chances, random_engine ); bool run{ true }; DWORD delay{}; do { if( console.kbhit() ) { switch( console.getch() ) { case 'r': life.reset(); life.randomize( life_chances, random_engine ); break; case ' ': if( console.ungetch( console.getch() ) == ' ' ) console.getch(); break; case 0x1b: // esc run = false; break; case '+': if( delay ) delay -= 10; break; case '-': delay += 10; break; } } else { life.iterate(); for( std::size_t y{}; y < life.get_height(); ++y ) console.write_buffer( life[ y ], life.get_width(), 0, y ); console.flip(); console.set_title( life.generate_stats_line() ); console.sleep( delay ); } } while( run ); } catch( std::runtime_error & exception ) { std::cerr << exception.what() << "\n\n"; return EXIT_FAILURE; } catch( ... ) { std::cerr << "An unknown error occured!\n\n"; return EXIT_FAILURE; } }// edit: Code updated.
// edit: und auf < Romanlänge gekürzt.
// edit: dacht' ich zumindest
- Erweitere das Array auf allen vier Seiten um je eine Zelle die immer 0 bleibt. Das würde ich machen.