summaryrefslogtreecommitdiff
path: root/app/xlockmore/modes/life.c
diff options
context:
space:
mode:
authorMatthieu Herrb <matthieu@cvs.openbsd.org>2006-11-26 11:09:41 +0000
committerMatthieu Herrb <matthieu@cvs.openbsd.org>2006-11-26 11:09:41 +0000
commit95c2d1cbda23a41cdf6e63520c7f0b825e63dd5b (patch)
tree06d3ffa4312e568c4157f69fe1bddaddec9bc497 /app/xlockmore/modes/life.c
parent3928433848e2d6a9356f3d438a14b32a4f87f660 (diff)
Importing xlockmore 5.22
Diffstat (limited to 'app/xlockmore/modes/life.c')
-rw-r--r--app/xlockmore/modes/life.c2682
1 files changed, 2682 insertions, 0 deletions
diff --git a/app/xlockmore/modes/life.c b/app/xlockmore/modes/life.c
new file mode 100644
index 000000000..51c061340
--- /dev/null
+++ b/app/xlockmore/modes/life.c
@@ -0,0 +1,2682 @@
+/* -*- Mode: C; tab-width: 4 -*- */
+/* life --- Conway's game of Life */
+
+#if !defined( lint ) && !defined( SABER )
+static const char sccsid[] = "@(#)life.c 5.07 2003/02/27 xlockmore";
+
+#endif
+
+/*-
+ * Copyright (c) 1991 by Patrick J. Naughton.
+ * Copyright (c) 1997 by David Bagley.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * Revision History:
+ * 01-Mar-2003: Added shooters for triangular life.
+ * 26-Feb-2003: Added LWSS, MWSS, HWSS in shooter.
+ * 25-Feb-2003: Randomly rotate trilife
+ * 25-Jan-2003: Spawned a life.h
+ * 23-Jan-2003: Added life from Stephen Silver's Life Lexicon
+ * http://www.argentum.freeserve.co.uk/lex_home.htm
+ * 23-Jan-2003: Tri life from Carter Bays. Added B45/S34 which I use as
+ * the true trilife, also available B456/S45 & B45/S23.
+ * Right now the glider for trilife is not integrated into
+ * the "shooter" part of the program.
+ * Other neat ones are B4/S456, B46/S24, B4/S35, B456/S12,
+ * B3/S23, & B4/S46. See:
+ * http://www.cse.sc.edu/~bays/trilife3/home.html
+ * 15-Jan-2003: Moves on if screen blank or static
+ * 15-Jan-2003: Alternate life rules from Nathan Thompson's "Day and Night"
+ * B3678/S34678 and David I. Bell's "HighLife" B36/S23.
+ * See http://www.tip.net.au/~dbell/
+ * Other rules that may be neat:
+ * http://entropymine.com/jason/life/alt/
+ * "Diamoeba" B35678/S5678, "Move" B36(8)/S245
+ * 01-Nov-2000: Allocation checks
+ * 03-Oct-2000: Added more randomness in pattern 90 degree orientation and
+ * mirror image.
+ * 08-Dec-1997: Paul Callahan's B2a/S2b34 rule added.
+ * Described on the news site for cellular-automata.
+ * <ppc997@aber.ac.uk>
+ * http://www.cs.jhu.edu/~callahan/lifepage.html
+ * http://www.cs.jhu.edu/~callahan/hexrule.txt
+ * B2a/S2b34: Birth of x if 2a,
+ * Survival of x if 2b, 3, or 4 neighbors
+ * Assume symmetry.
+ * (2a, 2b, 2c: 2o, 2m, 2p original notation)
+ * O O O . O .
+ * 2a: . x . 2b: . x O 2c: . x .
+ * . . . . . O
+ * Also Bob Andreen's rule (my own notation for consistency)
+ * B2a3a4b/S2a2b4a (original notation: 234'B/22'4S)
+ * <andreen@msmc.edu>
+ * O O O O O .
+ * 3a: . x O 3b: . x . 3c: . x O
+ * . . . O O .
+ *
+ * O O O O O O
+ * 4a: . x O 4b: . x O 4c: . x .
+ * . O O . O O
+ * Some other rules
+ * B2a3b3c5/S12b2c3a4b4c6
+ * B23a3c4b4c6/S12b2c3c4a56
+ * B2a2c6/S13b
+ * 27-Oct-1997: xpm and ras capability added.
+ * 04-Jun-1997: Removed old algorithm, now use wator's. I could not
+ * understand it and had trouble adding more features.
+ * New algorithm is more efficient iff there lots of blank
+ * areas (ptr loop rather than a double array loop)
+ * 10-May-1997: Compatible with xscreensaver
+ * 07-May-1997: life neighbor option. Still have to fix -neighbor 3
+ * 07-Jan-1995: life now has a random soup pattern.
+ * 07-Dec-1994: life now has new organisms. They are now better centered.
+ * Some of the nonperiodic forms were removed. New life
+ * forms were taken from xlife (an AMAZING collection of life
+ * forms). life's gliders now come from the edge of the screen
+ * except when generated by a life form.
+ * 23-Nov-1994: Bug fix for different iconified window sizes
+ * 21-Jul-1994: Took out bzero & bcopy since memset & memcpy is more portable
+ * 10-Jun-1994: Changed name of function 'kill', which is a libc function on
+ * many systems from Victor Langeveld <vic@mbfys.kun.nl>
+ * Changes in original xlock
+ * 24-May-1991: Added wraparound code from johnson@bugs.comm.mot.com.
+ * Made old cells stay blue.
+ * Made batchcount control the number of generations until restart.
+ * 29-Jul-1990: support for multiple screens.
+ * 07-Feb-1990: remove bogus semi-colon after #include line.
+ * 15-Dec-1989: Fix for proper skipping of {White,Black}Pixel() in colors.
+ * 08-Oct-1989: Moved seconds() to an extern.
+ * 20-Sep-1989: Written, life algorithm courtesy of Jim Graham <flar@sun.com>
+ */
+
+/*-
+ Grid Number of Neighbors
+ ---- ------------------
+ Square 4 or 8
+ Hexagon 6
+ Triangle 3, 9, or 12
+
+ Conway's Life: -neighbors 8 -rule S23/B3 LIFE, CONWAY
+ Other things to try:
+ -neighbors 8 -rule S23/B36 <HIGHLIFE, BELL>
+ -neighbors 8 -rule S34678/B3678 <DAY_NIGHT, THOMPSON>
+ -neighbors 4 -rule S234/B2
+ -neighbors 6 -rule S23/B3
+ -neighbors 3 -rule S12/B23
+ -neighbors 6 -rule S2b34/B2a <CALLAHAN>
+ -neighbors 6 -rule S2a2b4a/B2b3a4b <ANDREEN>
+ -neighbors 12 -rule S34/B45 <TRILIFE, BAYS>
+ -neighbors 12 -rule S45/B456 <TRILIFE1, BAYS>
+ -neighbors 12 -rule S23/B45 <TRILIFE2, BAYS>
+*/
+
+#ifdef STANDALONE
+#define MODE_life
+#define PROGCLASS "Life"
+#define HACK_INIT init_life
+#define HACK_DRAW draw_life
+#define life_opts xlockmore_opts
+#define DEFAULTS "*delay: 750000 \n" \
+ "*count: 40 \n" \
+ "*cycles: 140 \n" \
+ "*size: 0 \n" \
+ "*ncolors: 200 \n" \
+ "*bitmap: \n" \
+ "*neighbors: 0 \n" \
+ "*verbose: False \n"
+#define UNIFORM_COLORS
+#include "xlockmore.h" /* in xscreensaver distribution */
+#else /* STANDALONE */
+#include "xlock.h" /* in xlockmore distribution */
+#include "color.h"
+#endif /* STANDALONE */
+#include "iostuff.h"
+#include "automata.h"
+
+#ifdef MODE_life
+#define LIFE_NAMES 1
+#include "life.h"
+
+#ifdef LIFE_NAMES
+#define DEF_LABEL "True"
+#define FONT_HEIGHT 19
+#define FONT_WIDTH 15
+#endif
+#define DEF_NEIGHBORS "0" /* choose best value (8) */
+#define DEF_SERIAL "False"
+
+#if 1
+#define DEF_RULE "G" /* All rules with known gliders */
+#else
+#define DEF_RULE "P" /* All rules with known patterns, currently G==P */
+#define DEF_RULE "S23/B3" /* "B3/S23" LIFE */
+#define DEF_RULE "S23/B36" /* "B36/S23" HIGHLIFE */
+#define DEF_RULE "S34678/B3678" /* "B3678/S34678" DAY_NIGHT*/
+#define DEF_RULE "S2b34/B2a" /* CALLAHAN */
+#define DEF_RULE "S2a2b4a/B2b3a4b" /* ANDREEN */
+#define DEF_RULE "S34/B45" /* TRILIFE */
+#define DEF_RULE "S45/B456" /* TRILIFE1 */
+#define DEF_RULE "S23/B45" /* TRILIFE2 */
+#endif
+
+#define DEF_CONWAY "False"
+#define DEF_HIGHLIFE "False"
+#define DEF_DAY_NIGHT "False"
+#define DEF_CALLAHAN "False"
+#define DEF_ANDREEN "False"
+#define DEF_TRILIFE "False"
+#define DEF_TRILIFE1 "False"
+#define DEF_TRILIFE2 "False"
+
+static int neighbors;
+static char *rule;
+static char *lifefile;
+#ifdef LIFE_NAMES
+static Bool label;
+#endif
+static Bool serial;
+static Bool conway;
+static Bool highlife;
+static Bool daynight;
+static Bool callahan;
+static Bool andreen;
+static Bool trilife;
+static Bool trilife1;
+static Bool trilife2;
+
+static XrmOptionDescRec opts[] =
+{
+#ifdef LIFE_NAMES
+ {(char *) "-label", (char *) ".life.label", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+label", (char *) ".life.label", XrmoptionNoArg, (caddr_t) "off"},
+#endif
+ {(char *) "-neighbors", (char *) ".life.neighbors", XrmoptionSepArg, (caddr_t) NULL},
+ {(char *) "-rule", (char *) ".life.rule", XrmoptionSepArg, (caddr_t) NULL},
+ {(char *) "-lifefile", (char *) ".life.lifefile", XrmoptionSepArg, (caddr_t) NULL},
+ {(char *) "-serial", (char *) ".life.serial", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+serial", (char *) ".life.serial", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-conway", (char *) ".life.conway", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+conway", (char *) ".life.conway", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-highlife", (char *) ".life.highlife", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+highlife", (char *) ".life.highlife", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-daynight", (char *) ".life.daynight", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+daynight", (char *) ".life.daynight", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-callahan", (char *) ".life.callahan", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+callahan", (char *) ".life.callahan", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-andreen", (char *) ".life.andreen", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+andreen", (char *) ".life.andreen", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-trilife", (char *) ".life.trilife", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+trilife", (char *) ".life.trilife", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-trilife1", (char *) ".life.trilife1", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+trilife1", (char *) ".life.trilife1", XrmoptionNoArg, (caddr_t) "off"},
+ {(char *) "-trilife2", (char *) ".life.trilife2", XrmoptionNoArg, (caddr_t) "on"},
+ {(char *) "+trilife2", (char *) ".life.trilife2", XrmoptionNoArg, (caddr_t) "off"}
+};
+static argtype vars[] =
+{
+#ifdef LIFE_NAMES
+ {(void *) & label, (char *) "label", (char *) "Label", (char *) DEF_LABEL, t_Bool},
+#endif
+ {(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
+ {(void *) & rule, (char *) "rule", (char *) "Rule", (char *) DEF_RULE, t_String},
+ {(void *) & lifefile, (char *) "lifefile", (char *) "LifeFile", (char *) "", t_String},
+ {(void *) & serial, (char *) "serial", (char *) "Serial", (char *) DEF_SERIAL, t_Bool},
+ {(void *) & conway, (char *) "conway", (char *) "Conway", (char *) DEF_CONWAY, t_Bool},
+ {(void *) & highlife, (char *) "highlife", (char *) "HighLife", (char *) DEF_HIGHLIFE, t_Bool},
+ {(void *) & daynight, (char *) "daynight", (char *) "DayNight", (char *) DEF_DAY_NIGHT, t_Bool},
+ {(void *) & callahan, (char *) "callahan", (char *) "Callahan", (char *) DEF_CALLAHAN, t_Bool},
+ {(void *) & andreen, (char *) "andreen", (char *) "Andreen", (char *) DEF_ANDREEN, t_Bool},
+ {(void *) & trilife, (char *) "trilife", (char *) "TriLife", (char *) DEF_TRILIFE, t_Bool},
+ {(void *) & trilife1, (char *) "trilife1", (char *) "TriLife1", (char *) DEF_TRILIFE1, t_Bool},
+ {(void *) & trilife2, (char *) "trilife2", (char *) "TriLife2", (char *) DEF_TRILIFE2, t_Bool}
+};
+static OptionStruct desc[] =
+{
+#ifdef LIFE_NAMES
+ {(char *) "-/+label", (char *) "turn on/off name labeling"},
+#endif
+ {(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"},
+ {(char *) "-rule string", (char *) "S<survival_neighborhood>/B<birth_neighborhood> parameters"},
+ {(char *) "-lifefile file", (char *) "life file"},
+ {(char *) "-/+serial", (char *) "turn on/off picking of sequential patterns"},
+ {(char *) "-/+conway", (char *) "turn on/off Conway's original Life rule B3/S23"},
+ {(char *) "-/+highlife", (char *) "turn on/off Bell's HighLife rule B36/S23"},
+ {(char *) "-/+daynight", (char *) "turn on/off Thompson's Day and Night rule B3678/S34678"},
+ {(char *) "-/+callahan", (char *) "turn on/off Callahan's hex rule B2a/S2b34"},
+ {(char *) "-/+andreen", (char *) "turn on/off Andreen's hex rule B2a3a4b/S2a2b4a"},
+ {(char *) "-/+trilife", (char *) "turn on/off Bay's tri rule B45/S34"},
+ {(char *) "-/+trilife1", (char *) "turn on/off Bay's tri rule B456/S45"},
+ {(char *) "-/+trilife2", (char *) "turn on/off Bay's tri rule B45/S23"}
+};
+
+ModeSpecOpt life_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
+
+#ifdef USE_MODULES
+ModStruct life_description =
+{"life", "init_life", "draw_life", "release_life",
+ "refresh_life", "change_life", (char *) NULL, &life_opts,
+ 750000, 40, 140, 0, 64, 1.0, "",
+ "Shows Conway's game of Life", 0, NULL};
+
+#endif
+
+/* aliases for vars defined in the bitmap file */
+#define CELL_WIDTH image_width
+#define CELL_HEIGHT image_height
+#define CELL_BITS image_bits
+
+#include "life.xbm"
+#ifdef XBM_GRELB
+#include "life2.xbm"
+#define CELL2_WIDTH image2_width
+#define CELL2_HEIGHT image2_height
+#define CELL2_BITS image2_bits
+static XImage bimage =
+{
+ 0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1
+};
+#endif
+
+#ifdef HAVE_XPM
+#define CELL_NAME image_name
+#if 1
+static char *image_name[] =
+{(char *) ""};
+#else
+/* Kind of boring... */
+#include "life.xpm"
+#endif
+#define DEFAULT_XPM 0
+#endif
+
+#define REDRAWSTEP 2000 /* How many cells to draw per cycle */
+#define MINGRIDSIZE 20
+#define MINSIZE 4
+#define DEAD 0
+#define LIVE 1
+#define STATES 2
+
+#define SetList(c,r) if (!setcell(mi,c,r,LIVE)) return
+
+typedef struct {
+ long position;
+ unsigned short age;
+ unsigned char state;
+ unsigned char toggle;
+} cellstruct;
+
+/* Singly linked list */
+typedef struct _CellList {
+ cellstruct info;
+ struct _CellList *previous, *next;
+} CellList;
+
+typedef struct {
+ Bool painted;
+ paramstruct param;
+ int pattern, patterned_rule;
+ int pixelmode;
+ int generation;
+ int xs, ys, xb, yb; /* cell size, grid border */
+ int nrows, ncols, npositions;
+ int width, height;
+ int state;
+ int noChangeCount;
+ int redrawing, redrawpos;
+ int ncells[STATES];
+ CellList *last[STATES], *first[STATES];
+ CellList **arr;
+ union {
+ XPoint hexagon[6];
+ XPoint triangle[2][3];
+ } shape;
+ XImage *logo;
+#ifdef XBM_GRELB
+ XImage *logo2;
+#endif
+ Colormap cmap;
+ unsigned long black;
+ int graphics_format;
+ GC backGC;
+ int neighbors;
+ int conway, highlife, daynight, callahan, andreen;
+ int trilife, trilife1, trilife2;
+ int allPatterns, allGliders;
+ paramstruct input_param;
+ int labelOffsetX, labelOffsetY;
+ char ruleString[80], nameString[80];
+} lifestruct;
+
+static lifestruct *lifes = (lifestruct *) NULL;
+
+static char *filePattern = (char *) NULL;
+
+static int
+invplot(int local_neighbors)
+{
+ switch (local_neighbors) {
+ case 3:
+ return 0;
+ case 4:
+ return 1;
+ case 6:
+ return 2;
+ case 8:
+ return 3;
+ case 9:
+ return 4;
+ case 12:
+ return 5;
+ default:
+ (void) fprintf(stderr, "no neighborhood like %d known\n", local_neighbors);
+ return 3;
+ }
+}
+
+static int
+codeToPatternedRule(int local_neighbors, paramstruct param)
+{
+ unsigned int i;
+ int g, neighbor_kind;
+
+ neighbor_kind = invplot(local_neighbors);
+ switch (local_neighbors) {
+ case 6:
+ for (i = 0; i < LIFE_6RULES; i++)
+ if (param_6rules[i].survival == param.survival &&
+ param_6rules[i].birth == param.birth) {
+ for (g = 0; g < maxgroups[neighbor_kind]; g++) {
+ if (param_6rules[i].survival_group[g] !=
+ param.survival_group[g] ||
+ param_6rules[i].birth_group[g] !=
+ param.birth_group[g]) {
+ break;
+ }
+ }
+ if (g == maxgroups[neighbor_kind])
+ return i;
+ }
+ return LIFE_6RULES;
+ case 8:
+ for (i = 0; i < LIFE_8RULES; i++)
+ if (param_8rules[i].survival == param.survival &&
+ param_8rules[i].birth == param.birth) {
+ for (g = 0; g < maxgroups[neighbor_kind]; g++) {
+ if (param_8rules[i].survival_group[g] !=
+ param.survival_group[g] ||
+ param_8rules[i].birth_group[g] !=
+ param.birth_group[g]) {
+ break;
+ }
+ }
+ if (g == maxgroups[neighbor_kind])
+ return i;
+ }
+ return LIFE_8RULES;
+ case 12:
+ for (i = 0; i < LIFE_12RULES; i++)
+ if (param_12rules[i].survival == param.survival &&
+ param_12rules[i].birth == param.birth) {
+ for (g = 0; g < maxgroups[neighbor_kind]; g++) {
+ if (param_12rules[i].survival_group[g] !=
+ param.survival_group[g] ||
+ param_12rules[i].birth_group[g] !=
+ param.birth_group[g]) {
+ break;
+ }
+ }
+ if (g == maxgroups[neighbor_kind])
+ return i;
+ }
+ return LIFE_12RULES;
+ }
+ return 0;
+}
+
+static void
+copyFromPatternedRule(int local_neighbors, paramstruct * param,
+ int patterned_rule)
+{
+ int i, neighbor_kind;
+
+ neighbor_kind = invplot(local_neighbors);
+ switch (local_neighbors) {
+ case 6:
+ param->survival = param_6rules[patterned_rule].survival;
+ param->birth = param_6rules[patterned_rule].birth;
+ for (i = 0; i < maxgroups[neighbor_kind]; i++) {
+ param->survival_group[i] =
+ param_6rules[patterned_rule].survival_group[i];
+ param->birth_group[i] =
+ param_6rules[patterned_rule].birth_group[i];
+ }
+ break;
+ case 8:
+ param->survival = param_8rules[patterned_rule].survival;
+ param->birth = param_8rules[patterned_rule].birth;
+ for (i = 0; i < maxgroups[neighbor_kind]; i++) {
+ param->survival_group[i] =
+ param_8rules[patterned_rule].survival_group[i];
+ param->birth_group[i] =
+ param_8rules[patterned_rule].birth_group[i];
+ }
+ break;
+ case 12:
+ param->survival = param_12rules[patterned_rule].survival;
+ param->birth = param_12rules[patterned_rule].birth;
+ for (i = 0; i < maxgroups[neighbor_kind]; i++) {
+ param->survival_group[i] =
+ param_12rules[patterned_rule].survival_group[i];
+ param->birth_group[i] =
+ param_12rules[patterned_rule].birth_group[i];
+ }
+ break;
+ }
+}
+
+static void
+printRule(int local_neighbors, char * string, paramstruct param, Bool verbose)
+{
+ int i = 1, l, g, neighbor_kind;
+ Bool found;
+
+ string[0] = 'S';
+ if (verbose)
+ (void) fprintf(stdout, "rule (Survival/Birth neighborhood): ");
+ neighbor_kind = invplot(local_neighbors);
+ for (l = 0; l <= local_neighbors && l < 10; l++) {
+ if (param.survival & (1 << l)) {
+ (void) sprintf(&(string[i]), "%d", l);
+ i++;
+ } else if (l >= FIRSTGROUP && l < FIRSTGROUP + maxgroups[neighbor_kind]) {
+ for (g = 0; g < groupnumber[neighbor_kind][l - FIRSTGROUP]; g++) {
+ if (param.survival_group[l - FIRSTGROUP] & (1 << g)) {
+ (void) sprintf(&(string[i]), "%d%c",
+ l, 'a' + g);
+ i += 2;
+ }
+ }
+ }
+ }
+ (void) sprintf(&(string[i]), "/B");
+ i += 2;
+ for (l = 0; l <= local_neighbors && l < 10; l++) {
+ if (param.birth & (1 << l)) {
+ (void) sprintf(&(string[i]), "%d", l);
+ i++;
+ } else if (l >= FIRSTGROUP && l < FIRSTGROUP + maxgroups[neighbor_kind]) {
+ for (g = 0; g < groupnumber[neighbor_kind][l - FIRSTGROUP]; g++) {
+ if (param.birth_group[l - FIRSTGROUP] & (1 << g)) {
+ (void) sprintf(&(string[i]), "%d%c",
+ l, 'a' + g);
+ i += 2;
+ }
+ }
+ }
+ }
+ string[i] = '\0';
+ if (verbose)
+ (void) fprintf(stdout, "%s\nbinary rule: Survival 0x%X, Birth 0x%X\n",
+ string, param.survival, param.birth);
+ found = False;
+ for (l = 0; l <= maxgroups[neighbor_kind]; l++) {
+ if (param.survival_group[l] || param.birth_group[l]) {
+ found = True;
+ break;
+ }
+ }
+ if (found && verbose)
+ for (l = 0; l < maxgroups[neighbor_kind]; l++) {
+ (void) fprintf(stdout,
+ "groups in neighborhood %d: Survival 0x%X, Birth 0x%X\n",
+ l + FIRSTGROUP, param.survival_group[l], param.birth_group[l]);
+ }
+}
+
+static int
+position_of_neighbor(lifestruct * lp, int n, int col, int row)
+{
+ int dir = n * 360 / lp->neighbors;
+
+ if (lp->neighbors == 4 || lp->neighbors == 6 || lp->neighbors == 8) {
+ switch (dir) {
+ case 0:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ break;
+ case 45:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 60:
+ if (!(row & 1))
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 90:
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 120:
+ if (row & 1)
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 135:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 180:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ break;
+ case 225:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 240:
+ if (row & 1)
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 270:
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 300:
+ if (!(row & 1))
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 315:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (row + 1 == lp->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) ? lp->ncols - 1 : col - 1;
+ break;
+ case 30:
+ case 40:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 60:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ if (row + 1 == lp->nrows)
+ row = 1;
+ else if (row + 2 == lp->nrows)
+ row = 0;
+ else
+ row = row + 2;
+ break;
+ case 80:
+ case 90:
+ if (row + 1 == lp->nrows)
+ row = 1;
+ else if (row + 2 == lp->nrows)
+ row = 0;
+ else
+ row = row + 2;
+ break;
+ case 120:
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 150:
+ case 160:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 180:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ break;
+ case 200:
+ case 210:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 240:
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 270:
+ case 280:
+ if (!row)
+ row = lp->nrows - 2;
+ else if (!(row - 1))
+ row = lp->nrows - 1;
+ else
+ row = row - 2;
+ break;
+ case 300:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ if (!row)
+ row = lp->nrows - 2;
+ else if (!(row - 1))
+ row = lp->nrows - 1;
+ else
+ row = row - 2;
+ break;
+ case 320:
+ case 330:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ default:
+ (void) fprintf(stderr, "wrong direction %d\n", dir);
+ }
+ } else { /* left */
+ switch (dir) {
+ case 0:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ break;
+ case 30:
+ case 40:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 60:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ if (!row)
+ row = lp->nrows - 2;
+ else if (row == 1)
+ row = lp->nrows - 1;
+ else
+ row = row - 2;
+ break;
+ case 80:
+ case 90:
+ if (!row)
+ row = lp->nrows - 2;
+ else if (row == 1)
+ row = lp->nrows - 1;
+ else
+ row = row - 2;
+ break;
+ case 120:
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 150:
+ case 160:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (!row) ? lp->nrows - 1 : row - 1;
+ break;
+ case 180:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ break;
+ case 200:
+ case 210:
+ col = (!col) ? lp->ncols - 1 : col - 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 240:
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ case 270:
+ case 280:
+ if (row + 1 == lp->nrows)
+ row = 1;
+ else if (row + 2 == lp->nrows)
+ row = 0;
+ else
+ row = row + 2;
+ break;
+ case 300:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ if (row + 1 == lp->nrows)
+ row = 1;
+ else if (row + 2 == lp->nrows)
+ row = 0;
+ else
+ row = row + 2;
+ break;
+ case 320:
+ case 330:
+ col = (col + 1 == lp->ncols) ? 0 : col + 1;
+ row = (row + 1 == lp->nrows) ? 0 : row + 1;
+ break;
+ default:
+ (void) fprintf(stderr, "wrong direction %d\n", dir);
+ }
+ }
+ }
+
+ return (row * lp->ncols + col);
+}
+
+static void
+parseRule(ModeInfo * mi, char * string)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int n, g = 0, l = 0, neighbor_kind;
+ char serving = 0;
+ static Bool found = False;
+
+ if (!found)
+ lp->input_param.survival = lp->input_param.birth = 0;
+ if (lp->input_param.survival || lp->input_param.birth)
+ return;
+ for (n = 0; n < MAXGROUPS; n++) {
+ lp->input_param.survival_group[n] = lp->input_param.birth_group[n] = 0;
+ }
+ if (lp->conway) {
+ lp->neighbors = 8;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_8rules[0].survival;
+ lp->input_param.birth = param_8rules[0].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_8rules[0].survival_group[n];
+ lp->input_param.birth_group[n] = param_8rules[0].birth_group[n];
+ }
+ return;
+ } else if (lp->highlife) {
+ lp->neighbors = 8;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_8rules[1].survival;
+ lp->input_param.birth = param_8rules[1].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_8rules[1].survival_group[n];
+ lp->input_param.birth_group[n] = param_8rules[1].birth_group[n];
+ }
+ return;
+ } else if (lp->daynight) {
+ lp->neighbors = 8;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_8rules[2].survival;
+ lp->input_param.birth = param_8rules[2].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_8rules[2].survival_group[n];
+ lp->input_param.birth_group[n] = param_8rules[2].birth_group[n];
+ }
+ return;
+ } else if (lp->callahan) {
+ lp->neighbors = 6;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_6rules[0].survival;
+ lp->input_param.birth = param_6rules[0].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_6rules[0].survival_group[n];
+ lp->input_param.birth_group[n] = param_6rules[0].birth_group[n];
+ }
+ return;
+ } else if (lp->andreen) {
+ lp->neighbors = 6;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_6rules[1].survival;
+ lp->input_param.birth = param_6rules[1].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_6rules[1].survival_group[n];
+ lp->input_param.birth_group[n] = param_6rules[1].birth_group[n];
+ }
+ return;
+ } else if (lp->trilife) {
+ lp->neighbors = 12;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_12rules[0].survival;
+ lp->input_param.birth = param_12rules[0].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_12rules[0].survival_group[n];
+ lp->input_param.birth_group[n] = param_12rules[0].birth_group[n];
+ }
+ return;
+ } else if (lp->trilife1) {
+ lp->neighbors = 12;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_12rules[1].survival;
+ lp->input_param.birth = param_12rules[1].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_12rules[1].survival_group[n];
+ lp->input_param.birth_group[n] = param_12rules[1].birth_group[n];
+ }
+ return;
+ } else if (lp->trilife2) {
+ lp->neighbors = 12;
+ neighbor_kind = invplot(lp->neighbors);
+ found = !MI_IS_FULLRANDOM(mi);
+ lp->input_param.survival = param_12rules[2].survival;
+ lp->input_param.birth = param_12rules[2].birth;
+ for (n = 0; n < maxgroups[neighbor_kind]; n++) {
+ lp->input_param.survival_group[n] = param_12rules[2].survival_group[n];
+ lp->input_param.birth_group[n] = param_12rules[2].birth_group[n];
+ }
+ return;
+ }
+ neighbor_kind = invplot(lp->neighbors);
+ if (rule) {
+ n = 0;
+ while (rule[n]) {
+ if (rule[n] == 'P' || rule[n] == 'p') {
+ lp->allPatterns = True;
+ found = True;
+ if (MI_IS_VERBOSE(mi))
+ (void) fprintf(stdout, "rule: All rules with known patterns\n");
+ return;
+ } else if (rule[n] == 'G' || rule[n] == 'g') {
+ lp->allGliders = True;
+ found = True;
+ if (MI_IS_VERBOSE(mi))
+ (void) fprintf(stdout, "rule: All rules with known gliders\n");
+ return;
+ } else if (rule[n] == 'S' || rule[n] == 'E' || rule[n] == 'L' || rule[n] == 's' || rule[n] == 'e' || rule[n] == 'l') {
+ serving = 'S';
+ } else if (rule[n] == 'B' || rule[n] == 'b') {
+ serving = 'B';
+ } else {
+ l = rule[n] - '0';
+ if (l >= 0 && l <= 9 && l <= lp->neighbors) { /* no 10, 11, 12 */
+ g = rule[n + 1] - 'a';
+ if (l >= FIRSTGROUP && l < FIRSTGROUP + maxgroups[neighbor_kind] &&
+ g >= 0 && g < groupnumber[neighbor_kind][l]) { /* Groupings */
+ if (serving == 'S' || rule[n] == 's') {
+ found = True;
+ lp->input_param.survival_group[l - FIRSTGROUP] |= (1 << g);
+ } else if (serving == 'B' || rule[n] == 'b') {
+ found = True;
+ lp->input_param.birth_group[l - FIRSTGROUP] |= (1 << g);
+ }
+ } else {
+ if (serving == 'S' || rule[n] == 's') {
+ found = True;
+ lp->input_param.survival |= (1 << l);
+ } else if (serving == 'B' || rule[n] == 'b') {
+ found = True;
+ lp->input_param.birth |= (1 << l);
+ }
+ }
+ }
+ }
+ n++;
+ }
+ }
+ if (!found || !(lp->input_param.survival || lp->input_param.birth)) {
+ /* Default to Conway's rule if rule does not make sense */
+ lp->allGliders = True;
+ found = !MI_IS_FULLRANDOM(mi);
+ if (MI_IS_VERBOSE(mi))
+ (void) fprintf(stdout,
+ "rule: Defaulting to all rules with known gliders\n");
+ return;
+ }
+ printRule(lp->neighbors, string, lp->input_param, MI_IS_VERBOSE(mi));
+}
+
+static void
+parseFile(ModeInfo *mi)
+{
+ FILE *file;
+ static Bool done = False;
+ int firstx, x = 0, y = 0, i = 0;
+ int c, size;
+ char line[256];
+
+ if (done)
+ return;
+ done = True;
+ if (MI_IS_FULLRANDOM(mi) || !lifefile || !*lifefile)
+ return;
+ if ((file = my_fopenSize(lifefile, "r", &size)) == NULL) {
+ (void) fprintf(stderr, "could not read file \"%s\"\n", lifefile);
+ return;
+ }
+ for (;;) {
+ if (!fgets(line, 256, file)) {
+ (void) fprintf(stderr, "could not read header of file \"%s\"\n",
+ lifefile);
+ (void) fclose(file);
+ return;
+ }
+ if (strncmp(line, "#P", (size_t) 2) == 0 &&
+ sscanf(line, "#P %d %d", &x, &y) == 2)
+ break;
+ }
+ c = getc(file);
+ while (c != EOF && !(c == '0' || c == 'O' || c == '*' || c == '.')) {
+ c = getc(file);
+ }
+ if (c == EOF || x <= -127 || y <= -127 || x >= 127 || y >= 127) {
+ (void) fprintf(stderr, "corrupt file \"%s\" or file to large\n",
+ lifefile);
+ (void) fclose(file);
+ return;
+ }
+ firstx = x;
+ if ((filePattern = (char *) malloc((2 * size) *
+ sizeof (char))) == NULL) {
+ (void) fprintf(stderr, "not enough memory\n");
+ (void) fclose(file);
+ return;
+ }
+
+ while (c != EOF && x < 127 && y < 127 && i < 2 * size) {
+ if (c == '0' || c == 'O' || c == '*') {
+ filePattern[i++] = x++;
+ filePattern[i++] = y;
+ } else if (c == '.') {
+ x++;
+ } else if (c == '\n') {
+ x = firstx;
+ y++;
+ }
+ c = getc(file);
+ }
+ (void) fclose(file);
+ filePattern[i] = 127;
+}
+
+static Bool
+init_list(lifestruct * lp, int state)
+{
+ /* Waste some space at the beginning and end of list
+ so we do not have to complicated checks against falling off the ends. */
+ if ((lp->last[state] = (CellList *) malloc(sizeof (CellList))) == NULL) {
+ return False;
+ }
+ if ((lp->first[state] = (CellList *) malloc(sizeof (CellList))) == NULL) {
+ free(lp->last[state]);
+ lp->last[state] = (CellList *) NULL;
+ return False;
+ }
+ lp->first[state]->previous = lp->last[state]->next =
+ (struct _CellList *) NULL;
+ lp->first[state]->next = lp->last[state]->previous =
+ (struct _CellList *) NULL;
+ lp->first[state]->next = lp->last[state];
+ lp->last[state]->previous = lp->first[state];
+ return True;
+}
+
+static Bool
+addto_list(lifestruct * lp, int state, cellstruct info)
+{
+ CellList *curr;
+
+ if ((curr = (CellList *) malloc(sizeof (CellList))) == NULL)
+ return False;
+ lp->last[state]->previous->next = curr;
+ curr->previous = lp->last[state]->previous;
+ curr->next = lp->last[state];
+ lp->last[state]->previous = curr;
+ curr->info = info;
+ if (info.position >= 0) {
+ lp->arr[info.position] = curr;
+ lp->ncells[state]++;
+ }
+ return True;
+}
+static void
+removefrom_list(lifestruct * lp, int state, CellList * curr)
+{
+ curr->previous->next = curr->next;
+ curr->next->previous = curr->previous;
+ if (curr->info.position >= 0) {
+ lp->arr[curr->info.position] = (CellList *) NULL;
+ lp->ncells[state]--;
+ }
+ free(curr);
+}
+
+#ifdef DEBUG
+static void
+print_state(ModeInfo * mi, int state)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ CellList *curr;
+ int i = 0;
+
+ curr = lp->first[state]->next;
+ (void) printf("state %d\n", state);
+ while (curr != lp->last[state]) {
+ (void) printf("%d: position %ld, age %d, state %d, toggle %d\n",
+ i, curr->info.position, curr->info.age,
+ curr->info.state, curr->info.toggle);
+ curr = curr->next;
+ i++;
+ }
+}
+
+#endif
+
+static void
+flush_list(lifestruct * lp, int state)
+{
+ while (lp->last[state]->previous != lp->first[state]) {
+ CellList *curr = lp->last[state]->previous;
+
+ curr->previous->next = lp->last[state];
+ lp->last[state]->previous = curr->previous;
+ free(curr);
+ }
+ lp->ncells[state] = 0;
+}
+
+
+static void
+draw_cell(ModeInfo * mi, cellstruct info)
+{
+ Display *display = MI_DISPLAY(mi);
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ GC gc = lp->backGC;
+ int col, row;
+
+ col = (int) (info.position % lp->ncols);
+ row = (int) (info.position / lp->ncols);
+ if (info.state == LIVE) {
+ if (MI_NPIXELS(mi) > 2)
+ XSetForeground(display, gc, MI_PIXEL(mi, info.age));
+ else
+ XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
+ } else
+ XSetForeground(display, gc, lp->black);
+
+ if (lp->neighbors == 6) {
+ int ccol = 2 * col + !(row & 1), crow = 2 * row;
+
+ lp->shape.hexagon[0].x = lp->xb + ccol * lp->xs;
+ lp->shape.hexagon[0].y = lp->yb + crow * lp->ys;
+ if (lp->xs == 1 && lp->ys == 1)
+ XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
+ lp->backGC, lp->shape.hexagon[0].x, lp->shape.hexagon[0].y);
+ else
+ XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), lp->backGC,
+ lp->shape.hexagon, 6, Convex, CoordModePrevious);
+ } else if (lp->neighbors == 4 || lp->neighbors == 8) {
+ if (lp->pixelmode || info.state == DEAD)
+ XFillRectangle(display, MI_WINDOW(mi), gc,
+ lp->xb + lp->xs * col, lp->yb + lp->ys * row,
+ lp->xs - (lp->xs > 3 && lp->pixelmode),
+ lp->ys - (lp->ys > 3 && lp->pixelmode));
+ else {
+/*-
+ * PURIFY 4.0.1 on SunOS4 and on Solaris 2 reports a 132 byte memory leak on
+ * the next line */
+#ifdef XBM_GRELB
+ if (lp->logo2) {
+ (void) XPutImage(display, MI_WINDOW(mi), gc,
+ (LRAND() & 1) ? lp->logo : lp->logo2,
+ 0, 0, lp->xb + lp->xs * col, lp->yb + lp->ys * row,
+ lp->logo->width, lp->logo->height);
+ } else
+#endif
+ {
+ (void) XPutImage(display, MI_WINDOW(mi), gc, lp->logo,
+ 0, 0, lp->xb + lp->xs * col, lp->yb + lp->ys * row,
+ lp->logo->width, lp->logo->height);
+ }
+ }
+ } else { /* TRI */
+ int orient = (col + row) % 2; /* O left 1 right */
+
+ lp->shape.triangle[orient][0].x = lp->xb + col * lp->xs;
+ lp->shape.triangle[orient][0].y = lp->yb + row * lp->ys;
+ if (lp->xs <= 3 || lp->ys <= 3)
+ XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), lp->backGC,
+ ((orient) ? -1 : 1) + lp->shape.triangle[orient][0].x,
+ lp->shape.triangle[orient][0].y);
+ else {
+ if (orient)
+ lp->shape.triangle[orient][0].x += (lp->xs / 2 - 1);
+ else
+ lp->shape.triangle[orient][0].x -= (lp->xs / 2 - 1);
+ XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), lp->backGC,
+ lp->shape.triangle[orient], 3, Convex, CoordModePrevious);
+
+ }
+ }
+}
+
+static void
+setcelltoggles(ModeInfo * mi, int col, int row)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int position;
+ CellList *curr;
+
+ position = row * lp->ncols + col;
+ curr = lp->arr[position];
+ if (!curr) {
+ (void) fprintf(stderr, "state toggling but not on list\n");
+ return;
+ }
+ curr->info.toggle = True;
+}
+
+static void
+free_cells(lifestruct * lp)
+{
+ if (lp->arr != NULL) {
+ free(lp->arr);
+ lp->arr = (CellList **) NULL;
+ }
+}
+
+static void
+free_stuff(Display * display, lifestruct * lp)
+{
+ if (lp->cmap != None) {
+ XFreeColormap(display, lp->cmap);
+ if (lp->backGC != None) {
+ XFreeGC(display, lp->backGC);
+ lp->backGC = None;
+ }
+ lp->cmap = None;
+ } else
+ lp->backGC = None;
+}
+
+static void
+free_life(Display * display, lifestruct * lp)
+{
+ int state;
+
+ for (state = 0; state < STATES; state++) {
+ if (lp->first[state])
+ flush_list(lp, state);
+ if (lp->last[state])
+ free(lp->last[state]);
+ lp->last[state] = (CellList *) NULL;
+ if (lp->first[state])
+ free(lp->first[state]);
+ lp->first[state] = (CellList *) NULL;
+ }
+ free_cells(lp);
+ free_stuff(display, lp);
+ if (lp->logo != None) {
+ destroyImage(&lp->logo, &lp->graphics_format);
+ lp->logo = None;
+ }
+}
+
+static Bool
+setcellfromtoggle(ModeInfo * mi, int col, int row)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int neighbor, n, position;
+ cellstruct info;
+ CellList *curr, *currn;
+
+ position = row * lp->ncols + col;
+ curr = lp->arr[position];
+ if ((curr && curr->info.state == DEAD && curr->info.toggle) ||
+ (curr && curr->info.state == LIVE && !curr->info.toggle)) {
+ for (n = 0; n < lp->neighbors; n++) {
+ neighbor = position_of_neighbor(lp, n, col, row);
+ currn = lp->arr[neighbor];
+ if (!currn) {
+ info.position = neighbor;
+ info.age = 0;
+ info.state = DEAD;
+ info.toggle = False;
+ if (!addto_list(lp, DEAD, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return False;
+ }
+ }
+ }
+ }
+ if (curr && curr->info.state == DEAD && curr->info.toggle) {
+ removefrom_list(lp, DEAD, curr);
+ info.age = 0;
+ info.position = position;
+ info.toggle = False;
+ info.state = LIVE;
+ if (!addto_list(lp, LIVE, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return False;
+ }
+ draw_cell(mi, info);
+ } else if (curr && curr->info.state == LIVE && !curr->info.toggle) {
+ info = curr->info;
+ /* if we aren't up to blue yet, then keep aging the cell. */
+ if ((MI_NPIXELS(mi) > 2) &&
+ (info.age < (unsigned short) (MI_NPIXELS(mi) * 0.7))) {
+ ++(info.age);
+ /* cc: error 1405: "/opt/ansic/lbin/ccom"
+ terminated abnormally with signal 11.
+ *** Error exit code 9 */
+ /* Next 2 line trips up HP cc -g -O, remove a flag */
+ curr->info.age = info.age;
+ draw_cell(mi, info);
+ }
+ }
+ return True;
+}
+
+static Bool
+setcell(ModeInfo * mi, int col, int row, int state)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int neighbor, n, position;
+ cellstruct info;
+ CellList *curr, *currn;
+
+ if (col < 0 || row < 0 || col >= lp->ncols || row >= lp->nrows) {
+#ifdef DEBUG
+ (void) printf("col %d, row %d outside grid\n", col, row);
+#endif
+ return True; /* Actually its a 3rd case */
+ }
+
+ position = row * lp->ncols + col;
+ curr = lp->arr[position];
+ /* cc: error 1405: "/opt/ansic/lbin/ccom"
+ terminated abnormally with signal 11.
+ *** Error exit code 9 */
+ /* Following lines trip up HP cc -g -O, remove a flag */
+ if (state == LIVE) {
+ if (curr && curr->info.state == DEAD) {
+ removefrom_list(lp, DEAD, curr);
+ curr = (CellList *) NULL;
+ }
+ if (!curr) {
+ for (n = 0; n < lp->neighbors; n++) {
+ neighbor = position_of_neighbor(lp, n, col, row);
+ currn = lp->arr[neighbor];
+ if (!currn) {
+ info.age = 0;
+ info.position = neighbor;
+ info.state = DEAD;
+ info.toggle = False;
+ if (!addto_list(lp, DEAD, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return False;
+ }
+ }
+ }
+ info.age = 0;
+ info.position = position;
+ info.state = LIVE;
+ info.toggle = False;
+ if (!addto_list(lp, LIVE, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return False;
+ }
+ draw_cell(mi, info);
+ } else {
+ info = curr->info;
+ info.age = 0;
+ draw_cell(mi, info);
+ }
+ } else if (curr && curr->info.state == LIVE) {
+ info.age = 0;
+ info.position = position;
+ info.state = DEAD;
+ info.toggle = False;
+ removefrom_list(lp, LIVE, curr); /* Just in case... */
+ if (!addto_list(lp, DEAD, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return False;
+ }
+ draw_cell(mi, info);
+ }
+ return True;
+}
+
+#if 0
+static int
+n_neighbors(lifestruct * lp, CellList * curr)
+{
+ int col, row, n, p, count = 0;
+
+ col = curr->info.position % lp->ncols;
+ row = curr->info.position / lp->ncols;
+ for (n = 0; n < lp->neighbors; n++) {
+ p = position_of_neighbor(lp, n, col, row);
+ if (lp->arr[p] && lp->arr[p]->info.state == LIVE) {
+ count++;
+ }
+ }
+ return count;
+}
+#endif
+
+static int ceil2(int z)
+{
+ if (z >= 0)
+ return (z + 1) / 2;
+ else
+ return z / 2;
+}
+
+static int
+ng_neighbors(lifestruct * lp, CellList * curr, int *group)
+{
+ int col, row, n, p, count = 0, gcount = 0;
+
+ col = (int) (curr->info.position % lp->ncols);
+ row = (int) (curr->info.position / lp->ncols);
+ for (n = 0; n < lp->neighbors; n++) {
+ p = position_of_neighbor(lp, n, col, row);
+ gcount <<= 1;
+ if (lp->arr[p] && lp->arr[p]->info.state == LIVE) {
+ count++;
+ gcount++;
+ }
+ }
+ *group = gcount;
+ return count;
+}
+
+static void
+RandomSoup(ModeInfo * mi, int n, int v)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int row, col;
+
+ v /= 2;
+ if (v < 1)
+ v = 1;
+ for (row = lp->nrows / 2 - v; row < lp->nrows / 2 + v; ++row)
+ for (col = lp->ncols / 2 - v; col < lp->ncols / 2 + v; ++col)
+ if (NRAND(100) < n) {
+ SetList(col, row);
+ }
+ (void) strcpy(lp->nameString, "random pattern");
+ if (MI_IS_VERBOSE(mi))
+ (void) fprintf(stdout, "%s\n", lp->nameString);
+}
+
+static void
+GetPattern(ModeInfo * mi, int pattern_rule, int pattern)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int row, col, orient, temp;
+ char *patptr = (char *) NULL;
+#ifdef LIFE_NAMES
+ int pat = 2 * pattern + 1;
+ char * patstrg = (char *) "";
+#else
+ int pat = pattern;
+#endif
+
+ if (filePattern) {
+ patptr = &filePattern[0];
+ } else {
+ switch (lp->neighbors) {
+ case 6:
+ switch (pattern_rule) {
+ case LIFE_6S2b34B2a:
+ patptr = &patterns_6S2b34B2a[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_6S2b34B2a[2 * pattern][0];
+#endif
+
+ break;
+ case LIFE_6S2a2b4aB2a3a4b:
+ patptr = &patterns_6S2a2b4aB2a3a4b[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_6S2a2b4aB2a3a4b[2 * pattern][0];
+#endif
+
+ break;
+ }
+ break;
+ case 8:
+ switch (pattern_rule) {
+ case LIFE_8S23B3:
+ if (pattern < (int) common_8size) {
+ patptr = &patterns_8S23B3_6[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_8S23B3_6[2 * pattern][0];
+#endif
+ } else {
+ patptr = &patterns_8S23B3[pat - DIV * common_8size][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_8S23B3[2 * pattern - 2 * common_8size][0];
+#endif
+ }
+ break;
+ case LIFE_8S23B36:
+ if (pattern < (int) common_8size) {
+ patptr = &patterns_8S23B3_6[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_8S23B3_6[2 * pattern][0];
+#endif
+ } else {
+ patptr = &patterns_8S23B36[pat - DIV * common_8size][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_8S23B36[2 * pattern - 2 * common_8size][0];
+#endif
+ }
+ break;
+ case LIFE_8S34678B3678:
+ patptr = &patterns_8S34678B3678[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_8S34678B3678[2 * pattern][0];
+#endif
+ break;
+ }
+ break;
+ case 12:
+ switch (pattern_rule) {
+ case LIFE_12S34B45:
+ patptr = &patterns_12S34B45[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_12S34B45[2 * pattern][0];
+#endif
+ break;
+ case LIFE_12S45B456:
+ patptr = &patterns_12S45B456[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_12S45B456[2 * pattern][0];
+#endif
+ break;
+ case LIFE_12S23B45:
+ patptr = &patterns_12S23B45[pat][0];
+#ifdef LIFE_NAMES
+ patstrg = &patterns_12S23B45[2 * pattern][0];
+#endif
+ break;
+ }
+ break;
+ }
+#ifdef LIFE_NAMES
+ (void) strcpy(lp->nameString, patstrg);
+#endif
+
+ }
+#ifdef DEBUG
+ orient = 0;
+#else
+ if (lp->neighbors == 4 || lp->neighbors == 8) {
+ orient = NRAND(8);
+ } else {
+ orient = NRAND(12);
+ }
+#endif
+ if (MI_IS_VERBOSE(mi) && !filePattern) {
+#ifdef LIFE_NAMES
+ (void) fprintf(stdout, "%s, ", patstrg);
+#endif
+ (void) fprintf(stdout, "table number %d\n", pattern);
+ }
+ while ((col = *patptr++) != 127) {
+ row = *patptr++;
+ if (lp->neighbors == 6) {
+ if (orient >= 6) {
+ temp = col;
+ col = row;
+ row = temp;
+ }
+ /* (a,b) => (b, b-a) */
+ switch (orient % 6) {
+ case 0:
+ break;
+ case 1:
+ temp = row;
+ row = temp - col;
+ col = temp;
+ break;
+ case 2:
+ temp = -col;
+ col = temp + row;
+ row = temp;
+ break;
+ case 3:
+ col = -col;
+ row = -row;
+ break;
+ case 4:
+ temp = -row;
+ row = temp + col;
+ col = temp;
+ break;
+ case 5:
+ temp = col;
+ col = temp - row;
+ row = temp;
+ break;
+ }
+ } else if (lp->neighbors == 4 || lp->neighbors == 8) {
+ if (orient >= 4) {
+ temp = col;
+ col = row;
+ row = temp;
+ }
+ /* Could have made it symmetrical with hexagons where
+ (a,b) => (-b, a), this should be equivalent */
+ if (orient % 4 >= 2) {
+ row = -row;
+ }
+ if (orient % 2) {
+ col = -col;
+ }
+ } else {
+ if (orient >= 6) {
+ row = -row;
+ }
+ /* (a,b) => (b, b-a) */
+ switch (orient % 6) {
+ case 0:
+ break;
+ case 1:
+ temp = col;
+ col = ceil2(temp + row);
+ row = ceil2(row - temp + 1) - temp;
+ break;
+ case 2:
+ temp = col;
+ col = ceil2(temp - row);
+ row = temp - ceil2(1 - row - temp);
+ break;
+ case 3:
+ col = -col + 1;
+ row = -row;
+ break;
+ case 4:
+ temp = col;
+ col = 1 - ceil2(temp + row);
+ row = temp - ceil2(row - temp + 1);
+ break;
+ case 5:
+ temp = col;
+ col = 1 - ceil2(temp - row);
+ row = ceil2(1 - row - temp) - temp;
+ break;
+ }
+ }
+ col += lp->ncols / 2;
+ if (lp->neighbors == 6) {
+ if (row < 0)
+ col += (lp->nrows / 2 % 2) ? -row / 2 : -(row - 1) / 2;
+ else
+ col += (lp->nrows / 2 % 2) ? -(row + 1) / 2 : -row / 2;
+ }
+ row += lp->nrows / 2;
+ if (lp->neighbors % 3 == 0 && lp->neighbors != 6 &&
+ (lp->nrows / 2 + lp->ncols / 2 + 1) % 2) {
+ row++;
+ }
+ SetList(col, row);
+ }
+}
+
+static void
+shooter(ModeInfo * mi)
+{
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+ int hsp, vsp, hoff = 1, voff = 1, temp;
+
+ /* Generate the glider at the edge of the screen */
+ if (lp->neighbors == 6 && (lp->patterned_rule == LIFE_6S2b34B2a ||
+ lp->patterned_rule == LIFE_6S2a2b4aB2a3a4b)) {
+ int hhex = 0, diagonal;
+
+ diagonal = NRAND(3);
+ if (diagonal) {
+ temp = MIN((lp->nrows + lp->ncols) / 3, 18);
+ temp = NRAND(temp) - temp / 2;
+ /* Take into account it is a 60 degree angle not 45 */
+ if ((lp->ncols + temp) * 1.35 > lp->nrows) {
+ hsp = (int) ((lp->ncols + temp) * 1.35 - lp->nrows) / 2;
+ vsp = 0;
+ } else {
+ hsp = 0;
+ vsp = (int) (lp->nrows - (lp->ncols - temp) * 1.35) / 2;
+ }
+ switch NRAND(4) {
+ case 0: /* Upper left */
+ break;
+ case 1: /* Upper right */
+ hhex = -1;
+ hoff = -1;
+ hsp = lp->ncols - 1 - hsp;
+ break;
+ case 2: /* Lower left */
+ hhex = 1;
+ voff = -1;
+ vsp = lp->nrows - 1 - vsp;
+ break;
+ case 3: /* Lower right */
+ voff = -1;
+ hoff = -1;
+ hsp = lp->ncols - 1 - hsp;
+ vsp = lp->nrows - 1 - vsp;
+ }
+ } else {
+ temp = MIN(lp->nrows / 3, 18);
+ vsp = lp->nrows / 2 + NRAND(temp) - temp / 2;
+ if (LRAND() & 1) {
+ hsp = lp->ncols - 1;
+ hoff = -1;
+ hhex = (vsp % 2) ? 0 : hoff;
+ } else {
+ hsp = 0;
+ hhex = (vsp % 2) ? hoff : 0;
+ }
+ voff = (LRAND() & 1) ? 1 : -1; /* Mirror image */
+ }
+ if (lp->patterned_rule == LIFE_6S2b34B2a) {
+ if (diagonal) {
+ SetList(hsp + hhex, vsp);
+ if (LRAND() & 1) {
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 4 * voff);
+ SetList(hsp + hhex, vsp + 4 * voff);
+ } else { /* Mirror image */
+ SetList(hsp + 3 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ SetList(hsp + 3 * hoff, vsp + 3 * voff);
+ }
+ } else {
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 3 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 4 * hoff, vsp - 1 * voff);
+ SetList(hsp + 3 * hoff + hhex, vsp - 2 * voff);
+ }
+ } else /* if (lp->patterned_rule == LIFE_6S2a2b4aB2a3a4b) */ {
+ if (diagonal) {
+ switch (NRAND(3)) { /* 3 different gliders */
+ case 0:
+ /* No mirror image */
+ SetList(hsp + 2 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 3 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff, vsp + 1 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp + 2 * voff);
+ break;
+ case 1:
+ if (LRAND() & 1) {
+ SetList(hsp + 0 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 4 * hoff, vsp + 1 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ } else {
+ SetList(hsp + 1 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff, vsp + 1 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp + 4 * voff);
+ }
+ break;
+ case 2:
+ if (LRAND() & 1) {
+ SetList(hsp + 1 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 3 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ } else {
+ SetList(hsp + 0 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 3 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ }
+ }
+ } else {
+ switch (NRAND(3)) { /* 3 different gliders */
+ case 0:
+ SetList(hsp + 0 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp - 2 * voff);
+ SetList(hsp + 0 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp - 1 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ break;
+ case 1:
+ SetList(hsp + 0 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp - 2 * voff);
+ SetList(hsp + 2 * hoff, vsp - 1 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 1 * voff);
+ break;
+ case 2:
+ SetList(hsp + 0 * hoff, vsp - 1 * voff);
+ SetList(hsp + 1 * hoff + hhex, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp - 2 * voff);
+ SetList(hsp + 2 * hoff, vsp - 1 * voff);
+ SetList(hsp + 2 * hoff + hhex, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 1 * voff);
+ }
+ }
+ }
+ } else if ((lp->neighbors == 8 && lp->patterned_rule == LIFE_8S23B3) ||
+ (lp->neighbors == 8 && lp->patterned_rule == LIFE_8S23B36)) {
+ if (NRAND(3) != 0) {
+ /* Generate a glider */
+ if (LRAND() & 1) {
+ hsp = (LRAND() & 1) ? 0 : lp->ncols - 1;
+ vsp = NRAND(lp->nrows);
+ } else {
+ vsp = (LRAND() & 1) ? 0 : lp->nrows - 1;
+ hsp = NRAND(lp->ncols);
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ SetList(hsp + 2 * hoff, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ } else {
+ /* Generate a space ship: LWSS, MWSS, HWSS */
+ /* where Light Weight ships are more common */
+ int coord;
+ int SS = NRAND(6);
+
+ SS = (SS < 3) ? 0 : ((SS >= 5) ? 2 : 1);
+ if (LRAND() & 1) {
+ hsp = (LRAND() & 1) ? 0 : lp->ncols - 1;
+ vsp = NRAND(lp->nrows / 2) + lp->nrows / 4;
+ coord = 1;
+ } else {
+ vsp = (LRAND() & 1) ? 0 : lp->nrows - 1;
+ hsp = NRAND(lp->ncols / 2) + lp->ncols / 4;
+ coord = 0;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ if (coord == 1) {
+ SetList(hsp + (SS + 4) * hoff, vsp + 0 * voff);
+ SetList(hsp + (SS + 3) * hoff, vsp + 0 * voff);
+ SetList(hsp + (SS + 2) * hoff, vsp + 0 * voff);
+ SetList(hsp + (SS + 1) * hoff, vsp + 0 * voff);
+ if (SS == 2)
+ SetList(hsp + 2 * hoff, vsp + 0 * voff);
+ if (SS != 0)
+ SetList(hsp + 1 * hoff, vsp + 0 * voff);
+ SetList(hsp + (SS + 4) * hoff, vsp + 1 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + (SS + 4) * hoff, vsp + 2 * voff);
+ SetList(hsp + (SS + 3) * hoff, vsp + 3 * voff);
+ } else {
+ SetList(hsp + 0 * hoff, vsp + (SS + 4) * voff);
+ SetList(hsp + 0 * hoff, vsp + (SS + 3) * voff);
+ SetList(hsp + 0 * hoff, vsp + (SS + 2) * voff);
+ SetList(hsp + 0 * hoff, vsp + (SS + 1) * voff);
+ if (SS == 2)
+ SetList(hsp + 0 * hoff, vsp + 2 * voff);
+ if (SS != 0)
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff, vsp + (SS + 4) * voff);
+ SetList(hsp + 1 * hoff, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + (SS + 4) * voff);
+ SetList(hsp + 3 * hoff, vsp + (SS + 3) * voff);
+ }
+ }
+ } else if (lp->neighbors == 8 && lp->patterned_rule == LIFE_8S34678B3678) {
+ /* Generate a butterfly */
+ if (LRAND() & 1) {
+ hsp = (LRAND() & 1) ? 0 : lp->ncols - 1;
+ vsp = NRAND(lp->nrows);
+ } else {
+ vsp = (LRAND() & 1) ? 0 : lp->nrows - 1;
+ hsp = NRAND(lp->ncols);
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ SetList(hsp + 4 * hoff, vsp + 3 * voff);
+ SetList(hsp + 4 * hoff, vsp + 2 * voff);
+ SetList(hsp + 4 * hoff, vsp + 1 * voff);
+ SetList(hsp + 3 * hoff, vsp + 4 * voff);
+ SetList(hsp + 3 * hoff, vsp + 2 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 3 * hoff, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 4 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff, vsp + 4 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 0 * hoff, vsp + 3 * voff);
+ } else if (lp->neighbors == 12 && lp->patterned_rule == LIFE_12S34B45) {
+ /* no diagonal, be careful parity matters */
+ if (LRAND() & 1) {
+ vsp = NRAND(lp->nrows);
+ if (LRAND() & 1) {
+ hsp = 0;
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ } else {
+ hsp = lp->ncols - 1;
+ if ((hsp + vsp) % 2 == 1)
+ hsp--;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ /* glider (p7, c/7) lower right */
+ SetList(hsp + 1 * hoff, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff, vsp + 1 * voff);
+ SetList(hsp + 4 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff, vsp + 2 * voff);
+ SetList(hsp + 4 * hoff, vsp + 2 * voff);
+ SetList(hsp + 5 * hoff, vsp + 2 * voff);
+ SetList(hsp + 4 * hoff, vsp + 3 * voff);
+ SetList(hsp + 5 * hoff, vsp + 3 * voff);
+ SetList(hsp + 4 * hoff, vsp + 4 * voff);
+ SetList(hsp + 4 * hoff, vsp + 5 * voff);
+ } else {
+ hsp = NRAND(lp->ncols / 2) + lp->ncols / 4;
+ if (LRAND() & 1) {
+ vsp = 0;
+ } else {
+ vsp = lp->nrows - 1;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2) {
+ hoff = -1;
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ } else {
+ if ((hsp + vsp) % 2 == 1)
+ hsp--;
+ }
+ SetList(hsp + 0 * hoff, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff, vsp + 0 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff, vsp + 1 * voff);
+ SetList(hsp + 0 * hoff, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 5 * voff);
+ SetList(hsp + 2 * hoff, vsp + 5 * voff);
+ SetList(hsp + 0 * hoff, vsp + 6 * voff);
+ SetList(hsp + 1 * hoff, vsp + 6 * voff);
+ SetList(hsp + 0 * hoff, vsp + 7 * voff);
+ SetList(hsp + 1 * hoff, vsp + 7 * voff);
+ SetList(hsp + 2 * hoff, vsp + 7 * voff);
+ }
+ } else if (lp->neighbors == 12 && lp->patterned_rule == LIFE_12S45B456) {
+ if (LRAND() & 1) {
+ vsp = NRAND(lp->nrows);
+ if (LRAND() & 1) {
+ hsp = 0;
+ if ((hsp + vsp) % 2 == 1)
+ hsp++;
+ } else {
+ hsp = lp->ncols - 1;
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ SetList(hsp + 1 * hoff, vsp + 1 * voff);
+ SetList(hsp + 1 * hoff, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 0 * hoff, vsp + 4 * voff);
+ SetList(hsp + 1 * hoff, vsp + 4 * voff);
+ SetList(hsp + 2 * hoff, vsp + 4 * voff);
+ SetList(hsp + 1 * hoff, vsp + 5 * voff);
+ SetList(hsp + 2 * hoff, vsp + 5 * voff);
+ } else {
+ /* glider (c/4, p8) flutters down */
+ hsp = NRAND(lp->ncols / 2) + lp->ncols / 4;
+ if (LRAND() & 1) {
+ vsp = 0;
+ } else {
+ vsp = lp->nrows - 1;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2) {
+ hoff = -1;
+ if ((hsp + vsp) % 2 == 1)
+ hsp--;
+ } else {
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ }
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + 0 * hoff, vsp + 2 * voff);
+ SetList(hsp + 1 * hoff, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff, vsp + 2 * voff);
+ SetList(hsp + 0 * hoff, vsp + 3 * voff);
+ SetList(hsp + 1 * hoff, vsp + 3 * voff);
+ SetList(hsp + 0 * hoff, vsp + 4 * voff);
+ SetList(hsp + 0 * hoff, vsp + 5 * voff);
+ SetList(hsp + 1 * hoff, vsp + 5 * voff);
+ }
+ } else if (lp->neighbors == 12 && lp->patterned_rule == LIFE_12S23B45) {
+ if (LRAND() & 1) {
+ vsp = NRAND(lp->nrows);
+ if (LRAND() & 1) {
+ hsp = 0;
+ if ((hsp + vsp) % 2 == 1)
+ hsp++;
+ } else {
+ hsp = lp->ncols - 1;
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2)
+ hoff = -1;
+ SetList(hsp + 0 * hoff, vsp + 0 * voff);
+ SetList(hsp + 0 * hoff, vsp + 1 * voff);
+ SetList(hsp + 3 * hoff, vsp + 1 * voff);
+ SetList(hsp + 2 * hoff, vsp + 2 * voff);
+ SetList(hsp + 3 * hoff, vsp + 2 * voff);
+ SetList(hsp + 2 * hoff, vsp + 3 * voff);
+ SetList(hsp + 3 * hoff, vsp + 3 * voff);
+ SetList(hsp + 2 * hoff, vsp + 4 * voff);
+ SetList(hsp + 3 * hoff, vsp + 4 * voff);
+ SetList(hsp + 2 * hoff, vsp + 5 * voff);
+ } else {
+ /* glider (c/4, p8) flutters down */
+ hsp = NRAND(lp->ncols / 2) + lp->ncols / 4;
+ if (LRAND() & 1) {
+ vsp = 0;
+ } else {
+ vsp = lp->nrows - 1;
+ }
+ if (vsp > lp->nrows / 2)
+ voff = -1;
+ if (hsp > lp->ncols / 2) {
+ hoff = -1;
+ if ((hsp + vsp) % 2 == 1)
+ hsp--;
+ } else {
+ if ((hsp + vsp) % 2 == 0)
+ hsp--;
+ }
+ SetList(hsp + 1 * hoff, vsp + 0 * voff);
+ SetList(hsp + 2 * hoff, vsp + 0 * voff);
+ SetList(hsp + 1 * hoff, vsp + 4 * voff);
+ SetList(hsp + 2 * hoff, vsp + 4 * voff);
+ SetList(hsp + 0 * hoff, vsp + 5 * voff);
+ SetList(hsp + 1 * hoff, vsp + 5 * voff);
+ SetList(hsp + 2 * hoff, vsp + 5 * voff);
+ SetList(hsp + 3 * hoff, vsp + 5 * voff);
+ SetList(hsp + 1 * hoff, vsp + 6 * voff);
+ SetList(hsp + 2 * hoff, vsp + 6 * voff);
+ }
+ }
+}
+
+static Bool
+init_stuff(ModeInfo * mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ lifestruct *lp = &lifes[MI_SCREEN(mi)];
+
+ if (lp->logo == NULL) {
+ getImage(mi, &lp->logo, CELL_WIDTH, CELL_HEIGHT, CELL_BITS,
+#ifdef HAVE_XPM
+ DEFAULT_XPM, CELL_NAME,
+#endif
+ &lp->graphics_format, &lp->cmap, &lp->black);
+ if (lp->logo == NULL) {
+ free_life(display, lp);
+ return False;
+ }
+#ifdef XBM_GRELB
+ if (lp->cmap == None && lp->graphics_format == IS_XBM) {
+ /* probably do not need the first but I am cautious... */
+ if (!bimage.data) { /* Only need to do this once */
+ bimage.data = (char *) CELL2_BITS;
+ bimage.width = CELL2_WIDTH;
+ bimage.height = CELL2_HEIGHT;
+ bimage.bytes_per_line = (CELL2_WIDTH + 7) / 8;
+ }
+ lp->logo2 = &bimage;
+ }
+#endif
+ }
+#ifndef STANDALONE
+ if (lp->cmap != None) {
+ setColormap(display, window, lp->cmap, MI_IS_INWINDOW(mi));
+ if (lp->backGC == None) {
+ XGCValues xgcv;
+
+ xgcv.background = lp->black;
+ xgcv.foreground = lp->black;
+ if ((lp->backGC = XCreateGC(display, window,
+ GCForeground | GCBackground,
+ &xgcv)) == None) {
+ free_life(display, lp);
+ return False;
+ }
+ }
+ } else
+#endif /* STANDALONE */
+ {
+ lp->black = MI_BLACK_PIXEL(mi);
+ lp->backGC = MI_GC(mi);
+ }
+ return True;
+}
+
+void
+init_life(ModeInfo * mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ int size = MI_SIZE(mi), npats, i;
+ lifestruct *lp;
+
+ if (lifes == NULL) {
+ if ((lifes = (lifestruct *) calloc(MI_NUM_SCREENS(mi),
+ sizeof (lifestruct))) == NULL)
+ return;
+ }
+ lp = &lifes[MI_SCREEN(mi)];
+
+ lp->generation = 0;
+ lp->redrawing = 0;
+
+ if (MI_IS_FULLRANDOM(mi)) {
+ int r6n1 = patterns_6rules[0] + patterns_6rules[1];
+ int r8n1 = patterns_8rules[0] + patterns_8rules[1];
+ int r8n2 = r8n1 + patterns_8rules[2];
+ int r12n1 = patterns_12rules[0] + patterns_12rules[1];
+ int r12n2 = r12n1 + patterns_12rules[2];
+#if 1
+ lp->neighbors = (NRAND(r8n2 + r6n1 + r12n2) < r8n2) ? 8 :
+ (NRAND(r6n1 + r12n2) < r6n1) ? 6 : 12;
+#else
+ lp->neighbors = 8;
+#endif
+ if (lp->neighbors == 8) {
+ int n = NRAND(r8n2);
+
+ lp->conway = (n < patterns_8rules[0]);
+ lp->highlife = (n >= patterns_8rules[0] && n < r8n1);
+ lp->daynight = (n >= r8n1);
+ } else {
+ lp->conway = lp->highlife = lp->daynight = False;
+ }
+ if (lp->neighbors == 6) {
+ lp->callahan = (NRAND(r6n1) < patterns_6rules[0]);
+ lp->andreen = !lp->callahan;
+ } else {
+ lp->andreen = lp->callahan = False;
+ }
+ if (lp->neighbors == 12) {
+ int n = NRAND(r12n2);
+
+ lp->trilife = (n < patterns_12rules[0]);
+ lp->trilife1 = (n >= patterns_12rules[0] && n < r12n1);
+ lp->trilife2 = (n >= r12n1);
+ } else {
+ lp->trilife = lp->trilife1 = lp->trilife2 = False;
+ }
+ } else {
+ lp->conway = conway;
+ lp->highlife = highlife;
+ lp->daynight = daynight;
+ lp->callahan = callahan;
+ lp->andreen = andreen;
+ lp->trilife = trilife;
+ lp->trilife1 = trilife1;
+ lp->trilife2 = trilife2;
+ }
+ if (!lp->neighbors) {
+ for (i = 0; i < NEIGHBORKINDS; i++) {
+ if (neighbors == plots[i]) {
+ lp->neighbors = neighbors;
+ break;
+ }
+ if (i == NEIGHBORKINDS - 1) {
+#if 0
+ lp->neighbors = plots[NRAND(NEIGHBORKINDS)];
+ lp->neighbors = (LRAND() & 1) ? 4 : 8;
+#else
+ lp->neighbors = 8;
+#endif
+ break;
+ }
+ }
+ }
+ lp->labelOffsetX = NRAND(8);
+ lp->labelOffsetY = NRAND(8);
+ parseRule(mi, lp->ruleString);
+ parseFile(mi);
+ if (lp->allPatterns) {
+ switch (lp->neighbors) {
+ case 6:
+ lp->patterned_rule = NRAND(LIFE_6RULES);
+ break;
+ case 8:
+ lp->patterned_rule = NRAND(LIFE_8RULES);
+ break;
+ case 12:
+ lp->patterned_rule = NRAND(LIFE_12RULES);
+ break;
+ }
+ copyFromPatternedRule(lp->neighbors, &lp->param, lp->patterned_rule);
+ printRule(lp->neighbors, lp->ruleString, lp->param,
+ MI_IS_VERBOSE(mi));
+ } else if (lp->allGliders) {
+ switch (lp->neighbors) {
+ case 6:
+ lp->patterned_rule = NRAND(LIFE_6GLIDERS);
+ break;
+ case 8:
+ lp->patterned_rule = NRAND(LIFE_8GLIDERS);
+ break;
+ case 12:
+ lp->patterned_rule = NRAND(LIFE_12GLIDERS);
+ break;
+ }
+ copyFromPatternedRule(lp->neighbors, &lp->param, lp->patterned_rule);
+ printRule(lp->neighbors, lp->ruleString, lp->param,
+ MI_IS_VERBOSE(mi));
+ } else {
+ lp->param.survival = lp->input_param.survival;
+ lp->param.birth = lp->input_param.birth;
+ for (i = 0; i < maxgroups[invplot(lp->neighbors)]; i++) {
+ lp->param.survival_group[i] = lp->input_param.survival_group[i];
+ lp->param.birth_group[i] = lp->input_param.birth_group[i];
+ }
+ lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
+ printRule(lp->neighbors, lp->ruleString, lp->param,
+ MI_IS_VERBOSE(mi));
+ }
+ lp->width = MI_WIDTH(mi);
+ lp->height = MI_HEIGHT(mi);
+
+ if (lp->first[0]) {
+ for (i = 0; i < STATES; i++)
+ flush_list(lp, i);
+ } else {
+ for (i = 0; i < STATES; i++)
+ if (!init_list(lp, i)) {
+ free_life(display, lp);
+ return;
+ }
+ }
+ free_cells(lp);
+ free_stuff(display, lp);
+
+ if (lp->neighbors == 6) {
+ int nccols, ncrows, sides;
+
+ if (lp->width < 2)
+ lp->width = 2;
+ if (lp->height < 4)
+ lp->height = 4;
+ if (size < -MINSIZE)
+ lp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
+ else if (size < MINSIZE) {
+ if (!size) {
+ int min = MIN(lp->width, lp->height) / (4 * MINGRIDSIZE);
+ int max = MIN(lp->width, lp->height) / (2 * MINGRIDSIZE);
+
+ lp->ys = MAX(MINSIZE, min + NRAND(max - min + 1));
+ } else
+ lp->ys = MINSIZE;
+ } else
+ lp->ys = MIN(size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE));
+ lp->xs = lp->ys;
+ nccols = MAX(lp->width / lp->xs - 2, 2);
+ ncrows = MAX(lp->height / lp->ys - 1, 2);
+ lp->ncols = nccols / 2;
+ lp->nrows = 2 * (ncrows / 4);
+ lp->xb = (lp->width - lp->xs * nccols) / 2 + lp->xs / 2;
+ lp->yb = (lp->height - lp->ys * (ncrows / 2) * 2) / 2 + lp->ys - 2;
+ for (sides = 0; sides < 6; sides++) {
+ lp->shape.hexagon[sides].x = (lp->xs - 1) * hexagonUnit[sides].x;
+ lp->shape.hexagon[sides].y =
+ ((lp->ys - 1) * hexagonUnit[sides].y / 2) * 4 / 3;
+ }
+ lp->black = MI_BLACK_PIXEL(mi);
+ lp->backGC = MI_GC(mi);
+ } else if (lp->neighbors == 4 || lp->neighbors == 8) {
+ if (!init_stuff(mi))
+ return;
+ if (lp->width < 2)
+ lp->width = 2;
+ if (lp->height < 2)
+ lp->height = 2;
+#if 0
+ if (size == 0 && !MI_IS_ICONIC(mi)) {
+ lp->pixelmode = False;
+ lp->xs = lp->logo->width;
+ lp->ys = lp->logo->height;
+ }
+#else
+ if (size == 0 ||
+ MINGRIDSIZE * size > lp->width || MINGRIDSIZE * size > lp->height) {
+ if (lp->width > MINGRIDSIZE * lp->logo->width &&
+ lp->height > MINGRIDSIZE * lp->logo->height) {
+ lp->pixelmode = False;
+ lp->xs = lp->logo->width;
+ lp->ys = lp->logo->height;
+ } else
+ {
+ int min = MIN(lp->width, lp->height) / (8 * MINGRIDSIZE);
+ int max = MIN(lp->width, lp->height) / (2 * MINGRIDSIZE);
+
+ lp->xs = lp->ys = MAX(MINSIZE, min + NRAND(max - min + 1));
+ lp->pixelmode = True;
+ }
+ }
+ else
+#endif
+ {
+ lp->pixelmode = True;
+ if (size < -MINSIZE)
+ lp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
+ else if (size < MINSIZE)
+ lp->ys = MINSIZE;
+ else
+ lp->ys = MIN(size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE));
+ lp->xs = lp->ys;
+ }
+ lp->ncols = MAX(lp->width / lp->xs, 4);
+ lp->nrows = MAX(lp->height / lp->ys, 4);
+ lp->xb = (lp->width - lp->xs * lp->ncols) / 2;
+ lp->yb = (lp->height - lp->ys * lp->nrows) / 2;
+ } else { /* TRI */
+ int orient, sides;
+
+ lp->black = MI_BLACK_PIXEL(mi);
+ lp->backGC = MI_GC(mi);
+ if (lp->width < 2)
+ lp->width = 2;
+ if (lp->height < 2)
+ lp->height = 2;
+ if (size < -MINSIZE)
+ lp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
+ else if (size < MINSIZE) {
+ if (!size) {
+ int min = MIN(lp->width, lp->height) / (4 * MINGRIDSIZE);
+ int max = MIN(lp->width, lp->height) / (MINGRIDSIZE);
+
+ lp->xs = lp->ys = MAX(MINSIZE, min + NRAND(max - min + 1));
+ } else
+ lp->ys = MINSIZE;
+ } else
+ lp->ys = MIN(size, MAX(MINSIZE, MIN(lp->width, lp->height) /
+ MINGRIDSIZE));
+ lp->xs = (int) (1.52 * lp->ys);
+ lp->ncols = (MAX(lp->width / lp->xs - 1, 2) / 2) * 2;
+ lp->nrows = (MAX(lp->height / lp->ys - 1, 2) / 2) * 2;
+ lp->xb = (lp->width - lp->xs * lp->ncols) / 2 + lp->xs / 2;
+ lp->yb = (lp->height - lp->ys * lp->nrows) / 2 + lp->ys / 2;
+ for (orient = 0; orient < 2; orient++) {
+ for (sides = 0; sides < 3; sides++) {
+ lp->shape.triangle[orient][sides].x =
+ (lp->xs - 2) * triangleUnit[orient][sides].x;
+ lp->shape.triangle[orient][sides].y =
+ (lp->ys - 2) * triangleUnit[orient][sides].y;
+ }
+ }
+ }
+ lp->npositions = lp->nrows * lp->ncols;
+
+ MI_CLEARWINDOWCOLORMAP(mi, lp->backGC, lp->black);
+ lp->painted = False;
+ if ((lp->arr = (CellList **) calloc(lp->npositions,
+ sizeof (CellList *))) == NULL) {
+ free_life(display, lp);
+ return;
+ }
+
+ lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
+ npats = 0;
+ switch (lp->neighbors) {
+ case 6:
+ if ((unsigned) lp->patterned_rule < LIFE_6RULES)
+ npats = patterns_6rules[lp->patterned_rule];
+ break;
+ case 8:
+ if ((unsigned) lp->patterned_rule < LIFE_8RULES)
+ npats = patterns_8rules[lp->patterned_rule];
+ break;
+ case 12:
+ if ((unsigned) lp->patterned_rule < LIFE_12RULES)
+ npats = patterns_12rules[lp->patterned_rule];
+ break;
+ }
+ lp->pattern = NRAND(npats + 2);
+ if (lp->pattern >= npats && !filePattern)
+ RandomSoup(mi, 30, MAX(2 * MIN(lp->nrows, lp->ncols) / 3, 15));
+ else
+ GetPattern(mi, lp->patterned_rule, lp->pattern);
+}
+
+void
+draw_life(ModeInfo * mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ GC gc = MI_GC(mi);
+ CellList *middle[STATES]; /* To distinguish between old and new stuff */
+ CellList *curr;
+ cellstruct info;
+ int i, count, gcount, neighbor_kind;
+ Bool visible = False;
+ lifestruct *lp;
+
+ if (lifes == NULL)
+ return;
+ lp = &lifes[MI_SCREEN(mi)];
+
+/*-
+ * LIVE list are the on cells
+ * DEAD list are the cells that may go on in the next iteration.
+ * Init plan:
+ Create live list and dead list which border all live cells
+ (no good for rules like 0000 :) )
+ * Big loop plan:
+ Setup toggles, toggle state next iteration?
+ Remove all from dead list except toggled and remove all from live list
+ that are dead (but in this case draw background square)
+ Toggle toggled states, age existing ones, create a new dead list, draw
+ */
+
+ /* Go through dead list to see if anything spawns (generate new lists),
+ then delete the used dead list */
+
+ MI_IS_DRAWN(mi) = True;
+
+ /* Setup toggles */
+ curr = lp->first[DEAD]->next;
+ while (curr != lp->last[DEAD]) {
+ count = ng_neighbors(lp, curr, &gcount);
+ if ((lp->param.birth & (1 << count)) || (count >= FIRSTGROUP &&
+ count < FIRSTGROUP + maxgroups[invplot(lp->neighbors)] &&
+ (lp->param.birth_group[count - FIRSTGROUP] &
+ (1 << style6[gcount])))) {
+ setcelltoggles(mi, (int) (curr->info.position % lp->ncols),
+ (int) (curr->info.position / lp->ncols));
+ visible = True;
+ }
+ curr = curr->next;
+ }
+ curr = lp->first[LIVE]->next;
+ neighbor_kind = invplot(lp->neighbors);
+ while (curr != lp->last[LIVE]) {
+ count = ng_neighbors(lp, curr, &gcount);
+ if (!((lp->param.survival & (1 << count)) || (count >= FIRSTGROUP &&
+ count < FIRSTGROUP + maxgroups[neighbor_kind] &&
+ (lp->param.survival_group[count - FIRSTGROUP] &
+ (1 << style6[gcount]))))) {
+ setcelltoggles(mi, (int) (curr->info.position % lp->ncols),
+ (int) (curr->info.position / lp->ncols));
+ visible = True;
+ }
+ curr = curr->next;
+ }
+
+ /* Bring out your dead! */
+ curr = lp->first[DEAD]->next;
+ while (curr != lp->last[DEAD]) {
+ curr = curr->next;
+ if (!curr->previous->info.toggle)
+ removefrom_list(lp, DEAD, curr->previous);
+ }
+ curr = lp->first[LIVE]->next;
+ while (curr != lp->last[LIVE]) {
+ if (curr->info.toggle) {
+ curr->info.state = DEAD;
+ draw_cell(mi, curr->info);
+ curr = curr->next;
+ removefrom_list(lp, LIVE, curr->previous);
+ } else
+ curr = curr->next;
+ }
+
+ /* Fence off the babies */
+ info.position = -1; /* dummy value */
+ info.age = 0; /* dummy value */
+ info.state = 0; /* dummy value */
+ info.toggle = 0; /* dummy value */
+ if (!addto_list(lp, DEAD, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return;
+ }
+ if (!addto_list(lp, LIVE, info)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return;
+ }
+ middle[DEAD] = lp->last[DEAD]->previous;
+ middle[LIVE] = lp->last[LIVE]->previous;
+
+ /* Toggle toggled states, age existing ones, create a new dead list */
+ while (lp->first[DEAD]->next != middle[DEAD]) {
+ curr = lp->first[DEAD]->next;
+ if (!setcellfromtoggle(mi, (int) (curr->info.position % lp->ncols),
+ (int) (curr->info.position / lp->ncols)))
+ return;
+ }
+ curr = lp->first[LIVE]->next;
+ while (curr != middle[LIVE]) {
+ if (!setcellfromtoggle(mi, (int) (curr->info.position % lp->ncols),
+ (int) (curr->info.position / lp->ncols)))
+ return;
+ curr = curr->next;
+ }
+ removefrom_list(lp, DEAD, middle[DEAD]);
+ removefrom_list(lp, LIVE, middle[LIVE]);
+
+ if (lp->redrawing) {
+ for (i = 0; i < REDRAWSTEP; i++) {
+ CellList *redraw_curr = lp->arr[lp->redrawpos];
+
+ /* TODO: More efficient to use list rather than array. */
+ if (redraw_curr && redraw_curr->info.state == LIVE) {
+ draw_cell(mi, redraw_curr->info);
+ }
+ if (++(lp->redrawpos) >= lp->npositions) {
+ lp->redrawing = 0;
+ break;
+ }
+ }
+ }
+ if (visible)
+ lp->noChangeCount = 0;
+ else
+ lp->noChangeCount++;
+ if (++lp->generation > MI_CYCLES(mi) || lp->noChangeCount >= 8)
+ init_life(mi);
+ else
+ lp->painted = True;
+
+ /*
+ * generate a randomized shooter aimed roughly toward the center of the
+ * screen after batchcount.
+ */
+
+ if (MI_COUNT(mi)) {
+ if (lp->generation && lp->generation %
+ ((MI_COUNT(mi) < 0) ? 1 : MI_COUNT(mi)) == 0)
+ shooter(mi);
+ }
+ 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(display, gc, MI_WHITE_PIXEL(mi));
+ XDrawString(display, window, gc,
+ 16 + lp->labelOffsetX,
+ 16 + lp->labelOffsetY + FONT_HEIGHT,
+ lp->ruleString, strlen(lp->ruleString));
+ XDrawString(display, window, gc,
+ 16 + lp->labelOffsetX, MI_HEIGHT(mi) - 16 -
+ lp->labelOffsetY - FONT_HEIGHT / 2,
+ lp->nameString, strlen(lp->nameString));
+ }
+ }
+}
+
+void
+release_life(ModeInfo * mi)
+{
+ if (lifes != NULL) {
+ int screen;
+
+ for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
+ free_life(MI_DISPLAY(mi), &lifes[screen]);
+ free(lifes);
+ lifes = (lifestruct *) NULL;
+ }
+}
+
+void
+refresh_life(ModeInfo * mi)
+{
+ lifestruct *lp;
+
+ if (lifes == NULL)
+ return;
+ lp = &lifes[MI_SCREEN(mi)];
+
+#ifdef HAVE_XPM
+ if (lp->graphics_format >= IS_XPM) {
+ /* This is needed when another program changes the colormap. */
+ free_life(MI_DISPLAY(mi), lp);
+ init_life(mi);
+ return;
+ }
+#endif
+ if (lp->painted) {
+ MI_CLEARWINDOWCOLORMAP(mi, lp->backGC, lp->black);
+ lp->redrawing = 1;
+ lp->redrawpos = 0;
+ lp->painted = False;
+ }
+}
+
+void
+change_life(ModeInfo * mi)
+{
+ int npats, i;
+ lifestruct *lp;
+
+ if (lifes == NULL)
+ return;
+ lp = &lifes[MI_SCREEN(mi)];
+
+ lp->generation = 0;
+ if (lp->first[0]) {
+ for (i = 0; i < STATES; i++)
+ flush_list(lp, i);
+ } else {
+ for (i = 0; i < STATES; i++)
+ if (!init_list(lp, i)) {
+ free_life(MI_DISPLAY(mi), lp);
+ return;
+ }
+ }
+ free_cells(lp);
+ if ((lp->arr = (CellList **) calloc(lp->npositions,
+ sizeof (CellList *))) == NULL) {
+ free_life(MI_DISPLAY(mi), lp);
+ return;
+ }
+
+ MI_CLEARWINDOWCOLORMAP(mi, lp->backGC, lp->black);
+
+ lp->pattern++;
+ lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
+ npats = 0;
+ switch (lp->neighbors) {
+ case 6:
+ if ((unsigned) lp->patterned_rule < LIFE_6RULES)
+ npats = patterns_6rules[lp->patterned_rule];
+ break;
+ case 8:
+ if ((unsigned) lp->patterned_rule < LIFE_8RULES)
+ npats = patterns_8rules[lp->patterned_rule];
+ break;
+ case 12:
+ if ((unsigned) lp->patterned_rule < LIFE_12RULES)
+ npats = patterns_12rules[lp->patterned_rule];
+ break;
+ }
+ if (lp->pattern >= npats + 2) {
+ lp->pattern = 0;
+ if (lp->allPatterns) {
+ lp->patterned_rule++;
+ switch (lp->neighbors) {
+ case 6:
+ if ((unsigned) lp->patterned_rule >= LIFE_6RULES)
+ lp->patterned_rule = 0;
+ break;
+ case 8:
+ if ((unsigned) lp->patterned_rule >= LIFE_8RULES)
+ lp->patterned_rule = 0;
+ break;
+ case 12:
+ if ((unsigned) lp->patterned_rule >= LIFE_12RULES)
+ lp->patterned_rule = 0;
+ break;
+ }
+ copyFromPatternedRule(lp->neighbors, &lp->param,
+ lp->patterned_rule);
+ printRule(lp->neighbors, lp->ruleString, lp->param,
+ MI_IS_VERBOSE(mi));
+ } else if (lp->allGliders) {
+ lp->patterned_rule++;
+ switch (lp->neighbors) {
+ case 6:
+ if ((unsigned) lp->patterned_rule >= LIFE_6GLIDERS)
+ lp->patterned_rule = 0;
+ break;
+ case 8:
+ if ((unsigned) lp->patterned_rule >= LIFE_8GLIDERS)
+ lp->patterned_rule = 0;
+ break;
+ case 12:
+ if ((unsigned) lp->patterned_rule >= LIFE_12GLIDERS)
+ lp->patterned_rule = 0;
+ break;
+ }
+ copyFromPatternedRule(lp->neighbors, &lp->param, lp->patterned_rule);
+ printRule(lp->neighbors, lp->ruleString, lp->param,
+ MI_IS_VERBOSE(mi));
+ }
+ }
+ if (!serial)
+ lp->pattern = NRAND(npats + 2);
+ if (lp->pattern >= npats)
+ RandomSoup(mi, 30, MAX(2 * MIN(lp->nrows, lp->ncols) / 3, 15));
+ else
+ GetPattern(mi, lp->patterned_rule, lp->pattern);
+}
+
+#endif /* MODE_life */