mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
224 lines
5.6 KiB
C++
224 lines
5.6 KiB
C++
/*******************************************************************************
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the Free
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
conway.cpp
|
|
Conway's Game of Life.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <libmaxsi/platform.h>
|
|
#include <libmaxsi/sortix-vga.h>
|
|
#include <sys/keycodes.h>
|
|
#include <sys/termmode.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
#include <string.h>
|
|
|
|
const int width = 80;
|
|
const int height = 25;
|
|
|
|
const int rowstride = width + 2;
|
|
const int buffersize = (height+2) * (width+2);
|
|
|
|
int vgafd;
|
|
uint16_t frame[width*height];
|
|
|
|
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; }
|
|
}
|
|
|
|
bool FlushVGA()
|
|
{
|
|
return writeall(vgafd, frame, sizeof(frame)) < sizeof(frame);
|
|
}
|
|
|
|
int Init()
|
|
{
|
|
bool has_vga_mode_set = true;
|
|
FILE* modefp = fopen("/dev/video/mode", "r");
|
|
if ( modefp )
|
|
{
|
|
char* mode = NULL;
|
|
size_t modesize;
|
|
if ( 0 <= getline(&mode, &modesize, modefp) )
|
|
{
|
|
if ( strcmp(mode, "driver=none\n") != 0 )
|
|
has_vga_mode_set = false;
|
|
free(mode);
|
|
}
|
|
fclose(modefp);
|
|
}
|
|
|
|
if ( !has_vga_mode_set )
|
|
{
|
|
fprintf(stderr, "Sorry, this game only works in VGA mode.\n");
|
|
return 1;
|
|
}
|
|
|
|
vgafd = open("/dev/vga", O_RDWR);
|
|
if ( vgafd < 0 ) { error(0, errno, "unable to open vga device /dev/vga"); return 1; }
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushVGA();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
// Read the keyboard input from the user.
|
|
unsigned termmode = TERMMODE_KBKEY
|
|
| TERMMODE_UNICODE
|
|
| TERMMODE_SIGNAL
|
|
| TERMMODE_NONBLOCK;
|
|
if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); }
|
|
while ( true )
|
|
{
|
|
uint32_t codepoint;
|
|
ssize_t numbytes = read(0, &codepoint, sizeof(codepoint));
|
|
if ( !numbytes ) { break; }
|
|
if ( numbytes < 0 ) { break; }
|
|
int kbkey = KBKEY_DECODE(codepoint);
|
|
if ( kbkey == KBKEY_R ) { running = !running; }
|
|
if ( running ) { continue; }
|
|
if ( kbkey == KBKEY_C ) { Clear(); }
|
|
if ( kbkey == KBKEY_W ) { if ( posy > 1 ) { posy--; } }
|
|
if ( kbkey == KBKEY_A ) { if ( posx > 1 ) { posx--; } }
|
|
if ( kbkey == KBKEY_S ) { if ( posy < height ) { posy++; } }
|
|
if ( kbkey == KBKEY_D ) { if ( posx < width ) { posx++; } }
|
|
if ( kbkey == KBKEY_SPACE )
|
|
{
|
|
lastframe[posy * rowstride + posx] = 1 - lastframe[posy * rowstride + posx];
|
|
}
|
|
}
|
|
|
|
// Run a cycle.
|
|
if ( running ) { Cycle(); }
|
|
|
|
Render();
|
|
}
|
|
|
|
int usage(int /*argc*/, char* argv[])
|
|
{
|
|
printf("usage: %s [OPTIONS]\n", argv[0]);
|
|
printf("Options:\n");
|
|
printf(" --speed <miliseconds> How many miliseconds between updates\n");
|
|
printf(" --usage Display this screen\n");
|
|
printf(" --help Display this screen\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
int sleepms = 50;
|
|
for ( int i = 1; i < argc; i++ )
|
|
{
|
|
if ( strcmp(argv[i], "--help") == 0 ) { return usage(argc, argv); }
|
|
if ( strcmp(argv[i], "--usage") == 0 ) { return usage(argc, argv); }
|
|
if ( strcmp(argv[i], "--speed") == 0 && 1 < argc-i )
|
|
{
|
|
sleepms = atoi(argv[++i]);
|
|
}
|
|
}
|
|
|
|
int result = Init();
|
|
if ( result != 0 ) { return result; }
|
|
|
|
// Update the game every 50th milisecond.
|
|
while ( true )
|
|
{
|
|
usleep(sleepms * 1000);
|
|
Update();
|
|
}
|
|
|
|
return 0;
|
|
}
|