242 lines
5.6 KiB
C
242 lines
5.6 KiB
C
|
/* life - Conway's game of life Author: Jim King */
|
||
|
|
||
|
/* clife.c - curses life simulator. Translated from Pascal to C implementing
|
||
|
* curses Oct 1988 by pulsar@lsrhs, not jek5036@ritvax.isc.rit.edu
|
||
|
* life needs about 18kb stack space on MINIX.
|
||
|
*
|
||
|
* Flags: -d draw your own screen using arrows and space bar
|
||
|
* -p print statistics on the bottom line during the game
|
||
|
*/
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <signal.h>
|
||
|
#include <time.h>
|
||
|
#include <curses.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#if __minix_vmd /* Use a more random rand(). */
|
||
|
#define srand(seed) srandom(seed)
|
||
|
#define rand() random()
|
||
|
#endif
|
||
|
|
||
|
/* A value of -1 will make it go forever */
|
||
|
/* A value of 0 will make it exit immediately */
|
||
|
#define REPSTOP -1 /* number of repetitions before stop */
|
||
|
|
||
|
int present[23][80]; /* screen 1 cycle ago */
|
||
|
int past[23][80]; /* screen this cycle */
|
||
|
int total; /* total # of changes */
|
||
|
int icnt; /* counter to check for repetition */
|
||
|
int maxrow = 22; /* some defines to represent the screen */
|
||
|
int maxcol = 79;
|
||
|
int minrow = 0;
|
||
|
int mincol = 0;
|
||
|
int pri = 0; /* flag for printing stats on bottom line */
|
||
|
int draw = 0; /* flag for drawing your own screen */
|
||
|
int i, j, k; /* loop counters */
|
||
|
int cycle; /* current cycle # */
|
||
|
int changes; /* # of changes this cycle (live + die) */
|
||
|
int die; /* number of deaths this cycle */
|
||
|
int live; /* number of births this cycle */
|
||
|
|
||
|
WINDOW *mns; /* Main Screen */
|
||
|
WINDOW *info; /* Bottom line */
|
||
|
|
||
|
_PROTOTYPE(void cleanup, (int s));
|
||
|
_PROTOTYPE(void initialize, (void));
|
||
|
_PROTOTYPE(void makscr, (void));
|
||
|
_PROTOTYPE(void update, (void));
|
||
|
_PROTOTYPE(void print, (void));
|
||
|
_PROTOTYPE(int main, (int ac, char *av[]));
|
||
|
|
||
|
/* Cleanup - cleanup then exit */
|
||
|
void cleanup(s)
|
||
|
int s;
|
||
|
{
|
||
|
move(23, 0); /* go to bottom of screen */
|
||
|
refresh(); /* update cursor */
|
||
|
|
||
|
endwin(); /* shutdown curses */
|
||
|
exit(1); /* exit */
|
||
|
}
|
||
|
|
||
|
/* Initialize - init windows, variables, and signals */
|
||
|
|
||
|
void initialize()
|
||
|
{
|
||
|
srand(getpid()); /* init random seed */
|
||
|
initscr(); /* init curses */
|
||
|
noecho();
|
||
|
curs_set(0);
|
||
|
signal(SIGINT, cleanup); /* catch ^C */
|
||
|
mns = newwin(maxrow, maxcol, 0, 0); /* new window */
|
||
|
scrollok(mns, FALSE);
|
||
|
info = newwin(1, 80, 23, 0);
|
||
|
scrollok(info, FALSE);
|
||
|
wclear(mns);
|
||
|
wclear(info);
|
||
|
wmove(info, 0, 0);
|
||
|
wrefresh(info);
|
||
|
if (!draw) { /* if no draw, make random pattern */
|
||
|
for (j = 0; j < maxrow; j++) {
|
||
|
for (k = 0; k < maxcol; k++) {
|
||
|
present[j][k] = rand() % 2;
|
||
|
if (present[j][k] == 1) changes++, live++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Makscr - make your own screen using arrow keys and space bar */
|
||
|
void makscr()
|
||
|
{
|
||
|
int curx, cury; /* current point on screen */
|
||
|
char c; /* input char */
|
||
|
|
||
|
wclear(info);
|
||
|
wmove(info, 0, 0);
|
||
|
wprintw(info, "Use arrow keys to move, space to place / erase, ^D to start", NULL);
|
||
|
wrefresh(info);
|
||
|
curx = cury = 1;
|
||
|
wmove(mns, cury - 1, curx - 1);
|
||
|
wrefresh(mns);
|
||
|
noecho();
|
||
|
for (;;) {
|
||
|
c = wgetch(mns);
|
||
|
if (c == '\004')
|
||
|
break;
|
||
|
else if (c == ' ') {
|
||
|
if (present[cury][curx]) {
|
||
|
--present[cury][curx];
|
||
|
changes++;
|
||
|
die++;
|
||
|
mvwaddch(mns, cury, curx, ' ');
|
||
|
} else {
|
||
|
++present[cury][curx];
|
||
|
changes++;
|
||
|
live++;
|
||
|
mvwaddch(mns, cury, curx, '*');
|
||
|
}
|
||
|
} else if (c == '\033') {
|
||
|
wgetch(mns);
|
||
|
switch (wgetch(mns)) {
|
||
|
case 'A': --cury; break;
|
||
|
case 'B': ++cury; break;
|
||
|
case 'C': ++curx; break;
|
||
|
case 'D': --curx; break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
if (cury > maxrow) cury = minrow;
|
||
|
if (cury < minrow) cury = maxrow;
|
||
|
if (curx > maxcol) curx = mincol;
|
||
|
if (curx < mincol) curx = maxcol;
|
||
|
wmove(mns, cury, curx);
|
||
|
wrefresh(mns);
|
||
|
}
|
||
|
wclear(info);
|
||
|
}
|
||
|
|
||
|
/* Update rules: 2 or 3 adjacent alive --- stay alive
|
||
|
* 3 adjacent alive -- dead to live
|
||
|
* all else die or stay dead
|
||
|
*/
|
||
|
void update()
|
||
|
{ /* Does all mathmatical calculations */
|
||
|
int howmany, w, x, y, z;
|
||
|
changes = die = live = 0;
|
||
|
for (j = 0; j < maxrow; j++) {
|
||
|
for (k = 0; k < maxcol; k++) {
|
||
|
w = j - 1;
|
||
|
x = j + 1;
|
||
|
y = k - 1;
|
||
|
z = k + 1;
|
||
|
|
||
|
howmany = (past[w][y] + past[w][k] + past[w][z] +
|
||
|
past[j][y] + past[j][z] + past[x][y] +
|
||
|
past[x][k] + past[x][z]);
|
||
|
|
||
|
switch (howmany) {
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 4:
|
||
|
case 5:
|
||
|
case 6:
|
||
|
case 7:
|
||
|
case 8:
|
||
|
present[j][k] = 0;
|
||
|
if (past[j][k]) changes++, die++;
|
||
|
break;
|
||
|
case 3:
|
||
|
present[j][k] = 1;
|
||
|
if (!past[j][k]) changes++, live++;
|
||
|
break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (live == die)
|
||
|
++icnt;
|
||
|
else
|
||
|
icnt = 0;
|
||
|
|
||
|
if (icnt == REPSTOP) cleanup(0);
|
||
|
}
|
||
|
|
||
|
/* Print - updates the screen according to changes from past to present */
|
||
|
void print()
|
||
|
{
|
||
|
/* Updates the screen, greatly improved using curses */
|
||
|
if (pri) {
|
||
|
wmove(info, 0, 0);
|
||
|
total += changes;
|
||
|
cycle++;
|
||
|
wprintw(info, "Cycle %5d | %5d changes: %5d died + %5d born = %5u total changes", (char *) cycle, changes, die, live, total);
|
||
|
wclrtoeol(info);
|
||
|
}
|
||
|
for (j = 1; j < maxrow; j++) {
|
||
|
for (k = 1; k < maxcol; k++) {
|
||
|
if (present[j][k] != past[j][k] && present[j][k] == 1) {
|
||
|
wmove(mns, j, k);
|
||
|
wprintw(mns, "*", NULL);
|
||
|
} else if (present[j][k] != past[j][k] && present[j][k] == 0) {
|
||
|
wmove(mns, j, k);
|
||
|
wprintw(mns, " ", NULL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (pri) wrefresh(info);
|
||
|
wrefresh(mns);
|
||
|
}
|
||
|
|
||
|
/* Main - main procedure */
|
||
|
int main(ac, av)
|
||
|
int ac;
|
||
|
char *av[];
|
||
|
{
|
||
|
if (ac > 1) {
|
||
|
for (j = 1; j < ac; j++) {
|
||
|
switch (av[j][1]) {
|
||
|
case 'd': ++draw; break;
|
||
|
case 'p': ++pri; break;
|
||
|
default:
|
||
|
fprintf(stderr, "%s: usage: %s [-d] [-p]\n", av[0], av[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
initialize();
|
||
|
if (draw) makscr();
|
||
|
|
||
|
for (;;) {
|
||
|
print();
|
||
|
for (j = 0; j < maxrow; j++) {
|
||
|
for (k = 0; k < maxcol; k++) past[j][k] = present[j][k];
|
||
|
}
|
||
|
update();
|
||
|
}
|
||
|
}
|