1046 lines
30 KiB
C
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 */
|