Last active
December 4, 2019 17:36
-
-
Save willeccles/facaf315e44fdd7cd69d20ec47ff59b3 to your computer and use it in GitHub Desktop.
Conway's Game of Life in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Input is from stdin. The first line should be the width of the grid. | |
* The second line should be the height. | |
* The third line should be the length of a generation in microseconds. | |
* The rest of the file is assumed to be the grid itself | |
* Anything out of bounds will be cut off. | |
* A space is a "dead" cell; anything else is assumed to be a "living" cell. | |
* | |
* This implementation doesn't allow cells to wrap around. Edges are solid | |
* and block cells. | |
* | |
* This implementation supports CRLF line endings, but beyond that does not | |
* support Windows in any way. YMMV. | |
* | |
* If you wish to change the printed cell values, modify cell_vals below. | |
* | |
* Example usage: | |
* life < input.txt | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <unistd.h> | |
#define SET_NEXT(c,v) ((c) = (c & 1), (c) = (c) | ((v) << 1)) | |
#define GET_NEXT(c) ((c) >> 1) | |
typedef enum LIFE_STATE { | |
DEAD = 0, | |
ALIVE = 1, | |
} life; | |
// characters used to draw dead and alive cells | |
const char* cell_vals[] = { | |
[DEAD] = "\e[7m \e[27m", | |
[ALIVE] = "\e[0m ", | |
}; | |
static unsigned long long generation = 0; | |
static void printgrid(life** grid, size_t height, size_t width) { | |
printf("\e[;H" // move to top left | |
"Generation: %llu\n", generation); | |
for (size_t r = 0; r < height; r++) { | |
for (size_t c = 0; c < width; c++) { | |
printf("%s", cell_vals[grid[r][c] & 1]); | |
} | |
puts(""); // newline | |
} | |
} | |
static int liveneighbors(size_t r, size_t c, life** grid, size_t h, size_t w) { | |
int count = 0; | |
if (r != 0) { | |
count += grid[r-1][c] & 1; | |
if (c != 0) { | |
count += grid[r-1][c-1] & 1; | |
} | |
if (c != w - 1) { | |
count += grid[r-1][c+1] & 1; | |
} | |
} | |
if (r != h - 1) { | |
count += grid[r+1][c] & 1; | |
if (c != 0) { | |
count += grid[r+1][c-1] & 1; | |
} | |
if (c != w - 1) { | |
count += grid[r+1][c+1] & 1; | |
} | |
} | |
if (c != 0) { | |
count += grid[r][c-1] & 1; | |
} | |
if (c != w - 1) { | |
count += grid[r][c+1] & 1; | |
} | |
return count; | |
} | |
int main(void) { | |
size_t height, width; | |
int gen_time; | |
if (EOF == scanf("%lu", &width)) { | |
fprintf(stderr, "EOF reached before width value!\n"); | |
return 1; | |
} | |
if (EOF == scanf("%lu", &height)) { | |
fprintf(stderr, "EOF reached before height value!\n"); | |
return 1; | |
} | |
if (EOF == scanf("%d", &gen_time)) { | |
fprintf(stderr, "EOF reached before generation time value!\n"); | |
return 1; | |
} | |
life** grid = malloc(sizeof(life*) * height); | |
if (grid == NULL) { | |
perror("malloc"); | |
return 1; | |
} | |
for (int i = 0; i < height; i++) { | |
grid[i] = malloc(sizeof(life) * width); | |
if (grid[i] == NULL) { | |
perror("malloc"); | |
return 1; | |
} | |
for (int j = 0; j < width; j++) { | |
grid[i][j] = DEAD; | |
} | |
} | |
// +2 because newline shouldn't count against the thing, then +1 again because | |
// then windows users can use it | |
char* buf = malloc(width + 3); | |
if (buf == NULL) { | |
perror("malloc"); | |
return 1; | |
} | |
int row = 0; | |
while (NULL != fgets(buf, width + 3, stdin)) { | |
int col = 0; | |
for (char* c = buf; *c && *c != '\n' && *c != '\r'; c++, col++) { | |
if (isgraph(*c)) { | |
grid[row][col] = ALIVE; | |
} else { | |
grid[row][col] = DEAD; | |
} | |
} | |
row++; | |
} | |
free(buf); | |
// clear the screen so we only have to do it once | |
printf("\e[2J"); | |
// print the grid once to start | |
printgrid(grid, height, width); | |
// wait a second at the start just to let the user see their grid setup | |
usleep(1000000); | |
while (1) { | |
/* The rules: | |
* 1. Any live cell with two or three neighbors survives | |
* 2. Any dead cell with three live neighbors becomes a live cell | |
* 3. All other live cells die in next generation | |
* 4. All other dead cells stay dead in the next generation | |
*/ | |
for (size_t r = 0; r < height; r++) { | |
for (size_t c = 0; c < width; c++) { | |
int ln = liveneighbors(r, c, grid, height, width); | |
if ((grid[r][c] & 1) == ALIVE) { | |
if (ln == 2 || ln == 3) { | |
// rule 1 | |
SET_NEXT(grid[r][c], ALIVE); | |
} else { | |
// rule 3 | |
SET_NEXT(grid[r][c], DEAD); | |
} | |
} else { | |
if (ln == 3) { | |
// rule 2 | |
SET_NEXT(grid[r][c], ALIVE); | |
} | |
} | |
} | |
} | |
for (size_t r = 0; r < height; r++) { | |
for (size_t c = 0; c < width; c++) { | |
grid[r][c] = GET_NEXT(grid[r][c]); | |
} | |
} | |
printgrid(grid, height, width); | |
generation++; | |
usleep(gen_time); | |
} | |
// clean up the grid after we are done with it | |
for (int i = 0; i < height; i++) { | |
free(grid[i]); | |
} | |
free(grid); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 | |
40 | |
50000 | |
# | |
# # | |
## ## ## | |
# # ## ## | |
## # # ## | |
## # # ## # # | |
# # # | |
# # | |
## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment