2 * life - john conways's game of life (and variations),
3 * sci. am. 223, october 1970, pp. 120—123, or
4 * http://en.wikipedia.org/wiki/Conway's_Game_of_Life
13 NLIFE = 256, /* life array size */
14 PX = 4, /* cell spacing */
15 BX = PX - 1, /* box size */
16 NADJUST = NLIFE * NLIFE,
20 * life[i][j] stores L+2*N, where L is 0 if the cell is dead and 1
21 * if alive, and N is the number of the cell's 8-connected neighbours
23 * row[i] indicates how many cells are alive in life[i][*].
24 * col[j] indicates how many cells are alive in life[*][j].
25 * Adjust contains pointers to cells that need to have their neighbour
26 * counts adjusted in the second pass of the generation procedure.
28 char life[NLIFE][NLIFE];
31 char action[18]; /* index by cell contents to find action */
32 char *adjust[NADJUST];
41 void centerlife(void);
44 int interest(int [NLIFE], int);
45 void main(int, char *[]);
47 void readlife(char *);
49 void setrules(char *);
52 static void reshape(void);
59 loc = Pt(cen.x + j*PX, cen.y + i*PX);
60 draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), box, nil, ZP);
68 loc = Pt(cen.x + j*PX, cen.y + i*PX);
69 draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), bg, nil, ZP);
77 for (a = action; a != &action[nelem(action)]; *a++ = *r++)
82 g9err(Display *, char *err)
84 static int entered = 0;
86 fprint(2, "%s: %s (%r)\n", argv0, err);
93 fprint(2, "Usage: %s [-3bo] [-d delay] [-r rules] file\n", argv0);
103 emouse(); /* throw away mouse events */
105 if ((c = ekbd()) == 'q' || c == 0177) /* watch keyboard ones */
112 main(int argc, char *argv[])
116 setrules(".d.d..b..d.d.d.d.d"); /* regular rules */
119 setrules(".d.d.db.b..d.d.d.d");
125 delay = atoi(EARGF(usage()));
127 sysfatal("invalid delay: %d", delay);
130 setrules(".d.d.db.b.b..d.d.d");
131 break; /* lineosc? */
132 case 'r': /* rules from cmdline */
133 setrules(EARGF(usage()));
141 initdraw(g9err, 0, argv0);
142 einit(Emouse|Ekeyboard); /* implies rawon() */
144 cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
145 Pt(NLIFE * PX, NLIFE * PX)), 2);
147 bg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DWhite^reverse);
149 sysfatal("allocimage: %r");
150 box = allocimage(display, Rect(0,0,BX,BX), screen->chan, 1, DBlack^reverse);
152 sysfatal("allocimage: %r");
157 flushimage(display, 1);
161 } while (generate());
166 * We can only have interest in a given row (or column) if there
167 * is something alive in it or in the neighbouring rows (or columns.)
170 interest(int rc[NLIFE], int i)
172 return(rc[i-1] != 0 || rc[i] != 0 || rc[i+1] != 0);
176 * A life generation proceeds in two passes. The first pass identifies
177 * cells that have births or deaths. The `alive bit' is updated, as are
178 * the screen and the row/col count deltas. Also, a record is made
179 * of the cell's address. In the second pass, the neighbours of all changed
180 * cells get their neighbour counts updated, and the row/col deltas get
181 * merged into the row/col count arrays.
183 * The border cells (i==0 || i==NLIFE-1 || j==0 || j==NLIFE-1) are not
184 * processed, purely for speed reasons. With a little effort, a wrap-around
185 * universe could be implemented.
187 * Generate returns 0 if there was no change from the last generation,
188 * and 1 if there were changes.
190 #define neighbour(di, dj, op) lp[(di)*NLIFE+(dj)] op= 2
191 #define neighbours(op)\
192 neighbour(-1, -1, op);\
193 neighbour(-1, 0, op);\
194 neighbour(-1, 1, op);\
195 neighbour( 0, -1, op);\
196 neighbour( 0, 1, op);\
197 neighbour( 1, -1, op);\
198 neighbour( 1, 0, op);\
205 char **p, **addp, **delp;
206 int i, j, j0 = NLIFE, j1 = -1;
207 int drow[NLIFE], dcol[NLIFE];
209 for (j = 1; j != NLIFE - 1; j++) {
210 drow[j] = dcol[j] = 0;
211 if (interest(col, j)) {
219 delp = &adjust[NADJUST];
220 for (i = 1; i != NLIFE - 1; i++)
221 if (interest(row, i)) {
222 for (j = j0, lp = &life[i][j0]; j <= j1; j++, lp++)
223 switch (action[*lp]) {
240 if (addp == adjust && delp == &adjust[NADJUST])
243 sysfatal("Out of space (delp < addp)");
250 while (p != &adjust[NADJUST]) {
254 for (i = 1; i != NLIFE - 1; i++) {
258 if (row[1] || row[NLIFE-2] || col[1] || col[NLIFE-2])
264 * Record a birth at (i,j).
271 if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
283 * Record a death at (i,j)
290 if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
302 readlife(char *filename)
308 if ((bp = Bopen(filename, OREAD)) == nil) {
309 snprint(name, sizeof name, "/sys/games/lib/life/%s", filename);
310 if ((bp = Bopen(name, OREAD)) == nil)
311 sysfatal("can't read %s: %r", name);
313 draw(screen, screen->r, bg, nil, ZP);
314 for (i = 0; i != NLIFE; i++) {
316 for (j = 0; j != NLIFE; j++)
320 for (i = 1; i != NLIFE - 1 && c >= 0; i++) {
322 while ((c = Bgetc(bp)) >= 0 && c != '\n')
341 return(a < b ? a : b);
347 int i, j, di, dj, iinc, jinc, t;
350 di = NLIFE / 2 - (i0 + i1) / 2;
353 else if (i1 + di >= NLIFE - 1)
355 dj = NLIFE / 2 - (j0 + j1) / 2;
358 else if (j1 + dj >= NLIFE - 1)
360 if (di != 0 || dj != 0) {
375 for (i = i0; i * iinc <= i1 * iinc; i += iinc)
376 for (j = j0; j * jinc <= j1 * jinc; j += jinc)
377 if (life[i][j] & 1) {
378 birth(i + di, j + dj);
390 draw(screen, screen->r, bg, nil, ZP);
391 for (i = i0; i <= i1; i++)
392 for (j = j0; j <= j1; j++)
400 for (i0 = 1; i0 != NLIFE - 2 && row[i0] == 0; i0++)
402 for (i1 = NLIFE - 2; i1 != i0 && row[i1] == 0; --i1)
404 for (j0 = 1; j0 != NLIFE - 2 && col[j0] == 0; j0++)
406 for (j1 = NLIFE - 2; j1 != j0 && col[j1] == 0; --j1)
416 // sqwid = Dx(screen->r) / (1 + bdp->cols + 1);
417 // dy12 = Dy(screen->r) / (1 + bdp->rows + 1 + 2);
420 // recompute(bdp, sqwid);
424 cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
425 Pt(NLIFE * PX, NLIFE * PX)), 2);
427 flushimage(display, 1);
430 /* called from graphics library */
432 eresized(int callgetwin)
434 needresize = callgetwin + 1;
437 /* was using Refmesg */
438 if (needresize > 1 && getwindow(display, Refnone) < 0)
439 sysfatal("can't reattach to window: %r");
441 /* destroyed window? */
442 if (Dx(screen->r) == 0 || Dy(screen->r) == 0)
443 exits("window gone");