/*	LIFE.C		The much implemented game of Life invented by John Conway

				This version was written to illustrate the use of the C88
				screen and keyboard interface.

				To generate:
				A>C88 B:LIFE
				A>ASM88 B:PCIO
				A>BIND B:LIFE B:PCIO		*/


/*
  global constant and data declarations
*/

#define	ROWS	24
#define COLS	80

/* control key translations */
#define up_char 30
#define down_char	31
#define left_char 29
#define right_char 28
#define bol_char 200
#define eol_char 201
#define pageup_char 202
#define pagedown_char 203
#define bof_char 204
#define eof_char 205
#define Ins_char 206
#define Del_char 207
#define NextWord_char 208
#define PrevWord_char 209

/* function keys */
#define M1 210
#define M2 211
#define M3 212
#define M4 213
#define M5 214
#define M6 215
#define M7 216
#define M8 217
#define M9 218
#define M10 219


char world[ROWS][COLS],create_mode=1,quit_flag;
int  population,generation,crow,ccol;

main() {

	scr_clr();
	instruct();
	setup();

	do {
		generation++;
		cycle();
		screen();
		}
	while (population && !quit_flag);
	scr_rowcol(ROWS,10);
	if (population == 0)
		puts("Nobody left, sorry about that.                       ");
	else puts("bye                                                 ");
	scr_curson();
	}

instruct() {		/*	print instructions	*/

puts("                The game of Life by John Conroy\n\n");
puts("      If started with a number, a random pattern starts the game.\n\n");
puts("  Otherwise, move the cursor with the four arrow keys to create life.\n\n");
puts("     DEL changes cursor movement to mean that cells are deleted\n\n");
puts("                 INS flips back to create mode.\n\n");
puts("          The '+' key will toggle the game on or off.\n\n");
puts("                     Hit ESC to bail out.\n\n");
puts("            Enter starting number of cells or hit CR   ");
	}

setup() {
	int rnumber;
	int i,row,col,seed,rnum;
	char ch;

	rnumber=0;
	while (1) {
		while ((ch=scr_csts()) == 0) seed++;
		if (ch < '0' || ch > '9') break;
		scr_co(ch);
		rnumber*=10;
		rnumber+=ch-'0';
		}
	scr_cursoff();
	scr_clr();
	scr_rowcol(ROWS,20);	/*	print population message	*/
	puts("Generation    0     Population    0");

	srand(seed);		/*	initilize random number generator	*/

	for (i=0; i < rnumber; i++) {
		rnum=rand();
		row=rnum%ROWS;
		col=(rnum/ROWS)%COLS;
		world[row][col]='X'; /* put in a cell */
		scr_rowcol(row,col);
		scr_co(2);
		}
	if (rnumber == 0) create(1);
	}


screen() {	/* update the screen and set world back to x's	*/
	int  row,col;
	char cell;

	population=0;
	for (row=0; row < ROWS; row++) {
		for (col=0; col < COLS; col++) {
			cell=world[row][col];
			/* stay alive if 3 neighbors, born if next to 2 or 3 */

			if (cell && (cell == 3 || cell == 'X'+2 || cell == 'X'+3)) {
				population++;
				if (cell < 'X') {
					scr_rowcol(row,col);
					scr_co(2);
					}
				cell='X';
				}
			else {
				if (cell >= 'X') {
					scr_rowcol(row,col);
					scr_co(' ');
					}
				cell=0;
				}
			world[row][col]=cell;
			}
		}
	scr_rowcol(ROWS,31);
	printf("%4d",generation);
	scr_rowcol(ROWS,51);
	printf("%4d",population);
	}


create(suspend)			/*	see if need to create or kill cells	*/
	char suspend; {
	char ch,wait;

	while ((ch=scr_csts()) || suspend) {
		switch (ch) {
			case up_char:	crow=crow ? crow-1: ROWS-1;
							break;
			case down_char:	crow=crow == ROWS-1 ? 0: crow+1;
							break;
			case left_char:	ccol=ccol ? ccol-1: COLS-1;
							break;
			case right_char:ccol=ccol == COLS-1 ? 0: ccol+1;
							break;
			case bol_char:	ccol=0;
							break;
			case eol_char:	ccol=COLS-1;
							break;
			case '+':		suspend=!suspend;
							continue;
			case Ins_char:	create_mode=1;
							continue;
			case Del_char:	create_mode=0;
							continue;
			case 0x1b:		quit_flag=1;	/* flag for stop */
							return;
			default:		continue;
			}
		world[crow][ccol]= create_mode ? 'X': 0;
		scr_rowcol(crow,ccol);
		if (create_mode) {
			scr_co(2);
			population++;
			}
		else {
			wait=30;
			while (wait--) {
				scr_co(1);
				scr_rowcol(crow,ccol);
				}
			scr_co(' ');
			}
		}
	}

cycle() {				/* cycle to the next generation */
	int  row,col;

	create(0);
	/*	take care of left and right column first	*/
	for (row=0; row < ROWS; row++) {
		if (world[row][0] >= 'X') add8(row,0);
		if (world[row][COLS-1] >= 'X') add8(row,COLS-1);
		}

	/*	take care of top and bottom line	*/
	for (col=1; col < COLS-1;col++) {
		if (world[0][col] >= 'X') add8(0,col);
		if (world[ROWS-1][col] >= 'X') add8(ROWS-1,col);
		}

	/*	fill in the box, ignoring border conditions	*/
	for (row=1; row < ROWS-1; row++) {
		for (col=1; col < COLS-1; col++) {
			if (world[row][col] >= 'X' ) {
				world[row-1][col-1]++;
				world[row-1][col]++;
				world[row-1][col+1]++;
				world[row][col-1]++;
				world[row][col+1]++;
				world[row+1][col-1]++;
				world[row+1][col]++;
				world[row+1][col+1]++;
				}
			}
		}
	}


add8(row,col)
	int  row,col; {
	int  rrow,ccol,rr,cc;

	for (rr=row-1; rr <= row+1; rr++) {
		for (cc=col-1; cc <= col+1; cc++) {
			rrow=rr != -1 ? rr : ROWS-1;
			ccol=cc != -1 ? cc : COLS-1;
			if (rrow >= ROWS) rrow=0;
			if (ccol >= COLS) ccol=0;
			world[rrow][ccol]++;
			}
		}
	world[row][col]--;
	}
	
