life.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  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
  5. */
  6. #include <u.h>
  7. #include <libc.h>
  8. #include <bio.h>
  9. #include <draw.h>
  10. #include <event.h>
  11. enum {
  12. NLIFE = 256, /* life array size */
  13. PX = 4, /* cell spacing */
  14. BX = PX - 1, /* box size */
  15. NADJUST = NLIFE * NLIFE,
  16. };
  17. /*
  18. * life[i][j] stores L+2*N, where L is 0 if the cell is dead and 1
  19. * if alive, and N is the number of the cell's 8-connected neighbours
  20. * which live.
  21. * row[i] indicates how many cells are alive in life[i][*].
  22. * col[j] indicates how many cells are alive in life[*][j].
  23. * Adjust contains pointers to cells that need to have their neighbour
  24. * counts adjusted in the second pass of the generation procedure.
  25. */
  26. char life[NLIFE][NLIFE];
  27. int row[NLIFE];
  28. int col[NLIFE];
  29. char action[18]; /* index by cell contents to find action */
  30. char *adjust[NADJUST];
  31. Point cen;
  32. Image *box;
  33. int i0, i1, j0, j1;
  34. int needresize;
  35. void birth(int, int);
  36. void centerlife(void);
  37. void death(int, int);
  38. int generate(void);
  39. int interest(int [NLIFE], int);
  40. void main(int, char *[]);
  41. int min(int, int);
  42. void readlife(char *);
  43. void redraw(void);
  44. void setrules(char *);
  45. void window(void);
  46. static void reshape(void);
  47. static void
  48. setbox(int i, int j)
  49. {
  50. Point loc;
  51. loc = Pt(cen.x + j*PX, cen.y + i*PX);
  52. draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), box, nil, ZP);
  53. }
  54. static void
  55. clrbox(int i, int j)
  56. {
  57. Point loc;
  58. loc = Pt(cen.x + j*PX, cen.y + i*PX);
  59. draw(screen, Rpt(loc, addpt(loc, Pt(BX, BX))), display->white, nil, ZP);
  60. }
  61. void
  62. setrules(char *r)
  63. {
  64. char *a;
  65. for (a = action; a != &action[nelem(action)]; *a++ = *r++)
  66. ;
  67. }
  68. static void
  69. g9err(Display *, char *err)
  70. {
  71. static int entered = 0;
  72. fprint(2, "%s: %s (%r)\n", argv0, err);
  73. exits(err);
  74. }
  75. void
  76. usage(void)
  77. {
  78. fprint(2, "Usage: %s [-3o] [-r rules] file\n", argv0);
  79. exits("usage");
  80. }
  81. void
  82. idle(void)
  83. {
  84. int c;
  85. while (ecanmouse())
  86. emouse(); /* throw away mouse events */
  87. while (ecankbd())
  88. if ((c = ekbd()) == 'q' || c == 0177) /* watch keyboard ones */
  89. exits(nil);
  90. if (needresize)
  91. reshape();
  92. }
  93. void
  94. main(int argc, char *argv[])
  95. {
  96. int delay = 1000;
  97. setrules(".d.d..b..d.d.d.d.d"); /* regular rules */
  98. ARGBEGIN {
  99. case '3':
  100. setrules(".d.d.db.b..d.d.d.d");
  101. break; /* 34-life */
  102. case 'o':
  103. setrules(".d.d.db.b.b..d.d.d");
  104. break; /* lineosc? */
  105. case 'r': /* rules from cmdline */
  106. setrules(EARGF(usage()));
  107. break;
  108. default:
  109. usage();
  110. } ARGEND
  111. if (argc != 1)
  112. usage();
  113. initdraw(g9err, 0, argv0);
  114. einit(Emouse|Ekeyboard); /* implies rawon() */
  115. cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
  116. Pt(NLIFE * PX, NLIFE * PX)), 2);
  117. box = allocimage(display, Rect(0, 0, BX, BX), RGB24, 1, DBlack);
  118. assert(box != nil);
  119. redraw();
  120. readlife(argv[0]);
  121. do {
  122. flushimage(display, 1);
  123. idle();
  124. sleep(delay);
  125. idle();
  126. } while (generate());
  127. exits(nil);
  128. }
  129. /*
  130. * We can only have interest in a given row (or column) if there
  131. * is something alive in it or in the neighbouring rows (or columns.)
  132. */
  133. int
  134. interest(int rc[NLIFE], int i)
  135. {
  136. return(rc[i-1] != 0 || rc[i] != 0 || rc[i+1] != 0);
  137. }
  138. /*
  139. * A life generation proceeds in two passes. The first pass identifies
  140. * cells that have births or deaths. The `alive bit' is updated, as are
  141. * the screen and the row/col count deltas. Also, a record is made
  142. * of the cell's address. In the second pass, the neighbours of all changed
  143. * cells get their neighbour counts updated, and the row/col deltas get
  144. * merged into the row/col count arrays.
  145. *
  146. * The border cells (i==0 || i==NLIFE-1 || j==0 || j==NLIFE-1) are not
  147. * processed, purely for speed reasons. With a little effort, a wrap-around
  148. * universe could be implemented.
  149. *
  150. * Generate returns 0 if there was no change from the last generation,
  151. * and 1 if there were changes.
  152. */
  153. #define neighbour(di, dj, op) lp[(di)*NLIFE+(dj)] op= 2
  154. #define neighbours(op)\
  155. neighbour(-1, -1, op);\
  156. neighbour(-1, 0, op);\
  157. neighbour(-1, 1, op);\
  158. neighbour( 0, -1, op);\
  159. neighbour( 0, 1, op);\
  160. neighbour( 1, -1, op);\
  161. neighbour( 1, 0, op);\
  162. neighbour( 1, 1, op)
  163. int
  164. generate(void)
  165. {
  166. char *lp;
  167. char **p, **addp, **delp;
  168. int i, j, j0 = NLIFE, j1 = -1;
  169. int drow[NLIFE], dcol[NLIFE];
  170. for (j = 1; j != NLIFE - 1; j++) {
  171. drow[j] = dcol[j] = 0;
  172. if (interest(col, j)) {
  173. if (j < j0)
  174. j0 = j;
  175. if (j1 < j)
  176. j1 = j;
  177. }
  178. }
  179. addp = adjust;
  180. delp = &adjust[NADJUST];
  181. for (i = 1; i != NLIFE - 1; i++)
  182. if (interest(row, i)) {
  183. for (j = j0, lp = &life[i][j0]; j <= j1; j++, lp++)
  184. switch (action[*lp]) {
  185. case 'b':
  186. ++*lp;
  187. ++drow[i];
  188. ++dcol[j];
  189. setbox(i, j);
  190. *addp++ = lp;
  191. break;
  192. case 'd':
  193. --*lp;
  194. --drow[i];
  195. --dcol[j];
  196. clrbox(i, j);
  197. *--delp = lp;
  198. break;
  199. }
  200. }
  201. if (addp == adjust && delp == &adjust[NADJUST])
  202. return 0;
  203. if (delp < addp)
  204. sysfatal("Out of space (delp < addp)");
  205. p = adjust;
  206. while (p != addp) {
  207. lp = *p++;
  208. neighbours(+);
  209. }
  210. p = delp;
  211. while (p != &adjust[NADJUST]) {
  212. lp = *p++;
  213. neighbours(-);
  214. }
  215. for (i = 1; i != NLIFE - 1; i++) {
  216. row[i] += drow[i];
  217. col[i] += dcol[i];
  218. }
  219. if (row[1] || row[NLIFE-2] || col[1] || col[NLIFE-2])
  220. centerlife();
  221. return 1;
  222. }
  223. /*
  224. * Record a birth at (i,j).
  225. */
  226. void
  227. birth(int i, int j)
  228. {
  229. char *lp;
  230. if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
  231. life[i][j] & 1)
  232. return;
  233. lp = &life[i][j];
  234. ++*lp;
  235. ++row[i];
  236. ++col[j];
  237. neighbours(+);
  238. setbox(i, j);
  239. }
  240. /*
  241. * Record a death at (i,j)
  242. */
  243. void
  244. death(int i, int j)
  245. {
  246. char *lp;
  247. if (i < 1 || NLIFE - 1 <= i || j < 1 || NLIFE - 1 <= j ||
  248. !(life[i][j] & 1))
  249. return;
  250. lp = &life[i][j];
  251. --*lp;
  252. --row[i];
  253. --col[j];
  254. neighbours(-);
  255. clrbox(i, j);
  256. }
  257. void
  258. readlife(char *filename)
  259. {
  260. int c, i, j;
  261. char name[256];
  262. Biobuf *bp;
  263. if ((bp = Bopen(filename, OREAD)) == nil) {
  264. snprint(name, sizeof name, "/sys/games/lib/life/%s", filename);
  265. if ((bp = Bopen(name, OREAD)) == nil)
  266. sysfatal("can't read %s: %r", name);
  267. }
  268. draw(screen, screen->r, display->white, nil, ZP);
  269. for (i = 0; i != NLIFE; i++) {
  270. row[i] = col[i] = 0;
  271. for (j = 0; j != NLIFE; j++)
  272. life[i][j] = 0;
  273. }
  274. c = 0;
  275. for (i = 1; i != NLIFE - 1 && c >= 0; i++) {
  276. j = 1;
  277. while ((c = Bgetc(bp)) >= 0 && c != '\n')
  278. if (j != NLIFE - 1)
  279. switch (c) {
  280. case '.':
  281. j++;
  282. break;
  283. case 'x':
  284. birth(i, j);
  285. j++;
  286. break;
  287. }
  288. }
  289. Bterm(bp);
  290. centerlife();
  291. }
  292. int
  293. min(int a, int b)
  294. {
  295. return(a < b ? a : b);
  296. }
  297. void
  298. centerlife(void)
  299. {
  300. int i, j, di, dj, iinc, jinc, t;
  301. window();
  302. di = NLIFE / 2 - (i0 + i1) / 2;
  303. if (i0 + di < 1)
  304. di = 1 - i0;
  305. else if (i1 + di >= NLIFE - 1)
  306. di = NLIFE - 2 - i1;
  307. dj = NLIFE / 2 - (j0 + j1) / 2;
  308. if (j0 + dj < 1)
  309. dj = 1 - j0;
  310. else if (j1 + dj >= NLIFE - 1)
  311. dj = NLIFE - 2 - j1;
  312. if (di != 0 || dj != 0) {
  313. if (di > 0) {
  314. iinc = -1;
  315. t = i0;
  316. i0 = i1;
  317. i1 = t;
  318. } else
  319. iinc = 1;
  320. if (dj > 0) {
  321. jinc = -1;
  322. t = j0;
  323. j0 = j1;
  324. j1 = t;
  325. } else
  326. jinc = 1;
  327. for (i = i0; i * iinc <= i1 * iinc; i += iinc)
  328. for (j = j0; j * jinc <= j1 * jinc; j += jinc)
  329. if (life[i][j] & 1) {
  330. birth(i + di, j + dj);
  331. death(i, j);
  332. }
  333. }
  334. }
  335. void
  336. redraw(void)
  337. {
  338. int i, j;
  339. window();
  340. draw(screen, screen->r, display->white, nil, ZP);
  341. for (i = i0; i <= i1; i++)
  342. for (j = j0; j <= j1; j++)
  343. if (life[i][j] & 1)
  344. setbox(i, j);
  345. }
  346. void
  347. window(void)
  348. {
  349. for (i0 = 1; i0 != NLIFE - 2 && row[i0] == 0; i0++)
  350. ;
  351. for (i1 = NLIFE - 2; i1 != i0 && row[i1] == 0; --i1)
  352. ;
  353. for (j0 = 1; j0 != NLIFE - 2 && col[j0] == 0; j0++)
  354. ;
  355. for (j1 = NLIFE - 2; j1 != j0 && col[j1] == 0; --j1)
  356. ;
  357. }
  358. static void
  359. reshape(void)
  360. {
  361. // int dy12;
  362. // if (needresize) {
  363. // sqwid = Dx(screen->r) / (1 + bdp->cols + 1);
  364. // dy12 = Dy(screen->r) / (1 + bdp->rows + 1 + 2);
  365. // if (sqwid > dy12)
  366. // sqwid = dy12;
  367. // recompute(bdp, sqwid);
  368. // }
  369. sleep(1000);
  370. needresize = 0;
  371. cen = divpt(subpt(addpt(screen->r.min, screen->r.max),
  372. Pt(NLIFE * PX, NLIFE * PX)), 2);
  373. redraw();
  374. flushimage(display, 1);
  375. }
  376. /* called from graphics library */
  377. void
  378. eresized(int callgetwin)
  379. {
  380. needresize = callgetwin + 1;
  381. /* new window? */
  382. /* was using Refmesg */
  383. if (needresize > 1 && getwindow(display, Refnone) < 0)
  384. sysfatal("can't reattach to window: %r");
  385. /* destroyed window? */
  386. if (Dx(screen->r) == 0 || Dy(screen->r) == 0)
  387. exits("window gone");
  388. reshape();
  389. }