diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2006-11-26 11:09:41 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2006-11-26 11:09:41 +0000 |
commit | 95c2d1cbda23a41cdf6e63520c7f0b825e63dd5b (patch) | |
tree | 06d3ffa4312e568c4157f69fe1bddaddec9bc497 /app/xlockmore/modes/life.c | |
parent | 3928433848e2d6a9356f3d438a14b32a4f87f660 (diff) |
Importing xlockmore 5.22
Diffstat (limited to 'app/xlockmore/modes/life.c')
-rw-r--r-- | app/xlockmore/modes/life.c | 2682 |
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 */ |