xenocara/app/xlockmore/modes/voters.c
2006-11-26 11:07:42 +00:00

869 lines
23 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* voters --- Dewdney's Voting Simulation */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)voters.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1997 by David Bagley.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*
* Revision History:
* 01-Nov-2000: Allocation checks
* 10-Jun-1997: Coded from A.K. Dewdney's "The Armchair Universe, Computer
* Recreations from the Pages of Scientific American Magazine"
* W.H. Freedman and Company, New York, 1988 (Apr 1985)
* Used wator.c and demon.c as a guide.
*/
#ifdef STANDALONE
#define MODE_voters
#define PROGCLASS "Voters"
#define HACK_INIT init_voters
#define HACK_DRAW draw_voters
#define voters_opts xlockmore_opts
#define DEFAULTS "*delay: 1000 \n" \
"*cycles: 327670 \n" \
"*size: 0 \n" \
"*ncolors: 64 \n" \
"*neighbors: 0 \n"
#define UNIFORM_COLORS
#define BRIGHT_COLORS
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#include "automata.h"
#ifdef MODE_voters
/*-
* neighbors of 0 randomizes it between 3, 4, 6, 8, 9, and 12.
*/
#define DEF_NEIGHBORS "0" /* choose random value */
static int neighbors;
static XrmOptionDescRec opts[] =
{
{(char *) "-neighbors", (char *) ".voters.neighbors", XrmoptionSepArg, (caddr_t) NULL}
};
static argtype vars[] =
{
{(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int
}
};
static OptionStruct desc[] =
{
{(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"}
};
ModeSpecOpt voters_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct voters_description =
{"voters", "init_voters", "draw_voters", "release_voters",
"refresh_voters", "init_voters", (char *) NULL, &voters_opts,
1000, 0, 327670, 0, 64, 1.0, "",
"Shows Dewdney's Voters", 0, NULL};
#endif
/*-
* From far left to right, at least in the currently in the US. By the way, I
* consider myself to be a proud bleeding heart liberal democrat, in
* case anyone wants to know.... Please, no fascist "improvements". :)
*/
#include "bitmaps/elephant.xbm"
#ifdef COMMIE
#include "bitmaps/sickle.xbm"
#else
#include "bitmaps/green.xbm"
#endif
#include "bitmaps/donkey.xbm"
#define MINPARTIES 2
#define BITMAPS 3
#define MINGRIDSIZE 10
#define MINSIZE 4
#define FACTOR 10
#define NEIGHBORKINDS 6
static XImage logo[BITMAPS] =
{
{0, 0, 0, XYBitmap, (char *) elephant_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) green_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) donkey_bits, LSBFirst, 8, LSBFirst, 8, 1},
};
/* Voter data */
typedef struct {
char kind;
int age;
int col, row;
} cellstruct;
/* Doublely linked list */
typedef struct _CellList {
cellstruct info;
struct _CellList *previous, *next;
} CellList;
typedef struct {
Bool painted;
int party; /* Currently working on donkey, elephant, or green? */
int xs, ys; /* Size of party icon */
int xb, yb; /* Bitmap offset for party icon */
int nparties; /* 2 parties or 3 */
int number_in_party[BITMAPS]; /* Good to know when one party rules */
int pixelmode;
int generation;
int ncols, nrows;
int npositions;
int width, height;
CellList *last, *first;
char *arr;
int neighbors;
int busyLoop;
XPoint hexagonList[6];
XPoint triangleList[2][3];
} voterstruct;
static char plots[NEIGHBORKINDS] =
{
3, 4, 6, 8, 9, 12 /* Neighborhoods */
};
static voterstruct *voters = (voterstruct *) NULL;
static int icon_width, icon_height;
static void
drawcell(ModeInfo * mi, int col, int row, unsigned long color, int bitmap,
Bool firstChange)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
voterstruct *vp = &voters[MI_SCREEN(mi)];
unsigned long colour = (MI_NPIXELS(mi) > 2) ?
MI_PIXEL(mi, color) : MI_WHITE_PIXEL(mi);
XSetForeground(display, gc, colour);
if (vp->neighbors == 6) {
int ccol = 2 * col + !(row & 1), crow = 2 * row;
vp->hexagonList[0].x = vp->xb + ccol * vp->xs;
vp->hexagonList[0].y = vp->yb + crow * vp->ys;
if (vp->xs == 1 && vp->ys == 1)
XDrawPoint(display, window, gc,
vp->hexagonList[0].x, vp->hexagonList[0].y);
else if (bitmap == BITMAPS - 1)
XFillPolygon(display, window, gc,
vp->hexagonList, 6, Convex, CoordModePrevious);
else {
if (firstChange) {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillPolygon(display, window, gc,
vp->hexagonList, 6, Convex, CoordModePrevious);
XSetForeground(display, gc, colour);
}
if (vp->xs <= 6 || vp->ys <= 2)
XFillRectangle(display, window, gc,
vp->hexagonList[0].x - 3 * vp->xs / 4,
vp->hexagonList[0].y + vp->ys / 4, vp->xs, vp->ys);
else
XFillArc(display, window, gc,
vp->xb + vp->xs * ccol - 3 * vp->xs / 4,
vp->yb + vp->ys * crow + vp->ys / 4,
2 * vp->xs - 6, 2 * vp->ys - 2,
0, 23040);
}
} else if (vp->neighbors == 4 || vp->neighbors == 8) {
if (vp->pixelmode) {
if (bitmap == BITMAPS - 1 || (vp->xs <= 2 || vp->ys <= 2))
XFillRectangle(display, window, gc,
vp->xb + vp->xs * col, vp->yb + vp->ys * row,
vp->xs - (vp->xs > 3), vp->ys - (vp->ys > 3));
else {
if (firstChange) {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillRectangle(display, window, gc,
vp->xb + vp->xs * col, vp->yb + vp->ys * row,
vp->xs, vp->ys);
XSetForeground(display, gc, colour);
}
XFillArc(display, window, gc,
vp->xb + vp->xs * col, vp->yb + vp->ys * row,
vp->xs - 1, vp->ys - 1,
0, 23040);
}
} else
(void) XPutImage(display, window, gc,
&logo[bitmap], 0, 0,
vp->xb + vp->xs * col, vp->yb + vp->ys * row,
icon_width, icon_height);
} else { /* TRI */
int orient = (col + row) % 2; /* O left 1 right */
vp->triangleList[orient][0].x = vp->xb + col * vp->xs;
vp->triangleList[orient][0].y = vp->yb + row * vp->ys;
if (vp->xs <= 3 || vp->ys <= 3)
XDrawPoint(display, window, gc,
((orient) ? -1 : 1) + vp->triangleList[orient][0].x,
vp->triangleList[orient][0].y);
else {
if (orient)
vp->triangleList[orient][0].x += (vp->xs / 2 - 1);
else
vp->triangleList[orient][0].x -= (vp->xs / 2 - 1);
if (bitmap == BITMAPS - 1)
XFillPolygon(display, window, gc,
vp->triangleList[orient], 3, Convex, CoordModePrevious);
else {
if (firstChange) {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillPolygon(display, window, gc,
vp->triangleList[orient], 3, Convex, CoordModePrevious);
XSetForeground(display, gc, colour);
}
XFillArc(display, window, gc,
vp->xb + vp->xs * col - 4 * vp->xs / 5 +
((orient) ? vp->xs / 3 : 3 * vp->xs / 5),
vp->yb + vp->ys * row - vp->ys / 2 + 1, vp->ys - 3, vp->ys - 3,
0, 23040);
}
}
}
}
static Bool
init_list(voterstruct * vp)
{
/* Waste some space at the beginning and end of list
so we do not have to complicated checks against falling off the ends. */
if (((vp->last = (CellList *) malloc(sizeof (CellList))) == NULL) ||
((vp->first = (CellList *) malloc(sizeof (CellList))) == NULL)) {
return False;
}
vp->first->previous = vp->last->next = (struct _CellList *) NULL;
vp->first->next = vp->last->previous = (struct _CellList *) NULL;
vp->first->next = vp->last;
vp->last->previous = vp->first;
return True;
}
static Bool
addto_list(voterstruct * vp, cellstruct info)
{
CellList *curr;
if ((curr = (CellList *) malloc(sizeof (CellList))) == NULL) {
return False;
}
vp->last->previous->next = curr;
curr->previous = vp->last->previous;
curr->next = vp->last;
vp->last->previous = curr;
curr->info = info;
return True;
}
static void
removefrom_list(CellList * ptr)
{
ptr->previous->next = ptr->next;
ptr->next->previous = ptr->previous;
free(ptr);
}
static void
flush_list(voterstruct * vp)
{
CellList *curr;
while (vp->last->previous != vp->first) {
curr = vp->last->previous;
curr->previous->next = vp->last;
vp->last->previous = curr->previous;
free(curr);
}
}
static char
neighbors_opinion(voterstruct * vp, int col, int row, int dir)
{
if (vp->neighbors == 6) {
switch (dir) {
case 0:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
break;
case 60:
if (!(row & 1))
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 120:
if (row & 1)
col = (!col) ? vp->ncols - 1 : col - 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? vp->ncols - 1 : col - 1;
break;
case 240:
if (row & 1)
col = (!col) ? vp->ncols - 1 : col - 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 300:
if (!(row & 1))
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else if (vp->neighbors == 4 || vp->neighbors == 8) {
switch (dir) {
case 0:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
break;
case 45:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 90:
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 135:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? vp->ncols - 1 : col - 1;
break;
case 225:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 270:
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 315:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else { /* TRI */
if ((col + row) % 2) { /* right */
switch (dir) {
case 0:
col = (!col) ? vp->ncols - 1 : col - 1;
break;
case 30:
case 40:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 60:
col = (!col) ? vp->ncols - 1 : col - 1;
if (row + 1 == vp->nrows)
row = 1;
else if (row + 2 == vp->nrows)
row = 0;
else
row = row + 2;
break;
case 80:
case 90:
if (row + 1 == vp->nrows)
row = 1;
else if (row + 2 == vp->nrows)
row = 0;
else
row = row + 2;
break;
case 120:
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 150:
case 160:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 180:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
break;
case 200:
case 210:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 240:
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 270:
case 280:
if (!row)
row = vp->nrows - 2;
else if (!(row - 1))
row = vp->nrows - 1;
else
row = row - 2;
break;
case 300:
col = (!col) ? vp->ncols - 1 : col - 1;
if (!row)
row = vp->nrows - 2;
else if (!(row - 1))
row = vp->nrows - 1;
else
row = row - 2;
break;
case 320:
case 330:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else { /* left */
switch (dir) {
case 0:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
break;
case 30:
case 40:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 60:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
if (!row)
row = vp->nrows - 2;
else if (row == 1)
row = vp->nrows - 1;
else
row = row - 2;
break;
case 80:
case 90:
if (!row)
row = vp->nrows - 2;
else if (row == 1)
row = vp->nrows - 1;
else
row = row - 2;
break;
case 120:
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 150:
case 160:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (!row) ? vp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? vp->ncols - 1 : col - 1;
break;
case 200:
case 210:
col = (!col) ? vp->ncols - 1 : col - 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 240:
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
case 270:
case 280:
if (row + 1 == vp->nrows)
row = 1;
else if (row + 2 == vp->nrows)
row = 0;
else
row = row + 2;
break;
case 300:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
if (row + 1 == vp->nrows)
row = 1;
else if (row + 2 == vp->nrows)
row = 0;
else
row = row + 2;
break;
case 320:
case 330:
col = (col + 1 == vp->ncols) ? 0 : col + 1;
row = (row + 1 == vp->nrows) ? 0 : row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
}
}
return vp->arr[row * vp->ncols + col];
}
static void
advanceColors(ModeInfo * mi, int col, int row)
{
voterstruct *vp = &voters[MI_SCREEN(mi)];
CellList *curr;
curr = vp->first->next;
while (curr != vp->last) {
if (curr->info.col == col && curr->info.row == row) {
curr = curr->next;
removefrom_list(curr->previous);
} else {
if (curr->info.age > 0)
curr->info.age--;
else if (curr->info.age < 0)
curr->info.age++;
drawcell(mi, curr->info.col, curr->info.row,
(MI_NPIXELS(mi) + curr->info.age / FACTOR +
(MI_NPIXELS(mi) * curr->info.kind / BITMAPS)) % MI_NPIXELS(mi),
curr->info.kind, False);
if (curr->info.age == 0) {
curr = curr->next;
removefrom_list(curr->previous);
} else
curr = curr->next;
}
}
}
void
refresh_voters(ModeInfo * mi)
{
int col, row, colrow;
voterstruct *vp;
if (voters == NULL)
return;
vp = &voters[MI_SCREEN(mi)];
if (vp->first == NULL)
return;
if (vp->painted) {
MI_CLEARWINDOW(mi);
vp->painted = False;
for (row = 0; row < vp->nrows; row++)
for (col = 0; col < vp->ncols; col++) {
colrow = col + row * vp->ncols;
/* Draw all old, will get corrected soon if wrong... */
drawcell(mi, col, row,
(unsigned long) (MI_NPIXELS(mi) * vp->arr[colrow] / BITMAPS),
vp->arr[colrow], False);
}
}
}
static void
free_voters(voterstruct *vp)
{
if (vp->first != NULL) {
flush_list(vp);
free(vp->first);
vp->first = (CellList *) NULL;
}
if (vp->last != NULL) {
free(vp->last);
vp->last = (CellList *) NULL;
}
if (vp->arr != NULL) {
free(vp->arr);
vp->arr = (char *) NULL;
}
}
void
init_voters(ModeInfo * mi)
{
int size = MI_SIZE(mi);
int i, col, row, colrow;
voterstruct *vp;
if (voters == NULL) {
if ((voters = (voterstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (voterstruct))) == NULL)
return;
}
vp = &voters[MI_SCREEN(mi)];
vp->generation = 0;
if (!vp->first) { /* Genesis of democracy */
icon_width = donkey_width;
icon_height = donkey_height;
if (!init_list(vp)) {
free_voters(vp);
return;
}
for (i = 0; i < BITMAPS; i++) {
logo[i].width = icon_width;
logo[i].height = icon_height;
logo[i].bytes_per_line = (icon_width + 7) / 8;
}
} else /* Exterminate all free thinking individuals */
flush_list(vp);
vp->width = MI_WIDTH(mi);
vp->height = MI_HEIGHT(mi);
for (i = 0; i < NEIGHBORKINDS; i++) {
if (neighbors == plots[i]) {
vp->neighbors = neighbors;
break;
}
if (i == NEIGHBORKINDS - 1) {
#if 0
vp->neighbors = plots[NRAND(NEIGHBORKINDS)];
vp->neighbors = (LRAND() & 1) ? 4 : 8;
#else
vp->neighbors = 8;
#endif
break;
}
}
if (vp->neighbors == 6) {
int nccols, ncrows, sides;
if (vp->width < 8)
vp->width = 8;
if (vp->height < 8)
vp->height = 8;
if (size < -MINSIZE)
vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) / MINGRIDSIZE);
else
vp->ys = MINSIZE;
} else
vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE));
vp->xs = vp->ys;
vp->pixelmode = True;
nccols = MAX(vp->width / vp->xs - 2, 2);
ncrows = MAX(vp->height / vp->ys - 1, 4);
vp->ncols = nccols / 2;
vp->nrows = 2 * (ncrows / 4);
vp->xb = (vp->width - vp->xs * nccols) / 2 + vp->xs / 2;
vp->yb = (vp->height - vp->ys * (ncrows / 2) * 2) / 2 + vp->ys - 2;
for (sides = 0; sides < 6; sides++) {
vp->hexagonList[sides].x = (vp->xs - 1) * hexagonUnit[sides].x;
vp->hexagonList[sides].y =
((vp->ys - 1) * hexagonUnit[sides].y / 2) * 4 / 3;
}
} else if (vp->neighbors == 4 || vp->neighbors == 8) {
if (vp->width < 2)
vp->width = 2;
if (vp->height < 2)
vp->height = 2;
if (size == 0 ||
MINGRIDSIZE * size > vp->width || MINGRIDSIZE * size > vp->height) {
if (vp->width > MINGRIDSIZE * icon_width &&
vp->height > MINGRIDSIZE * icon_height) {
vp->pixelmode = False;
vp->xs = icon_width;
vp->ys = icon_height;
} else {
vp->pixelmode = True;
vp->xs = vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE);
}
} else {
vp->pixelmode = True;
if (size < -MINSIZE)
vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE)
vp->ys = MINSIZE;
else
vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE));
vp->xs = vp->ys;
}
vp->ncols = MAX(vp->width / vp->xs, 2);
vp->nrows = MAX(vp->height / vp->ys, 2);
vp->xb = (vp->width - vp->xs * vp->ncols) / 2;
vp->yb = (vp->height - vp->ys * vp->nrows) / 2;
} else { /* TRI */
int orient, sides;
if (vp->width < 2)
vp->width = 2;
if (vp->height < 2)
vp->height = 2;
if (size < -MINSIZE)
vp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
vp->ys = MAX(MINSIZE, MIN(vp->width, vp->height) / MINGRIDSIZE);
else
vp->ys = MINSIZE;
} else
vp->ys = MIN(size, MAX(MINSIZE, MIN(vp->width, vp->height) /
MINGRIDSIZE));
vp->xs = (int) (1.52 * vp->ys);
vp->pixelmode = True;
vp->ncols = (MAX(vp->width / vp->xs - 1, 2) / 2) * 2;
vp->nrows = (MAX(vp->height / vp->ys - 1, 2) / 2) * 2;
vp->xb = (vp->width - vp->xs * vp->ncols) / 2 + vp->xs / 2;
vp->yb = (vp->height - vp->ys * vp->nrows) / 2 + vp->ys / 2;
for (orient = 0; orient < 2; orient++) {
for (sides = 0; sides < 3; sides++) {
vp->triangleList[orient][sides].x =
(vp->xs - 2) * triangleUnit[orient][sides].x;
vp->triangleList[orient][sides].y =
(vp->ys - 2) * triangleUnit[orient][sides].y;
}
}
}
vp->npositions = vp->ncols * vp->nrows;
if (vp->arr != NULL)
free(vp->arr);
if ((vp->arr = (char *) calloc(vp->npositions, sizeof (char))) == NULL) {
free_voters(vp);
return;
}
/* Play G-d with these numbers */
vp->nparties = MI_COUNT(mi);
if (vp->nparties < MINPARTIES || vp->nparties > BITMAPS)
vp->nparties = NRAND(BITMAPS - MINPARTIES + 1) + MINPARTIES;
if (vp->pixelmode)
vp->nparties = 2;
vp->busyLoop = 0;
MI_CLEARWINDOW(mi);
vp->painted = False;
for (i = 0; i < BITMAPS; i++)
vp->number_in_party[i] = 0;
for (row = 0; row < vp->nrows; row++)
for (col = 0; col < vp->ncols; col++) {
colrow = col + row * vp->ncols;
if (vp->nparties == 2)
i = (NRAND(vp->nparties) + 2) % BITMAPS;
else
i = NRAND(vp->nparties);
vp->arr[colrow] = (char) i;
drawcell(mi, col, row, (unsigned long) (MI_NPIXELS(mi) * i / BITMAPS),
i, False);
vp->number_in_party[i]++;
}
}
void
draw_voters(ModeInfo * mi)
{
int i, spineless_dude, neighbor_direction;
int spineless_col, spineless_row;
int new_opinion, old_opinion;
cellstruct info;
voterstruct *vp;
if (voters == NULL)
return;
vp = &voters[MI_SCREEN(mi)];
if (vp->first == NULL)
return;
MI_IS_DRAWN(mi) = True;
vp->painted = True;
if (vp->busyLoop) {
if (vp->busyLoop >= 5000)
vp->busyLoop = 0;
else
vp->busyLoop++;
return;
}
for (i = 0; i < BITMAPS; i++)
if (vp->number_in_party[i] == vp->npositions) { /* The End of the WORLD */
init_voters(mi); /* Create a more interesting planet */
}
spineless_dude = NRAND(vp->npositions);
neighbor_direction = NRAND(vp->neighbors) * 360 / vp->neighbors;
spineless_col = spineless_dude % vp->ncols;
spineless_row = spineless_dude / vp->ncols;
old_opinion = vp->arr[spineless_dude];
new_opinion = neighbors_opinion(vp, spineless_col, spineless_row,
neighbor_direction);
if (old_opinion != new_opinion) {
vp->number_in_party[old_opinion]--;
vp->number_in_party[new_opinion]++;
vp->arr[spineless_dude] = new_opinion;
info.kind = new_opinion;
info.age = (old_opinion - new_opinion);
if (info.age == 2)
info.age = -1;
if (info.age == -2)
info.age = 1;
info.age *= (FACTOR * MI_NPIXELS(mi)) / 3;
info.col = spineless_col;
info.row = spineless_row;
if (MI_NPIXELS(mi) > 2) {
advanceColors(mi, spineless_col, spineless_row);
if (!addto_list(vp, info)) {
free_voters(vp);
return;
}
}
drawcell(mi, spineless_col, spineless_row,
(MI_NPIXELS(mi) + info.age / FACTOR +
(MI_NPIXELS(mi) * new_opinion / BITMAPS)) % MI_NPIXELS(mi),
new_opinion, True);
} else if (MI_NPIXELS(mi) > 2)
advanceColors(mi, -1, -1);
vp->generation++;
for (i = 0; i < BITMAPS; i++)
if (vp->number_in_party[i] == vp->npositions) { /* The End of the WORLD */
vp->busyLoop = 1;
refresh_voters(mi);
}
}
void
release_voters(ModeInfo * mi)
{
if (voters != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_voters(&voters[screen]);
free(voters);
voters = (voterstruct *) NULL;
}
}
#endif /* MODE_voters */