life.c 8.7 KB

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