diff --git a/games/.gitignore b/games/.gitignore index 4f2c5fac..89ecd713 100644 --- a/games/.gitignore +++ b/games/.gitignore @@ -5,3 +5,4 @@ *.so *.a pong +conway diff --git a/games/Makefile b/games/Makefile index cb5115c9..5dae9cba 100644 --- a/games/Makefile +++ b/games/Makefile @@ -5,6 +5,7 @@ include ../crosscompilemakefile.mak INITRDDIR:=../initrd LOCALBINARIES:=\ pong \ +conway \ BINARIES:=$(addprefix $(INITRDDIR)/,$(BINARIES)) diff --git a/games/conway.cpp b/games/conway.cpp new file mode 100644 index 00000000..dad9c181 --- /dev/null +++ b/games/conway.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include + +using namespace Maxsi; +using namespace Maxsi::Keyboard; + +const int width = 80; +const int height = 25; + +const int rowstride = width + 2; +const int buffersize = (height+2) * (width+2); + +System::VGA::Frame* frame; + +bool running; +int posx; +int posy; + +char framea[buffersize]; +char frameb[buffersize]; +char* currentframe; +char* lastframe; + +void Clear() +{ + // Reset the game data. + for ( int i = 0; i < buffersize; i++ ) { framea[i] = 0; frameb[i] = 0; } +} + +int Init() +{ + // Create a VGA frame we can render onto. + frame = System::VGA::CreateFrame(); + if ( frame == NULL ) + { + Print("Could not create VGA frame\n"); + return -1; + } + + System::VGA::ChangeFrame(frame->fd); + + Clear(); + + // Initialize variables. + currentframe = framea; + lastframe = frameb; + + running = false; + posy = height / 2 + 1; + posx = width / 2 + 1; + + return 0; +} + +void Cycle() +{ + // Run a cycle of the game of life. + for ( int y = 1; y <= height; y++ ) + { + for ( int x = 1; x <= width; x++ ) + { + int alivearound = 0; + int current = lastframe[y * rowstride + x]; + + if ( lastframe[(y-1) * rowstride + (x-1)] > 0 ) { alivearound++; } + if ( lastframe[(y-1) * rowstride + (x-0)] > 0 ) { alivearound++; } + if ( lastframe[(y-1) * rowstride + (x+1)] > 0 ) { alivearound++; } + if ( lastframe[(y-0) * rowstride + (x-1)] > 0 ) { alivearound++; } + if ( lastframe[(y-0) * rowstride + (x+1)] > 0 ) { alivearound++; } + if ( lastframe[(y+1) * rowstride + (x-1)] > 0 ) { alivearound++; } + if ( lastframe[(y+1) * rowstride + (x-0)] > 0 ) { alivearound++; } + if ( lastframe[(y+1) * rowstride + (x+1)] > 0 ) { alivearound++; } + + currentframe[y * rowstride + x] = ( (current > 0 && (alivearound == 3 || alivearound == 2)) || (current == 0 && alivearound == 3) ) ? 1 : 0; + } + } + + // Swap buffers. + char* tmp = lastframe; lastframe = currentframe; currentframe = tmp; +} + +void Render() +{ + uint16_t* dest = frame->text; + + uint16_t set = 'O' | (COLOR8_LIGHT_GREY << 8); + uint16_t unset = ' ' | (COLOR8_LIGHT_GREY << 8); + + // Render each cell directly to the VGA framebuffer! + for ( int y = 1; y <= height; y++ ) + { + for ( int x = 1; x <= width; x++ ) + { + dest[(y-1) * width + (x-1)] = (lastframe[y * rowstride + x] > 0) ? set : unset; + + // Render the cursor. + if ( !running && x == posx && y == posy ) + { + dest[(y-1) * width + (x-1)] |= (COLOR8_RED << 12); + } + } + } +} + +void Update() +{ + // Read the keyboard input from the user. + unsigned method = System::Keyboard::POLL; + uint32_t codepoint; + while ( (codepoint = System::Keyboard::ReceieveKeystroke(method) ) != 0 ) + { + bool keyup = codepoint & DEPRESSED; + if ( keyup ) { continue; } + codepoint &= ~DEPRESSED; + + if ( codepoint == 'r' || codepoint == 'R' ) { running = !running; } + + if ( !running ) + { + if ( codepoint == 'c' || codepoint == 'C' ) { Clear(); } + if ( codepoint == 'w' || codepoint == 'W' ) { if ( posy > 1 ) { posy--; } } + if ( codepoint == 's' || codepoint == 'S' ) { if ( posy < height ) { posy++; } } + if ( codepoint == 'a' || codepoint == 'A' ) { if ( posx > 1 ) { posx--; } } + if ( codepoint == 'd' || codepoint == 'D' ) { if ( posx < width ) { posx++; } } + if ( codepoint == ' ' ) { lastframe[posy * rowstride + posx] = 1 - lastframe[posy * rowstride + posx]; } + } + } + + // Run a cycle. + if ( running ) { Cycle(); } + + Render(); +} + +int main(int argc, char* argv[]) +{ + int result = Init(); + if ( result != 0 ) { return result; } + + // Update the game every 50th milisecond. + while ( true ) + { + const int sleepms = 50; + Thread::USleep(sleepms * 1000); + Update(); + } + + return 0; +}