1426 lines
40 KiB
C
1426 lines
40 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/*-
|
|
* ant --- Chris Langton's generalized turing machine ants (also known
|
|
* as Greg Turk's turmites) whose tape is the screen
|
|
*/
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)ant.c 5.13 2004/06/23 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* Copyright (c) 1995 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
|
|
* 16-Apr-1997: -neighbors 3 and 8 added
|
|
* 01-Jan-1997: Updated ant.c to handle more kinds of ants. Thanks to
|
|
* J Austin David <Austin.David@tlogic.com>. Check it out in
|
|
* java at http://havoc.gtf.gatech.edu/austin He thought up the
|
|
* new Ladder ant.
|
|
* 04-Apr-1996: -neighbors 6 runtime-time option added for hexagonal ants
|
|
* (bees), coded from an idea of Jim Propp's in Science News,
|
|
* Oct 28, 1995 VOL. 148 page 287
|
|
* 20-Sep-1995: Memory leak in ant fixed. Now random colors.
|
|
* 05-Sep-1995: Coded from A.K. Dewdney's "Computer Recreations", Scientific
|
|
* American Magazine" Sep 1989 pp 180-183, Mar 1990 p 121
|
|
* Also used Ian Stewart's Mathematical Recreations, Scientific
|
|
* American Jul 1994 pp 104-107
|
|
* also used demon.c and life.c as a guide.
|
|
*/
|
|
|
|
/*-
|
|
Species Grid Number of Neighbors
|
|
------- ---- ------------------
|
|
Ants Square 4 (or 8)
|
|
Bees Hexagon 6
|
|
Bees Triangle 3 (or 9, 12)
|
|
|
|
Neighbors 6 and neighbors 3 produce the same Turk ants.
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_ant
|
|
#define PROGCLASS "Ant"
|
|
#define HACK_INIT init_ant
|
|
#define HACK_DRAW draw_ant
|
|
#define ant_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 1000 \n" \
|
|
"*count: -3 \n" \
|
|
"*cycles: 40000 \n" \
|
|
"*size: -12 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*neighbors: 0 \n" \
|
|
"*sharpturn: False \n" \
|
|
"*fullrandom: True \n" \
|
|
"*verbose: False \n"
|
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
|
#else /* STANDALONE */
|
|
#include "xlock.h" /* in xlockmore distribution */
|
|
#endif /* STANDALONE */
|
|
#include "automata.h"
|
|
|
|
#ifdef MODE_ant
|
|
|
|
#define DEF_LABEL "True"
|
|
#define FONT_HEIGHT 19
|
|
#define FONT_WIDTH 15
|
|
|
|
/*-
|
|
* neighbors of 0 randomizes it for 3, 4, 6, 8, 12 (last 2 are less likely)
|
|
*/
|
|
|
|
#define DEF_NEIGHBORS "0" /* choose random value */
|
|
#define DEF_RULE "A" /* All rules */
|
|
#define DEF_TRUCHET "False"
|
|
#define DEF_EYES "False"
|
|
#define DEF_SHARPTURN "False"
|
|
|
|
static int neighbors;
|
|
static char *rule;
|
|
static Bool truchet;
|
|
static Bool eyes;
|
|
static Bool sharpturn;
|
|
static Bool label;
|
|
|
|
static XrmOptionDescRec opts[] =
|
|
{
|
|
{(char *) "-label", (char *) ".ant.label", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+label", (char *) ".ant.label", XrmoptionNoArg, (caddr_t) "off"},
|
|
{(char *) "-neighbors", (char *) ".ant.neighbors", XrmoptionSepArg, (caddr_t) NULL},
|
|
{(char *) "-rule", (char *) ".ant.rule", XrmoptionSepArg, (caddr_t) NULL},
|
|
{(char *) "-truchet", (char *) ".ant.truchet", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+truchet", (char *) ".ant.truchet", XrmoptionNoArg, (caddr_t) "off"},
|
|
{(char *) "-eyes", (char *) ".ant.eyes", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+eyes", (char *) ".ant.eyes", XrmoptionNoArg, (caddr_t) "off"},
|
|
{(char *) "-sharpturn", (char *) ".ant.sharpturn", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+sharpturn", (char *) ".ant.sharpturn", XrmoptionNoArg, (caddr_t) "off"}
|
|
};
|
|
static argtype vars[] =
|
|
{
|
|
{(void *) & label, (char *) "label", (char *) "Label", (char *) DEF_LABEL, t_Bool},
|
|
{(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
|
|
{(void *) & rule, (char *) "rule", (char *) "Rule", (char *) DEF_RULE, t_String},
|
|
{(void *) & truchet, (char *) "truchet", (char *) "Truchet", (char *) DEF_TRUCHET, t_Bool},
|
|
{(void *) & eyes, (char *) "eyes", (char *) "Eyes", (char *) DEF_EYES, t_Bool},
|
|
{(void *) & sharpturn, (char *) "sharpturn", (char *) "SharpTurn", (char *) DEF_SHARPTURN, t_Bool}
|
|
};
|
|
static OptionStruct desc[] =
|
|
{
|
|
{(char *) "-/+label", (char *) "turn on/off rule labeling"},
|
|
{(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3 or 12"},
|
|
{(char *) "-rule string", (char *) "binary string for Turk's Ant"},
|
|
{(char *) "-/+truchet", (char *) "turn on/off Truchet lines"},
|
|
{(char *) "-/+eyes", (char *) "turn on/off eyes"},
|
|
{(char *) "-/+sharpturn", (char *) "turn on/off sharp turns (6, 8 or 12 neighbors only)"}
|
|
};
|
|
|
|
ModeSpecOpt ant_opts =
|
|
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
|
|
|
|
#ifdef USE_MODULES
|
|
const ModStruct ant_description =
|
|
{"ant",
|
|
"init_ant", "draw_ant", "release_ant",
|
|
"refresh_ant", "init_ant", (char *) NULL, &ant_opts,
|
|
1000, -3, 40000, -12, 64, 1.0, "",
|
|
"Shows Langton's and Turk's generalized ants", 0, NULL};
|
|
|
|
#endif
|
|
|
|
#define ANTBITS(n,w,h)\
|
|
if ((ap->pixmaps[ap->init_bits]=\
|
|
XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
|
|
free_ant(display,ap); return;} else {ap->init_bits++;}
|
|
|
|
/* If you change the table you may have to change the following 2 constants */
|
|
#define STATES 2
|
|
#define MINANTS 1
|
|
#define REDRAWSTEP 2000 /* How much tape to draw per cycle */
|
|
#define MINGRIDSIZE 24
|
|
#define MINSIZE 1
|
|
#define MINRANDOMSIZE 5
|
|
#define ANGLES 360
|
|
|
|
typedef struct {
|
|
unsigned char color;
|
|
short direction;
|
|
unsigned char next;
|
|
} statestruct;
|
|
|
|
typedef struct {
|
|
int col, row;
|
|
short direction;
|
|
unsigned char state;
|
|
} antstruct;
|
|
|
|
typedef struct {
|
|
Bool painted;
|
|
int neighbors;
|
|
int generation;
|
|
int xs, ys;
|
|
int xb, yb;
|
|
int init_dir;
|
|
int nrows, ncols;
|
|
int width, height;
|
|
unsigned char ncolors, nstates;
|
|
int n;
|
|
int redrawing, redrawpos;
|
|
Bool truchet; /* Only for Turk modes */
|
|
Bool eyes;
|
|
Bool sharpturn;
|
|
statestruct machine[NUMSTIPPLES * STATES];
|
|
unsigned char *tape;
|
|
unsigned char *truchet_state;
|
|
antstruct *ants;
|
|
int init_bits;
|
|
unsigned char colors[NUMSTIPPLES - 1];
|
|
GC stippledGC;
|
|
Pixmap pixmaps[NUMSTIPPLES - 1];
|
|
int labelOffsetX, labelOffsetY;
|
|
char ruleString[40];
|
|
union {
|
|
XPoint hexagon[7]; /* Need more than 6 for truchet */
|
|
XPoint triangle[2][4]; /* Need more than 3 for truchet */
|
|
} shape;
|
|
} antfarmstruct;
|
|
|
|
static char plots[] =
|
|
{3, 4, 6, 8,
|
|
#ifdef NUMBER_9
|
|
9,
|
|
#endif
|
|
12};
|
|
|
|
#define NEIGHBORKINDS ((long) (sizeof plots / sizeof *plots))
|
|
#define GOODNEIGHBORKINDS 3
|
|
|
|
/* Relative ant moves */
|
|
#define FS 0 /* Step */
|
|
#define TRS 1 /* Turn right, then step */
|
|
#define THRS 2 /* Turn hard right, then step */
|
|
#define TBS 3 /* Turn back, then step */
|
|
#define THLS 4 /* Turn hard left, then step */
|
|
#define TLS 5 /* Turn left, then step */
|
|
#define SF 6 /* Step */
|
|
#define STR 7 /* Step then turn right */
|
|
#define STHR 8 /* Step then turn hard right */
|
|
#define STB 9 /* Step then turn back */
|
|
#define STHL 10 /* Step then turn hard left */
|
|
#define STL 11 /* Step then turn left */
|
|
|
|
static antfarmstruct *antfarms = (antfarmstruct *) NULL;
|
|
|
|
/* LANGTON'S ANT (10) Chaotic after 500, Builder after 10,000 (104p) */
|
|
/* TURK'S 100 ANT Always chaotic?, tested past 150,000,000 */
|
|
/* TURK'S 101 ANT Always chaotic? */
|
|
/* TURK'S 110 ANT Builder at 150 (18p) */
|
|
/* TURK'S 1000 ANT Always chaotic? */
|
|
/* TURK'S 1100 SYMMETRIC ANT all even run 1's and 0's are symmetric */
|
|
/* other examples 1001, 110011, 110000, 1001101 */
|
|
/* TURK'S 1101 ANT Builder after 250,000 (388p) */
|
|
/* Once saw a chess horse type builder (i.e. non-45 degree builder) */
|
|
|
|
/* BEE ONLY */
|
|
/* All alternating 10 appear symmetric, no proof (i.e. 10, 1010, etc) */
|
|
/* Even runs of 0's and 1's are also symmetric */
|
|
/* I have seen Hexagonal builders but they are more rare. */
|
|
|
|
static unsigned char tables[][3 * NUMSTIPPLES * STATES + 2] =
|
|
{
|
|
#if 0
|
|
/* Here just so you can figure out notation */
|
|
{ /* Langton's ant (-rule 10) */
|
|
2, 1,
|
|
1, TLS, 0, 0, TRS, 0
|
|
},
|
|
#else
|
|
/* First 2 numbers are the size (ncolors, nstates) */
|
|
{ /* LADDER BUILDER */
|
|
4, 1,
|
|
1, STR, 0, 2, STL, 0, 3, TRS, 0, 0, TLS, 0
|
|
},
|
|
{ /* SPIRALING PATTERN */
|
|
2, 2,
|
|
1, TLS, 0, 0, FS, 1,
|
|
1, TRS, 0, 1, TRS, 0
|
|
},
|
|
{ /* SQUARE (HEXAGON) BUILDER */
|
|
2, 2,
|
|
1, TLS, 0, 0, FS, 1,
|
|
0, TRS, 0, 1, TRS, 0
|
|
},
|
|
#endif
|
|
};
|
|
|
|
#define NTABLES (sizeof tables / sizeof tables[0])
|
|
|
|
static void
|
|
position_of_neighbor(antfarmstruct * ap, int dir, int *pcol, int *prow)
|
|
{
|
|
int col = *pcol, row = *prow;
|
|
|
|
if (ap->neighbors == 4 || ap->neighbors == 6 || ap->neighbors == 8) {
|
|
switch (dir) {
|
|
case 0:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
break;
|
|
case 45:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 60:
|
|
if (!(row & 1))
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 90:
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 120:
|
|
if (row & 1)
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 135:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 180:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
break;
|
|
case 225:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 240:
|
|
if (row & 1)
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 270:
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 300:
|
|
if (!(row & 1))
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 315:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (row + 1 == ap->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) ? ap->ncols - 1 : col - 1;
|
|
break;
|
|
case 30:
|
|
case 40:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 60:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
if (!row)
|
|
row = ap->nrows - 2;
|
|
else if (!(row - 1))
|
|
row = ap->nrows - 1;
|
|
else
|
|
row = row - 2;
|
|
break;
|
|
case 80:
|
|
case 90:
|
|
if (!row)
|
|
row = ap->nrows - 2;
|
|
else if (!(row - 1))
|
|
row = ap->nrows - 1;
|
|
else
|
|
row = row - 2;
|
|
break;
|
|
case 120:
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 150:
|
|
case 160:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 180:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
break;
|
|
case 200:
|
|
case 210:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 240:
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 270:
|
|
case 280:
|
|
if (row + 1 == ap->nrows)
|
|
row = 1;
|
|
else if (row + 2 == ap->nrows)
|
|
row = 0;
|
|
else
|
|
row = row + 2;
|
|
break;
|
|
case 300:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
if (row + 1 == ap->nrows)
|
|
row = 1;
|
|
else if (row + 2 == ap->nrows)
|
|
row = 0;
|
|
else
|
|
row = row + 2;
|
|
break;
|
|
case 320:
|
|
case 330:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
default:
|
|
(void) fprintf(stderr, "wrong direction %d\n", dir);
|
|
}
|
|
} else { /* left */
|
|
switch (dir) {
|
|
case 0:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
break;
|
|
case 30:
|
|
case 40:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 60:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
if (row + 1 == ap->nrows)
|
|
row = 1;
|
|
else if (row + 2 == ap->nrows)
|
|
row = 0;
|
|
else
|
|
row = row + 2;
|
|
break;
|
|
case 80:
|
|
case 90:
|
|
if (row + 1 == ap->nrows)
|
|
row = 1;
|
|
else if (row + 2 == ap->nrows)
|
|
row = 0;
|
|
else
|
|
row = row + 2;
|
|
break;
|
|
case 120:
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 150:
|
|
case 160:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (row + 1 == ap->nrows) ? 0 : row + 1;
|
|
break;
|
|
case 180:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
break;
|
|
case 200:
|
|
case 210:
|
|
col = (!col) ? ap->ncols - 1 : col - 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 240:
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
case 270:
|
|
case 280:
|
|
if (!row)
|
|
row = ap->nrows - 2;
|
|
else if (row == 1)
|
|
row = ap->nrows - 1;
|
|
else
|
|
row = row - 2;
|
|
break;
|
|
case 300:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
if (!row)
|
|
row = ap->nrows - 2;
|
|
else if (row == 1)
|
|
row = ap->nrows - 1;
|
|
else
|
|
row = row - 2;
|
|
break;
|
|
case 320:
|
|
case 330:
|
|
col = (col + 1 == ap->ncols) ? 0 : col + 1;
|
|
row = (!row) ? ap->nrows - 1 : row - 1;
|
|
break;
|
|
default:
|
|
(void) fprintf(stderr, "wrong direction %d\n", dir);
|
|
}
|
|
}
|
|
}
|
|
*pcol = col;
|
|
*prow = row;
|
|
}
|
|
|
|
static void
|
|
fillcell(ModeInfo * mi, GC gc, int col, int row)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
|
|
if (ap->neighbors == 6) {
|
|
int ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
|
|
ap->shape.hexagon[0].x = ap->xb + ccol * ap->xs;
|
|
ap->shape.hexagon[0].y = ap->yb + crow * ap->ys;
|
|
if (ap->xs == 1 && ap->ys == 1)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
ap->shape.hexagon[0].x, ap->shape.hexagon[0].y);
|
|
else
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
ap->shape.hexagon, 6, Convex, CoordModePrevious);
|
|
} else if (ap->neighbors == 4 || ap->neighbors == 8) {
|
|
XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
ap->xb + ap->xs * col, ap->yb + ap->ys * row,
|
|
ap->xs - (ap->xs > 3), ap->ys - (ap->ys > 3));
|
|
} else { /* TRI */
|
|
int orient = (col + row) % 2; /* O left 1 right */
|
|
|
|
ap->shape.triangle[orient][0].x = ap->xb + col * ap->xs;
|
|
ap->shape.triangle[orient][0].y = ap->yb + row * ap->ys;
|
|
if (ap->xs <= 3 || ap->ys <= 3)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
((orient) ? -1 : 1) + ap->shape.triangle[orient][0].x,
|
|
ap->shape.triangle[orient][0].y);
|
|
else {
|
|
if (orient)
|
|
ap->shape.triangle[orient][0].x += (ap->xs / 2 - 1);
|
|
else
|
|
ap->shape.triangle[orient][0].x -= (ap->xs / 2 - 1);
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
ap->shape.triangle[orient], 3, Convex, CoordModePrevious);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
truchetcell(ModeInfo * mi, int col, int row, int truchetstate)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
|
|
if (ap->neighbors == 6) {
|
|
|
|
int ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
int side;
|
|
int fudge = 7; /* fudge because the hexagons are not exact */
|
|
XPoint hex, hex2;
|
|
|
|
if (ap->sharpturn) {
|
|
hex.x = ap->xb + ccol * ap->xs - (int) ((double) ap->xs / 2.0) - 1;
|
|
hex.y = ap->yb + crow * ap->ys - (int) ((double) ap->ys / 2.0) - 1;
|
|
for (side = 0; side < 6; side++) {
|
|
if (side) {
|
|
hex.x += ap->shape.hexagon[side].x;
|
|
hex.y += ap->shape.hexagon[side].y;
|
|
}
|
|
if (truchetstate == side % 2)
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
hex.x, hex.y, ap->xs, ap->ys,
|
|
((570 - (side * 60) + fudge) % 360) * 64, (120 - 2 * fudge) * 64);
|
|
}
|
|
} else {
|
|
/* Very crude approx of Sqrt 3, so it will not cause drawing errors. */
|
|
hex.x = ap->xb + ccol * ap->xs - (int) ((double) ap->xs * 1.6 / 2.0) - 1;
|
|
hex.y = ap->yb + crow * ap->ys - (int) ((double) ap->ys * 1.6 / 2.0) - 1;
|
|
for (side = 0; side < 6; side++) {
|
|
if (side) {
|
|
hex.x += ap->shape.hexagon[side].x;
|
|
hex.y += ap->shape.hexagon[side].y;
|
|
}
|
|
hex2.x = hex.x + ap->shape.hexagon[side + 1].x / 2;
|
|
hex2.y = hex.y + ap->shape.hexagon[side + 1].y / 2 + 1;
|
|
/* Lots of fudging here */
|
|
if (side == 0) {
|
|
hex2.x += 1;
|
|
} else if (side == 1) {
|
|
hex2.x += (short) (ap->xs * 0.1 + 1);
|
|
hex2.y += (short) (ap->ys * 0.1 - ((ap->ys > 5) ? 1 : 0));
|
|
} else if (side == 2) {
|
|
hex2.x += (short) (ap->xs * 0.1 + 1);
|
|
hex2.y += 1;
|
|
} else if (side == 3) {
|
|
hex2.y += 1;
|
|
} else if (side == 4) {
|
|
hex2.x += (short) (ap->xs * 0.1);
|
|
hex2.y += (short) (ap->ys * 0.1 - 1);
|
|
} else if (side == 5) {
|
|
hex2.x += (short) (ap->xs * 0.5);
|
|
hex2.y += (short) (-ap->ys * 0.3);
|
|
}
|
|
if (truchetstate == side % 3)
|
|
/* Crude approx of 120 deg, so it will not cause drawing errors. */
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
hex2.x, hex2.y,
|
|
(int) ((double) ap->xs * 1.5), (int) ((double) ap->ys * 1.5),
|
|
((555 - (side * 60)) % 360) * 64, 90 * 64);
|
|
}
|
|
}
|
|
} else if (ap->neighbors == 4) {
|
|
if (truchetstate) {
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
ap->xb + ap->xs * col - (ap->xs - 1) / 2,
|
|
ap->yb + ap->ys * row + (ap->ys - 1) / 2,
|
|
ap->xs - 2, ap->ys - 2,
|
|
0 * 64, 90 * 64);
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
ap->xb + ap->xs * col + (ap->xs - 1) / 2,
|
|
ap->yb + ap->ys * row - (ap->ys - 1) / 2,
|
|
ap->xs - 2, ap->ys - 2,
|
|
-90 * 64, -90 * 64);
|
|
} else {
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
ap->xb + ap->xs * col - (ap->xs - 1) / 2,
|
|
ap->yb + ap->ys * row - (ap->ys - 1) / 2,
|
|
ap->xs - 2, ap->ys - 2,
|
|
0 * 64, -90 * 64);
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
ap->xb + ap->xs * col + (ap->xs - 1) / 2,
|
|
ap->yb + ap->ys * row + (ap->ys - 1) / 2,
|
|
ap->xs - 2, ap->ys - 2,
|
|
90 * 64, 90 * 64);
|
|
}
|
|
} else if (ap->neighbors == 3) {
|
|
int orient = (col + row) % 2; /* O left 1 right */
|
|
int side, ang;
|
|
int fudge = 7; /* fudge because the triangles are not exact */
|
|
double fudge2 = 1.18;
|
|
XPoint tri;
|
|
|
|
tri.x = ap->xb + col * ap->xs;
|
|
tri.y = ap->yb + row * ap->ys;
|
|
if (orient) {
|
|
tri.x += (ap->xs / 2 - 1);
|
|
} else {
|
|
tri.x -= (ap->xs / 2 - 1);
|
|
}
|
|
for (side = 0; side < 3; side++) {
|
|
if (side > 0) {
|
|
tri.x += ap->shape.triangle[orient][side].x;
|
|
tri.y += ap->shape.triangle[orient][side].y;
|
|
}
|
|
if (truchetstate == side) {
|
|
if (orient)
|
|
ang = (510 - side * 120) % 360; /* Right */
|
|
else
|
|
ang = (690 - side * 120) % 360; /* Left */
|
|
XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
(int) (tri.x - ap->xs * fudge2 / 2),
|
|
(int) (tri.y - 3 * ap->ys * fudge2 / 4),
|
|
(unsigned int) (ap->xs * fudge2),
|
|
(unsigned int) (3 * ap->ys * fudge2 / 2),
|
|
(ang + fudge) * 64, (60 - 2 * fudge) * 64);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
drawcell(ModeInfo * mi, int col, int row, unsigned char color)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
GC gc;
|
|
|
|
if (!color) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (MI_NPIXELS(mi) > 2) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
|
|
MI_PIXEL(mi, ap->colors[color - 1]));
|
|
gc = MI_GC(mi);
|
|
} else {
|
|
XGCValues gcv;
|
|
|
|
gcv.stipple = ap->pixmaps[color - 1];
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
XChangeGC(MI_DISPLAY(mi), ap->stippledGC,
|
|
GCStipple | GCForeground | GCBackground, &gcv);
|
|
gc = ap->stippledGC;
|
|
}
|
|
fillcell(mi, gc, col, row);
|
|
}
|
|
|
|
static void
|
|
drawtruchet(ModeInfo * mi, int col, int row,
|
|
unsigned char color, unsigned char truchetstate)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
|
|
if (!color)
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
|
|
else if (MI_NPIXELS(mi) > 2 || color > ap->ncolors / 2)
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
else
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
|
|
truchetcell(mi, col, row, truchetstate);
|
|
}
|
|
|
|
static void
|
|
draw_anant(ModeInfo * mi, int direction, int col, int row)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
|
|
XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
|
|
fillcell(mi, MI_GC(mi), col, row);
|
|
if (ap->eyes) { /* Draw Eyes */
|
|
XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
if (ap->neighbors == 6) {
|
|
int ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
int side, ang;
|
|
XPoint hex;
|
|
|
|
if (!(ap->xs > 3 && ap->ys > 3))
|
|
return;
|
|
hex.x = ap->xb + ccol * ap->xs;
|
|
hex.y = ap->yb + crow * ap->ys + ap->ys / 2;
|
|
ang = direction * ap->neighbors / ANGLES;
|
|
for (side = 0; side < ap->neighbors; side++) {
|
|
if (side) {
|
|
hex.x -= ap->shape.hexagon[side].x / 2;
|
|
hex.y += ap->shape.hexagon[side].y / 2;
|
|
}
|
|
if (side == (ap->neighbors + ang - 2) % ap->neighbors)
|
|
XDrawPoint(display, window, MI_GC(mi), hex.x, hex.y);
|
|
if (side == (ap->neighbors + ang - 1) % ap->neighbors)
|
|
XDrawPoint(display, window, MI_GC(mi), hex.x, hex.y);
|
|
}
|
|
} else if (ap->neighbors == 4 || ap->neighbors == 8) {
|
|
if (!(ap->xs > 3 && ap->ys > 3))
|
|
return;
|
|
switch (direction) {
|
|
case 0:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 3,
|
|
ap->yb + ap->ys * row + ap->ys / 2 - 2);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 3,
|
|
ap->yb + ap->ys * row + ap->ys / 2);
|
|
break;
|
|
case 45:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 4,
|
|
ap->yb + ap->ys * row + 1);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 3,
|
|
ap->yb + ap->ys * row + 2);
|
|
break;
|
|
case 90:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + ap->xs / 2 - 2,
|
|
ap->yb + ap->ys * row + 1);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + ap->xs / 2,
|
|
ap->yb + ap->ys * row + 1);
|
|
break;
|
|
case 135:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 2,
|
|
ap->yb + ap->ys * row + 1);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 1,
|
|
ap->yb + ap->ys * row + 2);
|
|
break;
|
|
case 180:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 1,
|
|
ap->yb + ap->ys * row + ap->ys / 2 - 2);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 1,
|
|
ap->yb + ap->ys * row + ap->ys / 2);
|
|
break;
|
|
case 225:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 2,
|
|
ap->yb + ap->ys * (row + 1) - 3);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + 1,
|
|
ap->yb + ap->ys * (row + 1) - 4);
|
|
break;
|
|
case 270:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + ap->xs / 2 - 2,
|
|
ap->yb + ap->ys * (row + 1) - 3);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * col + ap->xs / 2,
|
|
ap->yb + ap->ys * (row + 1) - 3);
|
|
break;
|
|
case 315:
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 4,
|
|
ap->yb + ap->ys * (row + 1) - 3);
|
|
XDrawPoint(display, window, MI_GC(mi),
|
|
ap->xb + ap->xs * (col + 1) - 3,
|
|
ap->yb + ap->ys * (row + 1) - 4);
|
|
break;
|
|
default:
|
|
(void) fprintf(stderr, "wrong eyes direction %d for ant eyes\n", direction);
|
|
}
|
|
} else { /* TRI */
|
|
int orient = (col + row) % 2; /* O left 1 right */
|
|
int side, ang;
|
|
XPoint tri;
|
|
|
|
if (!(ap->xs > 6 && ap->ys > 6))
|
|
return;
|
|
tri.x = ap->xb + col * ap->xs;
|
|
tri.y = ap->yb + row * ap->ys;
|
|
if (orient)
|
|
tri.x += (ap->xs / 6 - 1);
|
|
else
|
|
tri.x -= (ap->xs / 6 - 1);
|
|
ang = direction * ap->neighbors / ANGLES;
|
|
/* approx... does not work that well for even numbers */
|
|
if (
|
|
#ifdef NUMBER_9
|
|
ap->neighbors == 9 ||
|
|
#endif
|
|
ap->neighbors == 12) {
|
|
#ifdef UNDER_CONSTRUCTION
|
|
/* Not sure why this does not work */
|
|
ang = ((ang + ap->neighbors / 6) / (ap->neighbors / 3)) % 3;
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
for (side = 0; side < 3; side++) {
|
|
if (side) {
|
|
tri.x += ap->shape.triangle[orient][side].x / 3;
|
|
tri.y += ap->shape.triangle[orient][side].y / 3;
|
|
}
|
|
/* Either you have the eyes in back or one eye in front */
|
|
#if 0
|
|
if (side == ang)
|
|
XDrawPoint(display, window, MI_GC(mi), tri.x, tri.y);
|
|
#else
|
|
if (side == (ang + 2) % 3)
|
|
XDrawPoint(display, window, MI_GC(mi), tri.x, tri.y);
|
|
if (side == (ang + 1) % 3)
|
|
XDrawPoint(display, window, MI_GC(mi), tri.x, tri.y);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
RandomSoup(mi)
|
|
ModeInfo *mi;
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
int row, col, mrow = 0;
|
|
|
|
for (row = 0; row < ap->nrows; ++row) {
|
|
for (col = 0; col < ap->ncols; ++col) {
|
|
ap->old[col + mrow] = (unsigned char) NRAND((int) ap->ncolors);
|
|
drawcell(mi, col, row, ap->old[col + mrow]);
|
|
}
|
|
mrow += ap->nrows;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static short
|
|
fromTableDirection(unsigned char dir, int local_neighbors)
|
|
{
|
|
/* Crafted to work for odd number of neighbors */
|
|
switch (dir) {
|
|
case FS:
|
|
return 0;
|
|
case TLS:
|
|
return (ANGLES / local_neighbors);
|
|
case THLS:
|
|
return (2 * ANGLES / local_neighbors);
|
|
case TBS:
|
|
return ((local_neighbors / 2) * ANGLES / local_neighbors);
|
|
case THRS:
|
|
return (ANGLES - 2 * ANGLES / local_neighbors);
|
|
case TRS:
|
|
return (ANGLES - ANGLES / local_neighbors);
|
|
case SF:
|
|
return ANGLES;
|
|
case STL:
|
|
return (ANGLES + ANGLES / local_neighbors);
|
|
case STHL:
|
|
return (ANGLES + 2 * ANGLES / local_neighbors);
|
|
case STB:
|
|
return (ANGLES + (local_neighbors / 2) * ANGLES / local_neighbors);
|
|
case STHR:
|
|
return (2 * ANGLES - 2 * ANGLES / local_neighbors);
|
|
case STR:
|
|
return (2 * ANGLES - ANGLES / local_neighbors);
|
|
default:
|
|
(void) fprintf(stderr, "wrong direction %d from table\n", dir);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
getTable(ModeInfo * mi, int i)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
int j, total;
|
|
unsigned char *patptr;
|
|
statestruct *status;
|
|
|
|
patptr = &tables[i][0];
|
|
ap->ncolors = *patptr++;
|
|
ap->nstates = *patptr++;
|
|
total = ap->ncolors * ap->nstates;
|
|
if (MI_IS_VERBOSE(mi))
|
|
(void) fprintf(stdout,
|
|
"ants %d, neighbors %d, table number %d, colors %d, states %d\n",
|
|
ap->n, ap->neighbors, i, ap->ncolors, ap->nstates);
|
|
if (label)
|
|
(void) sprintf(ap->ruleString, "table number %d", i);
|
|
for (j = 0; j < total; j++) {
|
|
status = &(ap->machine[j]);
|
|
status->color = *patptr++;
|
|
if (ap->sharpturn && ap->neighbors > 4) {
|
|
int k = *patptr++;
|
|
|
|
switch (k) {
|
|
case TRS:
|
|
k = THRS;
|
|
break;
|
|
case THRS:
|
|
k = TRS;
|
|
break;
|
|
case THLS:
|
|
k = TLS;
|
|
break;
|
|
case TLS:
|
|
k = THLS;
|
|
break;
|
|
case STR:
|
|
k = STHR;
|
|
break;
|
|
case STHR:
|
|
k = STR;
|
|
break;
|
|
case STHL:
|
|
k = STL;
|
|
break;
|
|
case STL:
|
|
k = STHL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
status->direction = fromTableDirection(k, ap->neighbors);
|
|
} else {
|
|
status->direction = fromTableDirection(*patptr++, ap->neighbors);
|
|
}
|
|
status->next = *patptr++;
|
|
}
|
|
ap->truchet = False;
|
|
}
|
|
|
|
static void
|
|
getTurk(ModeInfo * mi, int logClass, int number)
|
|
{
|
|
antfarmstruct *ap = &antfarms[MI_SCREEN(mi)];
|
|
int power2, j, total;
|
|
|
|
power2 = 1 << (logClass - 1);
|
|
ap->ncolors = logClass;
|
|
ap->nstates = 1;
|
|
total = ap->ncolors * ap->nstates;
|
|
for (j = 0; j < total; j++) {
|
|
ap->machine[j].color = (j + 1) % total;
|
|
if (ap->sharpturn && ap->neighbors > 4) {
|
|
ap->machine[j].direction = (power2 & number) ?
|
|
fromTableDirection(THRS, ap->neighbors) :
|
|
fromTableDirection(THLS, ap->neighbors);
|
|
} else {
|
|
ap->machine[j].direction = (power2 & number) ?
|
|
fromTableDirection(TRS, ap->neighbors) :
|
|
fromTableDirection(TLS, ap->neighbors);
|
|
}
|
|
ap->machine[j].next = 0;
|
|
power2 >>= 1;
|
|
}
|
|
ap->truchet = (ap->truchet && ap->xs > 2 && ap->ys > 2 &&
|
|
(ap->neighbors == 3 || ap->neighbors == 4 || ap->neighbors == 6));
|
|
if (MI_IS_VERBOSE(mi))
|
|
(void) fprintf(stdout,
|
|
"ants %d, neighbors %d, Turk's number 0x%x, colors %d\n",
|
|
ap->n, ap->neighbors, number, ap->ncolors);
|
|
if (label)
|
|
(void) sprintf(ap->ruleString, "Turk's number 0x%x", number);
|
|
}
|
|
|
|
static void
|
|
parseRule(ModeInfo * mi)
|
|
{
|
|
if (rule) {
|
|
int n = 0, l, number = 0, logClass = 0, power2;
|
|
Bool aZero = False;
|
|
|
|
while (rule[n]) {
|
|
if (rule[n] == 'T' || rule[n] == 't') {
|
|
n++;
|
|
while (rule[n]) {
|
|
number = number * 10 +
|
|
(int) (rule[n] - '0');
|
|
n++;
|
|
}
|
|
getTable(mi, number);
|
|
return;
|
|
}
|
|
if (rule[n] == 'A' || rule[n] == 'a') {
|
|
if (!NRAND(NUMSTIPPLES)) {
|
|
getTable(mi, (int) (NRAND(NTABLES)));
|
|
return;
|
|
} else {
|
|
logClass = (int) (NRAND(NUMSTIPPLES - 2)) + 2;
|
|
/* <logClass = 4;> has 4 binary digits */
|
|
power2 = 1 << (logClass - 1);
|
|
/* Do not want numbers which in binary are all 1's. */
|
|
number = NRAND(power2 - 1) + power2;
|
|
getTurk(mi, logClass, number);
|
|
return;
|
|
}
|
|
} else {
|
|
l = rule[n] - '0';
|
|
if (l >= 0 && l <= 1) {
|
|
number = (number << 1) + l;
|
|
if (logClass > 0 || l != 0)
|
|
logClass++;
|
|
if (logClass > 0 && l == 0)
|
|
aZero = True;
|
|
}
|
|
}
|
|
n++;
|
|
}
|
|
if (!aZero) {
|
|
getTurk(mi, 2, 2); /* Default to Langton's ant */
|
|
} else {
|
|
getTurk(mi, logClass, number);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_ant(Display *display, antfarmstruct *ap)
|
|
{
|
|
int shade;
|
|
|
|
if (ap->stippledGC != None) {
|
|
XFreeGC(display, ap->stippledGC);
|
|
ap->stippledGC = None;
|
|
}
|
|
for (shade = 0; shade < ap->init_bits; shade++) {
|
|
XFreePixmap(display, ap->pixmaps[shade]);
|
|
}
|
|
ap->init_bits = 0;
|
|
if (ap->tape != NULL) {
|
|
free(ap->tape);
|
|
ap->tape = (unsigned char *) NULL;
|
|
}
|
|
if (ap->ants != NULL) {
|
|
free(ap->ants);
|
|
ap->ants = (antstruct *) NULL;
|
|
}
|
|
if (ap->truchet_state != NULL) {
|
|
free(ap->truchet_state);
|
|
ap->truchet_state = (unsigned char *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
init_ant(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
int size = MI_SIZE(mi);
|
|
antfarmstruct *ap;
|
|
int col, row, dir;
|
|
int i;
|
|
|
|
if (antfarms == NULL) {
|
|
if ((antfarms = (antfarmstruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (antfarmstruct))) == NULL)
|
|
return;
|
|
}
|
|
ap = &antfarms[MI_SCREEN(mi)];
|
|
|
|
ap->redrawing = 0;
|
|
if (MI_NPIXELS(mi) <= 2) {
|
|
if (ap->stippledGC == None) {
|
|
XGCValues gcv;
|
|
|
|
gcv.fill_style = FillOpaqueStippled;
|
|
if ((ap->stippledGC = XCreateGC(display, window,
|
|
GCFillStyle, &gcv)) == None) {
|
|
free_ant(display, ap);
|
|
return;
|
|
}
|
|
}
|
|
if (ap->init_bits == 0) {
|
|
for (i = 1; i < NUMSTIPPLES; i++) {
|
|
ANTBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
|
|
}
|
|
}
|
|
}
|
|
ap->generation = 0;
|
|
ap->n = MI_COUNT(mi);
|
|
if (ap->n < -MINANTS) {
|
|
/* if ap->n is random ... the size can change */
|
|
if (ap->ants != NULL) {
|
|
free(ap->ants);
|
|
ap->ants = (antstruct *) NULL;
|
|
}
|
|
ap->n = NRAND(-ap->n - MINANTS + 1) + MINANTS;
|
|
} else if (ap->n < MINANTS)
|
|
ap->n = MINANTS;
|
|
|
|
ap->width = MI_WIDTH(mi);
|
|
ap->height = MI_HEIGHT(mi);
|
|
|
|
for (i = 0; i < NEIGHBORKINDS; i++) {
|
|
if (neighbors == plots[i]) {
|
|
ap->neighbors = plots[i];
|
|
break;
|
|
}
|
|
if (i == NEIGHBORKINDS - 1) {
|
|
if (!NRAND(10)) {
|
|
/* Make above 6 rare */
|
|
ap->neighbors = plots[NRAND(NEIGHBORKINDS)];
|
|
} else {
|
|
ap->neighbors = plots[NRAND(GOODNEIGHBORKINDS)];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ap->neighbors == 6) {
|
|
int nccols, ncrows;
|
|
|
|
if (ap->width < 8)
|
|
ap->width = 8;
|
|
if (ap->height < 8)
|
|
ap->height = 8;
|
|
if (size < -MINSIZE) {
|
|
ap->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
if (ap->ys < MINRANDOMSIZE)
|
|
ap->ys = MIN(MINRANDOMSIZE,
|
|
MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE));
|
|
} else if (size < MINSIZE) {
|
|
if (!size)
|
|
ap->ys = MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE);
|
|
else
|
|
ap->ys = MINSIZE;
|
|
} else
|
|
ap->ys = MIN(size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE));
|
|
ap->xs = ap->ys;
|
|
nccols = MAX(ap->width / ap->xs - 2, 2);
|
|
ncrows = MAX(ap->height / ap->ys - 1, 4);
|
|
ap->ncols = nccols / 2;
|
|
ap->nrows = 2 * (ncrows / 4);
|
|
ap->xb = (ap->width - ap->xs * nccols) / 2 + ap->xs / 2;
|
|
ap->yb = (ap->height - ap->ys * (ncrows / 2) * 2) / 2 + ap->ys - 2;
|
|
for (i = 0; i < 6; i++) {
|
|
ap->shape.hexagon[i].x = (ap->xs - 1) * hexagonUnit[i].x;
|
|
ap->shape.hexagon[i].y = ((ap->ys - 1) * hexagonUnit[i].y / 2) * 4 / 3;
|
|
}
|
|
/* Avoid array bounds read of hexagonUnit */
|
|
ap->shape.hexagon[6].x = 0;
|
|
ap->shape.hexagon[6].y = 0;
|
|
} else if (ap->neighbors == 4 || ap->neighbors == 8) {
|
|
if (size < -MINSIZE) {
|
|
ap->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
if (ap->ys < MINRANDOMSIZE)
|
|
ap->ys = MIN(MINRANDOMSIZE,
|
|
MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE));
|
|
} else if (size < MINSIZE) {
|
|
if (!size)
|
|
ap->ys = MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE);
|
|
else
|
|
ap->ys = MINSIZE;
|
|
} else
|
|
ap->ys = MIN(size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE));
|
|
ap->xs = ap->ys;
|
|
ap->ncols = MAX(ap->width / ap->xs, 2);
|
|
ap->nrows = MAX(ap->height / ap->ys, 2);
|
|
ap->xb = (ap->width - ap->xs * ap->ncols) / 2;
|
|
ap->yb = (ap->height - ap->ys * ap->nrows) / 2;
|
|
} else { /* TRI */
|
|
int orient;
|
|
|
|
if (ap->width < 2)
|
|
ap->width = 2;
|
|
if (ap->height < 2)
|
|
ap->height = 2;
|
|
if (size < -MINSIZE) {
|
|
ap->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
if (ap->ys < MINRANDOMSIZE)
|
|
ap->ys = MIN(MINRANDOMSIZE,
|
|
MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE));
|
|
} else if (size < MINSIZE) {
|
|
if (!size)
|
|
ap->ys = MAX(MINSIZE, MIN(ap->width, ap->height) / MINGRIDSIZE);
|
|
else
|
|
ap->ys = MINSIZE;
|
|
} else
|
|
ap->ys = MIN(size, MAX(MINSIZE, MIN(ap->width, ap->height) /
|
|
MINGRIDSIZE));
|
|
ap->xs = (int) (1.52 * ap->ys);
|
|
ap->ncols = (MAX(ap->width / ap->xs - 1, 2) / 2) * 2;
|
|
ap->nrows = (MAX(ap->height / ap->ys - 1, 2) / 2) * 2;
|
|
ap->xb = (ap->width - ap->xs * ap->ncols) / 2 + ap->xs / 2;
|
|
ap->yb = (ap->height - ap->ys * ap->nrows) / 2 + ap->ys;
|
|
for (orient = 0; orient < 2; orient++) {
|
|
for (i = 0; i < 3; i++) {
|
|
ap->shape.triangle[orient][i].x =
|
|
(ap->xs - 2) * triangleUnit[orient][i].x;
|
|
ap->shape.triangle[orient][i].y =
|
|
(ap->ys - 2) * triangleUnit[orient][i].y;
|
|
}
|
|
/* Avoid array bounds read of triangleUnit */
|
|
ap->shape.triangle[orient][3].x = 0;
|
|
ap->shape.triangle[orient][3].y = 0;
|
|
}
|
|
}
|
|
|
|
XSetLineAttributes(display, MI_GC(mi), 1, LineSolid, CapNotLast, JoinMiter);
|
|
MI_CLEARWINDOW(mi);
|
|
ap->painted = False;
|
|
|
|
if (MI_IS_FULLRANDOM(mi)) {
|
|
ap->truchet = (Bool) (LRAND() & 1);
|
|
ap->eyes = (Bool) (LRAND() & 1);
|
|
ap->sharpturn = (Bool) (LRAND() & 1);
|
|
} else {
|
|
ap->truchet = truchet;
|
|
ap->eyes = eyes;
|
|
ap->sharpturn = sharpturn;
|
|
}
|
|
ap->labelOffsetX = NRAND(8);
|
|
ap->labelOffsetY = NRAND(8);
|
|
parseRule(mi);
|
|
if (MI_NPIXELS(mi) > 2)
|
|
for (i = 0; i < (int) ap->ncolors - 1; i++)
|
|
ap->colors[i] = (unsigned char) (NRAND(MI_NPIXELS(mi)) +
|
|
i * MI_NPIXELS(mi)) / ((int) (ap->ncolors - 1));
|
|
if (ap->ants == NULL) {
|
|
if ((ap->ants = (antstruct *) malloc(ap->n * sizeof (antstruct))) ==
|
|
NULL) {
|
|
free_ant(display, ap);
|
|
return;
|
|
}
|
|
}
|
|
if (ap->tape != NULL)
|
|
free(ap->tape);
|
|
if ((ap->tape = (unsigned char *) calloc(ap->ncols * ap->nrows,
|
|
sizeof (unsigned char))) == NULL) {
|
|
free_ant(display, ap);
|
|
return;
|
|
}
|
|
if (ap->truchet_state != NULL)
|
|
free(ap->truchet_state);
|
|
if ((ap->truchet_state = (unsigned char *) calloc(ap->ncols * ap->nrows,
|
|
sizeof (unsigned char))) == NULL) {
|
|
free_ant(display, ap);
|
|
return;
|
|
}
|
|
|
|
row = ap->nrows / 2;
|
|
col = ap->ncols / 2;
|
|
if (col > 0 && ((ap->neighbors % 2) || ap->neighbors == 12) && (LRAND() & 1))
|
|
col--;
|
|
dir = NRAND(ap->neighbors) * ANGLES / ap->neighbors;
|
|
ap->init_dir = dir;
|
|
#ifdef NUMBER_9
|
|
if (ap->neighbors == 9 && !((col + row) & 1))
|
|
dir = (dir + ANGLES - ANGLES / (ap->neighbors * 2)) % ANGLES;
|
|
#endif
|
|
/* Have them all start in the same spot, why not? */
|
|
for (i = 0; i < ap->n; i++) {
|
|
ap->ants[i].col = col;
|
|
ap->ants[i].row = row;
|
|
ap->ants[i].direction = dir;
|
|
ap->ants[i].state = 0;
|
|
}
|
|
draw_anant(mi, dir, col, row);
|
|
}
|
|
|
|
void
|
|
draw_ant(ModeInfo * mi)
|
|
{
|
|
antstruct *anant;
|
|
statestruct *status;
|
|
int i, state_pos, tape_pos;
|
|
unsigned char color;
|
|
short chg_dir, old_dir;
|
|
antfarmstruct *ap;
|
|
|
|
if (antfarms == NULL)
|
|
return;
|
|
ap = &antfarms[MI_SCREEN(mi)];
|
|
if (ap->ants == NULL)
|
|
return;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
ap->painted = True;
|
|
for (i = 0; i < ap->n; i++) {
|
|
anant = &ap->ants[i];
|
|
tape_pos = anant->col + anant->row * ap->ncols;
|
|
color = ap->tape[tape_pos]; /* read tape */
|
|
state_pos = color + anant->state * ap->ncolors;
|
|
status = &(ap->machine[state_pos]);
|
|
drawcell(mi, anant->col, anant->row, status->color);
|
|
ap->tape[tape_pos] = status->color; /* write on tape */
|
|
|
|
/* Find direction of Bees or Ants. */
|
|
/* Translate relative direction to actual direction */
|
|
old_dir = anant->direction;
|
|
chg_dir = (2 * ANGLES - status->direction) % ANGLES;
|
|
anant->direction = (chg_dir + old_dir) % ANGLES;
|
|
if (ap->truchet) {
|
|
int a = 0, b;
|
|
|
|
if (ap->neighbors == 6) {
|
|
if (ap->sharpturn) {
|
|
a = (((ANGLES + anant->direction - old_dir) % ANGLES) == 240);
|
|
/* should be some way of getting rid of the init_dir dependency... */
|
|
b = !(ap->init_dir % 120);
|
|
a = ((a && !b) || (b && !a));
|
|
drawtruchet(mi, anant->col, anant->row, status->color, a);
|
|
} else {
|
|
a = (old_dir / 60) % 3;
|
|
b = (anant->direction / 60) % 3;
|
|
a = (a + b + 1) % 3;
|
|
drawtruchet(mi, anant->col, anant->row, status->color, a);
|
|
}
|
|
} else if (ap->neighbors == 4) {
|
|
a = old_dir / 180;
|
|
b = anant->direction / 180;
|
|
a = ((a && !b) || (b && !a));
|
|
drawtruchet(mi, anant->col, anant->row, status->color, a);
|
|
} else if (ap->neighbors == 3) {
|
|
if (chg_dir == 240)
|
|
a = (2 + anant->direction / 120) % 3;
|
|
else
|
|
a = (1 + anant->direction / 120) % 3;
|
|
drawtruchet(mi, anant->col, anant->row, status->color, a);
|
|
}
|
|
ap->truchet_state[tape_pos] = a + 1;
|
|
}
|
|
anant->state = status->next;
|
|
|
|
/* Allow step first then turn */
|
|
old_dir = ((status->direction < ANGLES) ? anant->direction : old_dir);
|
|
#ifdef DEBUG
|
|
(void) printf("old_dir %d, col %d, row %d", old_dir, anant->col, anant->row);
|
|
#endif
|
|
position_of_neighbor(ap, old_dir, &(anant->col), &(anant->row));
|
|
#ifdef DEBUG
|
|
(void) printf(", ncol %d, nrow %d\n", anant->col, anant->row);
|
|
#endif
|
|
draw_anant(mi, anant->direction, anant->col, anant->row);
|
|
}
|
|
if (++ap->generation > MI_CYCLES(mi)) {
|
|
init_ant(mi);
|
|
}
|
|
if (ap->redrawing) {
|
|
for (i = 0; i < REDRAWSTEP; i++) {
|
|
if (ap->tape[ap->redrawpos] ||
|
|
(ap->truchet && ap->truchet_state[ap->redrawpos])) {
|
|
drawcell(mi, ap->redrawpos % ap->ncols, ap->redrawpos / ap->ncols,
|
|
ap->tape[ap->redrawpos]);
|
|
if (ap->truchet)
|
|
drawtruchet(mi, ap->redrawpos % ap->ncols, ap->redrawpos / ap->ncols,
|
|
ap->tape[ap->redrawpos],
|
|
ap->truchet_state[ap->redrawpos] - 1);
|
|
}
|
|
if (++(ap->redrawpos) >= ap->ncols * ap->nrows) {
|
|
ap->redrawing = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (label) {
|
|
int size = MAX(MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) - 1, 1);
|
|
|
|
if (size >= 10 * FONT_WIDTH) {
|
|
/* hard code these to corners */
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
|
|
MI_WHITE_PIXEL(mi));
|
|
/*XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
16 + ap->labelOffsetX,
|
|
16 + ap->labelOffsetY + FONT_HEIGHT,
|
|
ap->ruleString, strlen(ap->ruleString)); */
|
|
XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
|
|
16 + ap->labelOffsetX, MI_HEIGHT(mi) - 16 -
|
|
ap->labelOffsetY - FONT_HEIGHT / 2,
|
|
ap->ruleString, strlen(ap->ruleString));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
release_ant(ModeInfo * mi)
|
|
{
|
|
if (antfarms != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_ant(MI_DISPLAY(mi), &antfarms[screen]);
|
|
free(antfarms);
|
|
antfarms = (antfarmstruct *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_ant(ModeInfo * mi)
|
|
{
|
|
antfarmstruct *ap;
|
|
|
|
if (antfarms == NULL)
|
|
return;
|
|
ap = &antfarms[MI_SCREEN(mi)];
|
|
|
|
if (ap->painted) {
|
|
MI_CLEARWINDOW(mi);
|
|
ap->redrawing = 1;
|
|
ap->redrawpos = 0;
|
|
}
|
|
}
|
|
|
|
#endif /* MODE_ant */
|