rendered paste body/*
Description: Conway's game of life.
Language: ISO C99 - Standard C
Author: Anthony Edwards
email: anthony.edwards@gmail.com
Reference: http://en.wikipedia.org/wiki/Conway's_Game_of_Life
Console application simulating cellular automaton.
gcc -Wall -pedantic -std=c99 -O2 -o gameoflife.exe gameoflife.c
hacked, faster, smaller: deepthawt
*/
#define UNIX 0
/* ======================================================================== */
#include <stdio.h>
#include <time.h>
#if (UNIX) // {
#include <stdlib.h>
#else // } {
#include <windows.h>
#include <wincon.h>
#include <conio.h>
#endif // }
/* ======================================================================== */
#define COLS 79 // universe width
#define ROWS 25 // universe height
#define DEAD ' ' // cell is dead
#define ALIVE '*' // cell is alive
/* ======================================================================== */
unsigned char universe[ROWS][COLS];
unsigned char next_gen[ROWS][COLS];
/* ======================================================================== */
static const signed char Organism[] =
// 0 block
{0,0 ,0,1 ,1,0 ,1,1 ,-1,-1
// 1 boat
,0,0 ,0,1 ,1,0 ,1,2 ,2,1 ,-1,-1
// 2 Blinker
,0,0 ,0,1 ,0,2 ,-1,-1
// 3 Toad
,0,1 ,0,2 ,0,3 ,1,0 ,1,1 ,1,2 ,-1,-1
// 4 Glider
,0,0 ,0,1 ,0,2 ,1,0 ,2,1 ,-1,-1
// 5 LWSS
,0,1 ,0,4 ,1,0 ,2,0 ,2,4 ,3,0 ,3,1 ,3,2 ,3,3 ,-1,-1
// 6 Pulsar
,0,2 ,0,3 ,0,4 ,1,1 ,1,3 ,1,5 ,2,0 ,2,1 ,2,2 ,2,4
,2,5 ,3,0 ,3,3 ,3,5 ,4,3 ,4,4 ,4,2 ,4,3 ,-1,-1
// 7 Diehard
,0,6 ,1,0 ,1,1 ,2,1 ,2,5 ,2,6 ,2,7 ,-1,-1
// 8 Acorn
,0,1 ,1,3 ,2,0 ,2,1 ,2,4 ,2,5 ,2,6 ,-1,-1
// 9 Gosper
,0,24 ,1,22 ,1,24 ,2,12 ,2,13 ,2,20 ,2,21 ,2,34
,2,35 ,3,11 ,3,15 ,3,20 ,3,21 ,3,34 ,3,35 ,4, 0
,4, 1 ,4,10 ,4,16 ,4,20 ,4,21 ,5, 0 ,5, 1 ,5,10
,5,14 ,5,16 ,5,17 ,5,22 ,5,24 ,6,10 ,6,16 ,6,24
,7,11 ,7,15 ,8,12 ,8,13 ,-1,-1
};
/* ======================================================================== */
#if UNIX // {
/* ======================================================================== */
#define PUTCHAR putchar
#define GETCHAR getchar
#define PUTS puts
#define CLOCK clock
static void gotoxy(int x, int y)
{
printf("\033[%d;%dH", x+1, y+1);
}
static void clear_screen(void)
{
printf("\033[H\033[2J");
}
/* ======================================================================== */
#else // } {
/* ======================================================================== */
// find and cache the console output handle
static const HANDLE con_out(void)
{
static HANDLE out;
if (!out)
out = GetStdHandle(STD_OUTPUT_HANDLE);
return out;
}
int PUTCHAR(int c)
{
DWORD writ;
WriteFile(con_out(), &c, 1, &writ,0);
return writ;
}
int GETCHAR(void)
{
DWORD num_keys;
char inrec;
while (!ReadFile(GetStdHandle(STD_INPUT_HANDLE),&inrec,1,&num_keys,0))
;
return inrec;
}
static clock_t CLOCK()
{
return GetTickCount();
}
/* ======================================================================== */
// crappy rand() just like the real crappy rand()
long randseed;
static void srand(unsigned int seed)
{
randseed = (long)seed;
}
static int rand(void)
{
return ((randseed = randseed * 214013L + 2531011L) >> 16) & 0x7fff;
}
/* ======================================================================== */
// set col and row position for subsequent text
static void gotoxy(const unsigned char x, const unsigned char y)
{
COORD pos;
CONSOLE_SCREEN_BUFFER_INFO info;
pos.X = x;
pos.Y = y;
GetConsoleScreenBufferInfo(con_out(),&info);
if (pos.X < 0)
pos.X = 0;
else
if (pos.X >= info.dwSize.X)
pos.X = info.dwSize.X;
if(pos.Y < 0)
pos.Y = 0;
else
if (pos.Y >= info.dwSize.Y)
pos.Y = info.dwSize.Y;
SetConsoleCursorPosition(con_out(),pos);
}
/* ======================================================================== */
// home and clear the screen
static void clear_screen(void)
{
CONSOLE_SCREEN_BUFFER_INFO info;
int num_chars;
DWORD written;
gotoxy(0,0);
GetConsoleScreenBufferInfo(con_out(),&info);
num_chars = info.dwSize.X
- info.dwCursorPosition.X
+ info.dwSize.X * (info.dwSize.Y-info.dwCursorPosition.Y - 1);
FillConsoleOutputCharacter(con_out(),DEAD,num_chars,info.dwCursorPosition,&written);
FillConsoleOutputAttribute(con_out(),info.wAttributes,num_chars,info.dwCursorPosition,&written);
}
/* ======================================================================== */
#endif // }
/* ======================================================================== */
static void PUTX(const char *s)
{
while (*s)
PUTCHAR(*s++);
}
/* ======================================================================== */
static void draw_generation(void)
{
unsigned char r,c;
for (r = 0; r < ROWS; ++r)
{ for (c = 0; c < COLS; ++c)
{ if (universe[r][c] != next_gen[r][c])
{ gotoxy(c,r);
PUTCHAR(next_gen[r][c]);
universe[r][c] = next_gen[r][c];
}
}
}
#if UNIX
fflush(stdout);
#endif
}
/* ======================================================================== */
/*
Test each cell in the universe against the 8 adjacent cells
applying the four rules of Conway's game of life.
Note we do not test cells that are off the grid.
Rule 1. Any live cell with fewer than two live neighbours dies.
Rule 2. Any live cell with more than three live neighbours dies.
Rule 3. Any live cell with two or three live neighbours lives.
Rule 4. Any dead cell with exactly three live neighbours lives.
*/
static void create_generation(void)
{
unsigned char r,c;
int adjacent_live_cells;
memcpy(next_gen,universe,sizeof(next_gen));
for (r = 0; r < ROWS; ++r)
{ for (c = 0; c < COLS; ++c)
{ adjacent_live_cells = 0;
if (r > 0)
{ if (universe[r - 1][c] == ALIVE)
++adjacent_live_cells;
if (c > 0)
{ if (universe[r - 1][c - 1] == ALIVE)
++adjacent_live_cells;
}
if (c < (COLS - 1))
{ if (universe[r - 1][c + 1] == ALIVE)
++adjacent_live_cells;
}
}
if (c > 0)
{ if (universe[r][c - 1] == ALIVE)
++adjacent_live_cells;
}
if (c < (COLS - 1))
{ if (universe[r][c + 1] == ALIVE)
++adjacent_live_cells;
}
if (r < (ROWS - 1))
{ if (c > 0)
{ if (universe[r + 1][c - 1] == ALIVE)
++adjacent_live_cells;
}
if (universe[r + 1][c] == ALIVE)
++adjacent_live_cells;
if (c < (COLS - 1))
{ if (universe[r + 1][c + 1] == ALIVE)
++adjacent_live_cells;
}
}
if (adjacent_live_cells < 2) next_gen[r][c] = DEAD;
else if (adjacent_live_cells > 3) next_gen[r][c] = DEAD;
else if (adjacent_live_cells == 3) next_gen[r][c] = ALIVE;
}
}
}
/* ======================================================================== */
static unsigned char getMenuOption(void)
{
unsigned char key;
for(;;)
{
clear_screen();
PUTX("Choose organism:\n"
"0) Block\n"
"1) Boat\n"
"2) Blinker\n"
"3) Toad\n"
"4) Glider\n"
"5) LWSS\n"
"6) Pulsar\n"
"7) Diehard\n"
"8) Acorn\n"
"9) Gosper's gun\n"
"r) Random\n"
"Ctrl-C exit\n"
"Choice: ");
key = GETCHAR();
if ((key >= '0' && key <= '9') || key == 'r')
break;
}
if (key == 'r')
key = 10;
else
key -= '0';
return key;
}
/* ======================================================================== */
static void select_organism(void)
{
// set base coordinates for our organism to start in center of screen
unsigned char r = ROWS >> 1;
unsigned char c = COLS >> 1;
unsigned char opt;
signed char x,y;
// intial state of screen and universe
memset(universe,DEAD,sizeof(universe));
memset(next_gen,DEAD,sizeof(next_gen));
opt = getMenuOption();
if (opt <= 9)
{ // use a prwedefined organism from a table
unsigned char n;
const signed char *p;
if (opt == 9)
{ // Gospers glider gun is not centered
r = 5;
c = 20;
}
// find the organism
n = 0;
p = &Organism[0];
while (n != opt)
{ y = *p++;
x = *p++;
if (x == -1)
++n;
}
// populate the organism
for (;;)
{ y = *p++;
x = *p++;
if (x == -1)
break;
next_gen[r + y][c + x] = ALIVE;
}
}
else
{ // Random fill of 5*5 matrix
srand((unsigned)CLOCK());
for (x =- 2; x <= 2; ++x)
{ for (y = -2; y <= 2; ++y)
{ next_gen[r - y][c - x] = rand() & 1 ? ALIVE : DEAD;
}
}
}
}
/* ======================================================================== */
#if (UNIX)
int main(int argc, char *argv)
#else
int start(void)
#endif
{
select_organism();
clear_screen();
for(;;)
{ draw_generation();
create_generation();
// Sleep(1);
}
return 0;
}
/* ======================================================================== */