1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* wator --- Dewdney's Wa-Tor, water torus simulation */
3 
4 #if 0
5 static const char sccsid[] = "@(#)wator.c	5.24 2007/01/18 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1994 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  * 18-Jan-2007: Added vertical option.
26  * 01-Nov-2000: Allocation checks
27  * 10-May-1997: Compatible with xscreensaver
28  * 29-Aug-1995: Efficiency improvements.
29  * 12-Dec-1994: Coded from A.K. Dewdney's "The Armchair Universe, Computer
30  *              Recreations from the Pages of Scientific American Magazine"
31  *              W.H. Freedman and Company, New York, 1988  (Dec 1984 and
32  *              June 1985) also used life.c as a guide.
33  */
34 
35 #ifdef STANDALONE
36 #define MODE_wator
37 #define DEFAULTS "*delay: 750000 \n" \
38 	"*cycles: 32767 \n" \
39 	"*size: 0 \n" \
40 	"*ncolors: 200 \n" \
41 
42 # define free_wator 0
43 # define reshape_wator 0
44 # define wator_handle_event 0
45 #include "xlockmore.h"		/* in xscreensaver distribution */
46 #else /* STANDALONE */
47 #include "xlock.h"		/* in xlockmore distribution */
48 #endif /* STANDALONE */
49 #include "automata.h"
50 
51 #ifdef MODE_wator
52 
53 /*-
54  * neighbors of 0 randomizes it between 3, 4, 6, 8, 9, and 12.
55  */
56 #define DEF_NEIGHBORS  "0"      /* choose random value */
57 #define DEF_VERTICAL "False"
58 
59 static int  neighbors;
60 static Bool vertical;
61 
62 static XrmOptionDescRec opts[] =
63 {
64 	{(char *) "-neighbors", (char *) ".wator.neighbors", XrmoptionSepArg, (caddr_t) NULL},
65 	{(char *) "-vertical", (char *) ".wator.vertical", XrmoptionNoArg, (caddr_t) "on"},
66 	{(char *) "+vertical", (char *) ".wator.vertical", XrmoptionNoArg, (caddr_t) "off"}
67 };
68 
69 static argtype vars[] =
70 {
71 	{(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
72 	{(void *) & vertical, (char *) "vertical", (char *) "Vertical", (char *) DEF_VERTICAL, t_Bool}
73 };
74 
75 static OptionStruct desc[] =
76 {
77 	{(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"},
78 	{(char *) "-/+vertical", (char *) "change orientation for hexagons and triangles"}
79 };
80 
81 ENTRYPOINT ModeSpecOpt wator_opts =
82 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
83 
84 #ifdef USE_MODULES
85 ModStruct   wator_description =
86 {"wator", "init_wator", "draw_wator", "release_wator",
87  "refresh_wator", "init_wator", (char *) NULL, &wator_opts,
88  750000, 1, 32767, 0, 64, 1.0, "",
89  "Shows Dewdney's Water-Torus planet of fish and sharks", 0, NULL};
90 
91 #endif
92 
93 #include "bitmaps/fish-0.xbm"
94 #include "bitmaps/fish-1.xbm"
95 #include "bitmaps/fish-2.xbm"
96 #include "bitmaps/fish-3.xbm"
97 #include "bitmaps/fish-4.xbm"
98 #include "bitmaps/fish-5.xbm"
99 #include "bitmaps/fish-6.xbm"
100 #include "bitmaps/fish-7.xbm"
101 #include "bitmaps/shark-0.xbm"
102 #include "bitmaps/shark-1.xbm"
103 #include "bitmaps/shark-2.xbm"
104 #include "bitmaps/shark-3.xbm"
105 #include "bitmaps/shark-4.xbm"
106 #include "bitmaps/shark-5.xbm"
107 #include "bitmaps/shark-6.xbm"
108 #include "bitmaps/shark-7.xbm"
109 
110 #define FISH 0
111 #define SHARK 1
112 #define KINDS 2
113 #define ORIENTS 4
114 #define REFLECTS 2
115 #define BITMAPS (ORIENTS*REFLECTS*KINDS)
116 #define KINDBITMAPS (ORIENTS*REFLECTS)
117 #define MINGRIDSIZE 10		/* It is possible for the fish to take over with 3 */
118 #define MINSIZE 4
119 #define NEIGHBORKINDS 6
120 
121 static XImage logo[BITMAPS] =
122 {
123 	{0, 0, 0, XYBitmap, (char *) fish0_bits, LSBFirst, 8, LSBFirst, 8, 1},
124 	{0, 0, 0, XYBitmap, (char *) fish1_bits, LSBFirst, 8, LSBFirst, 8, 1},
125 	{0, 0, 0, XYBitmap, (char *) fish2_bits, LSBFirst, 8, LSBFirst, 8, 1},
126 	{0, 0, 0, XYBitmap, (char *) fish3_bits, LSBFirst, 8, LSBFirst, 8, 1},
127 	{0, 0, 0, XYBitmap, (char *) fish4_bits, LSBFirst, 8, LSBFirst, 8, 1},
128 	{0, 0, 0, XYBitmap, (char *) fish5_bits, LSBFirst, 8, LSBFirst, 8, 1},
129 	{0, 0, 0, XYBitmap, (char *) fish6_bits, LSBFirst, 8, LSBFirst, 8, 1},
130 	{0, 0, 0, XYBitmap, (char *) fish7_bits, LSBFirst, 8, LSBFirst, 8, 1},
131 	{0, 0, 0, XYBitmap, (char *) shark0_bits, LSBFirst, 8, LSBFirst, 8, 1},
132 	{0, 0, 0, XYBitmap, (char *) shark1_bits, LSBFirst, 8, LSBFirst, 8, 1},
133 	{0, 0, 0, XYBitmap, (char *) shark2_bits, LSBFirst, 8, LSBFirst, 8, 1},
134 	{0, 0, 0, XYBitmap, (char *) shark3_bits, LSBFirst, 8, LSBFirst, 8, 1},
135 	{0, 0, 0, XYBitmap, (char *) shark4_bits, LSBFirst, 8, LSBFirst, 8, 1},
136 	{0, 0, 0, XYBitmap, (char *) shark5_bits, LSBFirst, 8, LSBFirst, 8, 1},
137 	{0, 0, 0, XYBitmap, (char *) shark6_bits, LSBFirst, 8, LSBFirst, 8, 1},
138 	{0, 0, 0, XYBitmap, (char *) shark7_bits, LSBFirst, 8, LSBFirst, 8, 1},
139 };
140 
141 /* Fish and shark data */
142 typedef struct {
143 	char        kind, age, food, direction;
144 	unsigned long color;
145 	int         col, row;
146 } cellstruct;
147 
148 /* Doubly linked list */
149 typedef struct _CellList {
150 	cellstruct  info;
151 	struct _CellList *previous, *next;
152 } CellList;
153 
154 typedef struct {
155 	Bool        painted, vertical;
156 	int         nkind[KINDS];	/* Number of fish and sharks */
157 	int         breed[KINDS];	/* Breeding time of fish and sharks */
158 	int         sstarve;	/* Time the sharks starve if they don't find a fish */
159 	int         kind;	/* Currently working on fish or sharks? */
160 	int         xs, ys;	/* Size of fish and sharks */
161 	int         xb, yb;	/* Bitmap offset for fish and sharks */
162 	int         pixelmode;
163 	int         generation;
164 	int         ncols, nrows, positions;
165 	int         width, height;
166 	CellList   *currkind, *babykind, *lastkind[KINDS + 1], *firstkind[KINDS + 1];
167 	CellList  **arr;	/* 0=empty or pts to a fish or shark */
168 	int         neighbors, polygon;
169 	union {
170 		XPoint      hexagon[6];
171 		XPoint      triangle[2][3];
172 	} shape;
173 } watorstruct;
174 
175 static char plots[NEIGHBORKINDS] =
176 {
177 	3, 4, 6, 8, 9, 12	/* Neighborhoods */
178 };
179 
180 static watorstruct *wators = (watorstruct *) NULL;
181 static int  icon_width, icon_height;
182 
183 #if 0
184 /*-
185  * shape either a bitmap or 0 for circle and 1 for polygon
186  * (if triangle shape:  -1, 0, 2 or 3 to differentiate left and right)
187  */
188 drawshape(ModeInfo * mi, int x, int y, int sizex, int sizey,
189 	  int sides, int shape)
190 {
191 	Display    *display = MI_DISPLAY(mi);
192 	Window      window = MI_WINDOW(mi);
193 	GC          gc = MI_GC(mi);
194 
195 	if (sides == 4 && sizex == 0 && sizey == 0) {
196 		(void) XPutImage(display, window, gc, &logo[shape], 0, 0,
197 		x - icon_width, y - icon_height / 2, icon_width, icon_height);
198 	} else if (sizex < 3 || sizey < 3 || (sides == 4 && shape == 1)) {
199 		XFillRectangle(display, window, gc,
200 			x - sizex / 2, y - sizey / 2,
201 			sizex - (sizey > 3), sizey - (sizey > 3));
202 	} else if (sides == 4 && shape == 0) {
203 	} else if (sides == 6 && shape == 1) {
204 	} else if (sides == 6 && shape == 0) {
205 	} else if (sides == 3 && shape == 1) {
206 	} else if (sides == 3 && shape == 2) {
207 	} else if (sides == 3 && shape == -1) {
208 	} else if (sides == 3 && shape == 0) {
209 	}
210 }
211 #endif
212 
213 static void
drawcell(ModeInfo * mi,int col,int row,unsigned long color,int bitmap,Bool alive)214 drawcell(ModeInfo * mi, int col, int row, unsigned long color, int bitmap,
215 		Bool alive)
216 {
217 	Display    *display = MI_DISPLAY(mi);
218 	Window      window = MI_WINDOW(mi);
219 	GC          gc = MI_GC(mi);
220 	watorstruct *wp = &wators[MI_SCREEN(mi)];
221 	unsigned long colour;
222 
223 	if (!alive)
224 		colour = MI_BLACK_PIXEL(mi);
225 	else if (MI_NPIXELS(mi) > 2)
226 		colour = MI_PIXEL(mi, color);
227 	else
228 		colour = MI_WHITE_PIXEL(mi);
229 	XSetForeground(display, gc, colour);
230 	if (wp->neighbors == 6) {
231 		int ccol = 2 * col + !(row & 1), crow = 2 * row;
232 
233 		if (wp->vertical) {
234 			wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
235 			wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
236 		} else {
237 			wp->shape.hexagon[0].y = wp->xb + ccol * wp->xs;
238 			wp->shape.hexagon[0].x = wp->yb + crow * wp->ys;
239 		}
240 		if (wp->xs == 1 && wp->ys == 1)
241 			XDrawPoint(display, window, gc,
242 				wp->shape.hexagon[0].x,
243 				wp->shape.hexagon[0].y);
244 		else if (bitmap >= KINDBITMAPS || !alive)
245 			XFillPolygon(display, window, gc,
246 				wp->shape.hexagon, 6,
247 				Convex, CoordModePrevious);
248 		else {
249 			int ix = 0, iy = 0, sx, sy;
250 
251 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
252 			XFillPolygon(display, window, gc,
253 				wp->shape.hexagon, 6,
254 				Convex, CoordModePrevious);
255 			XSetForeground(display, gc, colour);
256 			if (wp->vertical) {
257 				wp->shape.hexagon[0].x -= wp->xs;
258 				wp->shape.hexagon[0].y += wp->ys / 4;
259                                 sx = 2 * wp->xs - 6;
260                                 sy = 2 * wp->ys - 2;
261 				if (wp->xs <= 6 || wp->ys <= 2) {
262 					ix = 3;
263 					iy = 1;
264 				} else
265 					ix = 5;
266 			} else {
267 				wp->shape.hexagon[0].y -= wp->xs;
268 				wp->shape.hexagon[0].x += wp->ys / 4;
269                                 sy = 2 * wp->xs - 6;
270                                 sx = 2 * wp->ys - 2;
271 				if (wp->xs <= 6 || wp->ys <= 2) {
272 					iy = 3;
273 					ix = 1;
274 				} else
275 					iy = 5;
276 			}
277 			if (wp->xs <= 6 || wp->ys <= 2)
278 				XFillRectangle(display, window, gc,
279 					wp->shape.hexagon[0].x + ix,
280 					wp->shape.hexagon[0].y + iy,
281 					wp->xs, wp->ys);
282 			else
283 				XFillArc(display, window, gc,
284 					wp->shape.hexagon[0].x + ix,
285 					wp->shape.hexagon[0].y + iy,
286 					sx, sy,
287 					0, 23040);
288 		}
289 	} else if (wp->neighbors == 4 || wp->neighbors == 8) {
290 		if (wp->pixelmode) {
291 			if (bitmap >= KINDBITMAPS || (wp->xs <= 2 || wp->ys <= 2) || !alive)
292 				XFillRectangle(display, window, gc,
293 					wp->xb + wp->xs * col,
294 					wp->yb + wp->ys * row,
295 					wp->xs - (wp->xs > 3),
296 					wp->ys - (wp->ys > 3));
297 			else {
298 				XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
299 				XFillRectangle(display, window, gc,
300 					wp->xb + wp->xs * col,
301 					wp->yb + wp->ys * row,
302 					wp->xs, wp->ys);
303 				XSetForeground(display, gc, colour);
304 				XFillArc(display, window, gc,
305 					wp->xb + wp->xs * col,
306 					wp->yb + wp->ys * row,
307 					wp->xs - 1, wp->ys - 1,
308 					0, 23040);
309 			}
310 		} else
311 			(void) XPutImage(display, window, gc,
312 				&logo[bitmap], 0, 0,
313 				wp->xb + wp->xs * col, wp->yb + wp->ys * row,
314 				icon_width, icon_height);
315 	} else { /* TRI */
316 		int orient = (col + row) % 2;	/* O left 1 right */
317 		Bool small = (wp->xs <= 3 || wp->ys <= 3);
318 		int ix = 0, iy = 0;
319 
320 		if (wp->vertical) {
321 			wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
322 			wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
323 			if (small)
324 				wp->shape.triangle[orient][0].x +=
325 					((orient) ? -1 : 1);
326 			else
327 				wp->shape.triangle[orient][0].x +=
328 					(wp->xs / 2  - 1) * ((orient) ? 1 : -1);
329 		} else {
330 			wp->shape.triangle[orient][0].y = wp->xb + col * wp->xs;
331 			wp->shape.triangle[orient][0].x = wp->yb + row * wp->ys;
332 			if (small)
333 				wp->shape.triangle[orient][0].y +=
334 					((orient) ? -1 : 1);
335 			else
336 				wp->shape.triangle[orient][0].y +=
337 					(wp->xs / 2  - 1) * ((orient) ? 1 : -1);
338 		}
339 		if (small)
340 			XDrawPoint(display, window, gc,
341 				wp->shape.triangle[orient][0].x,
342 				wp->shape.triangle[orient][0].y);
343 		else {
344 			if (bitmap >= KINDBITMAPS || !alive)
345 				XFillPolygon(display, window, gc,
346 					wp->shape.triangle[orient], 3,
347 					Convex, CoordModePrevious);
348 			else {
349 				XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
350 				XFillPolygon(display, window, gc,
351 					wp->shape.triangle[orient], 3,
352 					Convex, CoordModePrevious);
353 				XSetForeground(display, gc, colour);
354 				if (wp->vertical) {
355 					wp->shape.triangle[orient][0].x += -4 * wp->xs / 5 +
356                                         	((orient) ? wp->xs / 3 : 3 * wp->xs / 5);
357                                         wp->shape.triangle[orient][0].y += -wp->ys / 2 + 1;
358 					ix = ((orient) ? -wp->xs / 2 : wp->xs / 2);
359 				} else {
360 					wp->shape.triangle[orient][0].y += -4 * wp->xs / 5 +
361                                         	((orient) ? wp->xs / 3 : 3 * wp->xs / 5);
362                                         wp->shape.triangle[orient][0].x += -wp->ys / 2 + 1;
363 					iy = ((orient) ? -wp->xs / 2 : wp->xs / 2);
364 				}
365 				XFillArc(display, window, gc,
366 					wp->shape.triangle[orient][0].x + ix,
367 					wp->shape.triangle[orient][0].y + iy,
368 					wp->ys - 3, wp->ys - 3,
369 					0, 23040);
370 			}
371 		}
372 	}
373 }
374 
375 static Bool
init_kindlist(watorstruct * wp,int kind)376 init_kindlist(watorstruct * wp, int kind)
377 {
378 	/* Waste some space at the beginning and end of list
379 	   so we do not have to complicated checks against falling off the ends. */
380 	if (((wp->lastkind[kind] = (CellList *) malloc(sizeof (CellList))) ==
381 			NULL) ||
382 	    ((wp->firstkind[kind] = (CellList *) malloc(sizeof (CellList))) ==
383 			 NULL)) {
384 		return False;
385 	}
386 	wp->firstkind[kind]->previous = wp->lastkind[kind]->next =
387 		(struct _CellList *) NULL;
388 	wp->firstkind[kind]->next = wp->lastkind[kind]->previous =
389 		(struct _CellList *) NULL;
390 	wp->firstkind[kind]->next = wp->lastkind[kind];
391 	wp->lastkind[kind]->previous = wp->firstkind[kind];
392 	return True;
393 }
394 
395 static Bool
addto_kindlist(watorstruct * wp,int kind,cellstruct info)396 addto_kindlist(watorstruct * wp, int kind, cellstruct info)
397 {
398 	if ((wp->currkind = (CellList *) malloc(sizeof (CellList))) == NULL) {
399 		return False;
400 	}
401 	wp->lastkind[kind]->previous->next = wp->currkind;
402 	wp->currkind->previous = wp->lastkind[kind]->previous;
403 	wp->currkind->next = wp->lastkind[kind];
404 	wp->lastkind[kind]->previous = wp->currkind;
405 	wp->currkind->info = info;
406 	return True;
407 }
408 
409 static void
removefrom_kindlist(watorstruct * wp,CellList * ptr)410 removefrom_kindlist(watorstruct * wp, CellList * ptr)
411 {
412 	ptr->previous->next = ptr->next;
413 	ptr->next->previous = ptr->previous;
414 	wp->arr[ptr->info.col + ptr->info.row * wp->ncols] = 0;
415 	free(ptr);
416 }
417 
418 static Bool
dupin_kindlist(watorstruct * wp)419 dupin_kindlist(watorstruct * wp)
420 {
421 	CellList   *temp;
422 
423 	if ((temp = (CellList *) malloc(sizeof (CellList))) == NULL) {
424 		return False;
425 	}
426 	temp->previous = wp->babykind;
427 	temp->next = wp->babykind->next;
428 	wp->babykind->next = temp;
429 	temp->next->previous = temp;
430 	temp->info = wp->babykind->info;
431 	wp->babykind = temp;
432 	return True;
433 }
434 
435 /*-
436  * new fish at end of list, this rotates who goes first, young fish go last
437  * this most likely will not change the feel to any real degree
438  */
439 static void
cutfrom_kindlist(watorstruct * wp)440 cutfrom_kindlist(watorstruct * wp)
441 {
442 	wp->babykind = wp->currkind;
443 	wp->currkind = wp->currkind->previous;
444 	wp->currkind->next = wp->babykind->next;
445 	wp->babykind->next->previous = wp->currkind;
446 	wp->babykind->next = wp->lastkind[KINDS];
447 	wp->babykind->previous = wp->lastkind[KINDS]->previous;
448 	wp->babykind->previous->next = wp->babykind;
449 	wp->babykind->next->previous = wp->babykind;
450 }
451 
452 static void
reattach_kindlist(watorstruct * wp,int kind)453 reattach_kindlist(watorstruct * wp, int kind)
454 {
455 	wp->currkind = wp->lastkind[kind]->previous;
456 	wp->currkind->next = wp->firstkind[KINDS]->next;
457 	wp->currkind->next->previous = wp->currkind;
458 	wp->lastkind[kind]->previous = wp->lastkind[KINDS]->previous;
459 	wp->lastkind[KINDS]->previous->next = wp->lastkind[kind];
460 	wp->lastkind[KINDS]->previous = wp->firstkind[KINDS];
461 	wp->firstkind[KINDS]->next = wp->lastkind[KINDS];
462 }
463 
464 static void
flush_kindlist(watorstruct * wp,int kind)465 flush_kindlist(watorstruct * wp, int kind)
466 {
467 	while (wp->lastkind[kind]->previous != wp->firstkind[kind]) {
468 		wp->currkind = wp->lastkind[kind]->previous;
469 		wp->currkind->previous->next = wp->lastkind[kind];
470 		wp->lastkind[kind]->previous = wp->currkind->previous;
471 		/* wp->arr[wp->currkind->info.col + wp->currkind->info.row * wp->ncols] = 0; */
472 		free(wp->currkind);
473 	}
474 }
475 
476 static int
positionOfNeighbor(watorstruct * wp,int n,int col,int row)477 positionOfNeighbor(watorstruct * wp, int n, int col, int row)
478 {
479 	int dir = n * (360 / wp->neighbors);
480 
481 	if (wp->polygon == 4 || wp->polygon == 6) {
482 		switch (dir) {
483 		case 0:
484 			col = (col + 1 == wp->ncols) ? 0 : col + 1;
485 			break;
486 		case 45:
487 			col = (col + 1 == wp->ncols) ? 0 : col + 1;
488 			row = (!row) ? wp->nrows - 1 : row - 1;
489 			break;
490 		case 60:
491 			if (!(row & 1))
492 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
493 			row = (!row) ? wp->nrows - 1 : row - 1;
494 			break;
495 		case 90:
496 			row = (!row) ? wp->nrows - 1 : row - 1;
497 			break;
498 		case 120:
499 			if (row & 1)
500 				col = (!col) ? wp->ncols - 1 : col - 1;
501 			row = (!row) ? wp->nrows - 1 : row - 1;
502 			break;
503 		case 135:
504 			col = (!col) ? wp->ncols - 1 : col - 1;
505 			row = (!row) ? wp->nrows - 1 : row - 1;
506 			break;
507 		case 180:
508 			col = (!col) ? wp->ncols - 1 : col - 1;
509 			break;
510 		case 225:
511 			col = (!col) ? wp->ncols - 1 : col - 1;
512 			row = (row + 1 == wp->nrows) ? 0 : row + 1;
513 			break;
514 		case 240:
515 			if (row & 1)
516 				col = (!col) ? wp->ncols - 1 : col - 1;
517 			row = (row + 1 == wp->nrows) ? 0 : row + 1;
518 			break;
519 		case 270:
520 			row = (row + 1 == wp->nrows) ? 0 : row + 1;
521 			break;
522 		case 300:
523 			if (!(row & 1))
524 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
525 			row = (row + 1 == wp->nrows) ? 0 : row + 1;
526 			break;
527 		case 315:
528 			col = (col + 1 == wp->ncols) ? 0 : col + 1;
529 			row = (row + 1 == wp->nrows) ? 0 : row + 1;
530 			break;
531 		default:
532 			(void) fprintf(stderr, "wrong direction %d\n", dir);
533 		}
534 	} else if (wp->polygon == 3) {
535 		if ((col + row) & 1) {	/* right */
536 			switch (dir) {
537 			case 0:
538 				col = (!col) ? wp->ncols - 1 : col - 1;
539 				break;
540 			case 30:
541 			case 40:
542 				col = (!col) ? wp->ncols - 1 : col - 1;
543 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
544 				break;
545 			case 60:
546 				col = (!col) ? wp->ncols - 1 : col - 1;
547 				if (row + 1 == wp->nrows)
548 					row = 1;
549 				else if (row + 2 == wp->nrows)
550 					row = 0;
551 				else
552 					row = row + 2;
553 				break;
554 			case 80:
555 			case 90:
556 				if (row + 1 == wp->nrows)
557 					row = 1;
558 				else if (row + 2 == wp->nrows)
559 					row = 0;
560 				else
561 					row = row + 2;
562 				break;
563 			case 120:
564 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
565 				break;
566 			case 150:
567 			case 160:
568 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
569 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
570 				break;
571 			case 180:
572 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
573 				break;
574 			case 200:
575 			case 210:
576 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
577 				row = (!row) ? wp->nrows - 1 : row - 1;
578 				break;
579 			case 240:
580 				row = (!row) ? wp->nrows - 1 : row - 1;
581 				break;
582 			case 270:
583 			case 280:
584 				if (!row)
585 					row = wp->nrows - 2;
586 				else if (!(row - 1))
587 					row = wp->nrows - 1;
588 				else
589 					row = row - 2;
590 				break;
591 			case 300:
592 				col = (!col) ? wp->ncols - 1 : col - 1;
593 				if (!row)
594 					row = wp->nrows - 2;
595 				else if (!(row - 1))
596 					row = wp->nrows - 1;
597 				else
598 					row = row - 2;
599 				break;
600 			case 320:
601 			case 330:
602 				col = (!col) ? wp->ncols - 1 : col - 1;
603 				row = (!row) ? wp->nrows - 1 : row - 1;
604 				break;
605 			default:
606 				(void) fprintf(stderr, "wrong direction %d\n",
607 					dir);
608 			}
609 		} else {	/* left */
610 			switch (dir) {
611 			case 0:
612 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
613 				break;
614 			case 30:
615 			case 40:
616 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
617 				row = (!row) ? wp->nrows - 1 : row - 1;
618 				break;
619 			case 60:
620 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
621 				if (!row)
622 					row = wp->nrows - 2;
623 				else if (row == 1)
624 					row = wp->nrows - 1;
625 				else
626 					row = row - 2;
627 				break;
628 			case 80:
629 			case 90:
630 				if (!row)
631 					row = wp->nrows - 2;
632 				else if (row == 1)
633 					row = wp->nrows - 1;
634 				else
635 					row = row - 2;
636 				break;
637 			case 120:
638 				row = (!row) ? wp->nrows - 1 : row - 1;
639 				break;
640 			case 150:
641 			case 160:
642 				col = (!col) ? wp->ncols - 1 : col - 1;
643 				row = (!row) ? wp->nrows - 1 : row - 1;
644 				break;
645 			case 180:
646 				col = (!col) ? wp->ncols - 1 : col - 1;
647 				break;
648 			case 200:
649 			case 210:
650 				col = (!col) ? wp->ncols - 1 : col - 1;
651 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
652 				break;
653 			case 240:
654 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
655 				break;
656 			case 270:
657 			case 280:
658 				if (row + 1 == wp->nrows)
659 					row = 1;
660 				else if (row + 2 == wp->nrows)
661 					row = 0;
662 				else
663 					row = row + 2;
664 				break;
665 			case 300:
666 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
667 				if (row + 1 == wp->nrows)
668 					row = 1;
669 				else if (row + 2 == wp->nrows)
670 					row = 0;
671 				else
672 					row = row + 2;
673 				break;
674 			case 320:
675 			case 330:
676 				col = (col + 1 == wp->ncols) ? 0 : col + 1;
677 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
678 				break;
679 			default:
680 				(void) fprintf(stderr, "wrong direction %d\n",
681 					dir);
682 			}
683 		}
684 #if 0
685 	} else {
686 		int orient = ((row & 1) * 2 + col) % 4;
687 		switch (orient) { /* up, down, left, right */
688 		case 0:
689 			switch (dir) {
690 			case 0:
691 				col++;
692 				break;
693 			case 51: /* 7 */
694 			case 72: /* 5 */
695 				col = (col + 2 >= wp->ncols) ? 0 : col + 2;
696 				break;
697 			case 102: /* 7 corner */
698 				col = (col + 3 >= wp->ncols) ? 1 : col + 3;
699 				row = (row == 0) ? wp->nrows - 1 : row - 1;
700 				break;
701 			case 144: /* 5 */
702 			case 153: /* 7 */
703 				col++;
704 				row = (row == 0) ? wp->nrows - 1 : row - 1;
705 				break;
706 			case 204: /* 7 */
707 			case 216: /* 5 */
708 				row = (row == 0) ? wp->nrows - 1 : row - 1;
709 				break;
710 			case 255: /* 7 */
711 				col = (col == 0) ? wp->ncols - 1 : col - 1;
712 				row = (row == 0) ? wp->nrows - 1 : row - 1;
713 				break;
714 			case 288: /* 5 */
715 			case 306: /* 7 */
716 				col = (col == 0) ? wp->ncols - 1 : col - 1;
717 				break;
718 			default:
719 				(void) fprintf(stderr, "wrong direction %d\n",
720 					dir);
721 			}
722 			break;
723 		case 1:
724 			switch (dir) {
725 			case 0:
726 				col--;
727 				break;
728 			case 51: /* 7 */
729 			case 72: /* 5 */
730 				col = (col == 1) ? wp->ncols - 1 : col - 2;
731 				break;
732 			case 102: /* 7 */
733 				col = (col == 1) ? wp->ncols - 2 : col - 3;
734 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
735 				break;
736 			case 144: /* 5 */
737 			case 153: /* 7 */
738 				col--;
739 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
740 				break;
741 			case 204: /* 7 */
742 			case 216: /* 5 */
743 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
744 				break;
745 			case 255: /* 7 */
746 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
747 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
748 				break;
749 			case 288: /* 5 */
750 			case 306: /* 7 */
751 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
752 				break;
753 			default:
754 				(void) fprintf(stderr, "wrong direction %d\n",
755 					dir);
756 			}
757 			break;
758 		case 2:
759 			switch (dir) {
760 			case 0:
761 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
762 				break;
763 			case 51: /* 7 */
764 			case 72: /* 5 */
765 				row = (row == 0) ? wp->nrows - 1 : row - 1;
766 				col++;
767 				break;
768 			case 102: /* 7 */
769 				col = (col == 0) ? wp->ncols - 1 : col - 1;
770 				row = (row == 0) ? wp->nrows - 1 : row - 1;
771 				break;
772 			case 144: /* 5 */
773 			case 153: /* 7 */
774 				col = (col == 0) ? wp->ncols - 2 : col - 2;
775 				break;
776 			case 204: /* 7 */
777 			case 216: /* 5 */
778 				col = (col == 0) ? wp->ncols - 1 : col - 1;
779 				break;
780 			case 255: /* 7 */
781 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
782 				col = (col == 0) ? wp->ncols - 1 : col - 1;
783 				break;
784 			case 288: /* 5 */
785 			case 306: /* 7 */
786 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
787 				break;
788 			default:
789 				(void) fprintf(stderr, "wrong direction %d\n",
790 					dir);
791 			}
792 			break;
793 		case 3:
794 			switch (dir) {
795 			case 0:
796 				col--;
797 				break;
798 			case 51: /* 7 */
799 			case 72: /* 5 */
800 				col = (col == 0) ? wp->ncols - 1 : col - 1;
801 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
802 				break;
803 			case 102: /* 7 */
804 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
805 				row = (row + 1 == wp->nrows) ? 0 : row + 1;
806 				break;
807 			case 144: /* 5 */
808 			case 153: /* 7 */
809 				col = (col + 2 >= wp->ncols) ? 1 : col + 2;
810 				break;
811 			case 204: /* 7 */
812 			case 216: /* 5 */
813 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
814 				break;
815 			case 255: /* 7 */
816 				col = (col + 1 >= wp->ncols) ? 0 : col + 1;
817 				row = (row == 0) ? wp->nrows - 1 : row - 1;
818 				break;
819 			case 288: /* 5 */
820 			case 306: /* 7 */
821 				row = (row == 0) ? wp->nrows - 1 : row - 1;
822 				break;
823 			default:
824 				(void) fprintf(stderr, "wrong direction %d\n",
825 					dir);
826 			}
827 			break;
828 		default:
829 			(void) fprintf(stderr, "wrong orient %d\n",
830 				orient);
831 		}
832 #endif
833 	}
834 	return (row * wp->ncols + col);
835 }
836 
837 static void
free_wator_screen(watorstruct * wp)838 free_wator_screen(watorstruct *wp)
839 {
840 	int kind;
841 
842 	if (wp == NULL) {
843 		return;
844 	}
845 	for (kind = 0; kind <= KINDS; kind++) {
846 		if (wp->firstkind[kind] != NULL) {
847 			flush_kindlist(wp, kind);
848 			free(wp->firstkind[kind]);
849 			wp->firstkind[kind] = (struct _CellList *) NULL;
850 		}
851 		if (wp->lastkind[kind] != NULL) {
852 			free(wp->lastkind[kind]);
853 			wp->lastkind[kind] = (struct _CellList *) NULL;
854 		}
855 	}
856 	if (wp->arr != NULL) {
857 		free(wp->arr);
858 		wp->arr = (CellList **) NULL;
859 	}
860 	wp = NULL;
861 }
862 
863 ENTRYPOINT void
init_wator(ModeInfo * mi)864 init_wator(ModeInfo * mi)
865 {
866 	int         size = MI_SIZE(mi);
867 	int         i, col, row, colrow, kind;
868 	cellstruct  info;
869 	watorstruct *wp;
870 
871 	MI_INIT(mi, wators);
872 	wp = &wators[MI_SCREEN(mi)];
873 
874 	wp->generation = 0;
875 	if (!wp->firstkind[0]) {	/* Genesis */
876 		icon_width = fish0_width;
877 		icon_height = fish0_height;
878 		/* Set up what will be a 'triply' linked list.
879 		   doubly linked list, doubly linked to an array */
880 		for (kind = FISH; kind <= KINDS; kind++)
881 			if (!init_kindlist(wp, kind)) {
882 				free_wator_screen(wp);
883 				return;
884 			}
885 		for (i = 0; i < BITMAPS; i++) {
886 			logo[i].width = icon_width;
887 			logo[i].height = icon_height;
888 			logo[i].bytes_per_line = (icon_width + 7) / 8;
889 		}
890 	} else			/* Exterminate all  */
891 		for (i = FISH; i <= KINDS; i++)
892 			flush_kindlist(wp, i);
893 	if (MI_IS_FULLRANDOM(mi)) {
894 		wp->vertical = (Bool) (LRAND() & 1);
895 	} else {
896 		wp->vertical = vertical;
897         }
898 	wp->width = MI_WIDTH(mi);
899 	wp->height = MI_HEIGHT(mi);
900 	if (wp->width < 2)
901 		wp->width = 2;
902 	if (wp->height < 2)
903 		wp->height = 2;
904 
905 	for (i = 0; i < NEIGHBORKINDS; i++) {
906 		if (neighbors == plots[i]) {
907 			wp->neighbors = neighbors;
908 			break;
909 		}
910 		if (i == NEIGHBORKINDS - 1) {
911 #if 0
912 			wp->neighbors = plots[NRAND(NEIGHBORKINDS)];
913 			wp->neighbors = (LRAND() & 1) ? 4 : 8;
914 #else
915 			wp->neighbors = 4;
916 #endif
917 			break;
918 		}
919 	}
920 
921 	if (wp->neighbors == 6) {
922 		int nccols, ncrows, sides;
923 
924 		wp->polygon = 6;
925 		if (!wp->vertical) {
926 			wp->height = MI_WIDTH(mi);
927 			wp->width = MI_HEIGHT(mi);
928 		}
929 		if (wp->width < 8)
930 			wp->width = 8;
931 		if (wp->height < 8)
932 			wp->height = 8;
933 		if (size < -MINSIZE)
934 			wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
935 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
936 		else if (size < MINSIZE) {
937 			if (!size)
938 				wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
939 			else
940 				wp->ys = MINSIZE;
941 		} else
942 			wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
943 					       MINGRIDSIZE));
944 		wp->xs = wp->ys;
945 		wp->pixelmode = True;
946 		nccols = MAX(wp->width / wp->xs - 2, 2);
947 		ncrows = MAX(wp->height / wp->ys - 1, 4);
948 		wp->ncols = nccols / 2;
949 		wp->nrows = 2 * (ncrows / 4);
950 		wp->xb = (wp->width - wp->xs * nccols) / 2 + wp->xs / 2;
951 		wp->yb = (wp->height - wp->ys * (ncrows / 2) * 2) / 2 +
952 			wp->ys / 3;
953 		for (sides = 0; sides < 6; sides++) {
954 			if (wp->vertical) {
955 				wp->shape.hexagon[sides].x =
956 					(wp->xs - 1) * hexagonUnit[sides].x;
957 				wp->shape.hexagon[sides].y =
958 					((wp->ys - 1) * hexagonUnit[sides].y /
959 					2) * 4 / 3;
960 			} else {
961 				wp->shape.hexagon[sides].y =
962 					(wp->xs - 1) * hexagonUnit[sides].x;
963 				wp->shape.hexagon[sides].x =
964 					((wp->ys - 1) * hexagonUnit[sides].y /
965 					2) * 4 / 3;
966 			}
967 		}
968 	} else if (wp->neighbors == 4 || wp->neighbors == 8) {
969 		wp->polygon = 4;
970 		if (wp->width < 2)
971 			wp->width = 2;
972 		if (wp->height < 2)
973 			wp->height = 2;
974 		if (size == 0 ||
975 		    MINGRIDSIZE * size > wp->width || MINGRIDSIZE * size > wp->height) {
976 			if (wp->width > MINGRIDSIZE * icon_width &&
977 			    wp->height > MINGRIDSIZE * icon_height) {
978 				wp->pixelmode = False;
979 				wp->xs = icon_width;
980 				wp->ys = icon_height;
981 			} else {
982 				wp->pixelmode = True;
983 				wp->xs = wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) /
984 						      MINGRIDSIZE);
985 			}
986 		} else {
987 			wp->pixelmode = True;
988 			if (size < -MINSIZE)
989 				wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
990 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
991 			else if (size < MINSIZE)
992 				wp->ys = MINSIZE;
993 			else
994 				wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
995 						       MINGRIDSIZE));
996 			wp->xs = wp->ys;
997 		}
998 		wp->ncols = MAX(wp->width / wp->xs, 2);
999 		wp->nrows = MAX(wp->height / wp->ys, 2);
1000 		wp->xb = (wp->width - wp->xs * wp->ncols) / 2;
1001 		wp->yb = (wp->height - wp->ys * wp->nrows) / 2;
1002 	} else {		/* TRI */
1003 		int orient, sides;
1004 
1005 		wp->polygon = 3;
1006 		if (!wp->vertical) {
1007 			wp->height = MI_WIDTH(mi);
1008 			wp->width = MI_HEIGHT(mi);
1009 		}
1010 		if (wp->width < 2)
1011 			wp->width = 2;
1012 		if (wp->height < 2)
1013 			wp->height = 2;
1014 		if (size < -MINSIZE)
1015 			wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1016 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1017 		else if (size < MINSIZE) {
1018 			if (!size)
1019 				wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
1020 			else
1021 				wp->ys = MINSIZE;
1022 		} else
1023 			wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1024 					       MINGRIDSIZE));
1025 		wp->xs = (int) (1.52 * wp->ys);
1026 		wp->pixelmode = True;
1027 		wp->ncols = (MAX(wp->width / wp->xs - 1, 2) / 2) * 2;
1028 		wp->nrows = (MAX(wp->height / wp->ys - 1, 2) / 2) * 2;
1029 		wp->xb = (wp->width - wp->xs * wp->ncols) / 2 + wp->xs / 2;
1030 		wp->yb = (wp->height - wp->ys * wp->nrows) / 2 + wp->ys / 2;
1031 		for (orient = 0; orient < 2; orient++) {
1032 			for (sides = 0; sides < 3; sides++) {
1033 				if (wp->vertical) {
1034 					wp->shape.triangle[orient][sides].x =
1035 						(wp->xs - 2) * triangleUnit[orient][sides].x;
1036 					wp->shape.triangle[orient][sides].y =
1037 						(wp->ys - 2) * triangleUnit[orient][sides].y;
1038 				} else {
1039 					wp->shape.triangle[orient][sides].y =
1040 						(wp->xs - 2) * triangleUnit[orient][sides].x;
1041 					wp->shape.triangle[orient][sides].x =
1042 						(wp->ys - 2) * triangleUnit[orient][sides].y;
1043 				}
1044 			}
1045 		}
1046 	}
1047 
1048 	wp->positions = wp->ncols * wp->nrows;
1049 
1050 	if (wp->arr != NULL)
1051 		free(wp->arr);
1052 	if ((wp->arr = (CellList **) calloc(wp->positions,
1053 			sizeof (CellList *))) == NULL) {
1054 		free_wator_screen(wp);
1055 		return;
1056 	}
1057 
1058 	/* Play G-d with these numbers */
1059 	wp->nkind[FISH] = wp->positions / 3;
1060 	wp->nkind[SHARK] = wp->nkind[FISH] / 10;
1061 	wp->kind = FISH;
1062 	if (!wp->nkind[SHARK])
1063 		wp->nkind[SHARK] = 1;
1064 	wp->breed[FISH] = MI_COUNT(mi);
1065 	wp->breed[SHARK] = 10;
1066 	if (wp->breed[FISH] < 1)
1067 		wp->breed[FISH] = 1;
1068 	else if (wp->breed[FISH] > wp->breed[SHARK])
1069 		wp->breed[FISH] = 4;
1070 	wp->sstarve = 3;
1071 
1072 	MI_CLEARWINDOW(mi);
1073 	wp->painted = False;
1074 
1075 	for (kind = FISH; kind <= SHARK; kind++) {
1076 		i = 0;
1077 		while (i < wp->nkind[kind]) {
1078 			col = NRAND(wp->ncols);
1079 			row = NRAND(wp->nrows);
1080 			colrow = col + row * wp->ncols;
1081 			if (!wp->arr[colrow]) {
1082 				i++;
1083 				info.kind = kind;
1084 				info.age = NRAND(wp->breed[kind]);
1085 				info.food = NRAND(wp->sstarve);
1086 				info.direction = NRAND(KINDBITMAPS) + kind * KINDBITMAPS;
1087 				if (MI_NPIXELS(mi) > 2)
1088 					info.color = NRAND(MI_NPIXELS(mi));
1089 				else
1090 					info.color = 0;
1091 				info.col = col;
1092 				info.row = row;
1093 				if (!addto_kindlist(wp, kind, info)) {
1094 					free_wator_screen(wp);
1095 					return;
1096 				}
1097 				wp->arr[colrow] = wp->currkind;
1098 				drawcell(mi, col, row,
1099 					 wp->currkind->info.color, wp->currkind->info.direction, True);
1100 			}
1101 		}
1102 	}
1103 }
1104 
1105 ENTRYPOINT void
draw_wator(ModeInfo * mi)1106 draw_wator(ModeInfo * mi)
1107 {
1108 	int         col, row;
1109 	int         colrow, cr, position;
1110 	int         i, numok;
1111 	struct {
1112 		int         pos, dir;
1113 	} acell[12];
1114 	watorstruct *wp;
1115 
1116 	if (wators == NULL)
1117 		return;
1118 	wp = &wators[MI_SCREEN(mi)];
1119 	if (wp->arr == NULL)
1120 		return;
1121 
1122 	MI_IS_DRAWN(mi) = True;
1123 	wp->painted = True;
1124 	/* Alternate updates, fish and sharks live out of phase with each other */
1125 	wp->kind = (wp->kind + 1) % KINDS;
1126 	{
1127 		wp->currkind = wp->firstkind[wp->kind]->next;
1128 
1129 		while (wp->currkind != wp->lastkind[wp->kind]) {
1130 			col = wp->currkind->info.col;
1131 			row = wp->currkind->info.row;
1132 			colrow = col + row * wp->ncols;
1133 			numok = 0;
1134 			if (wp->kind == SHARK) {	/* Scan for fish */
1135 				for (i = 0; i < wp->neighbors; i++) {
1136 					position = positionOfNeighbor(wp, i, col, row);
1137 					if (wp->arr[position] && wp->arr[position]->info.kind == FISH) {
1138 						acell[numok].pos = position;
1139 						acell[numok++].dir = i;
1140 					}
1141 				}
1142 				if (numok) {	/* No thanks, I'm a vegetarian */
1143 					i = NRAND(numok);
1144 					wp->nkind[FISH]--;
1145 					cr = acell[i].pos;
1146 					removefrom_kindlist(wp, wp->arr[cr]);
1147 					wp->arr[cr] = wp->currkind;
1148 					if (wp->neighbors == 4) {
1149 						wp->currkind->info.direction = (5 - acell[i].dir) % ORIENTS +
1150 							((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
1151 					} else if (wp->neighbors == 8) {
1152 						wp->currkind->info.direction = (char) (5 - (acell[i].dir / 2 +
1153 											    ((acell[i].dir % 2) ? LRAND() & 1 : 0))) % ORIENTS +
1154 							((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
1155 					} else
1156 						wp->currkind->info.direction = wp->kind * KINDBITMAPS;
1157 					wp->currkind->info.col = acell[i].pos % wp->ncols;
1158 					wp->currkind->info.row = acell[i].pos / wp->ncols;
1159 					wp->currkind->info.food = wp->sstarve;
1160 					drawcell(mi, wp->currkind->info.col, wp->currkind->info.row,
1161 						 wp->currkind->info.color, wp->currkind->info.direction, True);
1162 					if (++(wp->currkind->info.age) >= wp->breed[wp->kind]) {	/* breed */
1163 						cutfrom_kindlist(wp);	/* This rotates out who goes first */
1164 						wp->babykind->info.age = 0;
1165 						if (!dupin_kindlist(wp)) {
1166 							free_wator_screen(wp);
1167 							return;
1168 						}
1169 						wp->arr[colrow] = wp->babykind;
1170 						wp->babykind->info.col = col;
1171 						wp->babykind->info.row = row;
1172 						wp->babykind->info.age = -1;	/* Make one a little younger */
1173 #if 0
1174 						if (MI_NPIXELS(mi) > 2 && (LRAND() & 1))
1175 							/* A color mutation */
1176 							if (++(wp->babykind->info.color) >= MI_NPIXELS(mi))
1177 								wp->babykind->info.color = 0;
1178 #endif
1179 						wp->nkind[wp->kind]++;
1180 					} else {
1181 						wp->arr[colrow] = 0;
1182 						drawcell(mi, col, row, 0, 0, False);
1183 					}
1184 				} else {
1185 					if (wp->currkind->info.food-- < 0) {	/* Time to die, Jaws */
1186 						/* back up one or else in void */
1187 						wp->currkind = wp->currkind->previous;
1188 						removefrom_kindlist(wp, wp->arr[colrow]);
1189 						wp->arr[colrow] = 0;
1190 						drawcell(mi, col, row, 0, 0, False);
1191 						wp->nkind[wp->kind]--;
1192 						numok = -1;	/* Want to escape from next if */
1193 					}
1194 				}
1195 			}
1196 			if (!numok) {	/* Fish or shark search for a place to go */
1197 				for (i = 0; i < wp->neighbors; i++) {
1198 					position = positionOfNeighbor(wp, i, col, row);
1199 					if (!wp->arr[position]) {	/* Found an empty spot */
1200 						acell[numok].pos = position;
1201 						acell[numok++].dir = i;
1202 					}
1203 				}
1204 				if (numok) {	/* Found a place to go */
1205 					i = NRAND(numok);
1206 					wp->arr[acell[i].pos] = wp->currkind;
1207 					if (wp->neighbors == 4) {
1208 						wp->currkind->info.direction = (5 - acell[i].dir) % ORIENTS +
1209 							((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
1210 					} else if (wp->neighbors == 8) {
1211 						wp->currkind->info.direction = (char) (5 - (acell[i].dir / 2 +
1212 											    ((acell[i].dir % 2) ? LRAND() & 1 : 0))) % ORIENTS +
1213 							((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
1214 					} else
1215 						wp->currkind->info.direction = wp->kind * KINDBITMAPS;
1216 					wp->currkind->info.col = acell[i].pos % wp->ncols;
1217 					wp->currkind->info.row = acell[i].pos / wp->ncols;
1218 					drawcell(mi,
1219 						 wp->currkind->info.col, wp->currkind->info.row,
1220 						 wp->currkind->info.color, wp->currkind->info.direction, True);
1221 					if (++(wp->currkind->info.age) >= wp->breed[wp->kind]) {	/* breed */
1222 						cutfrom_kindlist(wp);	/* This rotates out who goes first */
1223 						wp->babykind->info.age = 0;
1224 						if (!dupin_kindlist(wp)) {
1225 							free_wator_screen(wp);
1226 							return;
1227 						}
1228 						wp->arr[colrow] = wp->babykind;
1229 						wp->babykind->info.col = col;
1230 						wp->babykind->info.row = row;
1231 						wp->babykind->info.age = -1;	/* Make one a little younger */
1232 						wp->nkind[wp->kind]++;
1233 					} else {
1234 						wp->arr[colrow] = 0;
1235 						drawcell(mi, col, row, 0, 0, False);
1236 					}
1237 				} else {
1238 					/* I'll just sit here and wave my tail so you know I am alive */
1239 					wp->currkind->info.direction =
1240 						(wp->currkind->info.direction + ORIENTS) % KINDBITMAPS +
1241 						wp->kind * KINDBITMAPS;
1242 					drawcell(mi, col, row, wp->currkind->info.color,
1243 					 wp->currkind->info.direction, True);
1244 				}
1245 			}
1246 			wp->currkind = wp->currkind->next;
1247 		}
1248 		reattach_kindlist(wp, wp->kind);
1249 	}
1250 
1251 	if ((wp->nkind[FISH] >= wp->positions) ||
1252 	    (!wp->nkind[FISH] && !wp->nkind[SHARK]) ||
1253 	    wp->generation >= MI_CYCLES(mi)) {
1254 		init_wator(mi);
1255 	}
1256 	if (wp->kind == SHARK)
1257 		wp->generation++;
1258 }
1259 
1260 ENTRYPOINT void
release_wator(ModeInfo * mi)1261 release_wator(ModeInfo * mi)
1262 {
1263 	if (wators != NULL) {
1264 		int         screen;
1265 
1266 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1267 			free_wator_screen(&wators[screen]);
1268 		free(wators);
1269 		wators = (watorstruct *) NULL;
1270 	}
1271 }
1272 
1273 #ifndef STANDALONE
1274 ENTRYPOINT void
refresh_wator(ModeInfo * mi)1275 refresh_wator(ModeInfo * mi)
1276 {
1277 	watorstruct *wp;
1278 
1279 	if (wators == NULL)
1280 		return;
1281 	wp = &wators[MI_SCREEN(mi)];
1282 
1283 	if (wp->painted) {
1284 		MI_CLEARWINDOW(mi);
1285 		wp->painted = False;
1286 	}
1287 }
1288 #endif
1289 
1290 XSCREENSAVER_MODULE ("Wator", wator)
1291 
1292 #endif /* MODE_wator */
1293