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