xref: /plan9/sys/src/games/life.c (revision 6bbfed0d85c6d7248503ef0614d0f1e40438b735)
14e17d7c8SDavid du Colombier /*
24e17d7c8SDavid du Colombier  * life - john conways's game of life (and variations),
34e17d7c8SDavid du Colombier  * sci. am. 223, october 1970, pp. 120—123, or
44e17d7c8SDavid du Colombier  * http://en.wikipedia.org/wiki/Conway's_Game_of_Life
54e17d7c8SDavid du Colombier  */
64e17d7c8SDavid du Colombier #include <u.h>
74e17d7c8SDavid du Colombier #include <libc.h>
84e17d7c8SDavid du Colombier #include <bio.h>
94e17d7c8SDavid du Colombier #include <draw.h>
104e17d7c8SDavid du Colombier #include <event.h>
114e17d7c8SDavid du Colombier 
124e17d7c8SDavid du Colombier enum {
134e17d7c8SDavid du Colombier 	NLIFE	= 256,		/* life array size */
144e17d7c8SDavid du Colombier 	PX	= 4,		/* cell spacing */
154e17d7c8SDavid du Colombier 	BX	= PX - 1,	/* box size */
164e17d7c8SDavid du Colombier 	NADJUST	= NLIFE * NLIFE,
174e17d7c8SDavid du Colombier };
184e17d7c8SDavid du Colombier 
194e17d7c8SDavid du Colombier /*
204e17d7c8SDavid du Colombier  * life[i][j] stores L+2*N, where L is 0 if the cell is dead and 1
214e17d7c8SDavid du Colombier  * if alive, and N is the number of the cell's 8-connected neighbours
224e17d7c8SDavid du Colombier  * which live.
234e17d7c8SDavid du Colombier  * row[i] indicates how many cells are alive in life[i][*].
244e17d7c8SDavid du Colombier  * col[j] indicates how many cells are alive in life[*][j].
254e17d7c8SDavid du Colombier  * Adjust contains pointers to cells that need to have their neighbour
264e17d7c8SDavid du Colombier  * counts adjusted in the second pass of the generation procedure.
274e17d7c8SDavid du Colombier  */
284e17d7c8SDavid du Colombier char	life[NLIFE][NLIFE];
294e17d7c8SDavid du Colombier int	row[NLIFE];
304e17d7c8SDavid du Colombier int	col[NLIFE];
314e17d7c8SDavid du Colombier char	action[18];		/* index by cell contents to find action */
324e17d7c8SDavid du Colombier char	*adjust[NADJUST];
33*6bbfed0dSDavid du Colombier int		delay;
344e17d7c8SDavid du Colombier 
354e17d7c8SDavid du Colombier Point	cen;
364e17d7c8SDavid du Colombier Image	*box;
374e17d7c8SDavid du Colombier int	i0, i1, j0, j1;
384e17d7c8SDavid du Colombier int	needresize;
394e17d7c8SDavid du Colombier 
404e17d7c8SDavid du Colombier void	birth(int, int);
414e17d7c8SDavid du Colombier void	centerlife(void);
424e17d7c8SDavid du Colombier void	death(int, int);
434e17d7c8SDavid du Colombier int	generate(void);
444e17d7c8SDavid du Colombier int	interest(int [NLIFE], int);
454e17d7c8SDavid du Colombier void	main(int, char *[]);
464e17d7c8SDavid du Colombier int	min(int, int);
474e17d7c8SDavid du Colombier void	readlife(char *);
484e17d7c8SDavid du Colombier void	redraw(void);
494e17d7c8SDavid du Colombier void	setrules(char *);
504e17d7c8SDavid du Colombier void	window(void);
514e17d7c8SDavid du Colombier 
524e17d7c8SDavid du Colombier static void	reshape(void);
534e17d7c8SDavid du Colombier 
544e17d7c8SDavid du Colombier static void
setbox(int i,int j)554e17d7c8SDavid du Colombier setbox(int i, int j)
564e17d7c8SDavid du Colombier {
574e17d7c8SDavid du Colombier 	Point loc;
584e17d7c8SDavid du Colombier 
594e17d7c8SDavid du Colombier 	loc = Pt(cen.x + j*PX, cen.y + i*PX);
604e17d7c8SDavid du Colombier 	draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), box, nil, ZP);
614e17d7c8SDavid du Colombier }
624e17d7c8SDavid du Colombier 
634e17d7c8SDavid du Colombier static void
clrbox(int i,int j)644e17d7c8SDavid du Colombier clrbox(int i, int j)
654e17d7c8SDavid du Colombier {
664e17d7c8SDavid du Colombier 	Point loc;
674e17d7c8SDavid du Colombier 
684e17d7c8SDavid du Colombier 	loc = Pt(cen.x + j*PX, cen.y + i*PX);
694e17d7c8SDavid du Colombier 	draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), display->white, nil, ZP);
704e17d7c8SDavid du Colombier }
714e17d7c8SDavid du Colombier 
724e17d7c8SDavid du Colombier void
setrules(char * r)734e17d7c8SDavid du Colombier setrules(char *r)
744e17d7c8SDavid du Colombier {
754e17d7c8SDavid du Colombier 	char *a;
764e17d7c8SDavid du Colombier 
774e17d7c8SDavid du Colombier 	for (a = action; a != &action[nelem(action)]; *a++ = *r++)
784e17d7c8SDavid du Colombier 		;
794e17d7c8SDavid du Colombier }
804e17d7c8SDavid du Colombier 
814e17d7c8SDavid du Colombier static void
g9err(Display *,char * err)824e17d7c8SDavid du Colombier g9err(Display *, char *err)
834e17d7c8SDavid du Colombier {
844e17d7c8SDavid du Colombier 	static int entered = 0;
854e17d7c8SDavid du Colombier 
864e17d7c8SDavid du Colombier 	fprint(2, "%s: %s (%r)\n", argv0, err);
874e17d7c8SDavid du Colombier 	exits(err);
884e17d7c8SDavid du Colombier }
894e17d7c8SDavid du Colombier 
904e17d7c8SDavid du Colombier void
usage(void)914e17d7c8SDavid du Colombier usage(void)
924e17d7c8SDavid du Colombier {
934e17d7c8SDavid du Colombier 	fprint(2, "Usage: %s [-3o] [-r rules] file\n", argv0);
944e17d7c8SDavid du Colombier 	exits("usage");
954e17d7c8SDavid du Colombier }
964e17d7c8SDavid du Colombier 
974e17d7c8SDavid du Colombier void
idle(void)984e17d7c8SDavid du Colombier idle(void)
994e17d7c8SDavid du Colombier {
1004e17d7c8SDavid du Colombier 	int c;
1014e17d7c8SDavid du Colombier 
1024e17d7c8SDavid du Colombier 	while (ecanmouse())
1034e17d7c8SDavid du Colombier 		emouse();			/* throw away mouse events */
104*6bbfed0dSDavid du Colombier 	while (ecankbd()){
105*6bbfed0dSDavid du Colombier 		c = ekbd();
106*6bbfed0dSDavid du Colombier 		if (c  == 'q' || c == 0177) /* watch keyboard ones */
1074e17d7c8SDavid du Colombier 			exits(nil);
108*6bbfed0dSDavid du Colombier 		if (c >= '1' && c <= '9')
109*6bbfed0dSDavid du Colombier 			delay = (c - '0') * 100;
110*6bbfed0dSDavid du Colombier 		else if (c == '0')
111*6bbfed0dSDavid du Colombier 			delay = 1000;
112*6bbfed0dSDavid du Colombier 	}
1134e17d7c8SDavid du Colombier 	if (needresize)
1144e17d7c8SDavid du Colombier 		reshape();
1154e17d7c8SDavid du Colombier }
1164e17d7c8SDavid du Colombier 
1174e17d7c8SDavid du Colombier void
main(int argc,char * argv[])1184e17d7c8SDavid du Colombier main(int argc, char *argv[])
1194e17d7c8SDavid du Colombier {
120*6bbfed0dSDavid du Colombier 	delay = 1000;
1214e17d7c8SDavid du Colombier 	setrules(".d.d..b..d.d.d.d.d");			/* regular rules */
1224e17d7c8SDavid du Colombier 	ARGBEGIN {
1234e17d7c8SDavid du Colombier 	case '3':
1244e17d7c8SDavid du Colombier 		setrules(".d.d.db.b..d.d.d.d");
1254e17d7c8SDavid du Colombier 		break;					/* 34-life */
1264e17d7c8SDavid du Colombier 	case 'o':
1274e17d7c8SDavid du Colombier 		setrules(".d.d.db.b.b..d.d.d");
1284e17d7c8SDavid du Colombier 		break;					/* lineosc? */
1294e17d7c8SDavid du Colombier 	case 'r':					/* rules from cmdline */
1304e17d7c8SDavid du Colombier 		setrules(EARGF(usage()));
1314e17d7c8SDavid du Colombier 		break;
1324e17d7c8SDavid du Colombier 	default:
1334e17d7c8SDavid du Colombier 		usage();
1344e17d7c8SDavid du Colombier 	} ARGEND
1354e17d7c8SDavid du Colombier 	if (argc != 1)
1364e17d7c8SDavid du Colombier 		usage();
1374e17d7c8SDavid du Colombier 
1384e17d7c8SDavid du Colombier 	initdraw(g9err, 0, argv0);
1394e17d7c8SDavid du Colombier 	einit(Emouse|Ekeyboard);	/* implies rawon() */
1404e17d7c8SDavid du Colombier 
1414e17d7c8SDavid du Colombier 	cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
1424e17d7c8SDavid du Colombier 		Pt(NLIFE * PX, NLIFE * PX)), 2);
1434e17d7c8SDavid du Colombier 	box  = allocimage(display, Rect(0, 0, BX, BX), RGB24, 1, DBlack);
1444e17d7c8SDavid du Colombier 	assert(box != nil);
1454e17d7c8SDavid du Colombier 
1464e17d7c8SDavid du Colombier 	redraw();
1474e17d7c8SDavid du Colombier 	readlife(argv[0]);
1484e17d7c8SDavid du Colombier 	do {
1494e17d7c8SDavid du Colombier 		flushimage(display, 1);
1504e17d7c8SDavid du Colombier 		idle();
1514e17d7c8SDavid du Colombier 		sleep(delay);
1524e17d7c8SDavid du Colombier 		idle();
1534e17d7c8SDavid du Colombier 	} while (generate());
1544e17d7c8SDavid du Colombier 	exits(nil);
1554e17d7c8SDavid du Colombier }
1564e17d7c8SDavid du Colombier 
1574e17d7c8SDavid du Colombier /*
1584e17d7c8SDavid du Colombier  * We can only have interest in a given row (or column) if there
1594e17d7c8SDavid du Colombier  * is something alive in it or in the neighbouring rows (or columns.)
1604e17d7c8SDavid du Colombier  */
1614e17d7c8SDavid du Colombier int
interest(int rc[NLIFE],int i)1624e17d7c8SDavid du Colombier interest(int rc[NLIFE], int i)
1634e17d7c8SDavid du Colombier {
1644e17d7c8SDavid du Colombier 	return(rc[i-1] != 0 || rc[i] != 0 || rc[i+1] != 0);
1654e17d7c8SDavid du Colombier }
1664e17d7c8SDavid du Colombier 
1674e17d7c8SDavid du Colombier /*
1684e17d7c8SDavid du Colombier  * A life generation proceeds in two passes.  The first pass identifies
1694e17d7c8SDavid du Colombier  * cells that have births or deaths.  The `alive bit' is updated, as are
1704e17d7c8SDavid du Colombier  * the screen and the row/col count deltas.  Also, a record is made
1714e17d7c8SDavid du Colombier  * of the cell's address.  In the second pass, the neighbours of all changed
1724e17d7c8SDavid du Colombier  * cells get their neighbour counts updated, and the row/col deltas get
1734e17d7c8SDavid du Colombier  * merged into the row/col count arrays.
1744e17d7c8SDavid du Colombier  *
1754e17d7c8SDavid du Colombier  * The border cells (i==0 || i==NLIFE-1 || j==0 || j==NLIFE-1) are not
1764e17d7c8SDavid du Colombier  * processed, purely for speed reasons.  With a little effort, a wrap-around
1774e17d7c8SDavid du Colombier  * universe could be implemented.
1784e17d7c8SDavid du Colombier  *
1794e17d7c8SDavid du Colombier  * Generate returns 0 if there was no change from the last generation,
1804e17d7c8SDavid du Colombier  * and 1 if there were changes.
1814e17d7c8SDavid du Colombier  */
1824e17d7c8SDavid du Colombier #define	neighbour(di, dj, op) lp[(di)*NLIFE+(dj)] op= 2
1834e17d7c8SDavid du Colombier #define	neighbours(op)\
1844e17d7c8SDavid du Colombier 	neighbour(-1, -1, op);\
1854e17d7c8SDavid du Colombier 	neighbour(-1,  0, op);\
1864e17d7c8SDavid du Colombier 	neighbour(-1,  1, op);\
1874e17d7c8SDavid du Colombier 	neighbour( 0, -1, op);\
1884e17d7c8SDavid du Colombier 	neighbour( 0,  1, op);\
1894e17d7c8SDavid du Colombier 	neighbour( 1, -1, op);\
1904e17d7c8SDavid du Colombier 	neighbour( 1,  0, op);\
1914e17d7c8SDavid du Colombier 	neighbour( 1,  1, op)
1924e17d7c8SDavid du Colombier 
1934e17d7c8SDavid du Colombier int
generate(void)1944e17d7c8SDavid du Colombier generate(void)
1954e17d7c8SDavid du Colombier {
1964e17d7c8SDavid du Colombier 	char *lp;
1974e17d7c8SDavid du Colombier 	char **p, **addp, **delp;
1984e17d7c8SDavid du Colombier 	int i, j, j0 = NLIFE, j1 = -1;
1994e17d7c8SDavid du Colombier 	int drow[NLIFE], dcol[NLIFE];
2004e17d7c8SDavid du Colombier 
2014e17d7c8SDavid du Colombier 	for (j = 1; j != NLIFE - 1; j++) {
2024e17d7c8SDavid du Colombier 		drow[j] = dcol[j] = 0;
2034e17d7c8SDavid du Colombier 		if (interest(col, j)) {
2044e17d7c8SDavid du Colombier 			if (j < j0)
2054e17d7c8SDavid du Colombier 				j0 = j;
2064e17d7c8SDavid du Colombier 			if (j1 < j)
2074e17d7c8SDavid du Colombier 				j1 = j;
2084e17d7c8SDavid du Colombier 		}
2094e17d7c8SDavid du Colombier 	}
2104e17d7c8SDavid du Colombier 	addp = adjust;
2114e17d7c8SDavid du Colombier 	delp = &adjust[NADJUST];
2124e17d7c8SDavid du Colombier 	for (i = 1; i != NLIFE - 1; i++)
2134e17d7c8SDavid du Colombier 		if (interest(row, i)) {
2144e17d7c8SDavid du Colombier 			for (j = j0, lp = &life[i][j0]; j <= j1; j++, lp++)
2154e17d7c8SDavid du Colombier 				switch (action[*lp]) {
2164e17d7c8SDavid du Colombier 				case 'b':
2174e17d7c8SDavid du Colombier 					++*lp;
2184e17d7c8SDavid du Colombier 					++drow[i];
2194e17d7c8SDavid du Colombier 					++dcol[j];
2204e17d7c8SDavid du Colombier 					setbox(i, j);
2214e17d7c8SDavid du Colombier 					*addp++ = lp;
2224e17d7c8SDavid du Colombier 					break;
2234e17d7c8SDavid du Colombier 				case 'd':
2244e17d7c8SDavid du Colombier 					--*lp;
2254e17d7c8SDavid du Colombier 					--drow[i];
2264e17d7c8SDavid du Colombier 					--dcol[j];
2274e17d7c8SDavid du Colombier 					clrbox(i, j);
2284e17d7c8SDavid du Colombier 					*--delp = lp;
2294e17d7c8SDavid du Colombier 					break;
2304e17d7c8SDavid du Colombier 				}
2314e17d7c8SDavid du Colombier 		}
2324e17d7c8SDavid du Colombier 	if (addp == adjust && delp == &adjust[NADJUST])
2334e17d7c8SDavid du Colombier 		return 0;
2344e17d7c8SDavid du Colombier 	if (delp < addp)
2354e17d7c8SDavid du Colombier 		sysfatal("Out of space (delp < addp)");
2364e17d7c8SDavid du Colombier 	p = adjust;
2374e17d7c8SDavid du Colombier 	while (p != addp) {
2384e17d7c8SDavid du Colombier 		lp = *p++;
2394e17d7c8SDavid du Colombier 		neighbours(+);
2404e17d7c8SDavid du Colombier 	}
2414e17d7c8SDavid du Colombier 	p = delp;
2424e17d7c8SDavid du Colombier 	while (p != &adjust[NADJUST]) {
2434e17d7c8SDavid du Colombier 		lp = *p++;
2444e17d7c8SDavid du Colombier 		neighbours(-);
2454e17d7c8SDavid du Colombier 	}
2464e17d7c8SDavid du Colombier 	for (i = 1; i != NLIFE - 1; i++) {
2474e17d7c8SDavid du Colombier 		row[i] += drow[i];
2484e17d7c8SDavid du Colombier 		col[i] += dcol[i];
2494e17d7c8SDavid du Colombier 	}
2504e17d7c8SDavid du Colombier 	if (row[1] || row[NLIFE-2] || col[1] || col[NLIFE-2])
2514e17d7c8SDavid du Colombier 		centerlife();
2524e17d7c8SDavid du Colombier 	return 1;
2534e17d7c8SDavid du Colombier }
2544e17d7c8SDavid du Colombier 
2554e17d7c8SDavid du Colombier /*
2564e17d7c8SDavid du Colombier  * Record a birth at (i,j).
2574e17d7c8SDavid du Colombier  */
2584e17d7c8SDavid du Colombier void
birth(int i,int j)2594e17d7c8SDavid du Colombier birth(int i, int j)
2604e17d7c8SDavid du Colombier {
2614e17d7c8SDavid du Colombier 	char *lp;
2624e17d7c8SDavid du Colombier 
2634e17d7c8SDavid du Colombier 	if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
2644e17d7c8SDavid du Colombier 	    life[i][j] & 1)
2654e17d7c8SDavid du Colombier 		return;
2664e17d7c8SDavid du Colombier 	lp = &life[i][j];
2674e17d7c8SDavid du Colombier 	++*lp;
2684e17d7c8SDavid du Colombier 	++row[i];
2694e17d7c8SDavid du Colombier 	++col[j];
2704e17d7c8SDavid du Colombier 	neighbours(+);
2714e17d7c8SDavid du Colombier 	setbox(i, j);
2724e17d7c8SDavid du Colombier }
2734e17d7c8SDavid du Colombier 
2744e17d7c8SDavid du Colombier /*
2754e17d7c8SDavid du Colombier  * Record a death at (i,j)
2764e17d7c8SDavid du Colombier  */
2774e17d7c8SDavid du Colombier void
death(int i,int j)2784e17d7c8SDavid du Colombier death(int i, int j)
2794e17d7c8SDavid du Colombier {
2804e17d7c8SDavid du Colombier 	char *lp;
2814e17d7c8SDavid du Colombier 
2824e17d7c8SDavid du Colombier 	if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
2834e17d7c8SDavid du Colombier 	    !(life[i][j] & 1))
2844e17d7c8SDavid du Colombier 		return;
2854e17d7c8SDavid du Colombier 	lp = &life[i][j];
2864e17d7c8SDavid du Colombier 	--*lp;
2874e17d7c8SDavid du Colombier 	--row[i];
2884e17d7c8SDavid du Colombier 	--col[j];
2894e17d7c8SDavid du Colombier 	neighbours(-);
2904e17d7c8SDavid du Colombier 	clrbox(i, j);
2914e17d7c8SDavid du Colombier }
2924e17d7c8SDavid du Colombier 
2934e17d7c8SDavid du Colombier void
readlife(char * filename)2944e17d7c8SDavid du Colombier readlife(char *filename)
2954e17d7c8SDavid du Colombier {
2964e17d7c8SDavid du Colombier 	int c, i, j;
2974e17d7c8SDavid du Colombier 	char name[256];
2984e17d7c8SDavid du Colombier 	Biobuf *bp;
2994e17d7c8SDavid du Colombier 
3004e17d7c8SDavid du Colombier 	if ((bp = Bopen(filename, OREAD)) == nil) {
3014e17d7c8SDavid du Colombier 		snprint(name, sizeof name, "/sys/games/lib/life/%s", filename);
3024e17d7c8SDavid du Colombier 		if ((bp = Bopen(name, OREAD)) == nil)
3034e17d7c8SDavid du Colombier 			sysfatal("can't read %s: %r", name);
3044e17d7c8SDavid du Colombier 	}
3054e17d7c8SDavid du Colombier 	draw(screen, screen->r, display->white, nil, ZP);
3064e17d7c8SDavid du Colombier 	for (i = 0; i != NLIFE; i++) {
3074e17d7c8SDavid du Colombier 		row[i] = col[i] = 0;
3084e17d7c8SDavid du Colombier 		for (j = 0; j != NLIFE; j++)
3094e17d7c8SDavid du Colombier 			life[i][j] = 0;
3104e17d7c8SDavid du Colombier 	}
3114e17d7c8SDavid du Colombier 	c = 0;
3124e17d7c8SDavid du Colombier 	for (i = 1; i != NLIFE - 1 && c >= 0; i++) {
3134e17d7c8SDavid du Colombier 		j = 1;
3144e17d7c8SDavid du Colombier 		while ((c = Bgetc(bp)) >= 0 && c != '\n')
3154e17d7c8SDavid du Colombier 			if (j != NLIFE - 1)
3164e17d7c8SDavid du Colombier 				switch (c) {
3174e17d7c8SDavid du Colombier 				case '.':
3184e17d7c8SDavid du Colombier 					j++;
3194e17d7c8SDavid du Colombier 					break;
3204e17d7c8SDavid du Colombier 				case 'x':
3214e17d7c8SDavid du Colombier 					birth(i, j);
3224e17d7c8SDavid du Colombier 					j++;
3234e17d7c8SDavid du Colombier 					break;
3244e17d7c8SDavid du Colombier 				}
3254e17d7c8SDavid du Colombier 	}
3264e17d7c8SDavid du Colombier 	Bterm(bp);
3274e17d7c8SDavid du Colombier 	centerlife();
3284e17d7c8SDavid du Colombier }
3294e17d7c8SDavid du Colombier 
3304e17d7c8SDavid du Colombier int
min(int a,int b)3314e17d7c8SDavid du Colombier min(int a, int b)
3324e17d7c8SDavid du Colombier {
3334e17d7c8SDavid du Colombier 	return(a < b ? a : b);
3344e17d7c8SDavid du Colombier }
3354e17d7c8SDavid du Colombier 
3364e17d7c8SDavid du Colombier void
centerlife(void)3374e17d7c8SDavid du Colombier centerlife(void)
3384e17d7c8SDavid du Colombier {
3394e17d7c8SDavid du Colombier 	int i, j, di, dj, iinc, jinc, t;
3404e17d7c8SDavid du Colombier 
3414e17d7c8SDavid du Colombier 	window();
3424e17d7c8SDavid du Colombier 	di = NLIFE / 2 - (i0 + i1) / 2;
3434e17d7c8SDavid du Colombier 	if (i0 + di < 1)
3444e17d7c8SDavid du Colombier 		di = 1 - i0;
3454e17d7c8SDavid du Colombier 	else if (i1 + di >= NLIFE - 1)
3464e17d7c8SDavid du Colombier 		di = NLIFE - 2 - i1;
3474e17d7c8SDavid du Colombier 	dj = NLIFE / 2 - (j0 + j1) / 2;
3484e17d7c8SDavid du Colombier 	if (j0 + dj < 1)
3494e17d7c8SDavid du Colombier 		dj = 1 - j0;
3504e17d7c8SDavid du Colombier 	else if (j1 + dj >= NLIFE - 1)
3514e17d7c8SDavid du Colombier 		dj = NLIFE - 2 - j1;
3524e17d7c8SDavid du Colombier 	if (di != 0 || dj != 0) {
3534e17d7c8SDavid du Colombier 		if (di > 0) {
3544e17d7c8SDavid du Colombier 			iinc = -1;
3554e17d7c8SDavid du Colombier 			t = i0;
3564e17d7c8SDavid du Colombier 			i0 = i1;
3574e17d7c8SDavid du Colombier 			i1 = t;
3584e17d7c8SDavid du Colombier 		} else
3594e17d7c8SDavid du Colombier 			iinc = 1;
3604e17d7c8SDavid du Colombier 		if (dj > 0) {
3614e17d7c8SDavid du Colombier 			jinc = -1;
3624e17d7c8SDavid du Colombier 			t = j0;
3634e17d7c8SDavid du Colombier 			j0 = j1;
3644e17d7c8SDavid du Colombier 			j1 = t;
3654e17d7c8SDavid du Colombier 		} else
3664e17d7c8SDavid du Colombier 			jinc = 1;
3674e17d7c8SDavid du Colombier 		for (i = i0; i * iinc <= i1 * iinc; i += iinc)
3684e17d7c8SDavid du Colombier 			for (j = j0; j * jinc <= j1 * jinc; j += jinc)
3694e17d7c8SDavid du Colombier 				if (life[i][j] & 1) {
3704e17d7c8SDavid du Colombier 					birth(i + di, j + dj);
3714e17d7c8SDavid du Colombier 					death(i, j);
3724e17d7c8SDavid du Colombier 				}
3734e17d7c8SDavid du Colombier 	}
3744e17d7c8SDavid du Colombier }
3754e17d7c8SDavid du Colombier 
3764e17d7c8SDavid du Colombier void
redraw(void)3774e17d7c8SDavid du Colombier redraw(void)
3784e17d7c8SDavid du Colombier {
3794e17d7c8SDavid du Colombier 	int i, j;
3804e17d7c8SDavid du Colombier 
3814e17d7c8SDavid du Colombier 	window();
3824e17d7c8SDavid du Colombier 	draw(screen, screen->r, display->white, nil, ZP);
3834e17d7c8SDavid du Colombier 	for (i = i0; i <= i1; i++)
3844e17d7c8SDavid du Colombier 		for (j = j0; j <= j1; j++)
3854e17d7c8SDavid du Colombier 			if (life[i][j] & 1)
3864e17d7c8SDavid du Colombier 				setbox(i, j);
3874e17d7c8SDavid du Colombier }
3884e17d7c8SDavid du Colombier 
3894e17d7c8SDavid du Colombier void
window(void)3904e17d7c8SDavid du Colombier window(void)
3914e17d7c8SDavid du Colombier {
3924e17d7c8SDavid du Colombier 	for (i0 = 1; i0 != NLIFE - 2 && row[i0] == 0; i0++)
3934e17d7c8SDavid du Colombier 		;
3944e17d7c8SDavid du Colombier 	for (i1 = NLIFE - 2; i1 != i0 && row[i1] == 0; --i1)
3954e17d7c8SDavid du Colombier 		;
3964e17d7c8SDavid du Colombier 	for (j0 = 1; j0 != NLIFE - 2 && col[j0] == 0; j0++)
3974e17d7c8SDavid du Colombier 		;
3984e17d7c8SDavid du Colombier 	for (j1 = NLIFE - 2; j1 != j0 && col[j1] == 0; --j1)
3994e17d7c8SDavid du Colombier 		;
4004e17d7c8SDavid du Colombier }
4014e17d7c8SDavid du Colombier 
4024e17d7c8SDavid du Colombier static void
reshape(void)4034e17d7c8SDavid du Colombier reshape(void)
4044e17d7c8SDavid du Colombier {
4054e17d7c8SDavid du Colombier //	int dy12;
4064e17d7c8SDavid du Colombier 
4074e17d7c8SDavid du Colombier //	if (needresize) {
4084e17d7c8SDavid du Colombier //		sqwid = Dx(screen->r) / (1 + bdp->cols + 1);
4094e17d7c8SDavid du Colombier //		dy12  = Dy(screen->r) / (1 + bdp->rows + 1 + 2);
4104e17d7c8SDavid du Colombier //		if (sqwid > dy12)
4114e17d7c8SDavid du Colombier //			sqwid = dy12;
4124e17d7c8SDavid du Colombier //		recompute(bdp, sqwid);
4134e17d7c8SDavid du Colombier //	}
4144e17d7c8SDavid du Colombier 	sleep(1000);
4154e17d7c8SDavid du Colombier 	needresize = 0;
4164e17d7c8SDavid du Colombier 	cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
4174e17d7c8SDavid du Colombier 		Pt(NLIFE * PX, NLIFE * PX)), 2);
4184e17d7c8SDavid du Colombier 	redraw();
4194e17d7c8SDavid du Colombier 	flushimage(display, 1);
4204e17d7c8SDavid du Colombier }
4214e17d7c8SDavid du Colombier 
4224e17d7c8SDavid du Colombier /* called from graphics library */
4234e17d7c8SDavid du Colombier void
eresized(int callgetwin)4244e17d7c8SDavid du Colombier eresized(int callgetwin)
4254e17d7c8SDavid du Colombier {
4264e17d7c8SDavid du Colombier 	needresize = callgetwin + 1;
4274e17d7c8SDavid du Colombier 
4284e17d7c8SDavid du Colombier 	/* new window? */
4294e17d7c8SDavid du Colombier 	/* was using Refmesg */
4304e17d7c8SDavid du Colombier 	if (needresize > 1 && getwindow(display, Refnone) < 0)
4314e17d7c8SDavid du Colombier 		sysfatal("can't reattach to window: %r");
4324e17d7c8SDavid du Colombier 
4334e17d7c8SDavid du Colombier 	/* destroyed window? */
4344e17d7c8SDavid du Colombier 	if (Dx(screen->r) == 0 || Dy(screen->r) == 0)
4354e17d7c8SDavid du Colombier 		exits("window gone");
4364e17d7c8SDavid du Colombier 
4374e17d7c8SDavid du Colombier 	reshape();
4384e17d7c8SDavid du Colombier }
439