[alt.sources] Game-o'-Life

staceyc@sco.COM (Stacey Campbell) (10/02/89)

This is the Game-o'-Life.  It should compile on almost
anything with a curses library (BSD, SYSV, XENIX etc).
The design is based on watching a running program and
trying to figure out the algorithm.  Cycles of 16 or
less are found and noted, this value can be increased in
the source.

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  README
#	  life.c
#	  Makefile
#	  style
#	  line
#	  sym
#	  death
#
sed 's/^X//' << 'SHAR_EOF' > README &&
XThis is the Game-o'-Life.  It should compile on almost
Xanything with a curses library (BSD, SYSV, XENIX etc).
XThe design is based on watching a running program and
Xtrying to figure out the algorithm.  Cycles of 16 or
Xless are found and noted, this value can be increased in
Xthe source.
X
XA couple of example files are included: style, line, sym
Xand death.  If you come up with a good file then please
Xmail it so I can have a look.
X
XUsage: life [file]
X
XThis game is probably too dinky for comp.sources.games.
X
XStacey Campbell
X
Xuunet!sco!staceyc
SHAR_EOF
chmod 0664 README || echo "restore of README fails"
sed 's/^X//' << 'SHAR_EOF' > life.c &&
X/*
X *	Game-o'-Life
X *
X *	Stacey Campbell - SCO - late September 1989 - uunet!sco!staceyc
X */
X
X#include <signal.h>
X#include <stdio.h>
X#include <curses.h>
X
X#define Y_MAX 21
X#define X_MAX 30
X#define NO_LIFE '.'
X#define LIFE    '@'
X#define DIR_COUNT 8
X#define OVERCROWDED 3
X#define BREED_COUNT 3
X#define LONELY 1
X#define LEGAL(y, x) ((y) >= 0 && (x) >= 0 && (y) < Y_MAX && (x) < X_MAX)
X#define BITS_IN_BYTE 8
X#define CYCLE_SIZE (((Y_MAX * X_MAX) / BITS_IN_BYTE) + 1)
X#define CYCLE_LIMIT 16
X
Xtypedef struct delta_t {
X	int dy;
X	int dx;
X	} delta_t;
Xtypedef int board_t;
X
Xtypedef struct cycle_t {
X	unsigned char board[CYCLE_SIZE];
X	struct cycle_t *next;
X	} cycle_t;
X
Xstatic delta_t Dirs[DIR_COUNT] =
X    {{-1 ,0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
X
Xint Life();
Xint DeathCheck();
Xint Breed();
Xvoid InitBoard();
Xvoid DisplayBoard();
Xvoid InitSignals();
Xvoid SigExit();
Xvoid ErrorExit();
Xvoid InitCycleCheck();
Xint CycleCheck();
Xvoid CompressCycle();
X
Xint main(argc, argv)
X
Xint argc;
Xchar *argv[];
X
X	{
X	board_t board[Y_MAX][X_MAX];
X	int status;
X	int cycle = 0;
X	int generation = 1;
X
X	InitBoard(board, argc, argv);
X	InitCycleCheck(board);
X	initscr();
X	InitSignals();
X	do	{
X		DisplayBoard(stdscr, board, generation);
X		status = Life(board);
X		if (status && ! cycle)
X			{
X			cycle = CycleCheck(board);
X			if (cycle)
X				{
X				wmove(stdscr, Y_MAX + 1, 0);
X				wprintw(stdscr, "Cycles every %d", cycle);
X				if (cycle == 1)
X					waddstr(stdscr, " generation.\n");
X				else
X					waddstr(stdscr, " generations.\n");
X				wrefresh(stdscr);
X				}
X			}
X		++generation;
X		sleep(1);
X		} while (status && cycle != 1);
X	DisplayBoard(stdscr, board, generation);
X	if (cycle != 1)
X		{
X		wmove(stdscr, Y_MAX + 1, 0);
X		waddstr(stdscr, "Extinction.");
X		}
X	wmove(stdscr, LINES - 1, 0);
X	wrefresh(stdscr);
X	endwin();
X
X	return 0;
X	}
X
Xstatic int Life(board)
X
Xboard_t board[Y_MAX][X_MAX];
X
X	{
X	int i, j;
X	int alive = 0;
X	board_t death_board[Y_MAX][X_MAX];
X	board_t life_board[Y_MAX][X_MAX];
X
X	for (i = 0; i < Y_MAX; ++i)
X		for (j = 0; j < X_MAX; ++j)
X			alive += Breed(life_board, board, i, j);
X	for (i = 0; i < Y_MAX; ++i)
X		for (j = 0; j < X_MAX; ++j)
X			alive += DeathCheck(death_board, board, i, j);
X	for (i = 0; i < Y_MAX; ++i)
X		for (j = 0; j < X_MAX; ++j)
X			board[i][j] = life_board[i][j] || death_board[i][j];
X
X	return alive;
X	}
X
Xstatic int Breed(new_board, old_board, y, x)
X
Xboard_t new_board[Y_MAX][X_MAX];
Xboard_t old_board[Y_MAX][X_MAX];
Xint y;
Xint x;
X
X	{
X	int i;
X	int ny, nx;
X	int neighbors = 0;
X
X	for (i = 0; i < DIR_COUNT; ++i)
X		{
X		ny = y + Dirs[i].dy;
X		nx = x + Dirs[i].dx;
X		if (LEGAL(ny, nx) && old_board[ny][nx])
X			++neighbors;
X		}
X	new_board[y][x] = neighbors == BREED_COUNT;
X
X	return new_board[y][x];
X	}
X
Xstatic int DeathCheck(new_board, board, y, x)
X
Xboard_t new_board[Y_MAX][X_MAX];
Xboard_t board[Y_MAX][X_MAX];
Xint y;
Xint x;
X
X	{
X	int i;
X	int ny, nx;
X	int neighbors = 0;
X
X	if (board[y][x])
X		for (i = 0; i < DIR_COUNT; ++i)
X			{
X			ny = y + Dirs[i].dy;
X			nx = x + Dirs[i].dx;
X			if (LEGAL(ny, nx) && board[ny][nx])
X				++neighbors;
X			}
X	new_board[y][x] = ! (neighbors <= LONELY || neighbors >= OVERCROWDED);
X
X	return new_board[y][x];
X	}
X
Xstatic void DisplayBoard(win, board, generation)
X
XWINDOW *win;
Xboard_t board[Y_MAX][X_MAX];
Xint generation;
X
X	{
X	int i, j;
X	static char life[2] = {NO_LIFE, LIFE};
X	
X	for (i = 0; i < Y_MAX; ++i)
X		for (j = 0; j < X_MAX; ++j)
X			mvwaddch(win, i, j * 2, life[board[i][j]]);
X	wmove(win, Y_MAX, 0);
X	wprintw(win, "Generation: %d\n", generation);
X	wmove(win, Y_MAX + 2, 0);
X	wrefresh(win);
X	}
X
Xstatic void InitBoard(board, argc, argv)
X
Xboard_t board[Y_MAX][X_MAX];
Xint argc;
Xchar *argv[];
X
X	{
X	int i, j;
X	int file_empty;
X	int line_empty;
X	FILE *fp;
X	char buffer[X_MAX + 2];
X	static char *ch_board[Y_MAX] = {
X		"", "", "", "", "",
X		"             *",
X		"            * *",
X		"           *   *",
X		"            *   *",
X		"             *   *",
X		"              *   *",
X		"               * *",
X		"                *",
X		0};
X
X	if (argc == 1)
X		{
X		file_empty = 0;
X		for (i = 0; i < Y_MAX; ++i)
X			{
X			if (! file_empty)
X				file_empty = ch_board[i] == 0;
X			line_empty = file_empty;
X			for (j = 0; j < X_MAX; ++j)
X				{
X				if (! line_empty)
X					line_empty = ch_board[i][j] == '\0';
X				if (! line_empty)
X					board[i][j] = ch_board[i][j] != ' ';
X				else
X					board[i][j] = 0;
X				}
X			}
X		return;
X		}
X	if (argc > 2)
X		ErrorExit(argv[0], "usage: %s [init-file]", argv[0]);
X	if ((fp = fopen(argv[1], "r")) == NULL)
X		ErrorExit(argv[0], "cannot read: %s", argv[1]);
X	file_empty = 0;
X	for (i = 0; i < Y_MAX; ++i)
X		{
X		memset(buffer, 0, sizeof(buffer));
X		if (! file_empty)
X			file_empty = fgets(buffer, sizeof(buffer), fp) == NULL;
X		buffer[strlen(buffer) - 1] = '\0';
X		for (j = 0; j < X_MAX; ++j)
X			board[i][j] = buffer[j] != ' ' && buffer[j] != '\0' &&
X			    buffer[j] != NO_LIFE;
X		}
X	(void)fclose(fp);
X	}
X
Xstatic void ErrorExit(prog, s1, s2)
X
Xchar *prog;
Xchar *s1;
Xchar *s2;
X
X	{
X	fprintf(stderr, "%s ", prog);
X	fprintf(stderr, s1, s2);
X	fprintf(stderr, "\n");
X	exit(1);
X	}
X
Xstatic void InitSignals()
X
X	{
X	signal(SIGINT, SigExit);
X	signal(SIGQUIT, SigExit);
X	}
X
Xstatic void SigExit(signo)
X
Xint signo;
X
X	{
X	endwin();
X	if (signo == SIGQUIT)
X		{
X		signal(SIGQUIT, SIG_DFL);
X		kill(getpid(), SIGQUIT);
X		pause();
X		}
X	exit(1);
X	}
X
Xstatic cycle_t *TopCycle;
Xstatic int CycleCount;
X
Xstatic void InitCycleCheck(board)
X
Xboard_t board[Y_MAX][X_MAX];
X
X	{
X	TopCycle = (cycle_t *)malloc(sizeof(cycle_t));
X	TopCycle->next = 0;
X	CompressCycle(board, TopCycle);
X	CycleCount = 1;
X	}
X
Xstatic int CycleCheck(board)
X
Xboard_t board[Y_MAX][X_MAX];
X
X	{
X	cycle_t *check_p;
X	cycle_t *cycle_p, *old_p, *very_old_p;
X	int i = 1;
X
X	check_p = (cycle_t *)malloc(sizeof(cycle_t));
X	CompressCycle(board, check_p);
X	old_p = 0;
X	very_old_p = 0;
X	cycle_p = TopCycle;
X	while (cycle_p)
X		if (memcmp(cycle_p->board, check_p->board,
X		    sizeof(check_p->board)) == 0)
X			{
X			free((cycle_t *)check_p);
X			return i;
X			}
X		else
X			{
X			++i;
X			very_old_p = old_p;
X			old_p = cycle_p;
X			cycle_p = cycle_p->next;
X			}
X	
X	if (CycleCount >= CYCLE_LIMIT)
X		if (very_old_p && very_old_p != TopCycle)
X			{
X			free((cycle_t *)old_p);
X			very_old_p->next = 0;
X			}
X	old_p = TopCycle;
X	TopCycle = check_p;
X	TopCycle->next = old_p;
X	++CycleCount;
X	return 0;
X	}
X
Xvoid CompressCycle(board, cycle_p)
X
Xint board[Y_MAX][X_MAX];
Xcycle_t *cycle_p;
X
X	{
X	int i, j;
X	int c_i, bit;
X
X	c_i = 0;
X	bit = 0;
X	memset(cycle_p->board, '\0', sizeof(cycle_p->board));
X	for (i = 0; i < Y_MAX; ++i)
X		for (j = 0; j < X_MAX; ++j)
X			{
X			cycle_p->board[c_i] |= board[i][j] << bit;
X			++bit;
X			if (bit == BITS_IN_BYTE)
X				{
X				++c_i;
X				bit = 0;
X				}
X			}
X	}
SHAR_EOF
chmod 0664 life.c || echo "restore of life.c fails"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# Makefile for life
X
X# System 5
X# LDLIBS= -lcurses
X
X# BSD
X# LDLIBS= -lcurses -ltermcap
X
X#XENIX
XLDLIBS= -ltcap -ltermlib
X
XOBJS= life.o
XSRC= life.c
XCFLAGS= -O
XEXE= life
XEXAMPLES= style line sym death
X
X$(EXE): $(OBJS)
X	cc -o $(EXE) $(CFLAGS) $(OBJS) $(LDLIBS)
X
Xshar:
X	xshar README life.c Makefile $(EXAMPLES) > $(EXE).shar
SHAR_EOF
chmod 0664 Makefile || echo "restore of Makefile fails"
sed 's/^X//' << 'SHAR_EOF' > style &&
X ** ***
X* ** ****
X** **
X ** 
X* * 
X**
X**
X *
X *
SHAR_EOF
chmod 0664 style || echo "restore of style fails"
sed 's/^X//' << 'SHAR_EOF' > line &&
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............**..............
X......*.......**.......*......
X....**.**... *  * ...**.**....
X......*.......**.......*......
X..............**..............
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
SHAR_EOF
chmod 0664 line || echo "restore of line fails"
sed 's/^X//' << 'SHAR_EOF' > sym &&
X*.**..........................
X.*..***.......................
X*.*.**........................
X*..*..........................
X.**.*.........................
X.**..*........................
X.*............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X..............................
X............................*.
X........................*..**.
X.........................*.**.
X..........................*..*
X........................**.*.*
X.......................***..*.
X..........................**.*
SHAR_EOF
chmod 0664 sym || echo "restore of sym fails"
sed 's/^X//' << 'SHAR_EOF' > death &&
X*............................*
X.*..........................*.
X..*........................*..
X...*......................*...
X....*....................*....
X.....*..................*.....
X......*................*......
X.......*..............*.......
X........*.....**.....*........
X......*..*....**....*..*......
X******.**.*. *  * .*.**.******
X......*..*....**....*..*......
X........*.....**.....*........
X.......*..............*.......
X......*................*......
X.....*..................*.....
X....*....................*....
X...*......................*...
X..*........................*..
X.*..........................*.
X*............................*
SHAR_EOF
chmod 0664 death || echo "restore of death fails"
exit 0
-- 
Stacey Campbell - uunet!sco!staceyc - The Santa Cruz Operation