/* NAME * hp - simulate the hodgepodge machine * NOTES * None. * RULES * If each cell can be in one of N states (labelled 0 to n - 1), * then cells in state 0 are ``healthy,'' cells in state n - 1 * are ``ill,'' and all other cells are ``infected.'' * * Within a cell's neighborhood, let Nill, Ninf, and S, denote * the number of ill cells, the number of infected cells, and the * sum of the states of all neighbors plus this cell's state. * The next state is determined by the three rules: * * Healthy: floor( Ninf / k1 ) + floor( Nill / k2 ) * * Infected: floor( S / ( Ninf + 1 ) ) + g * * Ill: magically becomes healthy, thus 0. * * Where k1, k2, and g are the parameters specified by the * command line options. * MISCELLANY * If you move from (to) an 8 cell neighborhood to (from) a 4 cell * neighborhood try dividing (multiplying) k1 and k2 by 2 to * produce a similar time evolution that occurred with the previous * neighborhood size. * * For some reason, 4 cell neighborhoods seem to produce patterns * with more spirals. * * This simulation can be frustratingly slow at times, so you * may wish to use the -freq option to cut down on the overhead * of plotting the states. * BUGS * No sanity checks are performed to make sure that any of the * options make sense. * 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" #define floor(x) ((int)(x)) int width = 50, height = 50, states = 100, wrap = 1, mag = 1; int seed = 0, invert = 0, steps = 1000, freq = 1, diag = 1; double k1 = 2, k2 = 3, g = 34; char *term = NULL; char help_string[] = "\ The time evolution of the hodgepodge machine is simulated and plotted \ according to the specified parameters. The neighborhood of a cell can \ optionally include or not include diagonal cells in a 3x3 area; Moreover, \ the neighborhood can also wrap around the edges so that the grid is \ topologically toroidal. With a proper choice of parameters, this system \ resembles the Belousov-Zhabotinsky reaction which forms self-perpetuating \ spirals in a lattice. See the RULES section of the manual pages \ or the source code for an explanation of how the cells change over time.\ "; OPTION options[] = { { "-width", OPT_INT, &width, "Width of the plot in pixels." }, { "-height", OPT_INT, &height, "Height of the plot in pixels." }, { "-states", OPT_INT, &states, "Number of cell states." }, { "-steps", OPT_INT, &steps, "Number of simulated steps." }, { "-seed", OPT_INT, &seed, "Random seed for initial state." }, { "-diag", OPT_SWITCH, &diag, "Diagonal cells are neighbors?" }, { "-wrap", OPT_SWITCH, &wrap, "Use a wrap-around space?" }, { "-g", OPT_DOUBLE, &g, "Infection progression rate." }, { "-k1", OPT_DOUBLE, &k1, "First weighting parameter." }, { "-k2", OPT_DOUBLE, &k2, "Second weighting parameter." }, { "-freq", OPT_INT, &freq, "Plot frequency." }, { "-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 } }; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void update_cell(int **oldstate, int **newstate, int x, int y) { int nx, ny, i, j, numinf, numill, sum; numinf = numill = sum = 0; sum = oldstate[x][y]; /* For every cell in the 3x3 neighborhood. */ for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) { /* Skip the current cell. */ if(i == 0 && j == 0) continue; /* If we are only looking at 4 neighbors, then skip diagonals. */ if(!diag && fabs(i) + fabs(j) == 2) continue; /* Get the proper indices of the neighbor. */ nx = x + i; ny = y + j; if(!wrap) { if(nx < 0 || nx > width -1 || ny < 0 || ny > height - 1) continue; } else { nx = (nx < 0) ? width - 1 : (nx > width - 1) ? 0 : nx; ny = (ny < 0) ? height - 1 : (ny > height - 1) ? 0 : ny; } /* Add to the sum of the states. */ sum += oldstate[nx][ny]; /* Count the number of ill and infected neighbors. */ if(oldstate[nx][ny] == states - 1) numill++; else if(oldstate[nx][ny] > 0) numinf++; } /* Healthy cell: */ if(oldstate[x][y] == 0) newstate[x][y] = floor(numinf / k1) + floor(numill / k2); /* Infected cell: */ else if(oldstate[x][y] < states - 1) newstate[x][y] = floor(sum / (numinf + 1)) + g; /* Ill cell: */ else newstate[x][y] = 0; /* Bound next state to sane limit. */ if(newstate[x][y] > states - 1) newstate[x][y] = states - 1; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ int main(int argc, char **argv) { extern int plot_mag; extern int plot_inverse; int t, i, j; int **oldstate, **newstate, **swap; get_options(argc, argv, options, help_string); plot_mag = mag; plot_inverse = invert; plot_init(width, height, states, term); plot_set_all(0); srandom(seed); /* Allocate and initial memory for cell states. */ oldstate = xmalloc(sizeof(int *) * width); newstate = xmalloc(sizeof(int *) * width); for(i = 0; i < width; i++) { oldstate[i] = xmalloc(sizeof(int) * height); newstate[i] = xmalloc(sizeof(int) * height); for(j = 0; j < height; j++) oldstate[i][j] = (int) random_range(0, states - 1); } /* For each time step... */ for(t = 0; t < steps; t++) { /* For every cell ... */ for(j = 0; j < height; j++) for(i = 0; i < width; i++) { /* Update the cell state. */ update_cell(oldstate, newstate, i, j); /* Update display, if appropriate. */ if(t % freq == 0) plot_point(i, j, oldstate[i][j]); } /* Make the next state equal to the new state. */ swap = oldstate; oldstate = newstate; newstate = swap; } plot_finish(); exit(0); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */