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

1046 lines
30 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* wator --- Dewdney's Wa-Tor, water torus simulation */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)wator.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1994 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-May-1997: Compatible with xscreensaver
* 29-Aug-1995: Efficiency improvements.
* 12-Dec-1994: 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 (Dec 1984 and
* June 1985) also used life.c as a guide.
*/
#ifdef STANDALONE
#define MODE_wator
#define PROGCLASS "Wator"
#define HACK_INIT init_wator
#define HACK_DRAW draw_wator
#define wator_opts xlockmore_opts
#define DEFAULTS "*delay: 750000 \n" \
"*cycles: 32767 \n" \
"*size: 0 \n" \
"*ncolors: 200 \n" \
"*neighbors: 0 \n"
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#include "automata.h"
#ifdef MODE_wator
/*-
* 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 *) ".wator.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 wator_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct wator_description =
{"wator", "init_wator", "draw_wator", "release_wator",
"refresh_wator", "init_wator", (char *) NULL, &wator_opts,
750000, 1, 32767, 0, 64, 1.0, "",
"Shows Dewdney's Water-Torus planet of fish and sharks", 0, NULL};
#endif
#include "bitmaps/fish-0.xbm"
#include "bitmaps/fish-1.xbm"
#include "bitmaps/fish-2.xbm"
#include "bitmaps/fish-3.xbm"
#include "bitmaps/fish-4.xbm"
#include "bitmaps/fish-5.xbm"
#include "bitmaps/fish-6.xbm"
#include "bitmaps/fish-7.xbm"
#include "bitmaps/shark-0.xbm"
#include "bitmaps/shark-1.xbm"
#include "bitmaps/shark-2.xbm"
#include "bitmaps/shark-3.xbm"
#include "bitmaps/shark-4.xbm"
#include "bitmaps/shark-5.xbm"
#include "bitmaps/shark-6.xbm"
#include "bitmaps/shark-7.xbm"
#define FISH 0
#define SHARK 1
#define KINDS 2
#define ORIENTS 4
#define REFLECTS 2
#define BITMAPS (ORIENTS*REFLECTS*KINDS)
#define KINDBITMAPS (ORIENTS*REFLECTS)
#define MINGRIDSIZE 10 /* It is possible for the fish to take over with 3 */
#define MINSIZE 4
#define NEIGHBORKINDS 6
static XImage logo[BITMAPS] =
{
{0, 0, 0, XYBitmap, (char *) fish0_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish1_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish2_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish3_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish4_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish5_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish6_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) fish7_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark0_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark1_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark2_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark3_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark4_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark5_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark6_bits, LSBFirst, 8, LSBFirst, 8, 1},
{0, 0, 0, XYBitmap, (char *) shark7_bits, LSBFirst, 8, LSBFirst, 8, 1},
};
/* Fish and shark data */
typedef struct {
char kind, age, food, direction;
unsigned long color;
int col, row;
} cellstruct;
/* Doublely linked list */
typedef struct _CellList {
cellstruct info;
struct _CellList *previous, *next;
} CellList;
typedef struct {
Bool painted;
int nkind[KINDS]; /* Number of fish and sharks */
int breed[KINDS]; /* Breeding time of fish and sharks */
int sstarve; /* Time the sharks starve if they dont find a fish */
int kind; /* Currently working on fish or sharks? */
int xs, ys; /* Size of fish and sharks */
int xb, yb; /* Bitmap offset for fish and sharks */
int pixelmode;
int generation;
int ncols, nrows, positions;
int width, height;
CellList *currkind, *babykind, *lastkind[KINDS + 1], *firstkind[KINDS + 1];
CellList **arr; /* 0=empty or pts to a fish or shark */
int neighbors;
union {
XPoint hexagon[6];
XPoint triangle[2][3];
} shape;
} watorstruct;
static char plots[NEIGHBORKINDS] =
{
3, 4, 6, 8, 9, 12 /* Neighborhoods */
};
static watorstruct *wators = (watorstruct *) NULL;
static int icon_width, icon_height;
#if 0
/*-
* shape either a bitmap or 0 for circle and 1 for polygon
* (if triangle shape: -1, 0, 2 or 3 to differentiate left and right)
*/
drawshape(ModeInfo * mi, int x, int y, int sizex, int sizey,
int sides, int shape)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
if (sides == 4 && sizex == 0 && sizey == 0) {
(void) XPutImage(display, window, gc, &logo[shape], 0, 0,
x - icon_width, y - icon_height / 2, icon_width, icon_height);
} else if (sizex < 3 || sizey < 3 || (sides == 4 && shape == 1)) {
XFillRectangle(display, window, gc,
x - sizex / 2, y - sizey / 2,
sizex - (sizey > 3), sizey - (sizey > 3));
} else if (sides == 4 && shape == 0) {
} else if (sides == 6 && shape == 1) {
} else if (sides == 6 && shape == 0) {
} else if (sides == 3 && shape == 1) {
} else if (sides == 3 && shape == 2) {
} else if (sides == 3 && shape == -1) {
} else if (sides == 3 && shape == 0) {
}
}
#endif
static void
drawcell(ModeInfo * mi, int col, int row, unsigned long color, int bitmap,
Bool alive)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
watorstruct *wp = &wators[MI_SCREEN(mi)];
unsigned long colour;
if (!alive)
colour = MI_BLACK_PIXEL(mi);
else if (MI_NPIXELS(mi) > 2)
colour = MI_PIXEL(mi, color);
else
colour = MI_WHITE_PIXEL(mi);
XSetForeground(display, gc, colour);
if (wp->neighbors == 6) {
int ccol = 2 * col + !(row & 1), crow = 2 * row;
wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
if (wp->xs == 1 && wp->ys == 1)
XDrawPoint(display, window, gc,
wp->shape.hexagon[0].x, wp->shape.hexagon[0].y);
else if (bitmap >= KINDBITMAPS || !alive)
XFillPolygon(display, window, gc,
wp->shape.hexagon, 6, Convex, CoordModePrevious);
else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillPolygon(display, window, gc,
wp->shape.hexagon, 6, Convex, CoordModePrevious);
XSetForeground(display, gc, colour);
if (wp->xs <= 6 || wp->ys <= 2)
XFillRectangle(display, window, gc,
wp->shape.hexagon[0].x - 3 * wp->xs / 4,
wp->shape.hexagon[0].y + wp->ys / 4, wp->xs, wp->ys);
else
XFillArc(display, window, gc,
wp->xb + wp->xs * ccol - 3 * wp->xs / 4,
wp->yb + wp->ys * crow + wp->ys / 4,
2 * wp->xs - 6, 2 * wp->ys - 2,
0, 23040);
}
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
if (wp->pixelmode) {
if (bitmap >= KINDBITMAPS || (wp->xs <= 2 || wp->ys <= 2) || !alive)
XFillRectangle(display, window, gc,
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
wp->xs - (wp->xs > 3), wp->ys - (wp->ys > 3));
else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillRectangle(display, window, gc,
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
wp->xs, wp->ys);
XSetForeground(display, gc, colour);
XFillArc(display, window, gc,
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
wp->xs - 1, wp->ys - 1,
0, 23040);
}
} else
(void) XPutImage(display, window, gc,
&logo[bitmap], 0, 0,
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
icon_width, icon_height);
} else { /* TRI */
int orient = (col + row) % 2; /* O left 1 right */
wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
if (wp->xs <= 3 || wp->ys <= 3)
XDrawPoint(display, window, gc,
((orient) ? -1 : 1) + wp->shape.triangle[orient][0].x,
wp->shape.triangle[orient][0].y);
else {
if (orient)
wp->shape.triangle[orient][0].x += (wp->xs / 2 - 1);
else
wp->shape.triangle[orient][0].x -= (wp->xs / 2 - 1);
if (bitmap >= KINDBITMAPS || !alive)
XFillPolygon(display, window, gc,
wp->shape.triangle[orient], 3, Convex, CoordModePrevious);
else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillPolygon(display, window, gc,
wp->shape.triangle[orient], 3, Convex, CoordModePrevious);
XSetForeground(display, gc, colour);
XFillArc(display, window, gc,
wp->xb + wp->xs * col - 4 * wp->xs / 5 +
((orient) ? wp->xs / 3 : 3 * wp->xs / 5),
wp->yb + wp->ys * row - wp->ys / 2 + 1, wp->ys - 3, wp->ys - 3,
0, 23040);
}
}
}
}
static Bool
init_kindlist(watorstruct * wp, int kind)
{
/* Waste some space at the beginning and end of list
so we do not have to complicated checks against falling off the ends. */
if (((wp->lastkind[kind] = (CellList *) malloc(sizeof (CellList))) ==
NULL) ||
((wp->firstkind[kind] = (CellList *) malloc(sizeof (CellList))) ==
NULL)) {
return False;
}
wp->firstkind[kind]->previous = wp->lastkind[kind]->next =
(struct _CellList *) NULL;
wp->firstkind[kind]->next = wp->lastkind[kind]->previous =
(struct _CellList *) NULL;
wp->firstkind[kind]->next = wp->lastkind[kind];
wp->lastkind[kind]->previous = wp->firstkind[kind];
return True;
}
static Bool
addto_kindlist(watorstruct * wp, int kind, cellstruct info)
{
if ((wp->currkind = (CellList *) malloc(sizeof (CellList))) == NULL) {
return False;
}
wp->lastkind[kind]->previous->next = wp->currkind;
wp->currkind->previous = wp->lastkind[kind]->previous;
wp->currkind->next = wp->lastkind[kind];
wp->lastkind[kind]->previous = wp->currkind;
wp->currkind->info = info;
return True;
}
static void
removefrom_kindlist(watorstruct * wp, CellList * ptr)
{
ptr->previous->next = ptr->next;
ptr->next->previous = ptr->previous;
wp->arr[ptr->info.col + ptr->info.row * wp->ncols] = 0;
free(ptr);
}
static Bool
dupin_kindlist(watorstruct * wp)
{
CellList *temp;
if ((temp = (CellList *) malloc(sizeof (CellList))) == NULL) {
return False;
}
temp->previous = wp->babykind;
temp->next = wp->babykind->next;
wp->babykind->next = temp;
temp->next->previous = temp;
temp->info = wp->babykind->info;
wp->babykind = temp;
return True;
}
/*-
* new fish at end of list, this rotates who goes first, young fish go last
* this most likely will not change the feel to any real degree
*/
static void
cutfrom_kindlist(watorstruct * wp)
{
wp->babykind = wp->currkind;
wp->currkind = wp->currkind->previous;
wp->currkind->next = wp->babykind->next;
wp->babykind->next->previous = wp->currkind;
wp->babykind->next = wp->lastkind[KINDS];
wp->babykind->previous = wp->lastkind[KINDS]->previous;
wp->babykind->previous->next = wp->babykind;
wp->babykind->next->previous = wp->babykind;
}
static void
reattach_kindlist(watorstruct * wp, int kind)
{
wp->currkind = wp->lastkind[kind]->previous;
wp->currkind->next = wp->firstkind[KINDS]->next;
wp->currkind->next->previous = wp->currkind;
wp->lastkind[kind]->previous = wp->lastkind[KINDS]->previous;
wp->lastkind[KINDS]->previous->next = wp->lastkind[kind];
wp->lastkind[KINDS]->previous = wp->firstkind[KINDS];
wp->firstkind[KINDS]->next = wp->lastkind[KINDS];
}
static void
flush_kindlist(watorstruct * wp, int kind)
{
while (wp->lastkind[kind]->previous != wp->firstkind[kind]) {
wp->currkind = wp->lastkind[kind]->previous;
wp->currkind->previous->next = wp->lastkind[kind];
wp->lastkind[kind]->previous = wp->currkind->previous;
/* wp->arr[wp->currkind->info.col + wp->currkind->info.row * wp->ncols] = 0; */
free(wp->currkind);
}
}
static int
neighbor_position(watorstruct * wp, int col, int row, int dir)
{
if (wp->neighbors == 6) {
switch (dir) {
case 0:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
break;
case 60:
if (!(row & 1))
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 120:
if (row & 1)
col = (!col) ? wp->ncols - 1 : col - 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? wp->ncols - 1 : col - 1;
break;
case 240:
if (row & 1)
col = (!col) ? wp->ncols - 1 : col - 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 300:
if (!(row & 1))
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
switch (dir) {
case 0:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
break;
case 45:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 90:
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 135:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? wp->ncols - 1 : col - 1;
break;
case 225:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 270:
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 315:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (row + 1 == wp->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) ? wp->ncols - 1 : col - 1;
break;
case 30:
case 40:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 60:
col = (!col) ? wp->ncols - 1 : col - 1;
if (row + 1 == wp->nrows)
row = 1;
else if (row + 2 == wp->nrows)
row = 0;
else
row = row + 2;
break;
case 80:
case 90:
if (row + 1 == wp->nrows)
row = 1;
else if (row + 2 == wp->nrows)
row = 0;
else
row = row + 2;
break;
case 120:
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 150:
case 160:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 180:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
break;
case 200:
case 210:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 240:
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 270:
case 280:
if (!row)
row = wp->nrows - 2;
else if (!(row - 1))
row = wp->nrows - 1;
else
row = row - 2;
break;
case 300:
col = (!col) ? wp->ncols - 1 : col - 1;
if (!row)
row = wp->nrows - 2;
else if (!(row - 1))
row = wp->nrows - 1;
else
row = row - 2;
break;
case 320:
case 330:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else { /* left */
switch (dir) {
case 0:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
break;
case 30:
case 40:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 60:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
if (!row)
row = wp->nrows - 2;
else if (row == 1)
row = wp->nrows - 1;
else
row = row - 2;
break;
case 80:
case 90:
if (!row)
row = wp->nrows - 2;
else if (row == 1)
row = wp->nrows - 1;
else
row = row - 2;
break;
case 120:
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 150:
case 160:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (!row) ? wp->nrows - 1 : row - 1;
break;
case 180:
col = (!col) ? wp->ncols - 1 : col - 1;
break;
case 200:
case 210:
col = (!col) ? wp->ncols - 1 : col - 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 240:
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
case 270:
case 280:
if (row + 1 == wp->nrows)
row = 1;
else if (row + 2 == wp->nrows)
row = 0;
else
row = row + 2;
break;
case 300:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
if (row + 1 == wp->nrows)
row = 1;
else if (row + 2 == wp->nrows)
row = 0;
else
row = row + 2;
break;
case 320:
case 330:
col = (col + 1 == wp->ncols) ? 0 : col + 1;
row = (row + 1 == wp->nrows) ? 0 : row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
}
}
return row * wp->ncols + col;
}
static void
free_wator(watorstruct *wp)
{
int kind;
for (kind = 0; kind <= KINDS; kind++) {
if (wp->firstkind[kind] != NULL) {
flush_kindlist(wp, kind);
free(wp->firstkind[kind]);
wp->firstkind[kind] = (struct _CellList *) NULL;
}
if (wp->lastkind[kind] != NULL) {
free(wp->lastkind[kind]);
wp->lastkind[kind] = (struct _CellList *) NULL;
}
}
if (wp->arr != NULL) {
free(wp->arr);
wp->arr = (CellList **) NULL;
}
}
void
init_wator(ModeInfo * mi)
{
int size = MI_SIZE(mi);
int i, col, row, colrow, kind;
cellstruct info;
watorstruct *wp;
if (wators == NULL) {
if ((wators = (watorstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (watorstruct))) == NULL)
return;
}
wp = &wators[MI_SCREEN(mi)];
wp->generation = 0;
if (!wp->firstkind[0]) { /* Genesis */
icon_width = fish0_width;
icon_height = fish0_height;
/* Set up what will be a 'triply' linked list.
doubly linked list, doubly linked to an array */
for (kind = FISH; kind <= KINDS; kind++)
if (!init_kindlist(wp, kind)) {
free_wator(wp);
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 */
for (i = FISH; i <= KINDS; i++)
flush_kindlist(wp, i);
wp->width = MI_WIDTH(mi);
wp->height = MI_HEIGHT(mi);
if (wp->width < 2)
wp->width = 2;
if (wp->height < 2)
wp->height = 2;
for (i = 0; i < NEIGHBORKINDS; i++) {
if (neighbors == plots[i]) {
wp->neighbors = neighbors;
break;
}
if (i == NEIGHBORKINDS - 1) {
#if 0
wp->neighbors = plots[NRAND(NEIGHBORKINDS)];
wp->neighbors = (LRAND() & 1) ? 4 : 8;
#else
wp->neighbors = 4;
#endif
break;
}
}
if (wp->neighbors == 6) {
int nccols, ncrows, sides;
if (wp->width < 8)
wp->width = 8;
if (wp->height < 8)
wp->height = 8;
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
else
wp->ys = MINSIZE;
} else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = wp->ys;
wp->pixelmode = True;
nccols = MAX(wp->width / wp->xs - 2, 2);
ncrows = MAX(wp->height / wp->ys - 1, 4);
wp->ncols = nccols / 2;
wp->nrows = 2 * (ncrows / 4);
wp->xb = (wp->width - wp->xs * nccols) / 2 + wp->xs / 2;
wp->yb = (wp->height - wp->ys * (ncrows / 2) * 2) / 2 + wp->ys - 2;
for (sides = 0; sides < 6; sides++) {
wp->shape.hexagon[sides].x = (wp->xs - 1) * hexagonUnit[sides].x;
wp->shape.hexagon[sides].y =
((wp->ys - 1) * hexagonUnit[sides].y / 2) * 4 / 3;
}
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
if (wp->width < 2)
wp->width = 2;
if (wp->height < 2)
wp->height = 2;
if (size == 0 ||
MINGRIDSIZE * size > wp->width || MINGRIDSIZE * size > wp->height) {
if (wp->width > MINGRIDSIZE * icon_width &&
wp->height > MINGRIDSIZE * icon_height) {
wp->pixelmode = False;
wp->xs = icon_width;
wp->ys = icon_height;
} else {
wp->pixelmode = True;
wp->xs = wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE);
}
} else {
wp->pixelmode = True;
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE)
wp->ys = MINSIZE;
else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = wp->ys;
}
wp->ncols = MAX(wp->width / wp->xs, 2);
wp->nrows = MAX(wp->height / wp->ys, 2);
wp->xb = (wp->width - wp->xs * wp->ncols) / 2;
wp->yb = (wp->height - wp->ys * wp->nrows) / 2;
} else { /* TRI */
int orient, sides;
if (wp->width < 2)
wp->width = 2;
if (wp->height < 2)
wp->height = 2;
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
else
wp->ys = MINSIZE;
} else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = (int) (1.52 * wp->ys);
wp->pixelmode = True;
wp->ncols = (MAX(wp->width / wp->xs - 1, 2) / 2) * 2;
wp->nrows = (MAX(wp->height / wp->ys - 1, 2) / 2) * 2;
wp->xb = (wp->width - wp->xs * wp->ncols) / 2 + wp->xs / 2;
wp->yb = (wp->height - wp->ys * wp->nrows) / 2 + wp->ys / 2;
for (orient = 0; orient < 2; orient++) {
for (sides = 0; sides < 3; sides++) {
wp->shape.triangle[orient][sides].x =
(wp->xs - 2) * triangleUnit[orient][sides].x;
wp->shape.triangle[orient][sides].y =
(wp->ys - 2) * triangleUnit[orient][sides].y;
}
}
}
wp->positions = wp->ncols * wp->nrows;
if (wp->arr != NULL)
free(wp->arr);
if ((wp->arr = (CellList **) calloc(wp->positions,
sizeof (CellList *))) == NULL) {
free_wator(wp);
return;
}
/* Play G-d with these numbers */
wp->nkind[FISH] = wp->positions / 3;
wp->nkind[SHARK] = wp->nkind[FISH] / 10;
wp->kind = FISH;
if (!wp->nkind[SHARK])
wp->nkind[SHARK] = 1;
wp->breed[FISH] = MI_COUNT(mi);
wp->breed[SHARK] = 10;
if (wp->breed[FISH] < 1)
wp->breed[FISH] = 1;
else if (wp->breed[FISH] > wp->breed[SHARK])
wp->breed[FISH] = 4;
wp->sstarve = 3;
MI_CLEARWINDOW(mi);
wp->painted = False;
for (kind = FISH; kind <= SHARK; kind++) {
i = 0;
while (i < wp->nkind[kind]) {
col = NRAND(wp->ncols);
row = NRAND(wp->nrows);
colrow = col + row * wp->ncols;
if (!wp->arr[colrow]) {
i++;
info.kind = kind;
info.age = NRAND(wp->breed[kind]);
info.food = NRAND(wp->sstarve);
info.direction = NRAND(KINDBITMAPS) + kind * KINDBITMAPS;
if (MI_NPIXELS(mi) > 2)
info.color = NRAND(MI_NPIXELS(mi));
else
info.color = 0;
info.col = col;
info.row = row;
if (!addto_kindlist(wp, kind, info)) {
free_wator(wp);
return;
}
wp->arr[colrow] = wp->currkind;
drawcell(mi, col, row,
wp->currkind->info.color, wp->currkind->info.direction, True);
}
}
}
}
void
draw_wator(ModeInfo * mi)
{
int col, row;
int colrow, cr, position;
int i, numok;
struct {
int pos, dir;
} acell[12];
watorstruct *wp;
if (wators == NULL)
return;
wp = &wators[MI_SCREEN(mi)];
if (wp->arr == NULL)
return;
MI_IS_DRAWN(mi) = True;
wp->painted = True;
/* Alternate updates, fish and sharks live out of phase with each other */
wp->kind = (wp->kind + 1) % KINDS;
{
wp->currkind = wp->firstkind[wp->kind]->next;
while (wp->currkind != wp->lastkind[wp->kind]) {
col = wp->currkind->info.col;
row = wp->currkind->info.row;
colrow = col + row * wp->ncols;
numok = 0;
if (wp->kind == SHARK) { /* Scan for fish */
for (i = 0; i < wp->neighbors; i++) {
position = neighbor_position(wp, col, row, i * 360 / wp->neighbors);
if (wp->arr[position] && wp->arr[position]->info.kind == FISH) {
acell[numok].pos = position;
acell[numok++].dir = i;
}
}
if (numok) { /* No thanks, I'm a vegetarian */
i = NRAND(numok);
wp->nkind[FISH]--;
cr = acell[i].pos;
removefrom_kindlist(wp, wp->arr[cr]);
wp->arr[cr] = wp->currkind;
if (wp->neighbors == 4) {
wp->currkind->info.direction = (5 - acell[i].dir) % ORIENTS +
((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
} else if (wp->neighbors == 8) {
wp->currkind->info.direction = (char) (5 - (acell[i].dir / 2 +
((acell[i].dir % 2) ? LRAND() & 1 : 0))) % ORIENTS +
((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
} else
wp->currkind->info.direction = wp->kind * KINDBITMAPS;
wp->currkind->info.col = acell[i].pos % wp->ncols;
wp->currkind->info.row = acell[i].pos / wp->ncols;
wp->currkind->info.food = wp->sstarve;
drawcell(mi, wp->currkind->info.col, wp->currkind->info.row,
wp->currkind->info.color, wp->currkind->info.direction, True);
if (++(wp->currkind->info.age) >= wp->breed[wp->kind]) { /* breed */
cutfrom_kindlist(wp); /* This rotates out who goes first */
wp->babykind->info.age = 0;
if (!dupin_kindlist(wp)) {
free_wator(wp);
return;
}
wp->arr[colrow] = wp->babykind;
wp->babykind->info.col = col;
wp->babykind->info.row = row;
wp->babykind->info.age = -1; /* Make one a little younger */
#if 0
if (MI_NPIXELS(mi) > 2 && (LRAND() & 1))
/* A color mutation */
if (++(wp->babykind->info.color) >= MI_NPIXELS(mi))
wp->babykind->info.color = 0;
#endif
wp->nkind[wp->kind]++;
} else {
wp->arr[colrow] = 0;
drawcell(mi, col, row, 0, 0, False);
}
} else {
if (wp->currkind->info.food-- < 0) { /* Time to die, Jaws */
/* back up one or else in void */
wp->currkind = wp->currkind->previous;
removefrom_kindlist(wp, wp->arr[colrow]);
wp->arr[colrow] = 0;
drawcell(mi, col, row, 0, 0, False);
wp->nkind[wp->kind]--;
numok = -1; /* Want to escape from next if */
}
}
}
if (!numok) { /* Fish or shark search for a place to go */
for (i = 0; i < wp->neighbors; i++) {
position = neighbor_position(wp, col, row, i * 360 / wp->neighbors);
if (!wp->arr[position]) { /* Found an empty spot */
acell[numok].pos = position;
acell[numok++].dir = i;
}
}
if (numok) { /* Found a place to go */
i = NRAND(numok);
wp->arr[acell[i].pos] = wp->currkind;
if (wp->neighbors == 4) {
wp->currkind->info.direction = (5 - acell[i].dir) % ORIENTS +
((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
} else if (wp->neighbors == 8) {
wp->currkind->info.direction = (char) (5 - (acell[i].dir / 2 +
((acell[i].dir % 2) ? LRAND() & 1 : 0))) % ORIENTS +
((NRAND(REFLECTS)) ? 0 : ORIENTS) + wp->kind * KINDBITMAPS;
} else
wp->currkind->info.direction = wp->kind * KINDBITMAPS;
wp->currkind->info.col = acell[i].pos % wp->ncols;
wp->currkind->info.row = acell[i].pos / wp->ncols;
drawcell(mi,
wp->currkind->info.col, wp->currkind->info.row,
wp->currkind->info.color, wp->currkind->info.direction, True);
if (++(wp->currkind->info.age) >= wp->breed[wp->kind]) { /* breed */
cutfrom_kindlist(wp); /* This rotates out who goes first */
wp->babykind->info.age = 0;
if (!dupin_kindlist(wp)) {
free_wator(wp);
return;
}
wp->arr[colrow] = wp->babykind;
wp->babykind->info.col = col;
wp->babykind->info.row = row;
wp->babykind->info.age = -1; /* Make one a little younger */
wp->nkind[wp->kind]++;
} else {
wp->arr[colrow] = 0;
drawcell(mi, col, row, 0, 0, False);
}
} else {
/* I'll just sit here and wave my tail so you know I am alive */
wp->currkind->info.direction =
(wp->currkind->info.direction + ORIENTS) % KINDBITMAPS +
wp->kind * KINDBITMAPS;
drawcell(mi, col, row, wp->currkind->info.color,
wp->currkind->info.direction, True);
}
}
wp->currkind = wp->currkind->next;
}
reattach_kindlist(wp, wp->kind);
}
if ((wp->nkind[FISH] >= wp->positions) ||
(!wp->nkind[FISH] && !wp->nkind[SHARK]) ||
wp->generation >= MI_CYCLES(mi)) {
init_wator(mi);
}
if (wp->kind == SHARK)
wp->generation++;
}
void
release_wator(ModeInfo * mi)
{
if (wators != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_wator(&wators[screen]);
free(wators);
wators = (watorstruct *) NULL;
}
}
void
refresh_wator(ModeInfo * mi)
{
watorstruct *wp;
if (wators == NULL)
return;
wp = &wators[MI_SCREEN(mi)];
if (wp->painted) {
MI_CLEARWINDOW(mi);
wp->painted = False;
}
}
#endif /* MODE_wator */