1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* life1d --- Stephen Wolfram's 1d game of Life */
3 
4 #if 0
5 static const char sccsid[] = "@(#)life1d.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1995 by David Bagley.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 27-Oct-1997: xpm and ras capability added... it does not make too much
27  *              sense here but ...
28  * 10-May-1997: Compatible with xscreensaver
29  * 27-Jul-1995: written, used life.c as a basis, using totalistic rules
30  *              (default).  Special thanks to Harold V. McIntosh
31  *              <mcintosh@servidor.unam.mx> for providing me with the
32  *              LCAU collection and references.
33  *
34  * References:
35  * Dewdney, A.K., "The Armchair Universe, Computer Recreations from the
36  *   Pages of Scientific American Magazine", W.H. Freedman and Company,
37  *   New York, 1988 (May 1985).
38  * Perry, Kenneth E., "Abstract Mathematical Art", BYTE, December, 1986
39  *   pp. 181-192
40  * Hayes, Brian, "Computer Recreations", Scientific American, March 1984,
41  *   p. 12
42  * Wolfram, Stephen, "Cellular automata as models of complexity", Nature,
43  *   4 October 1984, pp. 419-424
44  * Wolfram, Stephen, "Computer Software in Science and Mathematics",
45  *   Scientific American, September 1984, pp. 188-203
46  *
47  */
48 
49 #ifdef STANDALONE
50 # define MODE_life1d
51 # define DEFAULTS	"*delay: 10000 \n" \
52 			"*cycles: 10 \n" \
53 			"*size: 0 \n" \
54 			"*ncolors: 200 \n" \
55 			"*bitmap: \n" \
56 			"*verbose: \n" \
57 
58 # define reshape_life1d 0
59 # define life1d_handle_event 0
60 #include "xlockmore.h"		/* in xscreensaver distribution */
61 #else /* STANDALONE */
62 # include "xlock.h"		/* in xlockmore distribution */
63 # include "color.h"
64 #define DO_STIPPLE
65 #include "iostuff.h"
66 #endif /* STANDALONE */
67 #include "automata.h"
68 
69 #ifdef MODE_life1d
70 
71 #define DEF_TOTALISTIC  "True"	/* False is LCAU */
72 
73 static Bool totalistic;
74 
75 static XrmOptionDescRec opts[] =
76 {
77 	{(char *) "-totalistic", (char *) ".life1d.totalistic", XrmoptionNoArg, (caddr_t) "on"},
78 	{(char *) "+totalistic", (char *) ".life1d.totalistic", XrmoptionNoArg, (caddr_t) "off"}
79 };
80 static argtype vars[] =
81 {
82 	{(void *) & totalistic, (char *) "totalistic", (char *) "Totalistic", (char *) DEF_TOTALISTIC, t_Bool}
83 };
84 static OptionStruct desc[] =
85 {
86 	{(char *) "-/+totalistic", (char *) "turn on/off totalistic rules (else LCAU rules)"}
87 };
88 
89 ENTRYPOINT ModeSpecOpt life1d_opts =
90 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
91 
92 #ifdef USE_MODULES
93 ModStruct   life1d_description =
94 {"life1d", "init_life1d", "draw_life1d", "release_life1d",
95  "refresh_life1d", "init_life1d", "free_life1d", &life1d_opts,
96  10000, 1, 10, 0, 64, 1.0, "",
97  "Shows Wolfram's game of 1D Life", 0, NULL};
98 
99 #endif
100 
101 #ifndef STANDALONE
102 #define LIFE1DBITS(n,w,h)\
103   if ((lp->pixmaps[lp->init_bits]=\
104   XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
105   free_life1d_screen(display,lp); return;} else {lp->init_bits++;}
106 
107 /* aliases for vars defined in the bitmap file */
108 #define CELL_WIDTH   image_width
109 #define CELL_HEIGHT    image_height
110 #define CELL_BITS    image_bits
111 
112 #include "life1d.xbm"
113 
114 #ifdef HAVE_XPM
115 static char *image_name[] =
116 {(char *) ""};
117 #endif
118 
119 #define CELL_NAME image_name
120 #define DEFAULT_XPM 0
121 #endif
122 
123 static int  maxstates;
124 static int  maxradius;
125 static int  maxsum_size;
126 
127 #define MINGRIDSIZE 10
128 #define MINSIZE 2		/* 3 may be better here */
129 #define MAXSTATES 5
130 
131 typedef struct {
132 	int         init_bits;
133 	int         pixelmode;
134 	int         xs, ys, xb, yb;	/* cell size, grid border */
135 	int         screen_generation, row;
136 	int         nrows, ncols, border;
137 	int         width, height;
138 	int         k, r;
139 	long        code;
140 	int         repeating;
141 	char       *nextstate;
142 	int         colors[MAXSTATES];
143 	Pixmap      pixmaps[MAXSTATES];
144 	GC          stippledGC;
145 	unsigned char *newcells;
146 	unsigned char *oldcells;
147 	unsigned char *buffer;
148 	unsigned char *previousBuffer;
149 	XImage     *logo;
150 	Colormap    cmap;
151 	unsigned long black;
152 	int         graphics_format;
153 	GC          backGC;
154 	int         busyLoop;
155 } life1dstruct;
156 
157 static life1dstruct *life1ds = (life1dstruct *) NULL;
158 
159 static int  totalistic_rules[][3] =
160 {
161 
162   /* Well behaved rules */
163   /* Scientific American (Dewdney) */
164 	{2, 2, 20},
165 	{2, 2, 52},		/* A bit too prolific but I like it anyway */
166 	{2, 3, 88},		/* James K. Park's 1D Gun (1111111111011) */
167 	{2, 4, 368},
168 
169   /* Nature */
170 	{3, 1, 792},
171 
172   /* BYTE (Translated to Wolfram's notation) */
173 	{4, 1, 39744},
174 	{4, 1, 81036},
175 	{4, 1, 126092},
176 	{4, 1, 147824},
177 	{4, 1, 156272},
178 	{4, 1, 189468},
179 	{4, 1, 176412},
180 	{4, 1, 214840},
181 	{4, 1, 245028},
182 	{4, 1, 267320},
183 	{4, 1, 257808},
184 	{4, 1, 258596},
185 	{4, 1, 260224},
186 	{4, 1, 267408},
187 	{4, 1, 290960},
188 	{4, 1, 330056},
189 	{4, 1, 330436},
190 	{4, 1, 400192},
191 	{4, 1, 433296},
192 	{4, 1, 434492},
193 	{4, 1, 447368},
194 	{4, 1, 453768},
195 	{4, 1, 454416},
196 	{4, 1, 485488},
197 	{4, 1, 505904},
198 	{4, 1, 618960},
199 	{4, 1, 642948},
200 	{4, 1, 680528},
201 	{4, 1, 708484},
202 	{4, 1, 741004},
203 	{4, 1, 749708},
204 	{4, 1, 756420},
205 	{4, 1, 761356},
206 	{4, 1, 769088},
207 	{4, 1, 778568},
208 	{4, 1, 779792},
209 	{4, 1, 797456},
210 	{4, 1, 803728},
211 	{4, 1, 844092},
212 	{4, 1, 874524},
213 	{4, 1, 881440},
214 	{4, 1, 921476},
215 	{4, 1, 936832},
216 	{4, 1, 937792},
217 	{4, 1, 1004600},
218 
219   /* Nature */
220 	{5, 1, 580020},
221 	{5, 1, 5694390},
222 	{5, 1, 59123000},
223 
224 #if 0				/* OK but annoying */
225 
226   /* BYTE */
227 	{4, 1, 10552},
228 	{4, 1, 14708},
229 	{4, 1, 25284},
230 	{4, 1, 42848},
231 	{4, 1, 44328},
232 	{4, 1, 51788},
233 	{4, 1, 107364},
234 	{4, 1, 111448},
235 	{4, 1, 155848},
236 	{4, 1, 173024},
237 	{4, 1, 224148},
238 	{4, 1, 238372},
239 	{4, 1, 241656},
240 	{4, 1, 243764},
241 	{4, 1, 255856},
242 	{4, 1, 259222},
243 	{4, 1, 310148},
244 	{4, 1, 324148},
245 	{4, 1, 346696},
246 	{4, 1, 364424},
247 	{4, 1, 403652},
248 	{4, 1, 436072},
249 	{4, 1, 456708},
250 	{4, 1, 461912},
251 	{4, 1, 534812},
252 	{4, 1, 546700},
253 	{4, 1, 552708},
254 	{4, 1, 569092},
255 	{4, 1, 616736},
256 	{4, 1, 658564},
257 	{4, 1, 717956},
258 	{4, 1, 748432},
259 	{4, 1, 800964},
260 	{4, 1, 800972},
261 	{4, 1, 801144},
262 	{4, 1, 821116},
263 	{4, 1, 840172},
264 	{4, 1, 858312},
265 	{4, 1, 865394},
266 	{4, 1, 914952},
267 	{4, 1, 919244},
268 	{4, 1, 984296},
269 	{4, 1, 997964},
270 	{4, 1, 1018488},
271 	{4, 1, 1018808},
272 	{4, 1, 1023864},
273 	{4, 1, 1024472},
274 	{4, 1, 1033776},
275 	{4, 1, 1033784},
276 	{4, 1, 1034552},
277 
278   /* Nature */
279 	{5, 1, 583330},
280 	{5, 1, 672900},
281 #endif
282 
283 #if 0				/* rejects */
284   /* Nature */
285 	{2, 1, 4},
286 	{2, 3, 18},
287 	{2, 3, 22},
288 	{2, 3, 90},
289 	{2, 3, 94},
290 	{2, 3, 126},
291 	{2, 3, 128},
292 
293   /* Scientific American (Dewdney) */
294 	{2, 3, 54},
295 	{3, 2, 66},		/* RIPPLE, Dewdney's personal 1d rule */
296 	{3, 1, 257},
297 
298   /* BYTE */
299 	{4, 1, 16},
300 	{4, 1, 56},
301 	{4, 1, 4408},
302 	{4, 1, 101988},
303 	{4, 1, 113688},
304 	{4, 1, 227892},
305 	{4, 1, 254636},
306 	{4, 1, 258598},
307 	{4, 1, 294146},
308 	{4, 1, 377576},
309 	{4, 1, 472095},
310 	{4, 1, 538992},
311 	{4, 1, 615028},
312 	{4, 1, 901544},
313 	{4, 1, 911876},
314 
315   /* Nature */
316 	{5, 1, 10175},
317 	{5, 1, 566780},
318 	{5, 1, 570090},
319 #endif
320 };
321 
322 #define TOTALISTICRULES (sizeof totalistic_rules / sizeof totalistic_rules[0])
323 
324 #if 0
325 static char lcau21_rules[][9] =
326 {
327 	"00010010",		/* 18 Hollow enlarging triangles */
328 	"00110110",		/* 54 Hollow triangles */
329 	"01101110",		/* 110 Hollow right triangles */
330 	"01111100",		/* 124 Hollow left triangles */
331 	"10010011",		/* 147 Solid triangles */
332 	"10001001",		/* 137 Solid right triangles */
333 	"11000001",		/* 193 Solid left triangles */
334 };
335 
336 #define LCAU2RULES (sizeof lcau21_rules / sizeof lcau21_rules[0])
337 #endif
338 
339 static char lcau31_rules[][28] =
340 {
341 	"000222111000111222222111000",	/* equal thirds */
342 	"002220210110110211110002200",	/* threads on triangles */
343 	"001121102222110110111002100",	/* interfaces of 2 vel */
344 	"020201010201010102010102020",	/* class iv, sparse */
345 	"020201011201011112011112120",	/* blue bground class iv */
346 	"021211110211110101110101020",	/* diagonal black gaps */
347 	"022221211221211012222111120",	/* macrocell w/ 012 membrane */
348 	"100002021002021210021210100",	/* totalistic rule 792 (iv) */
349 	"100002200112201200020121200",	/* binary counter */
350 	"101021220111100222112102020",	/* two patterns compete #1 */
351 	"110001000112221222112221000",	/* reversible rule */
352 	"111012101002110122121102120",	/* small blue triangles */
353 	"111021210012110202120212122",	/* 2 glider on 1 background */
354 	"111111101010202020202010101",	/* motes and triangles */
355 	"111220012222012120001221020",	/* black triangles */
356 	"112110201012001210101200010",	/* dendrites */
357 	"112122000000122112112122000",	/* reversible rule's reverse */
358 	"200211020111110002001101210",	/* crocodile skin */
359 	"202000200112001200120211100",	/* two slow gliders collide */
360 	"202211000201021001100200200",	/* Red Queen's binary counter */
361 	"210101012101012120012120200",	/* blue on red background */
362 	"212021022100200221201101020",	/* two patterns compete #2 */
363 	"220222010220211111000211010",	/* Fisch's cyclic eater */
364 	"222022001222211202022120012",	/* macrocell w/ 0122 membrane */
365 };
366 
367 #define LCAU3RULES (sizeof lcau31_rules / sizeof lcau31_rules[0])
368 
369 static char lcau41_rules[][65] =
370 {
371 	"0000000313131323232312121213232323231313131101010202020202010101",	/* skewed triangle */
372 	"0000020202000000020232220203300000000101020030000100322121003020",	/* slow glider - copies bar */
373 	"0000213323132331123303003213323113233123002033211332313233120001",	/* slightly chaotic symmetry */
374 	"0000332030033323010020122303001002120210000000100110020220000010",	/* shuttle squeeze */
375 	"0100030000030323200323120202001003000203000220001310220100200010",	/* cool gliders */
376 	"0121200113213320110223311213201013131010111011101120012132103210",	/* mixture of types */
377 	"0212301103023320313223121332310023203323333231301020023120303020",	/* slo gl w/ many f gl */
378 	"0320032100113332112321213211003321233210121032320000321000010200",	/* crosshatching */
379 	"0323323323313310323323313310310223313310310210213310310210210210",	/* Perry's 245028 */
380 	"0323330023210000121122232322122113310131032101002110122321102120",	/*  */
381 	"0332200131100112230221012111210202022013210332120210222311212100",	/* cycles on dgl bgrnd */
382 	"0332200131100112230221012111210202022013210332120210222311212300",	/* cycles on dgl bgrnd */
383 	"1230320301231030321132211021112010202330101010112312220310311000",	/* very complex glider */
384 	"1230320301321030321132211021112010212121232212112312220310311000",	/* nice cross hatching */
385 	"2031122112031101123123233000321210301112010303203232213000311000",	/* gliders among stills */
386 	"2202003300010200010011010011020003033000032302002203110033010200",	/* bin ctr */
387 	"2313032202112320111221023212120203132000233322021310311101010030",	/* diagonal growth on mesh */
388 	"2332102112210200233210120311023110322213112123233132331213211212",	/* gliderettes & latice */
389 	"3000213323132331123302003213323113233123001033211332313233120000",	/* symmetric rule */
390 	"3003003203213211003203213211211303213211211311323211211311321320",	/* Perry's blue background */
391 	"3010213223113300321221011010123112301033010221203333211323110100",	/* v. bars w/entanglement */
392 	"3111213323132331123313113213323113233123220133211332313233121110",	/* not purely symmetric */
393 	"3112001202313030102330122003321023102122030102001210223122203020",	/* slow & fast gliders */
394 	"3130123232333201012202313210121032302200211020313130002001103210",	/* crocodile skin */
395 	"3230120202031130033311232111223012311113322032103210123012303210",	/* y/o on b/g */
396 	"3330333312110010333032222222001033303222121111110000322212110010",	/* Fisch's rule */
397 	"3332032303133100221122122213220022000212021022100200221203103020",	/* slow glider */
398 	"3333101022220101323201012323101033331010222201013232010123231010",	/* reverse of rvble #22 */
399 	"3333111122000022333311112200002233111133002222003311113300222200",	/* reversible Rule 22 */
400 };
401 
402 #define LCAU4RULES (sizeof lcau41_rules / sizeof lcau41_rules[0])
403 
404 #ifdef LCAU2RULES
405 #define LCAURULES (LCAU2RULES + LCAU3RULES + LCAU4RULES)
406 #else
407 #define LCAURULES (LCAU3RULES + LCAU4RULES)
408 #endif
409 
410 static void
drawcell(ModeInfo * mi,int col,int row,unsigned int state)411 drawcell(ModeInfo * mi, int col, int row, unsigned int state)
412 {
413 	Display    *display = MI_DISPLAY(mi);
414 	Window      window = MI_WINDOW(mi);
415 	life1dstruct *lp = &life1ds[MI_SCREEN(mi)];
416 	GC          gc = lp->backGC;
417 
418 	if (!state) {
419 		XSetForeground(display, gc, lp->black);
420 		XFillRectangle(display, window, gc,
421 		lp->xb + lp->xs * col, lp->yb + lp->ys * row, lp->xs, lp->ys);
422 		return;
423 	}
424 	if (MI_NPIXELS(mi) > 2)
425 		XSetForeground(display, gc, MI_PIXEL(mi, lp->colors[state - 1]));
426 	if (lp->pixelmode || (MI_NPIXELS(mi) <= 2)) {
427 #ifndef STANDALONE
428 		if (MI_NPIXELS(mi) <= 2) {
429 			XGCValues   gcv;
430 
431 			gcv.stipple = lp->pixmaps[state - 1];
432 			gcv.foreground = MI_WHITE_PIXEL(mi);
433 			gcv.background = lp->black;
434 			gcv.fill_style = FillOpaqueStippled;
435 			XChangeGC(display, lp->stippledGC,
436 				  GCStipple | GCFillStyle | GCForeground | GCBackground, &gcv);
437 			XFillRectangle(display, window, lp->stippledGC,
438 				       lp->xb + lp->xs * col, lp->yb + lp->ys * row, lp->xs, lp->ys);
439 		} else
440 #endif
441 			XFillRectangle(display, window, gc,
442 				       lp->xb + lp->xs * col, lp->yb + lp->ys * row, lp->xs, lp->ys);
443 	}
444 	 else {
445 		(void) XPutImage(display, window, gc,
446 		lp->logo, 0, 0, lp->xb + lp->xs * col, lp->yb + lp->ys * row,
447 				 lp->logo->width, lp->logo->height);
448 	}
449 }
450 
451 static void
RandomSoup(life1dstruct * lp,int n,int v)452 RandomSoup(life1dstruct * lp, int n, int v)
453 {
454 	int         col;
455 
456 	v /= 2;
457 	if (v < 1)
458 		v = 1;
459 	for (col = lp->ncols / 2 - v; col < lp->ncols / 2 + v; ++col)
460 		if (NRAND(100) < n && col >= 0 && col < lp->ncols)
461 			lp->newcells[col + lp->border] = (unsigned char) NRAND(lp->k - 1) + 1;
462 }
463 
464 static long
power(int x,int n)465 power(int x, int n)
466 {				/* raise x to the nth power n >= 0 */
467 	int         i;
468 	long        p = 1;
469 
470 	for (i = 1; i <= n; ++i)
471 		p = p * x;
472 	return p;
473 }
474 
475 static void
GetRule(life1dstruct * lp,int i)476 GetRule(life1dstruct * lp, int i)
477 {
478 	long        sum_size, j;
479 
480 	if (totalistic) {
481 		long        code, pow_size;
482 
483 		lp->k = totalistic_rules[i][0];
484 		lp->r = totalistic_rules[i][1];
485 		sum_size = (lp->k - 1) * (lp->r * 2 + 1) + 1;
486 		code = lp->code = totalistic_rules[i][2];
487 
488 		pow_size = power(lp->k, (int) (sum_size - 1));	/* Should be < max long */
489 		for (j = 0; j < sum_size; j++) {
490 			lp->nextstate[sum_size - 1 - j] = (char) (code / pow_size);
491 			code -= ((long) lp->nextstate[sum_size - 1 - j]) * pow_size;
492 			pow_size /= (long) lp->k;
493 		}
494 	} else {
495 		lp->r = 1;
496 #ifdef LCAU2RULES
497 		if (i < (int) LCAU2RULES) {
498 			lp->k = 2;
499 			sum_size = power(lp->k, 2 * lp->r + 1);
500 			for (j = 0; j < sum_size; j++)
501 				lp->nextstate[sum_size - 1 - j] = lcau21_rules[i][j] - '0';
502 		} else if (i < LCAU2RULES + LCAU3RULES)
503 #else
504 		if (i < (int) LCAU3RULES)
505 #endif
506 		{
507 			lp->k = 3;
508 			sum_size = power(lp->k, 2 * lp->r + 1);
509 #ifdef LCAU2RULES
510 			i -= LCAU2RULES;
511 #endif
512 			for (j = 0; j < sum_size; j++)
513 				lp->nextstate[sum_size - 1 - j] = lcau31_rules[i][j] - '0';
514 		} else {
515 			lp->k = 4;
516 			sum_size = power(lp->k, 2 * lp->r + 1);
517 #ifdef LCAU2RULES
518 			i -= (LCAU2RULES + LCAU3RULES);
519 #else
520 			i -= LCAU3RULES;
521 #endif
522 			for (j = 0; j < sum_size; j++)
523 				lp->nextstate[sum_size - 1 - j] = lcau41_rules[i][j] - '0';
524 		}
525 	}
526 }
527 
528 static int
compare(ModeInfo * mi)529 compare(ModeInfo * mi)
530 {
531 	life1dstruct *lp = &life1ds[MI_SCREEN(mi)];
532 	int         row, col, tryagain = False;
533 	unsigned char *initl, *cmpr;
534 
535         /* The following sometimes does not detect when there
536 	   are multiple periodic life forms side by side. */
537 	initl = lp->buffer + lp->row * lp->ncols;
538 	for (row = lp->row - 1; row >= 0; row--) {
539 		cmpr = lp->buffer + row * lp->ncols;
540 		for (col = 0; col < lp->ncols; col++) {
541 			tryagain = False;
542 			if (*(initl + col) != *cmpr) {
543 				tryagain = True;
544 				break;
545 			}
546 			cmpr++;
547 		}
548 		if (!tryagain) {
549 			return True;
550 		}
551 	}
552         /* This is not guaranteed but its twice as good as without. */
553 	if (lp->previousBuffer) {
554 		initl = lp->buffer + lp->row * lp->ncols;
555 		for (row = lp->nrows - 1; row >= 0; row--) {
556 			cmpr = lp->previousBuffer + row * lp->ncols;
557 			for (col = 0; col < lp->ncols; col++) {
558 				tryagain = False;
559 				if (*(initl + col) != *cmpr) {
560 					tryagain = True;
561 					break;
562 				}
563 				cmpr++;
564 			}
565 			if (!tryagain) {
566 				return True;
567 			}
568 		}
569 	}
570 	return False;
571 
572 }
573 
574 static Bool
init_stuff(ModeInfo * mi)575 init_stuff(ModeInfo * mi)
576 {
577 	life1dstruct *lp = &life1ds[MI_SCREEN(mi)];
578 #ifndef STANDALONE
579 
580 	if (lp->logo == None) {
581 		getImage(mi, &lp->logo, CELL_WIDTH, CELL_HEIGHT, CELL_BITS,
582 #ifdef HAVE_XPM
583 			 DEFAULT_XPM, CELL_NAME,
584 #endif
585 			 &lp->graphics_format, &lp->cmap, &lp->black);
586 		if (lp->logo == None) {
587 			return False;
588 		}
589 	}
590 #endif /* STANDALONE */
591 	if (lp->cmap != None) {
592 		Display    *display = MI_DISPLAY(mi);
593 		Window      window = MI_WINDOW(mi);
594 
595 #ifndef STANDALONE
596 		setColormap(display, window, lp->cmap, MI_IS_INWINDOW(mi));
597 #endif
598 		if (lp->backGC == None) {
599 			XGCValues   xgcv;
600 
601 			xgcv.background = lp->black;
602 			if ((lp->backGC = XCreateGC(display, window, GCBackground,
603 					&xgcv)) == None) {
604 				return False;
605 			}
606 		}
607 	} else
608 	{
609 		lp->black = MI_BLACK_PIXEL(mi);
610 		lp->backGC = MI_GC(mi);
611 	}
612 	return True;
613 }
614 
615 static void
free_stuff(Display * display,life1dstruct * lp)616 free_stuff(Display * display, life1dstruct * lp)
617 {
618 	if (lp == NULL) {
619 		return;
620 	}
621 	if (lp->cmap != None) {
622 		XFreeColormap(display, lp->cmap);
623 		if (lp->backGC != None) {
624 			XFreeGC(display, lp->backGC);
625 			lp->backGC = None;
626 		}
627 		lp->cmap = None;
628 	} else
629 		lp->backGC = None;
630 	lp = NULL;
631 }
632 
633 static void
free_life1d_screen(Display * display,life1dstruct * lp)634 free_life1d_screen(Display * display, life1dstruct * lp)
635 {
636 	int         shade;
637 
638 	if (lp == NULL) {
639 		return;
640 	}
641 	if (lp->stippledGC != None) {
642 		XFreeGC(display, lp->stippledGC);
643 		lp->stippledGC = None;
644 	}
645 	if (lp->init_bits != 0) {
646 		for (shade = 0; shade < MAXSTATES; shade++)
647 			XFreePixmap(display, lp->pixmaps[shade]);
648 		lp->init_bits = 0;
649 	}
650 	if (lp->newcells != NULL) {
651 		free(lp->newcells);
652 		lp->newcells = (unsigned char *) NULL;
653 	}
654 	if (lp->oldcells != NULL) {
655 		free(lp->oldcells);
656 		lp->oldcells = (unsigned char *) NULL;
657 	}
658 	if (lp->buffer != NULL) {
659 		free(lp->buffer);
660 		lp->buffer = (unsigned char *) NULL;
661 	}
662 	if (lp->previousBuffer != NULL) {
663 		free(lp->previousBuffer);
664 		lp->previousBuffer = (unsigned char *) NULL;
665 	}
666 	if (lp->nextstate != NULL) {
667 		free(lp->nextstate);
668 		lp->nextstate = (char *) NULL;
669 	}
670 	free_stuff(display, lp);
671 #ifndef STANDALONE
672 	if (lp->logo != None) {
673 		destroyImage(&lp->logo, &lp->graphics_format);
674 		lp->logo = None;
675 	}
676 #endif
677 	lp = NULL;
678 }
679 
680 ENTRYPOINT void
free_life1d(ModeInfo * mi)681 free_life1d(ModeInfo * mi)
682 {
683 	free_life1d_screen(MI_DISPLAY(mi), &life1ds[MI_SCREEN(mi)]);
684 }
685 
686 ENTRYPOINT void
init_life1d(ModeInfo * mi)687 init_life1d(ModeInfo * mi)
688 {
689 	Display    *display = MI_DISPLAY(mi);
690 	int         size = MI_SIZE(mi);
691 	int         i;
692 	life1dstruct *lp;
693 
694 	MI_INIT(mi, life1ds);
695 	lp = &life1ds[MI_SCREEN(mi)];
696 
697 	if (!init_stuff(mi)) {
698 		free_life1d_screen(display, lp);
699 		return;
700 	}
701 
702 	lp->screen_generation = 0;
703 	lp->row = 0;
704 
705 	if (totalistic) {
706 		maxstates = MAXSTATES;
707 		maxradius = 4;
708 		maxsum_size = (maxstates - 1) * (maxradius * 2 + 1) + 1;
709 	} else {
710 		maxstates = MAXSTATES - 1;
711 		maxradius = 1;
712 		maxsum_size = (int) power(maxstates, (2 * maxradius + 1));
713 	}
714 	if (lp->nextstate == NULL) {
715 		if ((lp->nextstate = (char *) malloc(maxsum_size *
716 				sizeof (char))) == NULL) {
717 			free_life1d_screen(display, lp);
718 			return;
719 		}
720 	}
721 #ifndef STANDALONE
722 	if (lp->init_bits == 0) {
723 		Window      window = MI_WINDOW(mi);
724 		XGCValues   gcv;
725 
726 		gcv.fill_style = FillOpaqueStippled;
727 		if ((lp->stippledGC = XCreateGC(display, window, GCFillStyle,
728 				&gcv)) == None) {
729 			free_life1d_screen(display, lp);
730 			return;
731 		}
732 		for (i = 0; i < MAXSTATES - 1; i++) {
733 			LIFE1DBITS(stipples[i + NUMSTIPPLES - MAXSTATES + 1],
734 				   STIPPLESIZE, STIPPLESIZE);
735 		}
736 		LIFE1DBITS(stipples[NUMSTIPPLES / 2],
737 			   STIPPLESIZE, STIPPLESIZE);	/* grey */
738 	}
739 #endif
740 	if (lp->newcells != NULL)
741 		free(lp->newcells);
742 	if (lp->oldcells != NULL)
743 		free(lp->oldcells);
744 	if (lp->buffer != NULL)
745 		free(lp->buffer);
746 	if (lp->previousBuffer != NULL)
747 		free(lp->previousBuffer);
748 	lp->previousBuffer = (unsigned char *) NULL;
749 	lp->width = MI_WIDTH(mi);
750 	lp->height = MI_HEIGHT(mi);
751 	if (lp->width < 2)
752 		lp->width = 2;
753 	if (lp->height < 2)
754 		lp->height = 2;
755 #ifdef STANDALONE
756 	if (size == 0)
757 #else
758 	if (size == 0 ||
759 	 MINGRIDSIZE * size > lp->width || MINGRIDSIZE * size > lp->height) {
760 		if (lp->width > MINGRIDSIZE * lp->logo->width &&
761 		    lp->height > MINGRIDSIZE * lp->logo->height) {
762 			lp->pixelmode = False;
763 			lp->xs = lp->logo->width;
764 			lp->ys = lp->logo->height;
765 		} else
766 #endif
767 		{
768 			int min = MIN(lp->width, lp->height) / (12 * MINGRIDSIZE);
769 			int max = MIN(lp->width, lp->height) / (4 * MINGRIDSIZE);
770 
771 
772 			lp->xs = lp->ys = MAX(MINSIZE, min + NRAND(max - min + 1));
773 			lp->pixelmode = True;
774 		}
775 #ifndef STANDALONE
776 	}
777 #endif
778 	else {
779 		lp->pixelmode = True;
780 		if (size < -MINSIZE) {
781 			lp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(lp->width, lp->height) /
782 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
783 		} else if (size < MINSIZE) {
784 			lp->ys = MINSIZE;
785 		} else {
786 			lp->ys = MIN(size, MAX(MINSIZE, MIN(lp->width, lp->height) /
787 					       MINGRIDSIZE));
788 		}
789 		lp->xs = lp->ys;
790 	}
791 	lp->ncols = MAX(lp->width / lp->xs, 2);
792 	lp->nrows = MAX(lp->height / lp->ys, 2);
793 	lp->border = (lp->nrows / 2 + 1) * MI_CYCLES(mi);
794 	if ((lp->newcells = (unsigned char *) calloc(lp->ncols + 2 * lp->border,
795 			sizeof (unsigned char))) == NULL) {
796 		free_life1d_screen(display, lp);
797 		return;
798 	}
799 
800 	if ((lp->oldcells = (unsigned char *) calloc(lp->ncols + 2 *
801 			(maxradius + lp->border),
802 			sizeof (unsigned char))) == NULL) {
803 		free_life1d_screen(display, lp);
804 		return;
805 	}
806 
807 	if ((lp->buffer = (unsigned char *) calloc(lp->ncols * lp->nrows,
808 			sizeof (unsigned char))) == NULL) {
809 		free_life1d_screen(display, lp);
810 		return;
811 	}
812 
813 	lp->xb = (lp->width - lp->xs * lp->ncols) / 2;
814 	lp->yb = (lp->height - lp->ys * lp->nrows) / 2;
815 
816 	GetRule(lp, (int) NRAND((totalistic) ? TOTALISTICRULES : LCAURULES));
817 	if (MI_IS_VERBOSE(mi)) {
818 		(void) fprintf(stdout, "colors %d, radius %d, code %ld, ",
819 			       lp->k, lp->r, lp->code);
820 		if (totalistic) {
821 			(void) fprintf(stdout, "totalistic rule ");
822 			for (i = (lp->k - 1) * (lp->r * 2 + 1); i >= 0; i--)
823 				(void) fprintf(stdout, "%d", (int) lp->nextstate[i]);
824 		} else {
825 			(void) fprintf(stdout, "LCAU rule ");
826 			for (i = (int) power(lp->k, (lp->r * 2 + 1)); i >= 0; i--)
827 				(void) fprintf(stdout, "%d", (int) lp->nextstate[i]);
828 		}
829 		(void) fprintf(stdout, "\n");
830 	}
831 	if (MI_NPIXELS(mi) > 2) {
832 		int offset = NRAND(MI_NPIXELS(mi));
833 
834 		for (i = 0; i < lp->k - 1; i++) {
835 			lp->colors[i] = ((offset +
836 				(i * MI_NPIXELS(mi) / (lp->k - 1))) %
837 				MI_NPIXELS(mi));
838 		}
839 	}
840 	RandomSoup(lp, 40, 25);
841 	(void) memcpy((char *) (lp->oldcells + maxradius + lp->border),
842 		      (char *) (lp->newcells + lp->border), lp->ncols);
843 	lp->busyLoop = 0;
844 
845 	MI_CLEARWINDOWCOLORMAP(mi, lp->backGC, lp->black);
846 }
847 
848 ENTRYPOINT void
draw_life1d(ModeInfo * mi)849 draw_life1d(ModeInfo * mi)
850 {
851 	Display    *display = MI_DISPLAY(mi);
852 	int         col;
853 	life1dstruct *lp;
854 
855 	if (life1ds == NULL)
856 		return;
857 	lp = &life1ds[MI_SCREEN(mi)];
858 	if (lp->buffer == NULL)
859 		return;
860 
861 	MI_IS_DRAWN(mi) = True;
862 	if (lp->busyLoop) {
863 		if (lp->busyLoop >= 250)
864 			lp->busyLoop = 0;
865 		else
866 			lp->busyLoop++;
867 		return;
868 	}
869 	if (lp->row == 0) {
870 		lp->repeating = 0;
871 		if (lp->screen_generation > MI_CYCLES(mi))
872 			init_life1d(mi);
873 		if (!lp->previousBuffer && lp->screen_generation > 1) {
874 			lp->previousBuffer = (unsigned char *) calloc(lp->ncols *
875 				lp->nrows, sizeof (unsigned char));
876 		}
877 		if (lp->previousBuffer) {
878 			(void) memcpy((char *) (lp->previousBuffer),
879 				(char *) (lp->buffer), lp->nrows * lp->ncols);
880 		}
881 
882 		for (col = 0; col < lp->ncols; col++)
883 			if (lp->buffer[col] != lp->newcells[col + lp->border])
884 				drawcell(mi, col, 0, lp->newcells[col + lp->border]);
885 		(void) memcpy((char *) lp->buffer, (char *) (lp->newcells + lp->border),
886 			      lp->ncols);
887 	} else {
888 		for (col = 0; col < lp->ncols + 2 * lp->border; col++) {
889 			int         sum = 0, m;
890 
891 			if (totalistic) {
892 				for (m = col - lp->r; m <= col + lp->r; m++)
893 					sum += lp->oldcells[m + maxradius];
894 			} else {
895 				int         pow_size = 1;
896 
897 				for (m = col + lp->r; m >= col - lp->r; m--) {
898 					sum += lp->oldcells[m + maxradius] * pow_size;
899 					pow_size *= lp->k;
900 				}
901 			}
902 			lp->newcells[col] = (unsigned char) lp->nextstate[sum];
903 		}
904 		(void) memcpy((char *) (lp->oldcells + maxradius),
905 			  (char *) lp->newcells, lp->ncols + 2 * lp->border);
906 
907 		for (col = 0; col < lp->ncols; col++) {
908 			if (lp->buffer[col + lp->row * lp->ncols] !=
909 			    lp->newcells[col + lp->border])
910 				drawcell(mi, col, lp->row, lp->newcells[col + lp->border]);
911 		}
912 		(void) memcpy((char *) (lp->buffer + lp->row * lp->ncols),
913 			    (char *) (lp->newcells + lp->border), lp->ncols);
914 		{
915 			int         temp = compare(mi);
916 
917 			if (temp)
918 				lp->repeating += temp;
919 			else
920 				lp->repeating = 0;
921 		}
922 		lp->repeating += (lp->row == lp->nrows - 1) ?
923 			(lp->nrows - 1) * compare(mi) : 0;
924 	}
925 #ifndef STANDALONE
926 	if (lp->repeating >= 1) {
927 		XGCValues   gcv;
928 
929 		gcv.stipple = lp->pixmaps[MAXSTATES - 1];
930 		gcv.fill_style = FillStippled;
931 		gcv.foreground = lp->black;
932 		XChangeGC(MI_DISPLAY(mi), lp->stippledGC,
933 			  GCStipple | GCFillStyle | GCForeground, &gcv);
934 		XFillRectangle(display, MI_WINDOW(mi), lp->stippledGC,
935 			       0, lp->yb + lp->ys * lp->row,
936 			       lp->width, lp->ys);
937 	}
938 #endif
939 	lp->row++;
940 	if (lp->repeating >= lp->nrows - 1) {
941 		if (lp->row < lp->nrows) {
942 			XSetForeground(display, lp->backGC, lp->black);
943 			XFillRectangle(display, MI_WINDOW(mi), lp->backGC,
944 				       0, lp->yb + lp->ys * lp->row,
945 			  lp->width, lp->height - lp->ys * lp->row - lp->yb);
946 		}
947 		lp->screen_generation = MI_CYCLES(mi);
948 		lp->row = lp->nrows;
949 	}
950 	if (lp->row >= lp->nrows) {
951 		lp->screen_generation++;
952 		lp->busyLoop = 1;
953 		lp->row = 0;
954 	}
955 }
956 
957 ENTRYPOINT void
release_life1d(ModeInfo * mi)958 release_life1d(ModeInfo * mi)
959 {
960 	if (life1ds != NULL) {
961 		int         screen;
962 
963 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
964 			free_life1d_screen(MI_DISPLAY(mi), &life1ds[screen]);
965 		free(life1ds);
966 		life1ds = (life1dstruct *) NULL;
967 	}
968 }
969 
970 #ifndef STANDALONE
971 ENTRYPOINT void
refresh_life1d(ModeInfo * mi)972 refresh_life1d(ModeInfo * mi)
973 {
974 	int         row, col, nrow;
975 	life1dstruct *lp;
976 
977 	if (life1ds == NULL)
978 		return;
979 	lp = &life1ds[MI_SCREEN(mi)];
980 	if (lp->buffer == NULL)
981 		return;
982 
983 #ifdef HAVE_XPM
984         if (lp->graphics_format >= IS_XPM) {
985 		/* This is needed when another program changes the colormap. */
986 		free_life1d_screen(MI_DISPLAY(mi), lp);
987 		init_life1d(mi);
988 		return;
989 	}
990 #endif
991 
992 	for (row = 0; row < lp->nrows; row++) {
993 		nrow = row * lp->ncols;
994 		for (col = 0; col < lp->ncols; col++)
995 			drawcell(mi, col, row, lp->buffer[col + nrow]);
996 	}
997 }
998 #endif
999 
1000 XSCREENSAVER_MODULE ("Life1d", life1d)
1001 
1002 #endif /* MODE_life1d */
1003