Skip to content

Instantly share code, notes, and snippets.

@willeccles
Last active December 4, 2019 17:36
Show Gist options
  • Save willeccles/facaf315e44fdd7cd69d20ec47ff59b3 to your computer and use it in GitHub Desktop.
Save willeccles/facaf315e44fdd7cd69d20ec47ff59b3 to your computer and use it in GitHub Desktop.
Conway's Game of Life in C
/*
* 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;
}
40
40
50000
#
# #
## ## ##
# # ## ##
## # # ##
## # # ## # #
# # #
# #
##
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment