/* 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 #include #if (UNIX) // { #include #else // } { #include #include #include #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; } /* ======================================================================== */