/* 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);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */