|
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 Payments Humor Music |
John Conway's Game of Life cellular automaton in Borland C++ for MSDOSThe LIFE.CPP program plays the Game of Life, a cellular automaton originated by John Conway. It is described in the books Chaos Under Control, by Peak and Frame and 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 this MSDOS version has the 1d (1 dimensional) option, which can create some interesting designs like this one. The top line of pixels is the first generation, and successive generations grow down the screen:
|
/* 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 3 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);
}
|
|
|
|
|
|
Copyright ©2010 Steven Whitney. Last modified Thu 10/21/2010 02:08:04 -0700. |
||