1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* voters --- Dewdney's Voting Simulation */
3 
4 #if 0
5 static const char sccsid[] = "@(#)voters.c	5.24 2007/01/18 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1997 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-Jun-1997: Coded from A.K. Dewdney's "The Armchair Universe, Computer
28  *              Recreations from the Pages of Scientific American Magazine"
29  *              W.H. Freedman and Company, New York, 1988  (Apr 1985)
30  *              Used wator.c and demon.c as a guide.
31  */
32 
33 #ifdef STANDALONE
34 #define MODE_voters
35 #define DEFAULTS "*delay: 1000 \n" \
36 	"*cycles: 327670 \n" \
37 	"*size: 0 \n" \
38 	"*ncolors: 64 \n" \
39 
40 # define free_voters 0
41 # define reshape_voters 0
42 # define voters_handle_event 0
43 #define UNIFORM_COLORS
44 #define BRIGHT_COLORS
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_voters
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 *) ".voters.neighbors", XrmoptionSepArg, (caddr_t) NULL},
65 	{(char *) "-vertical", (char *) ".voters.vertical", XrmoptionNoArg, (caddr_t) "on"},
66         {(char *) "+vertical", (char *) ".voters.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 static OptionStruct desc[] =
75 {
76         {(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"},
77 	{(char *) "-/+vertical", (char *) "change orientation for hexagons and triangles"}
78 };
79 
80 ENTRYPOINT ModeSpecOpt voters_opts =
81 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
82 
83 
84 #ifdef USE_MODULES
85 ModStruct   voters_description =
86 {"voters", "init_voters", "draw_voters", "release_voters",
87  "refresh_voters", "init_voters", (char *) NULL, &voters_opts,
88  1000, 0, 327670, 0, 64, 1.0, "",
89  "Shows Dewdney's Voters", 0, NULL};
90 
91 #endif
92 
93 /*-
94  * From far left to right, at least in the currently in the US.  By the way, I
95  * consider myself to be a proud bleeding heart liberal democrat, in
96  * case anyone wants to know....  Please, no fascist "improvements".  :)
97  */
98 
99 #include "bitmaps/elephant.xbm"
100 #ifdef COMMIE
101 #include "bitmaps/sickle.xbm"
102 #else
103 #include "bitmaps/green.xbm"
104 #endif
105 #include "bitmaps/donkey.xbm"
106 
107 #define MINPARTIES 2
108 #define BITMAPS 3
109 #define MINGRIDSIZE 10
110 #define MINSIZE 4
111 #define FACTOR 10
112 #define NEIGHBORKINDS 6
113 
114 static XImage logo[BITMAPS] =
115 {
116     {0, 0, 0, XYBitmap, (char *) elephant_bits, LSBFirst, 8, LSBFirst, 8, 1},
117     {0, 0, 0, XYBitmap, (char *) green_bits, LSBFirst, 8, LSBFirst, 8, 1},
118     {0, 0, 0, XYBitmap, (char *) donkey_bits, LSBFirst, 8, LSBFirst, 8, 1},
119 };
120 
121 /* Voter data */
122 typedef struct {
123 	char        kind;
124 	int         age;
125 	int         col, row;
126 } cellstruct;
127 
128 /* Doubly linked list */
129 typedef struct _CellList {
130 	cellstruct  info;
131 	struct _CellList *previous, *next;
132 } CellList;
133 
134 typedef struct {
135 	Bool        painted, vertical;
136 	int         party;	/* Currently working on donkey, elephant, or green? */
137 	int         xs, ys;	/* Size of party icon */
138 	int         xb, yb;	/* Bitmap offset for party icon */
139 	int         nparties;	/* 2 parties or 3 */
140 	int         number_in_party[BITMAPS];	/* Good to know when one party rules */
141 	int         pixelmode;
142 	int         generation;
143 	int         ncols, nrows;
144 	int         npositions;
145 	int         width, height;
146 	CellList   *last, *first;
147 	char       *arr;
148 	int         neighbors, polygon;
149 	int         busyLoop;
150 	union {
151 		XPoint      hexagon[6];
152 		XPoint      triangle[2][3];
153 		XPoint      pentagon[4][5];
154 	} shape;
155 } voterstruct;
156 
157 static char plots[NEIGHBORKINDS] =
158 {
159 	3, 4, 6, 8, 9, 12	/* Neighborhoods */
160 };
161 
162 static voterstruct *voters = (voterstruct *) NULL;
163 static int  icon_width, icon_height;
164 
165 static void
drawCell(ModeInfo * mi,int col,int row,unsigned long color,int bitmap,Bool firstChange)166 drawCell(ModeInfo * mi, int col, int row, unsigned long color, int bitmap,
167 		Bool firstChange)
168 {
169 	Display    *display = MI_DISPLAY(mi);
170 	Window      window = MI_WINDOW(mi);
171 	GC          gc = MI_GC(mi);
172 	voterstruct *vp = &voters[MI_SCREEN(mi)];
173 	unsigned long colour = (MI_NPIXELS(mi) > 2) ?
174 		MI_PIXEL(mi, color) : MI_WHITE_PIXEL(mi);
175 
176 	XSetForeground(display, gc, colour);
177 	if (vp->neighbors == 6) {
178 		int ccol = 2 * col + !(row & 1), crow = 2 * row;
179 
180 		if (vp->vertical) {
181 			vp->shape.hexagon[0].x = vp->xb + ccol * vp->xs;
182 			vp->shape.hexagon[0].y = vp->yb + crow * vp->ys;
183 		} else {
184 			vp->shape.hexagon[0].y = vp->xb + ccol * vp->xs;
185 			vp->shape.hexagon[0].x = vp->yb + crow * vp->ys;
186 		}
187 		if (vp->xs == 1 && vp->ys == 1)
188 			XDrawPoint(display, window, gc,
189 				vp->shape.hexagon[0].x,
190 				vp->shape.hexagon[0].y);
191 		else if (bitmap == BITMAPS - 1)
192 			XFillPolygon(display, window, gc,
193 				vp->shape.hexagon, 6,
194 				Convex, CoordModePrevious);
195 		else {
196 			int ix = 0, iy = 0, sx, sy;
197 
198 			if (firstChange) {
199 				XSetForeground(display, gc,
200 					MI_BLACK_PIXEL(mi));
201 				XFillPolygon(display, window, gc,
202 					vp->shape.hexagon, 6,
203 					Convex, CoordModePrevious);
204 				XSetForeground(display, gc, colour);
205 			}
206 			if (vp->vertical) {
207 				vp->shape.hexagon[0].x -= vp->xs;
208 				vp->shape.hexagon[0].y += vp->ys / 4;
209 				sx = 2 * vp->xs - 6;
210 				sy = 2 * vp->ys - 2;
211 				if (vp->xs <= 6 || vp->ys <= 2) {
212 					ix = 3;
213 					iy = 1;
214 				} else
215 					ix = 5;
216 			} else {
217 				vp->shape.hexagon[0].y -= vp->xs;
218 				vp->shape.hexagon[0].x += vp->ys / 4;
219 				sy = 2 * vp->xs - 6;
220 				sx = 2 * vp->ys - 2;
221 				if (vp->xs <= 6 || vp->ys <= 2) {
222 					iy = 3;
223 					ix = 1;
224 				} else
225 					iy = 5;
226 			}
227 			if (vp->xs <= 6 || vp->ys <= 2)
228 				XFillRectangle(display, window, gc,
229 					vp->shape.hexagon[0].x + ix,
230 					vp->shape.hexagon[0].y + iy,
231 					vp->xs, vp->ys);
232 			else
233 				XFillArc(display, window, gc,
234 					vp->shape.hexagon[0].x + ix,
235 					vp->shape.hexagon[0].y + iy,
236 					sx, sy,
237 					0, 23040);
238 		}
239 	} else if (vp->neighbors == 4 || vp->neighbors == 8) {
240 		if (vp->pixelmode) {
241 			if (bitmap == BITMAPS - 1 || (vp->xs <= 2 || vp->ys <= 2))
242 				XFillRectangle(display, window, gc,
243 					vp->xb + vp->xs * col,
244 					vp->yb + vp->ys * row,
245 					vp->xs - (vp->xs > 3),
246 					vp->ys - (vp->ys > 3));
247 			else {
248 				if (firstChange) {
249 					XSetForeground(display, gc,
250 						MI_BLACK_PIXEL(mi));
251 					XFillRectangle(display, window, gc,
252 						vp->xb + vp->xs * col,
253 						vp->yb + vp->ys * row,
254 						vp->xs, vp->ys);
255 					XSetForeground(display, gc, colour);
256 				}
257 				XFillArc(display, window, gc,
258 					vp->xb + vp->xs * col,
259 					vp->yb + vp->ys * row,
260 					vp->xs - 1, vp->ys - 1,
261 					0, 23040);
262 			}
263 		} else
264 			(void) XPutImage(display, window, gc,
265 					 &logo[bitmap], 0, 0,
266 				vp->xb + vp->xs * col, vp->yb + vp->ys * row,
267 					 icon_width, icon_height);
268 	} else {		/* TRI */
269 		int orient = (col + row) % 2;	/* O left 1 right */
270 		Bool small = (vp->xs <= 3 || vp->ys <= 3);
271 		int ix = 0, iy = 0;
272 
273 		if (vp->vertical) {
274 			vp->shape.triangle[orient][0].x = vp->xb + col * vp->xs;
275 			vp->shape.triangle[orient][0].y = vp->yb + row * vp->ys;
276 			if (small)
277 				vp->shape.triangle[orient][0].x +=
278 					((orient) ? -1 : 1);
279 			else
280 				vp->shape.triangle[orient][0].x +=
281 					(vp->xs / 2  - 1) * ((orient) ? 1 : -1);
282 		} else {
283 			vp->shape.triangle[orient][0].y = vp->xb + col * vp->xs;
284 			vp->shape.triangle[orient][0].x = vp->yb + row * vp->ys;
285 			if (small)
286 				vp->shape.triangle[orient][0].y +=
287 					((orient) ? -1 : 1);
288 			else
289 				vp->shape.triangle[orient][0].y +=
290 					(vp->xs / 2  - 1) * ((orient) ? 1 : -1);
291 		}
292 		if (small)
293 			XDrawPoint(display, window, gc,
294 				vp->shape.triangle[orient][0].x,
295 				vp->shape.triangle[orient][0].y);
296 		else {
297 			if (bitmap == BITMAPS - 1)
298 				XFillPolygon(display, window, gc,
299 					vp->shape.triangle[orient], 3,
300 					Convex, CoordModePrevious);
301 			else {
302 				if (firstChange) {
303 					XSetForeground(display, gc,
304 						MI_BLACK_PIXEL(mi));
305 					XFillPolygon(display, window, gc,
306 						vp->shape.triangle[orient], 3,
307 						Convex, CoordModePrevious);
308 					XSetForeground(display, gc, colour);
309 				}
310 				if (vp->vertical) {
311 					vp->shape.triangle[orient][0].x += -4 * vp->xs / 5 +
312 						((orient) ? vp->xs / 3 : 3 * vp->xs / 5);
313 					vp->shape.triangle[orient][0].y += -vp->ys / 2 + 1;
314 					ix = ((orient) ? -vp->xs / 2 : vp->xs / 2);
315 				} else {
316 					vp->shape.triangle[orient][0].y += -4 * vp->xs / 5 +
317 						((orient) ? vp->xs / 3 : 3 * vp->xs / 5);
318 					vp->shape.triangle[orient][0].x += -vp->ys / 2 + 1;
319 					iy = ((orient) ? -vp->xs / 2 : vp->xs / 2);
320 				}
321 				XFillArc(display, window, gc,
322 					vp->shape.triangle[orient][0].x + ix,
323 					vp->shape.triangle[orient][0].y + iy,
324 					vp->ys - 3, vp->ys - 3,
325 					0, 23040);
326 			}
327 		}
328 	}
329 }
330 
331 static Bool
init_list(voterstruct * vp)332 init_list(voterstruct * vp)
333 {
334 	/* Waste some space at the beginning and end of list
335 	   so we do not have to complicated checks against falling off the ends. */
336 	if (((vp->last = (CellList *) malloc(sizeof (CellList))) == NULL) ||
337 	    ((vp->first = (CellList *) malloc(sizeof (CellList))) == NULL)) {
338 		return False;
339 	}
340 	vp->first->previous = vp->last->next = (struct _CellList *) NULL;
341 	vp->first->next = vp->last->previous = (struct _CellList *) NULL;
342 	vp->first->next = vp->last;
343 	vp->last->previous = vp->first;
344 	return True;
345 }
346 
347 static Bool
addto_list(voterstruct * vp,cellstruct info)348 addto_list(voterstruct * vp, cellstruct info)
349 {
350 	CellList   *curr;
351 
352 	if ((curr = (CellList *) malloc(sizeof (CellList))) == NULL) {
353 		return False;
354 	}
355 	vp->last->previous->next = curr;
356 	curr->previous = vp->last->previous;
357 	curr->next = vp->last;
358 	vp->last->previous = curr;
359 	curr->info = info;
360 	return True;
361 }
362 
363 static void
removefrom_list(CellList * ptr)364 removefrom_list(CellList * ptr)
365 {
366 	ptr->previous->next = ptr->next;
367 	ptr->next->previous = ptr->previous;
368 	free(ptr);
369 }
370 
371 static void
flush_list(voterstruct * vp)372 flush_list(voterstruct * vp)
373 {
374 	CellList   *curr;
375 
376 	while (vp->last->previous != vp->first) {
377 		curr = vp->last->previous;
378 		curr->previous->next = vp->last;
379 		vp->last->previous = curr->previous;
380 		free(curr);
381 	}
382 }
383 
384 
385 static int
positionOfNeighbor(voterstruct * vp,int n,int col,int row)386 positionOfNeighbor(voterstruct * vp, int n, int col, int row)
387 {
388 	int dir = n * (360 / vp->neighbors);
389 
390 	if (vp->polygon == 4 || vp->polygon == 6) {
391 		switch (dir) {
392 		case 0:
393 			col = (col + 1 == vp->ncols) ? 0 : col + 1;
394 			break;
395 		case 45:
396 			col = (col + 1 == vp->ncols) ? 0 : col + 1;
397 			row = (!row) ? vp->nrows - 1 : row - 1;
398 			break;
399 		case 60:
400 			if (!(row & 1))
401 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
402 			row = (!row) ? vp->nrows - 1 : row - 1;
403 			break;
404 		case 90:
405 			row = (!row) ? vp->nrows - 1 : row - 1;
406 			break;
407 		case 120:
408 			if (row & 1)
409 				col = (!col) ? vp->ncols - 1 : col - 1;
410 			row = (!row) ? vp->nrows - 1 : row - 1;
411 			break;
412 		case 135:
413 			col = (!col) ? vp->ncols - 1 : col - 1;
414 			row = (!row) ? vp->nrows - 1 : row - 1;
415 			break;
416 		case 180:
417 			col = (!col) ? vp->ncols - 1 : col - 1;
418 			break;
419 		case 225:
420 			col = (!col) ? vp->ncols - 1 : col - 1;
421 			row = (row + 1 == vp->nrows) ? 0 : row + 1;
422 			break;
423 		case 240:
424 			if (row & 1)
425 				col = (!col) ? vp->ncols - 1 : col - 1;
426 			row = (row + 1 == vp->nrows) ? 0 : row + 1;
427 			break;
428 		case 270:
429 			row = (row + 1 == vp->nrows) ? 0 : row + 1;
430 			break;
431 		case 300:
432 			if (!(row & 1))
433 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
434 			row = (row + 1 == vp->nrows) ? 0 : row + 1;
435 			break;
436 		case 315:
437 			col = (col + 1 == vp->ncols) ? 0 : col + 1;
438 			row = (row + 1 == vp->nrows) ? 0 : row + 1;
439 			break;
440 		default:
441 			(void) fprintf(stderr, "wrong direction %d\n", dir);
442 		}
443 	} else if (vp->polygon == 3) {
444 		if ((col + row) & 1) {	/* right */
445 			switch (dir) {
446 			case 0:
447 				col = (!col) ? vp->ncols - 1 : col - 1;
448 				break;
449 			case 30:
450 			case 40:
451 				col = (!col) ? vp->ncols - 1 : col - 1;
452 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
453 				break;
454 			case 60:
455 				col = (!col) ? vp->ncols - 1 : col - 1;
456 				if (row + 1 == vp->nrows)
457 					row = 1;
458 				else if (row + 2 == vp->nrows)
459 					row = 0;
460 				else
461 					row = row + 2;
462 				break;
463 			case 80:
464 			case 90:
465 				if (row + 1 == vp->nrows)
466 					row = 1;
467 				else if (row + 2 == vp->nrows)
468 					row = 0;
469 				else
470 					row = row + 2;
471 				break;
472 			case 120:
473 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
474 				break;
475 			case 150:
476 			case 160:
477 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
478 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
479 				break;
480 			case 180:
481 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
482 				break;
483 			case 200:
484 			case 210:
485 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
486 				row = (!row) ? vp->nrows - 1 : row - 1;
487 				break;
488 			case 240:
489 				row = (!row) ? vp->nrows - 1 : row - 1;
490 				break;
491 			case 270:
492 			case 280:
493 				if (!row)
494 					row = vp->nrows - 2;
495 				else if (!(row - 1))
496 					row = vp->nrows - 1;
497 				else
498 					row = row - 2;
499 				break;
500 			case 300:
501 				col = (!col) ? vp->ncols - 1 : col - 1;
502 				if (!row)
503 					row = vp->nrows - 2;
504 				else if (!(row - 1))
505 					row = vp->nrows - 1;
506 				else
507 					row = row - 2;
508 				break;
509 			case 320:
510 			case 330:
511 				col = (!col) ? vp->ncols - 1 : col - 1;
512 				row = (!row) ? vp->nrows - 1 : row - 1;
513 				break;
514 			default:
515 				(void) fprintf(stderr, "wrong direction %d\n",
516 					dir);
517 			}
518 		} else {	/* left */
519 			switch (dir) {
520 			case 0:
521 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
522 				break;
523 			case 30:
524 			case 40:
525 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
526 				row = (!row) ? vp->nrows - 1 : row - 1;
527 				break;
528 			case 60:
529 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
530 				if (!row)
531 					row = vp->nrows - 2;
532 				else if (row == 1)
533 					row = vp->nrows - 1;
534 				else
535 					row = row - 2;
536 				break;
537 			case 80:
538 			case 90:
539 				if (!row)
540 					row = vp->nrows - 2;
541 				else if (row == 1)
542 					row = vp->nrows - 1;
543 				else
544 					row = row - 2;
545 				break;
546 			case 120:
547 				row = (!row) ? vp->nrows - 1 : row - 1;
548 				break;
549 			case 150:
550 			case 160:
551 				col = (!col) ? vp->ncols - 1 : col - 1;
552 				row = (!row) ? vp->nrows - 1 : row - 1;
553 				break;
554 			case 180:
555 				col = (!col) ? vp->ncols - 1 : col - 1;
556 				break;
557 			case 200:
558 			case 210:
559 				col = (!col) ? vp->ncols - 1 : col - 1;
560 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
561 				break;
562 			case 240:
563 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
564 				break;
565 			case 270:
566 			case 280:
567 				if (row + 1 == vp->nrows)
568 					row = 1;
569 				else if (row + 2 == vp->nrows)
570 					row = 0;
571 				else
572 					row = row + 2;
573 				break;
574 			case 300:
575 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
576 				if (row + 1 == vp->nrows)
577 					row = 1;
578 				else if (row + 2 == vp->nrows)
579 					row = 0;
580 				else
581 					row = row + 2;
582 				break;
583 			case 320:
584 			case 330:
585 				col = (col + 1 == vp->ncols) ? 0 : col + 1;
586 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
587 				break;
588 			default:
589 				(void) fprintf(stderr, "wrong direction %d\n",
590 					dir);
591 			}
592 		}
593 #if 0
594 	} else {
595 		int orient = ((row & 1) * 2 + col) % 4;
596 		switch (orient) { /* up, down, left, right */
597 		case 0:
598 			switch (dir) {
599 			case 0:
600 				col++;
601 				break;
602 			case 51: /* 7 */
603 			case 72: /* 5 */
604 				col = (col + 2 >= vp->ncols) ? 0 : col + 2;
605 				break;
606 			case 102: /* 7 corner */
607 				col = (col + 3 >= vp->ncols) ? 1 : col + 3;
608 				row = (row == 0) ? vp->nrows - 1 : row - 1;
609 				break;
610 			case 144: /* 5 */
611 			case 153: /* 7 */
612 				col++;
613 				row = (row == 0) ? vp->nrows - 1 : row - 1;
614 				break;
615 			case 204: /* 7 */
616 			case 216: /* 5 */
617 				row = (row == 0) ? vp->nrows - 1 : row - 1;
618 				break;
619 			case 255: /* 7 */
620 				col = (col == 0) ? vp->ncols - 1 : col - 1;
621 				row = (row == 0) ? vp->nrows - 1 : row - 1;
622 				break;
623 			case 288: /* 5 */
624 			case 306: /* 7 */
625 				col = (col == 0) ? vp->ncols - 1 : col - 1;
626 				break;
627 			default:
628 				(void) fprintf(stderr, "wrong direction %d\n",
629 					dir);
630 			}
631 			break;
632 		case 1:
633 			switch (dir) {
634 			case 0:
635 				col--;
636 				break;
637 			case 51: /* 7 */
638 			case 72: /* 5 */
639 				col = (col == 1) ? vp->ncols - 1 : col - 2;
640 				break;
641 			case 102: /* 7 */
642 				col = (col == 1) ? vp->ncols - 2 : col - 3;
643 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
644 				break;
645 			case 144: /* 5 */
646 			case 153: /* 7 */
647 				col--;
648 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
649 				break;
650 			case 204: /* 7 */
651 			case 216: /* 5 */
652 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
653 				break;
654 			case 255: /* 7 */
655 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
656 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
657 				break;
658 			case 288: /* 5 */
659 			case 306: /* 7 */
660 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
661 				break;
662 			default:
663 				(void) fprintf(stderr, "wrong direction %d\n",
664 					dir);
665 			}
666 			break;
667 		case 2:
668 			switch (dir) {
669 			case 0:
670 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
671 				break;
672 			case 51: /* 7 */
673 			case 72: /* 5 */
674 				row = (row == 0) ? vp->nrows - 1 : row - 1;
675 				col++;
676 				break;
677 			case 102: /* 7 */
678 				col = (col == 0) ? vp->ncols - 1 : col - 1;
679 				row = (row == 0) ? vp->nrows - 1 : row - 1;
680 				break;
681 			case 144: /* 5 */
682 			case 153: /* 7 */
683 				col = (col == 0) ? vp->ncols - 2 : col - 2;
684 				break;
685 			case 204: /* 7 */
686 			case 216: /* 5 */
687 				col = (col == 0) ? vp->ncols - 1 : col - 1;
688 				break;
689 			case 255: /* 7 */
690 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
691 				col = (col == 0) ? vp->ncols - 1 : col - 1;
692 				break;
693 			case 288: /* 5 */
694 			case 306: /* 7 */
695 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
696 				break;
697 			default:
698 				(void) fprintf(stderr, "wrong direction %d\n",
699 					dir);
700 			}
701 			break;
702 		case 3:
703 			switch (dir) {
704 			case 0:
705 				col--;
706 				break;
707 			case 51: /* 7 */
708 			case 72: /* 5 */
709 				col = (col == 0) ? vp->ncols - 1 : col - 1;
710 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
711 				break;
712 			case 102: /* 7 */
713 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
714 				row = (row + 1 == vp->nrows) ? 0 : row + 1;
715 				break;
716 			case 144: /* 5 */
717 			case 153: /* 7 */
718 				col = (col + 2 >= vp->ncols) ? 1 : col + 2;
719 				break;
720 			case 204: /* 7 */
721 			case 216: /* 5 */
722 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
723 				break;
724 			case 255: /* 7 */
725 				col = (col + 1 >= vp->ncols) ? 0 : col + 1;
726 				row = (row == 0) ? vp->nrows - 1 : row - 1;
727 				break;
728 			case 288: /* 5 */
729 			case 306: /* 7 */
730 				row = (row == 0) ? vp->nrows - 1 : row - 1;
731 				break;
732 			default:
733 				(void) fprintf(stderr, "wrong direction %d\n",
734 					dir);
735 			}
736 			break;
737 		default:
738 			(void) fprintf(stderr, "wrong orient %d\n",
739 				orient);
740 		}
741 #endif
742 	}
743 	return (row * vp->ncols + col);
744 }
745 
746 static void
advanceColors(ModeInfo * mi,int col,int row)747 advanceColors(ModeInfo * mi, int col, int row)
748 {
749 	voterstruct *vp = &voters[MI_SCREEN(mi)];
750 	CellList   *curr;
751 
752 	curr = vp->first->next;
753 	while (curr != vp->last) {
754 		if (curr->info.col == col && curr->info.row == row) {
755 			curr = curr->next;
756 			removefrom_list(curr->previous);
757 		} else {
758 			if (curr->info.age > 0)
759 				curr->info.age--;
760 			else if (curr->info.age < 0)
761 				curr->info.age++;
762 			drawCell(mi, curr->info.col, curr->info.row,
763 				 (MI_NPIXELS(mi) + curr->info.age / FACTOR +
764 				  (MI_NPIXELS(mi) * curr->info.kind / BITMAPS)) % MI_NPIXELS(mi),
765 				 curr->info.kind, False);
766 			if (curr->info.age == 0) {
767 				curr = curr->next;
768 				removefrom_list(curr->previous);
769 			} else
770 				curr = curr->next;
771 		}
772 	}
773 }
774 
775 static void
free_voters_screen(voterstruct * vp)776 free_voters_screen(voterstruct *vp)
777 {
778 	if (vp == NULL) {
779 		return;
780 	}
781 	if (vp->first != NULL) {
782 		flush_list(vp);
783 		free(vp->first);
784 		vp->first = (CellList *) NULL;
785 	}
786 	if (vp->last != NULL) {
787 		free(vp->last);
788 		vp->last = (CellList *) NULL;
789 	}
790 	if (vp->arr != NULL) {
791 		free(vp->arr);
792 		vp->arr = (char *) NULL;
793 	}
794 	vp = NULL;
795 }
796 
797 ENTRYPOINT void
init_voters(ModeInfo * mi)798 init_voters(ModeInfo * mi)
799 {
800 	int         size = MI_SIZE(mi);
801 	int         i, col, row, colrow;
802 	voterstruct *vp;
803 
804 	MI_INIT(mi, voters);
805 	vp = &voters[MI_SCREEN(mi)];
806 
807 	vp->generation = 0;
808 	if (!vp->first) {	/* Genesis of democracy */
809 		icon_width = donkey_width;
810 		icon_height = donkey_height;
811 		if (!init_list(vp)) {
812 			free_voters_screen(vp);
813 			return;
814 		}
815 		for (i = 0; i < BITMAPS; i++) {
816 			logo[i].width = icon_width;
817 			logo[i].height = icon_height;
818 			logo[i].bytes_per_line = (icon_width + 7) / 8;
819 		}
820 	} else			/* Exterminate all free thinking individuals */
821 		flush_list(vp);
822 	if (MI_IS_FULLRANDOM(mi)) {
823 		vp->vertical = (Bool) (LRAND() & 1);
824 	} else {
825 		vp->vertical = vertical;
826 	}
827 	vp->width = MI_WIDTH(mi);
828 	vp->height = MI_HEIGHT(mi);
829 
830 	for (i = 0; i < NEIGHBORKINDS; i++) {
831 		if (neighbors == plots[i]) {
832 			vp->neighbors = neighbors;
833 			break;
834 		}
835 		if (i == NEIGHBORKINDS - 1) {
836 #if 0
837 			vp->neighbors = plots[NRAND(NEIGHBORKINDS)];
838 			vp->neighbors = (LRAND() & 1) ? 4 : 8;
839 #else
840 			vp->neighbors = 8;
841 #endif
842 			break;
843 		}
844 	}
845 
846 	if (vp->neighbors == 6) {
847 		int nccols, ncrows, sides;
848 
849 		vp->polygon = 6;
850 		if (!vp->vertical) {
851 			vp->height = MI_WIDTH(mi);
852 			vp->width = MI_HEIGHT(mi);
853 		}
854 		if (vp->width < 8)
855 			vp->width = 8;
856 		if (vp->height < 8)
857 			vp->height = 8;
858 		if (size < -MINSIZE)
859 			vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
860 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
861 		else if (size < MINSIZE) {
862 			if (!size)
863 				vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) / MINGRIDSIZE);
864 			else
865 				vp->ys = MINSIZE;
866 		} else
867 			vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
868 					       MINGRIDSIZE));
869 		vp->xs = vp->ys;
870 		vp->pixelmode = True;
871 		nccols = MAX(vp->width / vp->xs - 2, 2);
872 		ncrows = MAX(vp->height / vp->ys - 1, 4);
873 		vp->ncols = nccols / 2;
874 		vp->nrows = 2 * (ncrows / 4);
875 		vp->xb = (vp->width - vp->xs * nccols) / 2 + vp->xs / 2;
876 		vp->yb = (vp->height - vp->ys * (ncrows / 2) * 2) / 2 +
877 			vp->ys - 2;
878 		for (sides = 0; sides < 6; sides++) {
879 			if (vp->vertical) {
880 				vp->shape.hexagon[sides].x =
881 					(vp->xs - 1) * hexagonUnit[sides].x;
882 				vp->shape.hexagon[sides].y =
883 					((vp->ys - 1) * hexagonUnit[sides].y /
884 					2) * 4 / 3;
885 			} else {
886 				vp->shape.hexagon[sides].y =
887 					(vp->xs - 1) * hexagonUnit[sides].x;
888 				vp->shape.hexagon[sides].x =
889 					((vp->ys - 1) * hexagonUnit[sides].y /
890 					2) * 4 / 3;
891 			}
892 		}
893 	} else if (vp->neighbors == 4 || vp->neighbors == 8) {
894 		vp->polygon = 4;
895 		if (vp->width < 2)
896 			vp->width = 2;
897 		if (vp->height < 2)
898 			vp->height = 2;
899 		if (size == 0 ||
900 		    MINGRIDSIZE * size > vp->width || MINGRIDSIZE * size > vp->height) {
901 			if (vp->width > MINGRIDSIZE * icon_width &&
902 			    vp->height > MINGRIDSIZE * icon_height) {
903 				vp->pixelmode = False;
904 				vp->xs = icon_width;
905 				vp->ys = icon_height;
906 			} else {
907 				vp->pixelmode = True;
908 				vp->xs = vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) /
909 						      MINGRIDSIZE);
910 			}
911 		} else {
912 			vp->pixelmode = True;
913 			if (size < -MINSIZE)
914 				vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
915 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
916 			else if (size < MINSIZE)
917 				vp->ys = MINSIZE;
918 			else
919 				vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
920 						       MINGRIDSIZE));
921 			vp->xs = vp->ys;
922 		}
923 		vp->ncols = MAX(vp->width / vp->xs, 2);
924 		vp->nrows = MAX(vp->height / vp->ys, 2);
925 		vp->xb = (vp->width - vp->xs * vp->ncols) / 2;
926 		vp->yb = (vp->height - vp->ys * vp->nrows) / 2;
927 	} else {		/* TRI */
928 		int orient, sides;
929 
930 		vp->polygon = 3;
931 		if (!vp->vertical) {
932 			vp->height = MI_WIDTH(mi);
933 			vp->width = MI_HEIGHT(mi);
934 		}
935 		if (vp->width < 2)
936 			vp->width = 2;
937 		if (vp->height < 2)
938 			vp->height = 2;
939 		if (size < -MINSIZE)
940 			vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
941 				      MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
942 		else if (size < MINSIZE) {
943 			if (!size)
944 				vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) / MINGRIDSIZE);
945 			else
946 				vp->ys = MINSIZE;
947 		} else
948 			vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
949 					       MINGRIDSIZE));
950 		vp->xs = (int) (1.52 * vp->ys);
951 		vp->pixelmode = True;
952 		vp->ncols = (MAX(vp->width / vp->xs - 1, 2) / 2) * 2;
953 		vp->nrows = (MAX(vp->height / vp->ys - 1, 2) / 2) * 2;
954 		vp->xb = (vp->width - vp->xs * vp->ncols) / 2 + vp->xs / 2;
955 		vp->yb = (vp->height - vp->ys * vp->nrows) / 2 + vp->ys / 2;
956 		for (orient = 0; orient < 2; orient++) {
957 			for (sides = 0; sides < 3; sides++) {
958 				if (vp->vertical) {
959 					vp->shape.triangle[orient][sides].x =
960 						(vp->xs - 2) * triangleUnit[orient][sides].x;
961 					vp->shape.triangle[orient][sides].y =
962 						(vp->ys - 2) * triangleUnit[orient][sides].y;
963 				} else {
964 					vp->shape.triangle[orient][sides].y =
965 						(vp->xs - 2) * triangleUnit[orient][sides].x;
966 					vp->shape.triangle[orient][sides].x =
967 						(vp->ys - 2) * triangleUnit[orient][sides].y;
968 				}
969 			}
970 		}
971 	}
972 
973 	vp->npositions = vp->ncols * vp->nrows;
974 	if (vp->arr != NULL)
975 		free(vp->arr);
976 	if ((vp->arr = (char *) calloc(vp->npositions, sizeof (char))) == NULL) {
977 		free_voters_screen(vp);
978 		return;
979 	}
980 
981 	/* Play G-d with these numbers */
982 	vp->nparties = MI_COUNT(mi);
983 	if (vp->nparties < MINPARTIES || vp->nparties > BITMAPS)
984 		vp->nparties = NRAND(BITMAPS - MINPARTIES + 1) + MINPARTIES;
985 	if (vp->pixelmode)
986 		vp->nparties = 2;
987 
988 	vp->busyLoop = 0;
989 	MI_CLEARWINDOW(mi);
990 	vp->painted = False;
991 
992 	for (i = 0; i < BITMAPS; i++)
993 		vp->number_in_party[i] = 0;
994 
995 	for (row = 0; row < vp->nrows; row++)
996 		for (col = 0; col < vp->ncols; col++) {
997 			colrow = col + row * vp->ncols;
998 			if (vp->nparties == 2)
999 				i = (NRAND(vp->nparties) + 2) % BITMAPS;
1000 			else
1001 				i = NRAND(vp->nparties);
1002 			vp->arr[colrow] = (char) i;
1003 			drawCell(mi, col, row, (unsigned long) (MI_NPIXELS(mi) * i / BITMAPS),
1004 				 i, False);
1005 			vp->number_in_party[i]++;
1006 		}
1007 }
1008 
1009 ENTRYPOINT void
refresh_voters(ModeInfo * mi)1010 refresh_voters(ModeInfo * mi)
1011 {
1012 	int         col, row, colrow;
1013 	voterstruct *vp;
1014 
1015 	if (voters == NULL)
1016 		return;
1017 	vp = &voters[MI_SCREEN(mi)];
1018 	if (vp->first == NULL)
1019 		return;
1020 
1021 	if (vp->painted) {
1022 		MI_CLEARWINDOW(mi);
1023 		vp->painted = False;
1024 		for (row = 0; row < vp->nrows; row++)
1025 			for (col = 0; col < vp->ncols; col++) {
1026 				colrow = col + row * vp->ncols;
1027 				/* Draw all old, will get corrected soon if wrong... */
1028 				drawCell(mi, col, row,
1029 					 (unsigned long) (MI_NPIXELS(mi) * vp->arr[colrow] / BITMAPS),
1030 					 vp->arr[colrow], False);
1031 			}
1032 	}
1033 }
1034 
1035 ENTRYPOINT void
draw_voters(ModeInfo * mi)1036 draw_voters(ModeInfo * mi)
1037 {
1038 	int         i, spineless_dude, neighbor_direction;
1039 	int         spineless_col, spineless_row;
1040 	int         new_opinion, old_opinion;
1041 	cellstruct  info;
1042 	voterstruct *vp;
1043 
1044 	if (voters == NULL)
1045 		return;
1046 	vp = &voters[MI_SCREEN(mi)];
1047 	if (vp->first == NULL)
1048 		return;
1049 
1050 	MI_IS_DRAWN(mi) = True;
1051 	vp->painted = True;
1052 	if (vp->busyLoop) {
1053 		if (vp->busyLoop >= 5000)
1054 			vp->busyLoop = 0;
1055 		else
1056 			vp->busyLoop++;
1057 		return;
1058 	}
1059 	for (i = 0; i < BITMAPS; i++)
1060 		if (vp->number_in_party[i] == vp->npositions) {		/* The End of the WORLD */
1061 			init_voters(mi);	/* Create a more interesting planet */
1062 		}
1063 	spineless_dude = NRAND(vp->npositions);
1064 	neighbor_direction = NRAND(vp->neighbors);
1065 	spineless_col = spineless_dude % vp->ncols;
1066 	spineless_row = spineless_dude / vp->ncols;
1067 	old_opinion = vp->arr[spineless_dude];
1068 	/* neighbors opinion */
1069 	new_opinion = vp->arr[positionOfNeighbor(vp, neighbor_direction, spineless_col, spineless_row)];
1070 	if (old_opinion != new_opinion) {
1071 		vp->number_in_party[old_opinion]--;
1072 		vp->number_in_party[new_opinion]++;
1073 		vp->arr[spineless_dude] = new_opinion;
1074 		info.kind = new_opinion;
1075 		info.age = (old_opinion - new_opinion);
1076 		if (info.age == 2)
1077 			info.age = -1;
1078 		if (info.age == -2)
1079 			info.age = 1;
1080 		info.age *= (FACTOR * MI_NPIXELS(mi)) / 3;
1081 		info.col = spineless_col;
1082 		info.row = spineless_row;
1083 		if (MI_NPIXELS(mi) > 2) {
1084 			advanceColors(mi, spineless_col, spineless_row);
1085 			if (!addto_list(vp, info)) {
1086 				free_voters_screen(vp);
1087 				return;
1088 			}
1089 		}
1090 		drawCell(mi, spineless_col, spineless_row,
1091 			 (MI_NPIXELS(mi) + info.age / FACTOR +
1092 		  (MI_NPIXELS(mi) * new_opinion / BITMAPS)) % MI_NPIXELS(mi),
1093 			 new_opinion, True);
1094 	} else if (MI_NPIXELS(mi) > 2)
1095 		advanceColors(mi, -1, -1);
1096 	vp->generation++;
1097 	for (i = 0; i < BITMAPS; i++)
1098 		if (vp->number_in_party[i] == vp->npositions) {		/* The End of the WORLD */
1099 			vp->busyLoop = 1;
1100 			refresh_voters(mi);
1101 		}
1102 }
1103 
1104 ENTRYPOINT void
release_voters(ModeInfo * mi)1105 release_voters(ModeInfo * mi)
1106 {
1107 	if (voters != NULL) {
1108 		int         screen;
1109 
1110 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1111 			free_voters_screen(&voters[screen]);
1112 		free(voters);
1113 		voters = (voterstruct *) NULL;
1114 	}
1115 }
1116 
1117 XSCREENSAVER_MODULE ("Voters", voters)
1118 
1119 #endif /* MODE_voters */
1120