2683 lines
77 KiB
C
2683 lines
77 KiB
C
/* -*- 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 */
|