/*
* Conway's Game of Life - in your cursor.
* Adapted from /sys/src/games/life.c
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <cursor.h>
#include <mouse.h>
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)
;
}
|