Randomized Life Program
by Dave Aronson
/* randlife.c -- randomized version of the famous "life" screensaver */
/* Copyright 1997, David J. Aronson */
/** INCLUDES **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
/** OTHER DEFINES **/
/* maximum allowable width & height of world */
#define MAXWID 255
#define MAXHT 255
/* chars that represent dead/alive-ness */
#define DEAD ' '
#define ALIVE '@'
/** TYPES **/
typedef unsigned char bool;
typedef unsigned int uint;
/** SUPPORT ROUTINE PROTOTYPES **/
static void AllocMaps (char **newMap, char **oldMap, uint size);
static void Generate
(
char *newMap,
char *oldMap,
uint *birthChances,
uint *survChances,
uint xSize,
uint ySize,
bool xWrap,
bool yWrap
);
static void GiveUsage (uint *birthChances, uint *survChances);
static uint InitMaps
(
char **newMap,
char **oldMap,
uint *xSize,
uint *ySize,
int initChance,
char *fileName
);
static void ParseArgs
(
uint argc,
char *argv[],
uint *birthChances,
uint *survChances,
uint *gens,
char *fileName,
int *initChance,
bool *pause,
uint *xSize,
uint *ySize,
bool *xWrap,
bool *yWrap
);
static uint rnd100 (void);
static uint ShowGen
(
char *newMap,
uint xSize,
uint ySize,
uint gen,
uint gens,
uint totCells
);
/** MAIN ROUTINE **/
void main (uint argc, char *argv[])
{
uint alive;
uint birthChances[] = { 0, 12, 14, 17, 20, 25, 33, 50, 100 };
char fileName[256] = "";
uint gen = 0;
uint gens = 100;
int i;
int initChance = 25; /* probability of starting alive (dead if -) */
char *newMap;
char *oldMap;
bool pause = FALSE;
uint survChances[] = { 0, 10, 30, 100, 100, 100, 30, 10, 0 };
char *tmpPtr;
uint totCells;
uint xSize = 79; /* width of world */
bool xWrap = FALSE; /* is world "closed" on the x-axis? */
uint ySize = 24; /* height of world */
bool yWrap = FALSE; /* is world "closed" on the y-axis? */
/* parse args */
ParseArgs (argc, argv, birthChances, survChances, &gens, fileName,
&initChance, &pause, &xSize, &ySize, &xWrap, &yWrap);
/* seed random number generator */
srandom (time (NULL));
/* init maps and set total # of cells */
totCells = InitMaps (&newMap, &oldMap, &xSize, &ySize, initChance, fileName);
/* clear screen */
printf ("%c[H%c[J", 27, 27);
/* for each generation */
while (gen++ != gens || ! gens)
{
/* show it and get # of cells alive */
alive = ShowGen (newMap, xSize, ySize, gen, gens, totCells);
/* if all dead, STOP! */
if (0 == alive) break;
/* swap maps */
tmpPtr = newMap;
newMap = oldMap;
oldMap = tmpPtr;
/* generate new map from old */
Generate (newMap, oldMap, birthChances, survChances,
xSize, ySize, xWrap, yWrap);
/* if we're supposed to pause, AND this isn't the last generation */
if (pause && (gen != gens || ! gens))
{
/* tell user to press enter */
printf (" Press ENTER to continue: %c[K", 27);
/* look for enter... but parse other chars as commands! */
do
{
i = getchar();
switch (i)
{
/* clear */
case 'c':
memset (newMap, xSize * ySize, DEAD);
break;
/* fill */
case 'f':
memset (newMap, xSize * ySize, ALIVE);
break;
/* invert */
case 'i':
for (i = xSize * ySize - 1; i >= 0; --i)
newMap[i] = (DEAD == newMap[i]) ? ALIVE : DEAD;
break;
/* loop -- infinite # of gens, AND turn pause off */
case 'l':
gens = 0;
/* NO BREAK here; want to fall thru to p */
/* pause no more */
case 'p':
pause = FALSE;
break;
/* quit */
case 'q':
gen = gens = 1;
break;
}
} while (i != '\n');
/* end if pausing and not last generation */
}
/* end for each generation */
}
/* free all space we allocated */
free (newMap);
free (oldMap);
}
/* allocate old and new maps, of a given size */
static void AllocMaps (char **newMap, char **oldMap, uint size)
{
/* allocate current and old maps */
*newMap = malloc (size);
if (NULL == *newMap)
{
puts ("Could not allocate space for current map!");
exit (1);
}
*oldMap = malloc (size);
if (NULL == *oldMap)
{
puts ("Could not allocate space for temp map!");
free (*newMap);
exit (1);
}
}
/* generate a new generation from the old one */
static void Generate
(
char *newMap,
char *oldMap,
uint *birthChances,
uint *survChances,
uint xSize,
uint ySize,
bool xWrap,
bool yWrap
)
{
uint neighbors, x, xLeft, xRight, y, yDown, yUp;
/* for each spot in current map */
for (y = 0; y < ySize; y++) for (x = 0; x < xSize; x++)
{
/** see what x and y coords we need for the neighbors **/
/* if we're not on the left edge, left is to the left */
if (x > 0) xLeft = x - 1;
/* else if we're wrapping horizontally, it's the far right */
else if (xWrap) xLeft = xSize - 1;
/* else it's invalid */
else xLeft = MAXWID;
/* follow same logic for right, up, and down */
if (x < xSize - 1) xRight = x + 1;
else if (xWrap) xRight = 0;
else xRight = MAXWID;
if (y > 0) yUp = y - 1;
else if (yWrap) yUp = ySize - 1;
else yUp = MAXHT;
if (y < ySize - 1) yDown = y + 1;
else if (yWrap) yDown = 0;
else yDown = MAXHT;
/* now count them -- first init count to 0 */
neighbors = 0;
/* if we can count the ones on the left */
if (MAXWID != xLeft)
{
/* count the ones with valid y values */
if (MAXHT != yUp)
{
if (ALIVE == oldMap [yUp * xSize + xLeft]) ++neighbors;
}
if (ALIVE == oldMap [y * xSize + xLeft]) ++neighbors;
if (MAXHT != yDown)
{
if (ALIVE == oldMap [yDown * xSize + xLeft]) ++neighbors;
}
}
/* apply same logic to x proper (guaranteed valid) and right */
if (MAXHT != yUp)
{
if (ALIVE == oldMap [yUp * xSize + x]) ++neighbors;
}
if (MAXHT != yDown)
{
if (ALIVE == oldMap [yDown * xSize + x]) ++neighbors;
}
if (MAXWID != xRight)
{
/* count the ones with valid y values */
if (MAXHT != yUp)
{
if (ALIVE == oldMap [yUp * xSize + xRight]) ++neighbors;
}
if (ALIVE == oldMap [y * xSize + xRight]) ++neighbors;
if (MAXHT != yDown)
{
if (ALIVE == oldMap [yDown * xSize + xRight]) ++neighbors;
}
}
/* if it was alive, see if it survived, else see if it got born */
if (ALIVE == oldMap [y * xSize + x])
{
if (rnd100() <= survChances[neighbors]) newMap[y * xSize + x] = ALIVE;
else newMap[y * xSize + x] = DEAD;
}
else
{
if (rnd100() <= birthChances[neighbors]) newMap[y * xSize + x] = ALIVE;
else newMap[y * xSize + x] = DEAD;
}
/* end for each spot */
}
/* end Generate() */
}
static void GiveUsage (uint *birthChances, uint *survChances)
{
uint u;
puts ("\nRandomized Life, v1.0. Copyright 1997, Dave Aronson\n");
puts ("Usage: randlife [options...]\n");
puts ("Options:");
puts ("-bNC: set birth chance with N neighbors to C percent");
printf (" (default is");
for (u = 0; u <= 8; u++) printf (" %d", birthChances[u]);
puts (")");
puts ("-gN: set number of generations to run (default 100)");
puts ("-h: give this help");
puts ("-iC: set each spot's initial chance of life to C percent (default 25)");
puts ("-mF: read initial map from file named F.");
puts ("-p: pause for ENTER between generations");
puts ("-sNC: set survival chance with N neighbors to C percent");
printf (" (default is");
for (u = 0; u <= 8; u++) printf (" %d", survChances[u]);
puts (")");
printf ("-xS: set width to S, an integer from 0 to %d (default 75)\n", MAXWID);
printf ("-yS: set height to S, an integer from 0 to %d (default 20)\n", MAXHT);
puts ("-X: turn on x-axis wrap");
puts ("-Y: turn on y-axis wrap");
exit (1);
}
/* initialize the "current" and temp maps */
static uint InitMaps
(
char **newMap,
char **oldMap,
uint *xSize,
uint *ySize,
int initChance,
char *fileName
)
{
int c;
FILE *file;
uint i, total;
/* if we didn't get an initial map file */
if ('\0' == fileName[0])
{
total = *xSize * *ySize;
/* allocate space according to command line args */
AllocMaps (newMap, oldMap, total);
/* for each spot */
for (i = 0; i < total; i++)
{
/* if the init chance is that of LIFE, roll for that, else for death */
if (initChance > 0)
{
(*newMap) [i] = (rnd100() <= initChance) ? ALIVE : DEAD;
}
else (*newMap) [i] = (rnd100() <= initChance) ? DEAD : ALIVE;
}
}
/* else */
else
{
/* open the file to read AS TEXT */
file = fopen (fileName, "rt");
if (NULL == file)
{
printf ("Cannot open initial map file '%s'!\n", fileName);
exit (1);
}
/* read the x and y sizes */
fscanf (file, "%d %d", xSize, ySize);
total = *xSize * *ySize;
/* allocate space */
AllocMaps (newMap, oldMap, total);
/* for each char in the file */
i = 0;
do
{
c = getc (file);
/* if it's one we care about */
if (DEAD == c || ALIVE == c)
{
/* put it in the map */
(*newMap) [i++] = (char) c;
/* if out of room, fake end */
if (i == total) c = EOF;
}
} while (EOF != c);
/* close file */
fclose (file);
/* all remaining spots (if any) are dead */
if (i < total) memset (newMap + i, total - i, DEAD);
/* end if we got a file */
}
/* return total # of cells */
return total;
/* end InitMaps() */
}
static void ParseArgs
(
uint argc,
char *argv[],
uint *birthChances,
uint *survChances,
uint *gens,
char *fileName,
int *initChance,
bool *pause,
uint *xSize,
uint *ySize,
bool *xWrap,
bool *yWrap
)
{
uint i, what, which;
/* for each arg */
for (i = 1; i < argc; i++)
{
/* birth chance with N neighbors */
if (! strncmp (argv[i], "-b", 2))
{
which = argv[i][2] - '0';
if (0 > which || 8 < which)
{
puts ("Number of neighbors (N in -bNC) must be 0 to 8!");
GiveUsage (birthChances, survChances);
}
what = atoi (argv[i] + 3);
if (0 > what || 100 < what)
{
puts ("Birth chance (C in -bNC) must be from 0 to 100!");
GiveUsage (birthChances, survChances);
}
birthChances[which] = what;
}
/* # generations -- no invalid values possible. B-> */
else if (! strncmp (argv[i], "-g", 2)) *gens = atoi (argv[i] + 2);
/* help */
else if (! strcmp ("-h", argv[i])) GiveUsage (birthChances, survChances);
/* initial chance of life */
else if (! strncmp (argv[i], "-i", 2))
{
*initChance = atoi (argv[i] + 2);
if (100 < *initChance || 0 > *initChance)
{
puts ("Initial %% chance of life must be between 0 and 100!");
GiveUsage (birthChances, survChances);
}
}
/* initial map file */
else if (! strncmp (argv[i], "-m", 2)) strcpy (fileName, argv[i] + 2);
/* pause */
else if (! strcmp (argv[i], "-p")) *pause = TRUE;
/* survival chance with N neighbors */
else if (! strncmp (argv[i], "-s", 2))
{
which = argv[i][2] - '0';
if (0 > which || 8 < which)
{
puts ("Number of neighbors (N in -sNC) must be 0 to 8!");
GiveUsage (birthChances, survChances);
}
what = atoi (argv[i] + 3);
if (0 > what || 100 < what)
{
puts ("Survival chance (C in -sNC) must be from 0 to 100!");
GiveUsage (birthChances, survChances);
}
survChances[which] = what;
}
/* x size */
else if (! strncmp (argv[i], "-x", 2))
{
*xSize = atoi (argv[i] + 2);
if (*xSize < 0 || *xSize > MAXWID)
{
printf ("XSIZE must be between 1 and %d!\n", MAXWID);
GiveUsage (birthChances, survChances);
}
}
/* y size */
else if (! strncmp (argv[i], "-y", 2))
{
*ySize = atoi (argv[i] + 2);
if (*ySize < 0 || *ySize > MAXHT)
{
printf ("YSIZE must be between 1 and %d!\n", MAXHT);
GiveUsage (birthChances, survChances);
}
}
/* x wrap-toggle */
else if (! strcmp (argv[i], "-X")) *xWrap = TRUE;
/* y wrap-toggle */
else if (! strcmp (argv[i], "-Y")) *yWrap = TRUE;
/* unrecognized */
else
{
printf ("Unrecognized option: '%s'\n", argv[i]);
GiveUsage (birthChances, survChances);
}
/* end for each arg */
}
/* end ParseArgs() */
}
static uint rnd100 (void)
{
return (uint) (random() % 100) + 1;
}
static uint ShowGen
(
char *newMap,
uint xSize,
uint ySize,
uint gen,
uint gens,
uint totCells
)
{
uint alive, i, x, y;
/* go to top of screen */
printf ("%c[H", 27);
/* reset alive counter */
alive = 0;
/* reset current index */
i = 0;
/* for each row in current map */
for (y = 0; y < ySize; y++)
{
/* for each spot in current row */
for (x = 0; x < xSize; x++, i++)
{
/* display it */
putchar (newMap [i]);
/* if alive, count it */
if (ALIVE == newMap [i]) ++alive;
}
/* end row */
puts ("");
}
/* report on conditions */
printf ("Gen %u/%u (%d/%d = %.1f%% alive)%c[K",
gen, gens, alive, totCells, 100.0 * alive / totCells, 27);
return alive;
}
/* end randlife.c */