/* NAME * life - play Conway's Game of Life * NOTES * This implementation is relatively fast compared to a more * straightforward implementation because the sum of live * neighbors is computed once initially and then only updated * when cells are changed. Thus, the runtime will be * bounded by the copying of the new sums to the current sum * count plus plus any changes to the sum (which updates * only the sums which change). * MISCELLANY * You will need to use the -extra option in conjuction with * the -wrap option if you wish to simulate moving objects that * tend to fly off in one direction. Periodic patterns may need * the -extra option to give them a little bit of extra room to * work in. * BUGS * No sanity checks are performed to make sure that any of the * options make sense. * CREDITS * The data files for the initial states supplied with this * program were lifted and converted from the excellent and free * program xlife. * AUTHOR * Copyright (c) 1997, Gary William Flake. * * Permission granted for any use according to the standard GNU * ``copyleft'' agreement provided that the author's comments are * neither modified nor removed. No warranty is given or implied. */ #include #include #include #include "misc.h" char help_string[] = "\ Simulate Conway's Game of Life with an arbitrary set of initial \ conditions. Input files need to be in the PBM file format.\ "; int steps = 1000, invert = 1, mag = 10, width = 0, height = 0; int extra = 5, wrap = 1; char *term = NULL, *infile = "data/life/p8.pbm"; OPTION options[] = { { "-width", OPT_INT, &width, "Width of the plot in pixels." }, { "-height", OPT_INT, &height, "Height of the plot in pixels." }, { "-extra", OPT_INT, &extra, "Number of extra border pixels." }, { "-wrap", OPT_SWITCH, &wrap, "Wrap around world?" }, { "-infile", OPT_STRING, &infile, "Initial configuration file" }, { "-steps", OPT_INT, &steps, "Number of time steps to simulate." }, { "-inv", OPT_SWITCH, &invert, "Invert all colors?" }, { "-mag", OPT_INT, &mag, "Magnification factor." }, { "-term", OPT_STRING, &term, "How to plot points." }, { NULL, OPT_NULL, NULL, NULL } }; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* When a change is made to any of the cell states, this function is called so that the sum of live neighbors of the changed cell is updated for all eight neighbors. */ void updatecount(int **sum, int rw, int rh, int ii, int jj, int wrap, int change) { int i, j; if(wrap) { for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) if(i != 0 || j != 0) sum[(i + ii + rh) % rh][(j + jj + rw) % rw] += change; } else { for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) if((i != 0 || j != 0) && (i + ii) >= 0 && (i + ii) < rh && (j + jj) >= 0 && (j + jj) < rw) sum[i + ii][j + jj] += change; } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ int main(int argc, char **argv) { #ifdef PLOTX11 extern int x11_force_flush; #endif extern int plot_mag; extern int plot_inverse; int w, h, rw, rh; int i, j, t, ii, jj, change; int **data, **state, **sum, **newsum, **swap; get_options(argc, argv, options, help_string); /* Read in the data file and pick the real width and height * to be the max of the requested size and the save of the * initial configurations. */ data = read_pbm_file(infile, &w, &h); rw = MAX(w + extra * 2, width); rh = MAX(h + extra * 2, height); plot_mag = mag; plot_inverse = invert; plot_init(rw, rh, 2, term); plot_set_all(0); steps = (steps < 0) ? 10e10 : steps; /* Allocate memory for the states, the sum of live cells, * and the next sum of live cells. */ state = xmalloc(sizeof(int *) * rh); sum = xmalloc(sizeof(int *) * rh); newsum = xmalloc(sizeof(int *) * rh); for(i = 0; i < rh; i++) { state[i] = xmalloc(sizeof(int) * rw); sum[i] = xmalloc(sizeof(int) * rw); newsum[i] = xmalloc(sizeof(int) * rw); for(j = 0; j < rw; j++) state[i][j] = sum[i][j] = 0; } /* Set the state to the initial configuration and set the * sum of live neighbors appropriately. */ #ifdef PLOTX11 x11_force_flush = 1; #endif for(i = 0; i < h; i++) for(j = 0; j < w; j++) { ii = (rh - h) / 2 + i; jj = (rw - w) / 2 + j; state[ii][jj] = data[i][j]; if(data[i][j]) updatecount(sum, rw, rh, ii, jj, wrap, 1); plot_point(jj, ii, data[i][j]); } #ifdef PLOTX11 x11_force_flush = 0; #endif /* For each time step... */ for(t = 0; t < steps; t++) { change = 0; /* Copy the old sums to the new sum space. */ for(i = 0; i < rh; i++) for(j = 0; j < rw; j++) newsum[i][j] = sum[i][j]; /* For every cell in the grid... */ for(i = 0; i < rh; i++) for(j = 0; j < rw; j++) { /* If a dead cell should come alive... */ if(state[i][j] == 0 && sum[i][j] == 3) { updatecount(newsum, rw, rh, i, j, wrap, 1); state[i][j] = 1; plot_point(j, i, 1); change = 1; } /* If a live cell should die... */ else if(state[i][j] == 1 && (sum[i][j] < 2 || sum[i][j] > 3)) { updatecount(newsum, rw, rh, i, j, wrap, -1); state[i][j] = 0; plot_point(j, i, 0); change = 1; } } /* Make the next sum count to be the new one. */ swap = sum; sum = newsum; newsum = swap; /* If there has been no change of state, then the X11 plot * driver will need to be forcefully flushed so that the screen * reflects the current state. */ #ifdef PLOTX11 if(!change && !x11_force_flush) { x11_force_flush = 1; plot_point(0, 0, state[0][0]); } #endif } plot_finish(); exit(0); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */