25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Ads   Donate   Humor

Life.cpp: The Game of Life,
A cellular automaton.

This program plays the Game of Life, a cellular automaton originated by John Conway. It is described in Chaos Under Control, by Peak and Frame and in Complexity, by Waldrop, in their respective chapters on artificial life, and at Wikipedia.

An initial grid is set up in which some squares ("cells") are "on" and some are "off". The program then cycles through successive generations. Whether a cell remains on ("survives"), or turns off ("dies"), or turns on ("is born") depends on the number of neighbors it has. Aside from whatever value it has in the study of cellular automata, the shifting patterns are fun to watch.

The program was developed with Borland C++ 4.0 for MSDOS, and uses Borland BGI graphics.

Besides the standard Life game rules, you can create custom rules of your own and see how they behave as the game cycles.

If you download the Borland OWL Windows version of the program (WLife2d.cpp), this MSDOS program is also included in that zip file, along with project notes and an assortment of data files.

In this DOS version, the size of a cell is always 1 pixel, which is quite small. The Windows version allows enlarging the cell size to make viewing easier. However, only the DOS version has the 1d (1 dimensional) option, which can create some interesting designs like this one:

Screenshot of Life.cpp 1d option. Generations grow down from the top scan line.

Life.cpp

/*	life.cpp              			5-22-97
	Copyright (C)1995-1997 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License (GPL)
	Version 2 as published by the Free Software Foundation.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Game of Life, a cellular automaton, originated by John Conway.
This is only partially converted to C++. Some of its methods are still C.

The Windows version WLIFE2D.CPP has every feature from here except grow1d().

------
TO DO:

change FILE to streams
get rid of strtok
change printf to cout or constreams
change char* to strings
change gets() to getline()

maybe break 1d into a separate smaller program, life1d.

give the sets in life.rul an id#, in place of the C:, for reference.
will have to read through file once at pgm start for count.

------
Notes:
--all notes and all non-version-specific to do are now in complex.doc
--grow() routines are modified from avgpic.c (avgpic is now a function in SDib)
--A thorough search for the DeSmet C predecessor to this program turned up nothing.
  If there ever was one, it is lost. Restoring this version to DeSmet might not be
  too difficult.

*/
#include <dos.h>
#include <graphics.h>
#pragma hdrstop

#include "c:\bcs\my.h"
#include "c:\bcs\mylib.cpp"

/////////////////////////////////////////////////////////////////////////////
// global variables
BOOL beepon = FALSE;           	// whether to use sound
long population;              	// count of "on" cells
int lowx, highx, lowy, highy; 	// boundaries of "calculate" square
int colortally[16];           	// holds tally for each of 16 colors
int s[10], b[10];             	// if neighborcount == any value in array,
								// point either (s)urvives or is (b)orn

