[comp.sources.misc] v18i056: clife - Curses based Life simulator, Part01/01

jek5036@ultb.isc.rit.edu (J.E. King) (04/19/91)

Submitted-by: J.E. King <jek5036@ultb.isc.rit.edu>
Posting-number: Volume 18, Issue 56
Archive-name: clife/part01
Supersedes: clife: Volume 17, Issue 61

	With some help from someone out on the net (read the man page)
I have improved the life program.  Because it is such a small program
I thought I would repost it instead of patches (because the p[atches were
larger than the original).

This is called clife, or a curses implementation life simulator. It
simulates the life 'function' which is described in the manual page.
Basically it could be thought of as a game, but it isn't.  That's 
why the manual page is under clife.6.

Jim King, (jek5036@ultb.isc.rit.edu)

-- cut here -- cut here -- cut here -- cut here -- cut here -- cut here --
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./Makefile`
then
echo "writing ./Makefile"
cat > ./Makefile << '\End\Of\Shar\'
# On the next line, add a -DHJKL if you wish to use HJKL instead of
# arrow keys when building a life screen.

CFLAGS        = -g #-DHJKL
DEST	      = .
MANDEST       = /usr/man/man6
EXTHDRS	      = /usr/include/curses.h \
		/usr/include/sgtty.h \
		/usr/include/signal.h \
		/usr/include/stdio.h \
		/usr/include/sys/ioctl.h \
		/usr/include/sys/ttychars.h \
		/usr/include/sys/ttydev.h \
		/usr/include/sys/ttyio.h \
		/usr/include/time.h
HDRS	      =
LDFLAGS	      = -g
LIBS	      = -lcurses -ltermlib
LINKER	      = cc
MAKEFILE      = Makefile
OBJS	      = clife.o
PRINT	      = lpr
PROGRAM	      = clife
SRCS	      = clife.c

all:		$(PROGRAM)

$(PROGRAM):     $(OBJS)
		@echo -n "Loading $(PROGRAM) ... "
		@$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
		@echo "done"

clean:;		@rm -f $(OBJS)

depend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)

index:;		@ctags -wx $(HDRS) $(SRCS)

install:	$(PROGRAM)
		@echo Installing $(PROGRAM) in $(DEST)
		@install -s $(PROGRAM) $(DEST)
		@echo Copying manual pages...
		@cp clife.6 $(MANDEST)
		@chmod 644 $(MANDEST)/clife.6

print:;		@$(PRINT) $(HDRS) $(SRCS)

program:        $(PROGRAM)

tags:           $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS)

update:		$(DEST)/$(PROGRAM)

$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
		@make -f $(MAKEFILE) DEST=$(DEST) 
###
clife.o: /usr/include/signal.h /usr/include/time.h /usr/include/stdio.h \
	/usr/include/curses.h /usr/include/sgtty.h /usr/include/sys/ioctl.h \
	/usr/sys/h/ttychars.h /usr/sys/h/ttydev.h /usr/sys/h/ttyio.h \
	/usr/sys/h/sgtty.h /usr/include/sys/ttychars.h \
	/usr/include/sys/ttydev.h /usr/include/sys/ttyio.h
\End\Of\Shar\
else
  echo "will not over write ./Makefile"
fi
if [ `wc -c ./Makefile | awk '{printf $1}'` -ne 1646 ]
then
echo `wc -c ./Makefile | awk '{print "Got " $1 ", Expected " 1646}'`
fi
if `test ! -s ./Makefile.sysv`
then
echo "writing ./Makefile.sysv"
cat > ./Makefile.sysv << '\End\Of\Shar\'
# On the next line, add a -DHJKL if you wish to use HJKL instead of
# arrow keys when building a life screen.

CFLAGS        = -O -DHJKL
DEST	      = .
MANDEST       = /usr/man/man6
EXTHDRS	      = /usr/include/curses.h \
		/usr/include/sgtty.h \
		/usr/include/signal.h \
		/usr/include/stdio.h \
		/usr/include/time.h
HDRS	      =
LDFLAGS	      = -O
LIBS	      = -lcurses
LINKER	      = cc
MAKEFILE      = Makefile
OBJS	      = clife.o
PRINT	      = lpr
PROGRAM	      = clife
SRCS	      = clife.c

all:		$(PROGRAM)

$(PROGRAM):     $(OBJS)
		@echo -n "Loading $(PROGRAM) ... "
		@$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
		@echo "done"

clean:;		@rm -f $(OBJS)

depend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)

index:;		@ctags -wx $(HDRS) $(SRCS)

install:	$(PROGRAM)
		@echo Installing $(PROGRAM) in $(DEST)
		@install -s $(PROGRAM) $(DEST)
		@echo Copying manual pages...
		@cp clife.6 $(MANDEST)
		@chmod 644 $(MANDEST)/clife.6

print:;		@$(PRINT) $(HDRS) $(SRCS)

program:        $(PROGRAM)

tags:           $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS)

update:		$(DEST)/$(PROGRAM)

$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
		@make -f $(MAKEFILE) DEST=$(DEST) 
###
clife.o: /usr/include/signal.h /usr/include/time.h /usr/include/stdio.h \
	 /usr/include/curses.h
\End\Of\Shar\
else
  echo "will not over write ./Makefile.sysv"
fi
if [ `wc -c ./Makefile.sysv | awk '{printf $1}'` -ne 1302 ]
then
echo `wc -c ./Makefile.sysv | awk '{print "Got " $1 ", Expected " 1302}'`
fi
if `test ! -s ./clife.6`
then
echo "writing ./clife.6"
cat > ./clife.6 << '\End\Of\Shar\'
.TH clife 6
.SH Name
clife \- Life simulation using curses
.SH Syntax
.B clife
\fI[-dp]\fR
.SH Description
The
.PN clife
program is a simulator which has been around for quite some time.
The screen is set up with random 'people' on the screen.  Each
person is represented by a '*'.  The rules are simple:  If
three people are around any given square, a person is born there.  If
only two people are around any given square, then that person stays.
Any other number around a square kills the person there. (not enough food)
.PP
The clife (which stands for curses life) simulator will produce certain
distinguishable patterns attributable to the life equation.
.SH Options
The
.PN \-d
option allows you to draw your own pattern on the screen
and run it through the simulator.  This is done by moving around the
screen with the arrow keys, (or using the 'hjkl' keys if the program
was compiled with that option) and using the spacebar to put a person in the
square you are currently over. If a person is there already,
kill the person. (blank the space)

The
.PN \-p
option will print out information about the life cycles on the bottom
of the screen after each cycle.
.SH Author
Jim King, (jek5036@ritvax.isc.rit.edu)

.SH Additions
Sameer Parekh (zane@ddsw1.MCS.COM) added wrap ability, fixed quit bug,
enhanced draw segment to allow HJKL or arrows at compile time, and 
enhanced argument parsing.
 
\End\Of\Shar\
else
  echo "will not over write ./clife.6"
fi
if [ `wc -c ./clife.6 | awk '{printf $1}'` -ne 1399 ]
then
echo `wc -c ./clife.6 | awk '{print "Got " $1 ", Expected " 1399}'`
fi
if `test ! -s ./clife.c`
then
echo "writing ./clife.c"
cat > ./clife.c << '\End\Of\Shar\'
/*
 * clife.c - curses life simulator.  Translated from Pascal to C implementing
 *           curses Oct 1988 by pulsar@lsrhs, now jek5036@ritvax.isc.rit.edu
 *
 *	V2	pulsar@lsrhs Oct 1988
 *	   - 	Draw your own pattern then 'life' it.
 *
 *      V2.1	zane@ddsw1.MCS.COM  Apr 1991 
 *	   - 	Addition so the screen wraps around.
 *	   -	Fix of the bug which caused the shell to mess up after
 *		quitting with SIGINT.
 *	   -	Also enable all options with one argument.  (clife -dp
 *		instead of clife -d -p, both are usable.)
 *	   -	Enable choice of arrow keys or HJKL at compile-time.
 *
 *              jek5036@ultb.isc.rit.edu (formerly pulsar@lsrhs)
 *         -	SYSV Makefile (I hope it works..)
 *         -	Couple of minor fixes including random() instead of rand()
 */

#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <curses.h>

				/* a value of -1 will make it go forever */
				/* a value of 0 will make it exit immed. */
#define	REPSTOP		-1	/* number of repetitions before stop */
				/* in a looping pattern */

/*
 * I don't need any flames about global variables.
 */

#define	MAXROW	23
#define	MAXCOL	79

int	present[MAXROW+1][MAXCOL+1],	/* screen 1 cycle ago */
	past[MAXROW+1][MAXCOL+1],	/* screen this cycle */
	total,			/* total # of changes */
	icnt,			/* counter to check for repetition */
	minrow = 0,
	mincol = 0,
	pri = 0,
	draw = 0,
	i, j, k,		/* loop counters */
	cycle,			/* current cycle # */
	changes,		/* # of changes this cycle (live + die) */
	die,			/* number of deaths this cycle */
	live;			/* number of births this cycle */

WINDOW	*mns,				/* Main Screen */
	*info;				/* Bottom line */

/*
 * cleanup - cleanup then go back to makscr
 */

cleanup()
{
	move(23, 0);		/* go to bottom of screen */
	refresh();		/* update cursor */
	endwin();		/* Clean up curses */
	exit(1);
}

/*
 * initialize - init windows, variables, and signals
 */

initialize()
{
	srandom(getpid());		/* init rand seed */
	initscr();			/* init curses */
	signal(SIGINT, cleanup);	/* catch ^C */
	signal(SIGTERM, exit);		/* exit on kill -15 */
	mns = newwin(MAXROW, MAXCOL, 0, 0);	/* new window */
	scrollok(mns, FALSE);
	info = newwin(1, 80, 23, 0);
	scrollok(info, FALSE);
	wclear(mns);
	if (!draw) {		/* if no draw, make rand pattern */
		for (j = 0; j < MAXROW; j++) {
			for (k = 0; k < MAXCOL; k++) {
				present[j][k] = random()%2;
				if (present[j][k] == 1) changes++, live++;
			}
		} 
	}
}

/*
 * makscr - make your own screen using arrow keys and space bar
 */

makscr()
{
	int	curx, cury;		/* current point on screen */
	char	c;			/* input char */
	
	wclear(info);
	wmove(info, 0, 0);
#ifdef	HJKL
	wprintw(info, "Use 'hjkl' keys to move, space to place / erase, ^D to start");
#else
	wprintw(info, "Use arrow keys to move, space to place / erase, ^D to start");
#endif
	wrefresh(info);
	curx = cury = 1;
	wmove(mns, cury-1, curx-1); wrefresh(mns);
	noecho(); crmode();
	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, '*');
			}
#ifndef	HJKL	/* This portion is used for the arrow keys */
		} 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;
			}
		}
#else	/* This portion for HJKL */
		} else {
			switch(c) {
				case 'H':
				case 'h':
					--curx; break;
				case 'J':
				case 'j':
					++cury; break;
				case 'K':
				case 'k':
					--cury; break;
				case 'L':
				case 'l':
					++curx; break;
				default:
					break;
			}
		}
#endif	/* End the HJKL section */

		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
 */

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 < 0) ? (MAXROW-1) : j-1;
			x = (j+1 > MAXROW-1) ? 0 : j+1;
			y = (k-1 < 0) ? (MAXCOL-1) : k-1;
			z = (k+1 > MAXCOL-1) ? 0 : k+1;

/*
 * Nice addition above four lines for wrap.. wouldn't have thought
 * of it myself.. -- Jim
 */

			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();
}

/*
 * print - updates the screen according to changes from past to present
 */

print()		/* Updates the screen, greatly improved using curses */
{
	if (pri) {
		wmove(info, 0, 0);
		total += changes;
		wprintw(info, "Cycle %d | %d changes: %d died and %d born & %d total changes", ++cycle, changes, die, live, total);
		wclrtoeol(info);
	}
	for (j = 0; j < MAXROW; j++) {
		for (k = 0; k < MAXCOL; k++) {
			if (present[j][k] != past[j][k] && present[j][k] == 1) {
				wmove(mns, j, k);
				wprintw(mns, "*");
			} else if (present[j][k] != past[j][k] && present[j][k] == 0) {
				wmove(mns, j, k);
				wprintw(mns, " ");
			}
		}
	}
	if (pri) wrefresh(info);
	wrefresh(mns);
}

/*
 * main - main procedure
 */

main(ac, av)
int	ac;
char	*av[];
{
	if (ac > 1) {
		for (j = 1; j < ac; j++) {
			if (av[j][0] == '-') {
				for(i = 1; i < strlen(av[j]); i++) {
					switch(av[j][i]) {
					case 'd':
						++draw;
						break;
					case 'p':
						++pri;
						break;
					default:
						fprintf(stderr, "%s: usage: %s [-dpf] <filename>]\n", av[0], av[0]);
						exit(1);
					}
				}
			}
		}
	}

	if (draw)
		printf("User-built screen\n");
	if (pri)
		printf("Print statistics\n");

	initialize();
	if (draw)
		makscr();

	wclear(info);
	wmove(info, 0, 0);
	wprintw(info, "Life V2.1 by Jim King (jek5036@ultb.isc.rit.edu), additions by Sameer Parekh");
	wrefresh(info);

	for (;;) {
		print();
		for (j = 0; j < MAXROW; j++) {
			for (k = 0; k < MAXCOL; k++)
				past[j][k] = present[j][k];
		}
		update();
	}
}
\End\Of\Shar\
else
  echo "will not over write ./clife.c"
fi
if [ `wc -c ./clife.c | awk '{printf $1}'` -ne 6649 ]
then
echo `wc -c ./clife.c | awk '{print "Got " $1 ", Expected " 6649}'`
fi
echo "Finished archive 1 of 1"
exit

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.