/* * Conway's Game of Life - in your cursor. * Adapted from /sys/src/games/life.c */ #include #include #include #include #include #include enum { NLIFE = 16, /* life array size */ PX = 1, /* cell spacing */ BX = 1, /* box size */ NADJUST = NLIFE * NLIFE, }; /* * life[i][j] stores L+2*N, where L is 0 if the cell is dead and 1 * if alive, and N is the number of the cell's 8-connected neighbours * which live. * row[i] indicates how many cells are alive in life[i][*]. * col[j] indicates how many cells are alive in life[*][j]. * Adjust contains pointers to cells that need to have their neighbour * counts adjusted in the second pass of the generation procedure. */ char life[NLIFE][NLIFE]; int row[NLIFE]; int col[NLIFE]; char action[18]; /* index by cell contents to find action */ char *adjust[NADJUST]; Point cen; //Image *box; int i0, i1, j0, j1; int needresize; void birth(int, int); void death(int, int); int generate(void); int interest(int [NLIFE], int); void main(int, char *[]); int min(int, int); void readlife(char *); void setrules(char *); void window(void); void setrules(char *r) { char *a; for (a = action; a != &action[nelem(action)]; *a++ = *r++) ; } void usage(void) { fprint(2, "Usage: %s [-d msec] [-3o] [-r rules] file\n", argv0); exits("usage"); } void printcurs() { int i, j, k, m; print(" {-7, -7},\n"); /* Background is clear for now. */ print(" {\n"); for (i = 0 ; i < 4 ; i++) { print(" "); for (j = 0 ; j < 8 ; j++) print("0x00, "); print("\n"); } print(" },\n"); /* Paint live cells black. */ print(" {\n"); for (i = 0 ; i < NLIFE ; i++) { print(" "); for (j = 0, k = 0 ; j < NLIFE ;) { for (m = 0 ; m < 8 ; m++, j++) { k += (life[i][j] & 1) << 7-m; } print("%#x, ", k); k = 0; } print("\n"); } print(" }\n"); } void curslife() { Cursor c = { {-1, -1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, }; int i, j, k, m, p, fd; fd = open("#m/cursor", OWRITE); char curs[2*4+2*2*16]; BPLONG(curs+0*4, c.offset.x); BPLONG(curs+1*4, c.offset.y); memmove(curs+2*4, c.clr, 2*16); memmove(curs+2*4+2*16, c.set, 2*16); for (i = 0, p = 2*4 ; i < NLIFE ; i++) { for (j = 0, k = 0 ; j < NLIFE ;) { for (m = 0 ; m < 8 ; m++, j++) { k += (life[i][j] & 1) << 7-m; } curs[p] = k; p++; k = 0; } } write(fd, curs, 2*4+2*2*16); close(fd); } void main(int argc, char *argv[]) { int delay = 1000; int verbose = 0; setrules(".d.d..b..d.d.d.d.d"); /* regular rules */ ARGBEGIN { case '3': setrules(".d.d.db.b..d.d.d.d"); break; /* 34-life */ case 'o': setrules(".d.d.db.b.b..d.d.d"); break; /* lineosc? */ case 'r': /* rules from cmdline */ setrules(EARGF(usage())); break; case 'd': delay = atoi(EARGF(usage())); break; case 'v': verbose++; break; default: usage(); } ARGEND if (argc != 1) usage(); readlife(argv[0]); do { sleep(delay); // printcurs(); curslife(); } while (generate()); exits(nil); } /* * We can only have interest in a given row (or column) if there * is something alive in it or in the neighbouring rows (or columns.) */ int interest(int rc[NLIFE], int i) { return(rc[i-1] != 0 || rc[i] != 0 || rc[i+1] != 0); } /* * A life generation proceeds in two passes. The first pass identifies * cells that have births or deaths. The `alive bit' is updated, as are * the screen and the row/col count deltas. Also, a record is made * of the cell's address. In the second pass, the neighbours of all changed * cells get their neighbour counts updated, and the row/col deltas get * merged into the row/col count arrays. * * The border cells (i==0 || i==NLIFE-1 || j==0 || j==NLIFE-1) are not * processed, purely for speed reasons. With a little effort, a wrap-around * universe could be implemented. * * Generate returns 0 if there was no change from the last generation, * and 1 if there were changes. */ #define neighbour(di, dj, op) lp[(di)*NLIFE+(dj)] op= 2 #define neighbours(op)\ neighbour(-1, -1, op);\ neighbour(-1, 0, op);\ neighbour(-1, 1, op);\ neighbour( 0, -1, op);\ neighbour( 0, 1, op);\ neighbour( 1, -1, op);\ neighbour( 1, 0, op);\ neighbour( 1, 1, op) int generate(void) { char *lp; char **p, **addp, **delp; int i, j, j0 = NLIFE, j1 = -1; int drow[NLIFE], dcol[NLIFE]; for (j = 1; j != NLIFE - 1; j++) { drow[j] = dcol[j] = 0; if (interest(col, j)) { if (j < j0) j0 = j; if (j1 < j) j1 = j; } } addp = adjust; delp = &adjust[NADJUST]; for (i = 1; i != NLIFE - 1; i++) if (interest(row, i)) { for (j = j0, lp = &life[i][j0]; j <= j1; j++, lp++) switch (action[*lp]) { case 'b': ++*lp; ++drow[i]; ++dcol[j]; *addp++ = lp; break; case 'd': --*lp; --drow[i]; --dcol[j]; *--delp = lp; break; } } if (addp == adjust && delp == &adjust[NADJUST]) return 0; if (delp < addp) sysfatal("Out of space (delp < addp)"); p = adjust; while (p != addp) { lp = *p++; neighbours(+); } p = delp; while (p != &adjust[NADJUST]) { lp = *p++; neighbours(-); } for (i = 1; i != NLIFE - 1; i++) { row[i] += drow[i]; col[i] += dcol[i]; } return 1; } /* * Record a birth at (i,j). */ void birth(int i, int j) { char *lp; if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j || life[i][j] & 1) return; lp = &life[i][j]; ++*lp; ++row[i]; ++col[j]; neighbours(+); } /* * Record a death at (i,j) */ void death(int i, int j) { char *lp; if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j || !(life[i][j] & 1)) return; lp = &life[i][j]; --*lp; --row[i]; --col[j]; neighbours(-); } void readlife(char *filename) { int c, i, j; char name[256]; Biobuf *bp; if ((bp = Bopen(filename, OREAD)) == nil) { snprint(name, sizeof name, "/sys/games/lib/life/%s", filename); if ((bp = Bopen(name, OREAD)) == nil) sysfatal("can't read %s: %r", name); } for (i = 0; i != NLIFE; i++) { row[i] = col[i] = 0; for (j = 0; j != NLIFE; j++) life[i][j] = 0; } c = 0; for (i = 1; i != NLIFE - 1 && c >= 0; i++) { j = 1; while ((c = Bgetc(bp)) >= 0 && c != '\n') if (j != NLIFE - 1) switch (c) { case '.': j++; break; case 'x': birth(i, j); j++; break; } } Bterm(bp); } int min(int a, int b) { return(a < b ? a : b); } void window(void) { for (i0 = 1; i0 != NLIFE - 2 && row[i0] == 0; i0++) ; for (i1 = NLIFE - 2; i1 != i0 && row[i1] == 0; --i1) ; for (j0 = 1; j0 != NLIFE - 2 && col[j0] == 0; j0++) ; for (j1 = NLIFE - 2; j1 != j0 && col[j1] == 0; --j1) ; }