//---------------------------------------------------------------------------
// survive() determines whether a point survives, based on its rule
BOOL survive(int neighborcount)
{
for(int i = 0 ; s[i] != -1 ; i++)
	if(neighborcount == s[i])
		return(TRUE);
return(FALSE);
}
//---------------------------------------------------------------------------
// born() determines whether a point is born, based on its rule
BOOL born(int neighborcount)
{
for(int i = 0 ; b[i] != -1 ; i++)
	if(neighborcount == b[i])
		return(TRUE);
return(FALSE);
}
//---------------------------------------------------------------------------
// curblink() blinks the cursor by toggling the pixel
void curblink()
{
int color;
static int oldcolor;

switch(color = getpixel(getx(),gety()))
{
	case WHITE:                            // only possible if set previously
		putpixel(getx(),gety(),oldcolor);  // restore original color
		break;
	default:
		putpixel(getx(),gety(),WHITE);     // show "cursor"
		break;
}
oldcolor = color;
}
//---------------------------------------------------------------------------
// editscreen()  manually modify or set up display screen
// modified from main() from editpic.c
// put bar() back in, to clear or fill a screen area.
void editscreen()
{
BOOL draw = FALSE;				// MODE: CURSOR DRAWS OR JUST MOVES
int newlowx = lowx;         	// coordinates of a "defined" area that is
int newhighx = highx;         	// used for fills
int newlowy = lowy;           	// and optionally for defining "calc" area.
int newhighy = highy;

moveto(getmaxx()/2,getmaxy()/2);
curblink();                     // start with cursor on
while(1)                      	// DO FOREVER. EXIT IS IN LOOP
{
	int ch = ci();      		// wait for user input
	curblink();              	// turn cursor off during any activity
	switch(ch)
	{
						// UNSHIFTED KEYPAD NUMBERS USED AS ARROW KEYS
		case UP:        // shouldn't matter whether numlock is on/off
		case('8'):                      // 8 up
		case 18:						// ^R
			if(draw)
				linerel(0,-1);
			else
				moverel(0,-1);
			break;
		case DOWN:
		case('2'):                      // 2 down
		case 3:                       	// ^C
			if(draw)
				linerel(0,1);
			else
				moverel(0,1);
			break;
		case RIGHT:
		case('6'):                      // 6 right
		case 6:                         // ^F
			if(draw)
				linerel(1,0);
			else
				moverel(1,0);
			break;
		case LEFT:
		case('4'):                      // 4 left
		case 1:                       	// ^A
			if(draw)
				linerel(-1,0);
			else
				moverel(-1,0);
			break;
		case PGDN:
		case('3'):                      // 3 down-right
			if(draw)
				linerel(1,1);
			else
				moverel(1,1);
			break;
		case HOME:
		case('7'):                      // 7 up-left
			if(draw)
				linerel(-1,-1);
			else
				moverel(-1,-1);
			break;
		case PGUP:
		case('9'):                      // 9 up-right
			if(draw)
				linerel(1,-1);
			else
				moverel(1,-1);
			break;
		case END:
		case('1'):                      // 1 down-left
			if(draw)
				linerel(-1,1);
			else
				moverel(-1,1);
			break;
		case FIVE:
		case('5'):                              // GO TO CENTER OF SCREEN
			moveto(getmaxx()/2,getmaxy()/2);
			break;
		case INS:
		case '0':                     			// TOGGLE DRAW/MOVE MODE
			draw = !draw;
			break;
		case DEL:
		case '.':                          		// TOGGLE BETWEEN GREEN AND BLACK
			if(getcolor() == LIGHTGREEN)
			{
				setcolor(BLACK);
				setfillstyle(SOLID_FILL,BLACK);
			}
			else
			{
				setcolor(LIGHTGREEN);
				setfillstyle(SOLID_FILL,LIGHTGREEN);
			}
			break;
		case '*':						// CLEAR SCREEN
			clearviewport();
			break;

		case 't':                		// set top left of defined area
			newlowx = getx();
			newlowy = gety();
			break;
		case 'b':                		// set bottom right of defined area
			newhighx = getx();
			newhighy = gety();
			break;
		case 'c':                    	// make "defined" area the calc area
			if(newlowx < newhighx)    	// validate before changing
			{
				lowx = newlowx;
				highx = newhighx;
			}
			if(newlowy < newhighy)
			{
				lowy = newlowy;
				highy = newhighy;
			}
			break;
		case 'f':                     	// fill selected area with draw color
			bar(newlowx,newlowy,newhighx,newhighy);
			break;
		case 'q':  		              	// quit
			return;     	          	// leaves cursor off
		case 's':                		// save screen to .pic
			picsave("life.pic");
			break;
		case 'l':                     	// load file to start over
			picload("life.pic");
			break;
		default:
			break;
	}                        // END SWITCH
	curblink();              // cursor back on while idle
}                             // END WHILE(1)
}                             // END editscreen()
//---------------------------------------------------------------------------
// grow1d()  calc. successive 1-dimensional generations until stopped
// instead of counting neighbors, must look for specific pattern in
// surrounding neighborhood. (i.e. it is NOT outer-totalistic)
// successive generations appear on successive lines going down screen.
// At screen bottom, next generation starts over at top.
BOOL grow1d()
{
int screenline = 1;     // screen display line being computed (0-479)
int lineabove = 0;      // screen display line being referred to
int maxx = getmaxx()+1; // now it's the NUMBER of points in a screen line (640)
int maxy = getmaxy()+1; // now it's the NUMBER of lines in a screen (480)

// always use printable digits as the chars, NOT, for example char(1) (ctl-A)
// because char(0) is a null.
string s[14];
s[0] = "11111";
s[1] = "11110";
s[2] = "11100";
s[3] = "11010";
s[4] = "11000";
s[5] = "10111";
s[6] = "10110";
s[7] = "10010";
s[8] = "01111";
s[9] = "01110";
s[10] = "00111";
s[11] = "00100";
s[12] = "00011";
s[13] = "00000";

while(1)
{
	for(int x = 0 ; x < maxx ; x++)
	{
		string neighborhood;

		int testx = wrap(x - 2,0,getmaxx());		   					// point 2 to left
		neighborhood += ((getpixel(testx,lineabove) == BLACK) ? "0" : "1");

		//---inside here is for 3-cell neighborhood
		testx = wrap(x - 1,0,getmaxx());   								// point to left
		neighborhood += ((getpixel(testx,lineabove) == BLACK) ? "0" : "1");
		neighborhood += ((getpixel(x,lineabove) == BLACK) ? "0" : "1"); // (the point itself)

		testx = wrap(x + 1,0,getmaxx());					   			// point to right
		neighborhood += ((getpixel(testx,lineabove) == BLACK) ? "0" : "1");
		//---
		testx = wrap(x + 2,0,getmaxx()); 								// point 2 to right
		neighborhood += ((getpixel(testx,lineabove) == BLACK) ? "0" : "1");

		int color = BLACK;
		for(int i = 0 ; i < 14 ; i++)
			if(neighborhood == s[i])
			{
				color = LIGHTGREEN;
				break;
			}
		putpixel(x,screenline,color);
	}
	// wrap the lines independently of each other (one hits bottom before the other)
	if(++screenline == maxy)
		screenline = 0;
	if(++lineabove == maxy)
		lineabove = 0;

	if(kbhit())                             // ESC exits
		switch(tolower(getch()))
		{
			case 27:
				return(FALSE);
			case 's':
				picsave("life.pic");
				break;
			case 'l':                     	// load file to start over
				picload("life.pic");
				break;
			case 'b': beepon = !beepon; nosound(); break;
		}
}                             // end while(1)
}                   		// end grow1d()
//-------------------------------------------------------------------
// grow2d() compute and display the next generation (2-dimensional)
// returns TRUE on "success", FALSE on error or if user aborted
BOOL grow2d()
{
BOOL keepgoing = TRUE;		// value returned (whether to come right back)
int i, j, neighborcount, color;
int maxx;
int screenline;          	// screen display line being worked on (0-479)
int linesize;       		// number of chars in a buffer line
char *ptr[3];       		// pointers to buffers containing line data
char *temp;              	// for pointer swapping

maxx = getmaxx()+1; 		// now it's the NUMBER of points in a screen line (640)
linesize = maxx+2;  		// 1 pixel beyond each edge

for(i = 0 ; i < 3 ; i++) 	// allocate space for the line buffers
	if((ptr[i] = (char *)malloc(linesize)) == NULL)
	{
		for(j = 0 ; j < i ; j++) // free any already-allocated buffers
			if(ptr[j])
				free(ptr[j]);
		setcolor(WHITE);    	// still won't show if area is already white
		outtextxy(0,0,"Out of memory.");
		return(FALSE);
	}
setmem(ptr[0],linesize,0);    	// zero out imaginary line above screen top
setmem(ptr[1],linesize,0);    	// and zero out the others to avoid garbage
setmem(ptr[2],linesize,0);    	// 1st & last chars MUST be zeroed at minimum

									// read actual screen lines 0 and 1
									// into arrays 1 and 2
for(i = lowx ; i <= highx ; i++)   	// read points 0-639 (at most)
{
	ptr[1][i+1] = getpixel(i,lowy);  	// fill array elements 1-640 (max)
	ptr[2][i+1] = getpixel(i,lowy+1);  	// out of 0-641 available
}                                     	// leaving offscreen endpoints alone

for(screenline = lowy ; screenline <= highy ; screenline++)
{
	// calculate values for array line 1 directly to screen line 0
	for(i = lowx+1 ; i <= highx+1 ; i++)   // for i = 1 to 640
	{
		neighborcount = 0;
		if(ptr[0][i-1]) neighborcount++;   // above left
		if(ptr[0][i]) neighborcount++;     // above
		if(ptr[0][i+1]) neighborcount++;   // above right 
		if(ptr[1][i-1]) neighborcount++;   // point to left
//        (ptr[1][i])  // just for reference, this is the point itself
		if(ptr[1][i+1]) neighborcount++;   // point to right
		if(ptr[2][i-1]) neighborcount++;   // below left 
		if(ptr[2][i]) neighborcount++;     // point below
		if(ptr[2][i+1]) neighborcount++;   // below right 
								   

		color = BLACK;
		if(ptr[1][i])                 // if point already alive,
		{                             // see if it remains alive 
			if(survive(neighborcount))
				color = YELLOW;
			else
				population--;
		}
		else                          // if cell is dead,
		{                             // see whether it is born 
			if(born(neighborcount))
			{
				color = YELLOW;
				population++;                 
			}
		}
		putpixel(i-1,screenline,color);
		if(beepon)
			sound((int)((population % 15000)+50));
	}
	temp = ptr[0];           	// save top line, we're through with it
	ptr[0] = ptr[1];        	// line scrolls up, become top line
	ptr[1] = ptr[2];        	// line becomes the one being averaged
	ptr[2] = temp;          	// we're about to overwrite data in this
								// because we need a new bottom line

	if(screenline < (highy-1))             	// if not last pass,
		for(j = lowx ; j <= highx ; j++)   	// read points 0-639
			ptr[2][j+1] = getpixel(j,screenline+2);
	else                               		// for last pass, must create
		setmem(ptr[2],linesize,0);    		// imaginary line below screen bottom
}                             				// end for(process all screen lines)
if(kbhit())           // only allow these actions between whole screenfuls
	switch(tolower(getch()))
	{
		case 27:                           // ESC exits 
			keepgoing = FALSE;
			break;
		case 's':                          // save .pic file 
			picsave("life.pic");
			break;
		case 'b': beepon = !beepon; nosound(); break;
		case 'p': getch(); break;          // temporary pause
		case 'e': editscreen(); break;
	}
for(i = 0 ; i < 3 ; i++)
	free(ptr[i]);
return(keepgoing);
}                        // end grow2d()
//---------------------------------------------------------------------------
// determine commonest color among neighbors
// starts search at a random point within colortally[] and loops around
// through beginning if necessary.  Thus, if two or
// more colors tie for the mode, the choice of "winner" will be random,
// not determined by placement.
int calcmode()
{
int mode;
int highcount = 0;
int start = random(16);    // 0-15
int i = start;
do
{
	if(colortally[i] > highcount)
	{
		highcount = colortally[i];
		mode = i;
	}
	if(++i == 16)
		i = 0;
}
while(i != start);
return(mode);
}
//-------------------------------------------------------------------
// 2-dimensional using color
// returns TRUE on "success", FALSE on error or if user aborted
// color game rules are always hard programmed.  they can't be saved,
// loaded, or edited.  If you want multiple rule SETS, (so that a point
// can be calculated differently depending on its current state),
// add additional functions and select one.
// Don't clutter this one up any further with choices.
BOOL colorgrow()
{
int i, j, k, neighborcount, neighborsum, color, setcount;
int colorset[16];
int tc;                     // temp, holds color of points tested
int screenline;          	// screen display line being worked on (0-479)
int linesize;       		// number of chars in a buffer line
char *ptr[3];       		// pointers to buffers containing line data
char *temp;              	// for pointer swapping
BOOL keepgoing = TRUE;   	// value returned (whether to come right back)

int maxx = getmaxx()+1; 	// now it's the NUMBER of points in a screen line (640)
linesize = maxx+2;  		// 1 pixel beyond each edge

for(i = 0 ; i < 3 ; i++) 	// allocate space for the line buffers
	if((ptr[i] = (char *)malloc(linesize)) == NULL)
	{
		for(j = 0 ; j < i ; j++) // free any already-allocated buffers
			if(ptr[j])
				free(ptr[j]);
		setcolor(WHITE);    	// still won't show if area is already white
		outtextxy(0,0,"Out of memory.");
		return(FALSE);
	}
setmem(ptr[0],linesize,0);    	// zero out imaginary line above screen top
setmem(ptr[1],linesize,0);    	// and zero out the others to avoid garbage
setmem(ptr[2],linesize,0);    	// 1st & last chars MUST be zeroed at minimum

									// read actual screen lines 0 and 1
									// into arrays 1 and 2
for(i = lowx ; i <= highx ; i++)   	// read points 0-639 (at most)
{
	ptr[1][i+1] = getpixel(i,lowy);  	// fill array elements 1-640 (max)
	ptr[2][i+1] = getpixel(i,lowy+1);  	// out of 0-641 available
}                                     	// leaving offscreen endpoints alone

for(screenline = lowy ; screenline <= highy ; screenline++)
{
	// calculate values for array line 1 directly to screen line 0
	for(i = lowx+1 ; i <= highx+1 ; i++)   // for i = 1 to 640 
	{
		// compute all the various statistics we might use for rules
		for(j = 0 ; j < 16 ; j++)          // zero out tally values
			colortally[j] = 0;
		neighborsum = neighborcount = 0;
		tc = (int)(ptr[0][i-1]); 
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		tc = (int)(ptr[0][i]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		tc = (int)(ptr[0][i+1]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		tc = (int)(ptr[1][i-1]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		//  (ptr[1][i])    // just for reference, this is the point itself
		tc = (int)(ptr[1][i+1]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		tc = (int)(ptr[2][i-1]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }
		tc = (int)(ptr[2][i]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }    
		tc = (int)(ptr[2][i+1]);
		colortally[tc]++;
		if(tc) { neighborcount++; neighborsum += tc; }

		// every color can have its own rule set
		switch(color = (int)(ptr[1][i]))       // point's current color         
		{
			case 0:         // if cell is dead, see whether it is born 
					// point is born as the color of which there are 3 neighbors
					// if 3 of more than one color, the one to use is chosen at random.
				for(k = 0 ; k < 16 ; k++)
					colorset[k] = 0;
				setcount = 0;
				for(k = 1 ; k < 16 ; k++)
					if((colortally[k] == 3))
						colorset[setcount++] = k;
				if(setcount)
				{
					color = colorset[random(setcount)];
					population++;                 
				}
				break;
			case 1:				
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
			case 11:
			case 12:
			case 13:
			case 14:
			case 15:
//				if(neighborcount == 2 || neighborcount == 3)
				if((colortally[color] == 2) || (colortally[color] == 3))
				{
					color = color;
				}
				else
				{
					color = 0;			
				}
				if(color == 0) 
					population--;
				break;
		}								// end switch(color)
		putpixel(i-1,screenline,color);    
		if(population < 0) population = 0;
		if(beepon)
			sound((int)((population % 15000)+50));
	}
	temp = ptr[0];           // save top line, we're through with it
	ptr[0] = ptr[1];        // line scrolls up, become top line
	ptr[1] = ptr[2];        // line becomes the one being averaged 
	ptr[2] = temp;          // we're about to overwrite data in this 
							// because we need a new bottom line 

	if(screenline < (highy-1))             	// if not last pass,
		for(j = lowx ; j <= highx ; j++)   	// read points 0-639
			ptr[2][j+1] = getpixel(j,screenline+2);
	else                               		// for last pass, must create
		setmem(ptr[2],linesize,0);    		// imaginary line below screen bottom
}                             				// end for(process all screen lines)
if(kbhit())           // only allow these actions between whole screenfuls
	switch(tolower(getch()))
	{
		case 27: keepgoing = FALSE; break;     // ESC exits
		case 's': picsave("life.pic"); break;  // save .pic file
		case 'b': beepon = !beepon; nosound(); break;
		case 'p': getch(); break;               // temporary pause 
		case 'e': editscreen(); break;
	}
for(i = 0 ; i < 3 ; i++)
	free(ptr[i]);
return(keepgoing);
}                        // end colorgrow()
//---------------------------------------------------------------------------
// randscreen() plots random dots to start a game 
void randscreen(
int dimension,      // one or two-dimensional
long number,        // number of points to plot
int vertsize,       // space in which to squeeze random points
int horisize,       // space in which to squeeze random points
int gametype)       // determines whether to randomize colors
{
int x, y;
long i;

int xadj = (getmaxx()/2) - (horisize/2);
int yadj = (getmaxy()/2) - (vertsize/2);
switch(dimension)
{
	case 1:
		for(i = 0L ; i < number ; i++)               // random top line
		{
			x = random(horisize)+xadj;
			if(!getpixel(x,0))
				population++;
			putpixel(x,0,LIGHTGREEN);
		}
		break;

	case 2:
		for(i = 0L ; i < number ; i++)               // random whole screen
		{
			x = random(horisize)+xadj;
			y = random(vertsize)+yadj;
			if(!getpixel(x,y))
				population++;
			putpixel(x,y,(gametype == 5)? random(15)+1 : LIGHTGREEN);
		}
		break;
}
}                   // end randscreen()
//---------------------------------------------------------------------------
// randomrules() assign survive/birth rules randomly
void randomrules()
{
int k, m;
BOOL found;

// Fill from 1 to 9 slots in s[] with values from 0-8 without repeating any value.
									// first fill in s[]
int j = random(9) + 1;      		// from 1 to 9 blanks filled in
for(int i = 0 ; i < j ; i++)
{
	do
	{
		k = random(9);      		// a neighborcount value 0-8
		found = FALSE;
		for(m = 0 ; m < i ; m++) 	// already assigned?
			if(s[m] == k)
			{
				found = TRUE;
				break;
			}
	}
	while(found);
	s[i] = k;           			// put value in its slot
}
s[i] = -1;                         	// mark array end
j = random(9) + 1;            		// next fill in b[]
for(i = 0 ; i < j ; i++)
{
	do
	{
		k = random(9);      		// a neighborcount value 0-8
		found = FALSE;
		for(m = 0 ; m < i ; m++)	// already assigned?
			if(b[m] == k)
			{
				found = TRUE;
				break;
			}
	}
	while(found);
	b[i] = k;           			// put value in its slot
}
b[i] = -1;                         	// mark array end
}                 					// end randomrules()
//---------------------------------------------------------------------------
// browserules() browse and load rule set from file life.rul
// assumes display is in text mode
// use "skip" to bring up screen at same location as it was left last time.
void browserules()
{
int i;
char survive[80], born[80], comment[128], *temp;

ifstream infile("life.rul");
if(!infile)
	aborts("File LIFE.RUL not found.");

while(1)                 // only way to exit is to choose a rule set
{
	if(infile.fail())      					// used up file
	{
		infile.close();
		infile.open("life.rul");	        // restart from beginning
		continue;                          	// restart loop
	}
	infile.getline(survive,sizeof(survive));
	infile.getline(born,sizeof(born));
	infile.getline(comment,sizeof(comment));
	if(infile.fail())      					// premature end of file
		aborts("File LIFE.RUL corrupt.  Repair and rerun.");
	infile >> ws;

	clrscr();
	cout << survive << endl << born << endl << comment << endl;	// display rule set
	cout << "\n<CR> to accept, any other key for next: ";
	if(getch() == 13)                  	// load the rule set
	{
		i = 0;
		strtok(survive," ");          	// tokenize and ignore text "Survive:"
		while(temp = strtok(NULL," "))  // load s[] array
			s[i++] = atoi(temp);
		s[i] = -1;                      // mark array end
		strtok(born," ");             	// tokenize and ignore text "Born:"
		i = 0;
		while(temp = strtok(NULL," "))  // load b[] array
			b[i++] = atoi(temp);
		b[i] = -1;                      // mark array end
		break;
	}
}                             // end while(1)
}                        // end browserules()
//---------------------------------------------------------------------------
// editrules()  show current rule set and allow entering a different one
// assumes already in text mode upon entry
void editrules()
{
int i;
char buf[80], *temp;

clrscr();
printf("Current Rules:\n\n");
printf("Survive: ");
for(i = 0 ; s[i] != -1 ; i++)
	printf(" %d",s[i]);
printf("\nBorn: ");
for(i = 0 ; b[i] != -1 ; i++)
	printf(" %d",b[i]);
printf("\n\nEnter new SURVIVAL counts (Ex: # # #) (or CR for no change):\n");
gets(buf);
if(strlen(buf))
{
	i = 0;
	s[i++] = atoi(strtok(buf," "));         		// tokenize & load s[] array
	while((temp = strtok(NULL," ")) && (i < 9))     // prevent s[i] overrun
		s[i++] = atoi(temp);
	s[i] = -1;                                   	// mark array end
}
printf("\nEnter new BORN counts (Ex: # # #) (or CR for no change):\n");
gets(buf);
if(strlen(buf))
{
	i = 0;
	b[i++] = atoi(strtok(buf," "));         		// tokenize
	while((temp = strtok(NULL," ")) && (i < 9))     // load b[] array
		b[i++] = atoi(temp);
	b[i] = -1;                                   	// mark array end
}
}                        // end editrules()
//---------------------------------------------------------------------------
//   main
int main()
{
randomize();
string::set_case_sensitive(0);
string::set_paranoid_check(1);

int i;
int dimension;                     // 1 or 2
int setup;                         // setup option chosen
int gametype = 0;                  // how rules are assigned
BOOL edited = FALSE;			   // whether user has modified rule set
long startpoints = 0L;             // number of initial random points 
int x0, y0;                        // screen origin 
int vertsize, horisize;            // size of square holding random points
int maxx, maxy;                    // maximum screen dimensions 
char filename[80], buf[128], ch;
FILE *infile = NULL;
ofstream outfile;

s[9] = b[9] = -1;        // maximum end-of-array markers
s[0] = b[0] = -1;        // initially marks as "none assigned"
do
{
	printf("\n1-Dimensional or 2-Dimensional? (1 or 2): ");
	gets(buf);
	dimension = atoi(buf);
}
while((dimension != 1) && (dimension != 2));
if(dimension == 2)
{
	do
	{
		printf("\nGame Type:\n");
		puts("\n1 = Standard Life (survive=2,3)(born=3)");
		puts("2 = Randomly assign survival and birth rules");
		puts("3 = Manually assign rules");
		puts("4 = Browse and load rule set from file");
		puts("5 = Color game");
		printf("\nEnter game type: ");
		gets(buf);
		gametype = atoi(buf);
	}
	while((gametype < 1) || (gametype > 5));
	switch(gametype)         		// these assignments only need to be done once
	{
		case 1:                  	// standard Life
			s[0] = 2; s[1] = 3; s[2] = -1;
			b[0] = 3; b[1] = -1;
			break;
		case 3:                  	// manually assign rules
			editrules();   			// routine is for assignment OR changing (later)
			break;
		case 4:                  	// browse and load from file
			browserules();
			break;
	}
}              // end if(dimension == 2)
do
{
	puts("\nSetup Options:\n");
	puts("0 - Exit");
	puts("1 - Load File");
	puts("2 - Random Screen");
	puts("3 - Manual Setup\n");
	printf("Enter choice: ");
	gets(buf);
	setup = atoi(buf);
	printf("\n\n");
}
while((setup < 0) || (setup > 3));
switch(setup)
{
	case 0: return(0);
	case 1:                       // load from file (make sure it's there)
		do
		{
			printf("\nFull file name to load, or <CR> to quit: ");
			gets(filename);
			if(!strlen(filename))
				return(0);
			infile = fopen(filename,"rb");
		}
		while(!infile);
		fclose(infile);
		break;
	case 2:                       // random screen
		do
		{
			puts("This option plots a number of random points");
			puts("into a square at the screen center");
			puts("that is X dots high by Y dots wide.");
			puts("For FULL screen, enter 0 (zero) for both X and Y.");
			puts("For example:  1000,0,0");
			puts("Please enter, separated by commas:");
			printf("\nNumber of points, width, height: ");
			gets(buf);
			if(!strlen(buf))
				return(0);
			i = sscanf(buf,"%ld,%d,%d",&startpoints,&horisize,&vertsize);
		}
		while(i != 3);
		break;
	case 3:                       // manual setup
		break;
}
puts("\nWhile running:");
puts("S     Saves current screen to file LIFE.PIC, then continues.");
puts("<ESC> Stops processing.  Then press...");
puts("R     Starts a new game with current parameters");
puts("Any other key exits to DOS.\n");
printf("\n");
initgraf();							// 640x480
highx = maxx = getmaxx();          	// initialize variables
highy = maxy = getmaxy();
lowx = lowy = 0;                   	// defaults
x0 = maxx/2;
y0 = maxy/2;
setcolor(LIGHTGREEN);
setfillstyle(SOLID_FILL,LIGHTGREEN);
while(1)                            // starting point of each new run
{
	if(gametype == 2)
		randomrules();      		// assign rules randomly
	population = 0L;
	clearviewport();
	outfile.open("life.pop");
	outfile << "Survive:";       	// store to file for reference
	for(i = 0 ; s[i] != -1 ; i++)
		outfile << " " << s[i];
	outfile << "\nBorn:";
	for(i = 0 ; b[i] != -1 ; i++)
		outfile << " " << b[i];
	outfile << endl;
	switch(setup)
	{
		case 1:
			picload(filename);      	// must initgraph before this point
			editscreen();            	// allow editing screen before start
			break;
		case 2:                         // random screen
			if((vertsize <= 0) || (vertsize > maxy))
				vertsize = maxy;
			if((horisize <= 0) || (horisize > maxx))
				horisize = maxx;
			randscreen(dimension,startpoints,vertsize,horisize,gametype);
			// set "calculate" area to be 2x the random-plot area in each dimension,
			// but don't allow calculate area to enlarge automatically.
			lowx = max(x0 - horisize,0);
			highx = min(x0 + horisize,maxx);
			lowy = max(y0 - vertsize,0);
			highy = min(y0 + vertsize,maxy);
			break;
		case 3:                        	// manual setup
			editscreen();
			break;
	}
	// starting population - remember: excludes multiply-hit pixels.
	outfile << population << endl;
	switch(dimension)
	{
		case 1:
			grow1d();      			// it just cycles until user aborts
			break;
		case 2:
			if(gametype == 5)
				while(colorgrow())
					outfile << population << endl;
			else
				while(grow2d())
					outfile << population << endl;
			break;
	}
	nosound();
	outfile.close();
	gotoxy(1,1);                  	// apparently ok in graphics mode
									// only if random or manually set
	if(gametype != 5)             	// no save for color game
		if((gametype == 2) || (gametype == 3) || edited)
		{
			printf("To add this rule set to file, enter any comment, or <CR> to skip:\n");
			gets(buf);
			if(strlen(buf))
			{
				outfile.open("life.rul",ios::app);
				outfile << "Survive:";
				for(i = 0 ; s[i] != -1 ; i++)
					outfile << " " << s[i];
				outfile << "\nBorn:";
				for(i = 0 ; b[i] != -1 ; i++)
					outfile << " " << b[i];
				outfile << "\nC: " << buf << endl;
				outfile.close();
			}
		}
	printf("<Q>uit, <E>dit rules, <B>rowse rules, <C>ontinue: ");
	ch = tolower(getch());
	if(ch == 'q')
		break;
	if(gametype != 5)             		// no editing or browsing for color
		if((ch == 'e') || (ch == 'b'))
		{
			restorecrtmode();
			if(ch == 'e')
			{
				editrules();
				edited = TRUE;           
			}
			else
			{
				browserules();
				edited = FALSE;
			}
			setgraphmode(VGAHI);
		}
}                        // end while(1)
closegraph();
return(0);
}         

 

 

 

Valid HTML 4.01 Transitional Valid CSS
View content labeling at ICRA.
Copyright ©2008 Steven Whitney. Last modified 02/27/2008.