Catalyst improvements WIP

For scripts to aid with computation or simulation in cellular automata.
User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Catalyst improvements WIP

Post by simsim314 » May 6th, 2014, 5:25 pm

Following this: viewtopic.php?p=11858#p11858 I'm starting to work on improving the catalyst search utility by Gabriel Nivasch (and improved catgl by dvgrn).

Here is the road-map (I hope to keep it updated):

C code changes:


1. Nice to have: introduce new input parameter "generation step". That means the next catalyst is happening 0-20 ticks after the previous catalyst activated. This will limit the search into more "reasonable" and compact "linear action" sequence, that will allow fast search of 5-6 such limited catalyst.

Python script: Latest: viewtopic.php?p=11942#p11942

External analysis scripts:

1 Search for multiplication. Send the input signal multiple times and check if the initial pattern will reset.
2 Mark what should be alive and what should be dead at the "end", and report it.

In 1-2 Report the successes by placing the interesting patterns, in nearby column and focus on it.

3. Evaluation function, should "reorder" the most potential patterns first. Include the following factors:
3.1 The distance from initial block where catalyst can be placed
3.2 The "action" is close to the initial pattern.
3.3 No influence catalyst should be removed.
3.4 Similiar results catalysts should be "packed".

4. A simple reorganization tool. Delete + Move up + Move down (that can be applied on any generation, and will be changed in generation 0 automatically), inside the pile of results that come from patgl.py or from other analyzing scripts. Also it would be nice to "merge" piles.

Catgl.py improvements:

1. Send a pile of patterns (that got from the report) to catgl. Have the ability to define startY and endY (and X of the pile).

2. Have the ability to add the reactions.rle results, without harming the current open file. Just place all the result to the right of the right most pile.

3. Crush safe mechanism. Save "current state" of search into some "backup" file with option to continue from point where it crashed. Save the patterns into backup folder, before running the search.

-------------

You'll need GCC compiler to compile it. But except of gcc, this is out of the box code. compile with GCC (by far the simplest and fastest c compiler to install, figure how to use, and the executable can be made really fast). If someone has question about gcc you're welcome to ask as well. Here is some HOWTO on this ropic:
http://www.mingw.org/wiki/HOWTO


--------------------------

Realease hirstory:

V1.0
1. Catalist imported into single c file.
2. RLE output file added.


V1.0.1
1. catgl by dvgrn is now integrated.
2. The RLE has all initial patterns on the same x, while y of the initial pattern has steps of 50 + SZ (120 for current setup).

V1.0.2

1. Input parameters are now defined in setup.txt
2. Ouput files are now defined in setup
3. maxfirstgen added to setup, to allow parallel search. It defines the maximal allowed generation for the first catalyst to be place.

V1.0.2

Changes between catgl 1.0.3 and catgl 1.0.2:
- New inpit states added B,C,D.
- B sets history
- C same as '*' - always alive.
- D same as ',' - always dead.

Latest:

Code: Select all

/* catgl v1.0.3 released 5/13/2014 by Mciahel Simkin

Based on catgl 5/29/2001 by Dave Greene
Based on catalyst v1.0, released 3/21/2001
   Copyright (c) 2001, Gabriel Nivasch

Changes between catalyst 1.0 and catgl 1.0:

   - all source code combined into a single catgl.cpp file
   - added '1', '0', '+', '-' target options to input (see readme.txt)
   - glider-catcher code avoids rejecting glider-producing reactions
   
Changes between catgl 1.0.1 and catgl 1.0:
   - GCC compatible C code.
   - reactions.rle added output in rle format. 

Changes between catgl 1.0.2 and catgl 1.0.1:
   - Input parameters are now defined in setup.txt
   - Ouput files are now defined in setup
   - maxfirstgen added to setup, to allow parallel search. 
     It defines the maximal allowed generation for the first catalyst to be place.

Changes between catgl 1.0.3 and catgl 1.0.2:
    - New inpit states added B,C,D.
	- B sets history 
	- C same as '*' - always alive. 
	- D same as ',' - always dead. 
	
setup.txt sample: 

reactions.txt
reactions.rle
AAA
..A
AAA!
1
4
6
0
startinggen 1
gens 60
catsurvive 100
catstoplace 2
maxfirstgen 10
  
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SZ 180
#define MAXGENS 250
#define MAXCATS 10

#define NCATS 7
#define CATSZ 15

typedef struct
{
    int x, y;
} cell;

typedef struct
{
    char c[CATSZ][CATSZ];
    int x, y, name;
} catalyst;



/* The following array contains the state of the pattern at each
generation from 0 to the current one. For each generation there are two
arrays: now[SZ][SZ] and history[SZ][SZ]. now[][] contains the state of
the pattern at that generation. The format is as follows:
If the cell is alive, then bit #9 (LiveBit) is set.
In addition, exactly one of the bits #0 to #8 is set, indicating the
number of live neighbors the cell has.

history[][] contains the superposition (inclusive "OR") of all the
states each cell has been in, in all the previous generations.

The purpose of history[][] is to know whether the cell has ever been alive,
and if it was never alive, to know all the different neighbor counts it
has had. If bit #9 is set, it means the cell has been alive at some
point, and each of the bits #0 to #8 that is set indicates a neighbor
count the cell has had at some point.

For each generation there are also four variables indicating the current
boundaries of the pattern, the variable numcats, which indicates how
many catalysts have been placed this generation, and the variable numgliders,
which is the total number of gliders created as of this generation.
*/
struct board
{
   int now[SZ][SZ];
   int history[SZ][SZ];
   int xmin, xmax, ymin, ymax;
   int numcats, numgliders;
} stack[MAXGENS];

//Bit values used in now[][] and history[][]:
#define LiveBit 512
#define CountBitMask 511

//This macro function is used in advance1gen():
#define WillLive(x) ((x)==8 || (x)==516 || (x)==520)

/* The following array is used to print out the patterns created.
At the beginning the initial pattern is set in it. In addition, each
time a catalyst is placed on the board at a certain generation, it is
also placed here. When the catalyst is removed from the board, it is
also removed from here.
*/
int boardgen0[SZ][SZ];
int target[SZ][SZ];
int targetgen0[SZ][SZ];
int g0minx=SZ, g0miny=SZ, g0maxx=0, g0maxy=0;

/* This array indicates which cells are fixed, i.e. if they are dead,
they are not allowed to live, and if they are alive, they are not
allowed to die. There are two sources from which fixed cells can come:
From the catalysts (cells marked 'x', 'X', and '*'), or from the input
pattern of the user. Note that cells that were set fixed dead by the
user or by a catalyst, might be set again as fixed dead by another catalyst.
Then, when the second catalyst is removed, the cell should still remain fixed
dead because of the first catalyst or the user's input. Therefore, the
procedure is as follows:
To set a cell as fixed, add 1. To unset it, substract 1. The cell is free
only if the value is 0. If the value is less than 0, it is an error.
*/
int fixed[SZ][SZ];

/* The following array contains info for each catalyst that has
been placed on the board: position, catalyst number, orientation,
generation placed, and the old values of the pattern boundaries, which
are restored when the catalyst is removed.
*/
struct catposition
{
   int x, y, n, orient, gen;
   int oldxmin, oldxmax, oldymin, oldymax; //Used to restore the values
      //after removing the catalyst.
   int g0oldminx, g0oldminy, g0oldmaxx, g0oldmaxy;
      //Used to restore g0minx, etc., after removing the catalyst.
} catplaced[MAXCATS];

struct cell
{ int x, y;};

struct catalyst
{
   char c[CATSZ][CATSZ];
   int x, y, name;
};

const catalyst cata[NCATS]=
{
   {{{' ', ' ', 'x', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', '*', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '$', '%', '*', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     6, 6, 'eat1'},
   {{{' ', ' ', ' ', '.', '.', 'X', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', '$', '*', '$', 'o', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', '*', '*', '$', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '*', '%', '^', '$', '$', '$', '$', 'X', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', '*', '*', '$', '*', '*', '.', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', '%', '*', '^', '*', '$', '.', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ':', '*', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', '.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', '.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     9, 9, 'eat2'},
   {{{'x', '.', ':', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'2', '*', '$', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'X', '*', '*', '$', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'x', ':', ':', ':', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     4, 6, 'snak'},
   {{{'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     4, 4, 'bloc'},
   {{{' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ':', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ':', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', ':', 'o', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', '*', '$', 'o', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '$', '$', '*', 'X', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     11, 8, 'tubw'},
   {{{' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'o', '$', '*', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     5, 5, '_tub'},
   {{{' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'x', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'o', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'X', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     5, 5, 'boat'}
};

int catisused[NCATS]; //The user can select which catalysts to use.

int startinggen, gens, catstoplace, catspergen, catsurvive, maxfirstgen;
int currgen, currx, curry, currcatn, currorient;
int nCatsTotal=0; //Used to index catplaced[]
int callcount=0;
int nummatched=0;

int advance1gen(void);
int placecatalyst(void);
int removelastcat(void);
void printgen(int);
void printboard(FILE *);
void checkboard(FILE *);
void PrintAllBoard(FILE *);
void printinfo(FILE *);

int main()
{
   clock_t t;
    int initial[SZ][SZ];
    int x, y, g;
    int xmin=SZ, xmax=0, ymin=SZ, ymax=0;
    char ch;
    int tofile;
    FILE *fi, *ft;
   FILE *fsetup;
   char c;
   char str[100];
    char reactionstxt[256];
    char reactionsrle[256];

   t = clock();

   for (x=0; x<SZ; x++)
      for (y=0; y<SZ; y++)
      {
         initial[x][y]=0;
         boardgen0[x][y]=0;
         fixed[x][y]=0;

         for (g=0; g<MAXGENS; g++)
         {
            stack[g].now[x][y]= 1<<0;
            stack[g].history[x][y]= 1<<0;
         }
      }

   for (x=0; x<NCATS; x++)
      catisused[x]=0;

   tofile=1;
    fsetup = fopen("setup.txt", "r");

    fscanf (fsetup, "%s", reactionstxt);
    fscanf (fsetup, "%s", reactionsrle);

   if (tofile)
    {
      ft=fopen(reactionstxt, "w");
      fi=fopen(reactionsrle, "w");
      fprintf(fi, "x = 0, y = 0, rule = LifeHistory\n");
   }
   else
      fi=stdout;

   x=3;
   y=3;

   while ((c = getc(fsetup)) != '!')
   {
      if (c == '\n' || c == 'n' || c == 'N')
      {
         x=3;
         y++;
         continue;
      }

      if (c != ' ' && c != '.')
      {
         if (x>=SZ-3)
         { printf("Error: Pattern too wide.\n"); return 1; }
         if (y>=SZ-3)
         { printf("Error: Pattern too tall.\n"); return 1; }

         if (c == ',' || c == 'D')
         {
            initial[x][y]=1; //Fixed dead
            target[x][y]=1;  //Must (obviously) be dead in final pattern
         }
         else if (c == '*' || c == 'C')
         {
            initial[x][y]=2; //Fixed live
            target[x][y]=2;  //Must (obviously) be live in final pattern
         }
		 else if (c == 'B')
		 {
			 initial[x][y]=-1; //History Dead
		 }
         else if (c == '1')
            {
               initial[x][y]=3; //Live
               target[x][y]=2;  //Must be live in final pattern
            }
         else if (c == '0')
            target[x][y]=1;   //Must be dead in final pattern
         else if (c == '+')
            target[x][y]=2;   //Must be live in final pattern
         else if (c == '-')
            {
               initial[x][y]=3; //Live
               target[x][y]=1;  //Must be dead in final pattern
            }
         else 
            initial[x][y]=3; //Live

         if (x<xmin) xmin=x;
         if (x>xmax) xmax=x;
         if (y<ymin) ymin=y;
         if (y>ymax) ymax=y;
      }

      x++;
   }

   //Initialize fixed[][], stack[0], boardgen0[][], and targetgen0[][],
   //centering the pattern in the middle of the board:
   int xoffset=(SZ-xmax-xmin-2)/2, yoffset=(SZ-ymax-ymin-2)/2;
   for (x=xmin-1; x<=xmax+1; x++)
      for (y=ymin-1; y<=ymax+1; y++)
      {
         stack[0].now[x+xoffset][y+yoffset]=0;
		
		if (initial[x][y]==-1) //History Dead
		{
			 stack[0].history[x+xoffset][y+yoffset] = 1<<3;
		}
        
         if (initial[x][y]>1) //2 or 3, which is alive
         {
            stack[0].now[x+xoffset][y+yoffset]=LiveBit;
            //Little hack so that things work properly:
            stack[0].history[x+xoffset][y+yoffset] = 1<<3;

            boardgen0[x+xoffset][y+yoffset]=1;
         }

         if (initial[x][y]==1 || initial[x][y]==2) //Fixed
            fixed[x+xoffset][y+yoffset]=1;

         // copy target pattern to correct offset
         targetgen0[x+xoffset][y+yoffset]=target[x][y];

         //Count the neighbors:
         int count=(initial[x-1][y-1]>1) + (initial[x][y-1]>1) + (initial[x+1][y-1]>1)
                  +(initial[x-1][y  ]>1)                       + (initial[x+1][y  ]>1)
                  +(initial[x-1][y+1]>1) + (initial[x][y+1]>1) + (initial[x+1][y+1]>1);

         if (count<0 || count>8)
         { printf("Error: count is out of range.\n"); return 1; }

         //Check for inconsistency:
         if ((initial[x][y]==2 && count!=2 && count!=3) ||
            (initial[x][y]==1 && count==3))
         { printf("Error: Inconsistency in the starting pattern.\n"); return 1; }

         //Set the neighbor count bit:
         stack[0].now[x+xoffset][y+yoffset] |= 1<<count;
      } //for for

   //Set boundary variables:
   stack[0].xmin= g0minx= xmin-1 + xoffset;
   stack[0].xmax= g0maxx= xmax+1 + xoffset;
   stack[0].ymin= g0miny= ymin-1 + yoffset;
   stack[0].ymax= g0maxy= ymax+1 + yoffset;

   stack[0].numcats=0;

   //Repeat the initial pattern to the user:
   printf("\nYou entered:\n");
   if (tofile)
      fprintf(ft, "Initial pattern:\n");
   for (y=ymin; y<=ymax; y++)
   {
      for (x=xmin; x<=xmax; x++)
      {
         switch (initial[x][y])
         {
            case 0: ch='.'; break;
            case 1: ch=','; break;
            case 2: ch='*'; break;
            case 3: ch='o'; break;
         }
         printf("%c", ch);
         if (tofile)
            fprintf(ft, "%c", ch);
      }
      printf("\n");
      if (tofile)
         fprintf(ft, "\n");
   }

   //Also repeat the target grid (temp diagnostic)
   printf("\nYou entered a target pattern of:\n");
   if (tofile)
      fprintf(ft, "Target pattern:\n");
   for (y=ymin; y<=ymax; y++)
   {
      for (x=xmin; x<=xmax; x++)
      {
         switch (target[x][y])
         {
            case 0: ch='.'; break;
            case 1: ch=','; break;
            case 2: ch='*'; break;
         }
         printf("%c", ch);
         if (tofile)
            fprintf(ft, "%c", ch);
      }
      printf("\n");
      if (tofile)
         fprintf(ft, "\n");
   }

   while (1)
   {
      int i = 0;

      fscanf (fsetup, "%d", &i);


      if(i == 0)
         break;

      if (i>NCATS || i<0)
         continue;

      catisused[i - 1]=1;

      printf("The catalyst = %d\n",i);

   }

   if (tofile)
   {
        fprintf(ft, "\nCatalysts to use:\n");
      for (x=0; x<NCATS; x++)
         if (catisused[x])
                fprintf(ft, "%c%c%c%c\n", cata[x].name>>24, cata[x].name>>16,
               cata[x].name>>8, cata[x].name);
   }


   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &startinggen);
   printf("\nEarliest generation for placing catalysts: %d\n", startinggen);

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &gens);
   printf("\nLatest generation for placing catalysts:  %d\n", gens);

   if (tofile)
        fprintf(ft, "\nGenerations for placing catalysts: %d through %d.\n",
         startinggen, gens);

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &catsurvive);
   printf("\nHow many generations must a catalyst survive:   %d\n", catsurvive);

   gens += catsurvive;
   if (gens>=MAXGENS)
   {
      printf("Error: %d exceeds the maximum number of generations, which is %d.\n",
         gens, MAXGENS-1);
      return 1;
   }

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &catstoplace);
   printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &maxfirstgen);
   printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fclose(fsetup);

   if (catstoplace>MAXCATS)
   {
      printf("Error: %d exceeds the maximum number of catalysts, which is %d.\n",
         catstoplace, MAXCATS);
      return 1;
   }

   /* The purpose of this option was to allow the user to restrict the number of
   catalysts that can be placed in one generation, in order to reduce the
   huge number of possibilities that occur when catstoplace has a large value.
   However, searching with a large value of catstoplace is inconvenient in any
   case. Therefore, I deactivated this option but still left it in the code.
   To re-activate it, un-comment this code and remove the line
   "catspergen=catstoplace;":
   */
   /*printf("Maximum number of catalysts in one generation? ");
   scanf("%d", &catspergen);*/
   catspergen=catstoplace;


   printf("\n");

   if (tofile)
   {
        fprintf(ft, "Catalysts must survive %d generation%s.\n", catsurvive,
         catsurvive==1?"":"s");

        fprintf(ft, "%d catalysts maximum (%d maximum in one generation).\n\n",
           catstoplace, catspergen);
        fprintf(ft, "%d catalyst%s maximum.\n\n", catstoplace,
         catstoplace==1?"":"s");
   }

   currgen=0;
   currx=0;
   curry=0;
   currcatn=0;
   currorient=0;

   int numprinted=0;

   //Main loop:
   while(currgen>=0)
   {
        if(catplaced[0].gen > maxfirstgen)
            break;
      //Place another catalyst at this generation
      //until you can no more.
      if (currgen >= startinggen && currgen+catsurvive <= gens)
         while (nCatsTotal < catstoplace &&
            stack[currgen].numcats < catspergen &&
            placecatalyst())
         { }

      if (currgen==gens)
      {
            fprintf(ft, "Pattern # %d:\n", ++numprinted);
            PrintAllBoard(fi);
         printboard(ft);
            printinfo(ft);

         if (removelastcat())
            break; //Finished. Out of main loop.

         //Increment the current position:
         currx++;
         continue;
      }

      //Advance 1 generation.
      if (advance1gen())
      {
         if (nCatsTotal==0 ||
            currgen-catplaced[nCatsTotal-1].gen>=catsurvive)
         {
                fprintf(ft, "Pattern # %d:\n", ++numprinted);
                PrintAllBoard(fi);
            printboard(ft);
                printinfo(ft);
         }

         if (removelastcat())
            break; //Finished. Out of main loop.

         //Increment the current position:
         currx++;
      }

      if (callcount%1000==0)
      {
         printf("%d calls to advance1gen().\n", callcount);
         printinfo(stdout);
         if (tofile)
         {
                fprintf(ft, "%d calls to advance1gen().\n", callcount);
                printinfo(ft);
         }
      }


   }

   printf("The end.\n%d call%s to advance1gen()\n"
      "%d pattern%s printed.\n%d pattern%s matched target.\n",
      callcount, callcount==1?"":"s",
      numprinted, numprinted==1?"":"s",
      nummatched, nummatched==1?"":"s");
   if (tofile)
      fprintf(ft, "The end.\n%d call%s to advance1gen()\n"
         "%d pattern%s printed.\n%d pattern%s matched target.\n",
         callcount, callcount==1?"":"s",
         numprinted, numprinted==1?"":"s",
         nummatched, nummatched==1?"":"s");

   t = clock() - t;
   printf ("Total time: %d miliseconds\n",t);

   if(tofile)
   {
      fclose (ft);
      fclose (fi);
   }
   //getch();
   return 0;
} //main()

/* This function advances the pattern 1 generation and checks that nothing
of this occurs:
- A 'fixed' cell changes
- The pattern reaches the edge of the board

If one of these occurs the function returns 1.
*/
int advance1gen(void)
{
   int x, y, xtemp, ytemp;

   callcount++;

   if (currgen==MAXGENS)
   { printf("Error in advance1gen(): Too many generations.\n"); exit(1); }

   do
   {
      if (stack[currgen].xmin == 1)
      {
         xtemp=2;
         for (ytemp=2; ytemp<SZ-2; ytemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp+1][ytemp] &
                  stack[currgen].now[xtemp+2][ytemp+1] &
                  stack[currgen].now[xtemp+2][ytemp-1] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp+1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit)) break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
                  stack[currgen].now[xtemp+1][ytemp+2] | stack[currgen].now[xtemp+2][ytemp+2] |
                  stack[currgen].now[xtemp+3][ytemp+2] | stack[currgen].now[xtemp+3][ytemp+1] |
                  stack[currgen].now[xtemp+3][ytemp]) & LiveBit) break;
               if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
                  stack[currgen].now[xtemp+1][ytemp-2] | stack[currgen].now[xtemp+2][ytemp-2] |
                  stack[currgen].now[xtemp+3][ytemp-2] | stack[currgen].now[xtemp+3][ytemp-1] |
                  stack[currgen].now[xtemp+2][ytemp]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp+2][ytemp+1] = 1;
               stack[currgen].now[xtemp+2][ytemp-1] = 1;

               stack[currgen].history[xtemp-1][ytemp-1] = 1;
               stack[currgen].history[xtemp-1][ytemp] = 1;
               stack[currgen].history[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].xmin++;
            }
         }
      }
      if (stack[currgen].xmax == SZ-2)
      {
         xtemp=SZ-3;
         for (ytemp=2; ytemp<SZ-2; ytemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp-1][ytemp] &
                  stack[currgen].now[xtemp-2][ytemp+1] &
                  stack[currgen].now[xtemp-2][ytemp-1] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp-1][ytemp+1]) & LiveBit)) break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
                  stack[currgen].now[xtemp-1][ytemp+2] | stack[currgen].now[xtemp-2][ytemp+2] |
                  stack[currgen].now[xtemp-3][ytemp+2] | stack[currgen].now[xtemp-3][ytemp+1] |
                  stack[currgen].now[xtemp-3][ytemp]) & LiveBit) break;
               if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
                  stack[currgen].now[xtemp-1][ytemp-2] | stack[currgen].now[xtemp-2][ytemp-2] |
                  stack[currgen].now[xtemp-3][ytemp-2] | stack[currgen].now[xtemp-3][ytemp-1] |
                  stack[currgen].now[xtemp-2][ytemp]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp-2][ytemp+1] = 1;
               stack[currgen].now[xtemp-2][ytemp-1] = 1;

               stack[currgen].history[xtemp+1][ytemp-1] = 1;
               stack[currgen].history[xtemp+1][ytemp] = 1;
               stack[currgen].history[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].xmax--;
            }
         }
      }
      if (stack[currgen].ymin == 1)
      {
         ytemp=2;
         for (xtemp=2; xtemp<SZ-2; xtemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp][ytemp+1] &
                  stack[currgen].now[xtemp+1][ytemp+2] &
                  stack[currgen].now[xtemp-1][ytemp+2] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp+1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit))
                  break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
                  stack[currgen].now[xtemp+2][ytemp+1] | stack[currgen].now[xtemp+2][ytemp+2] |
                  stack[currgen].now[xtemp+2][ytemp+3] | stack[currgen].now[xtemp+1][ytemp+3] |
                  stack[currgen].now[xtemp][ytemp+3]) & LiveBit) break;
               if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
                  stack[currgen].now[xtemp-2][ytemp+1] | stack[currgen].now[xtemp-2][ytemp+2] |
                  stack[currgen].now[xtemp-2][ytemp+3] | stack[currgen].now[xtemp-1][ytemp+3] |
                  stack[currgen].now[xtemp][ytemp+2]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+2] = 1;
               stack[currgen].now[xtemp-1][ytemp+2] = 1;

               stack[currgen].history[xtemp-1][ytemp-1] = 1;
               stack[currgen].history[xtemp][ytemp-1] = 1;
               stack[currgen].history[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].ymin++;
            }
         }
      }
      if (stack[currgen].ymax == SZ-2)
      {
         ytemp=SZ-3;
         for (xtemp=2; xtemp<SZ-2; xtemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp][ytemp-1] &
                  stack[currgen].now[xtemp+1][ytemp-2] &
                  stack[currgen].now[xtemp-1][ytemp-2] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp-1]) & LiveBit))
                  break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
                  stack[currgen].now[xtemp+2][ytemp-1] | stack[currgen].now[xtemp+2][ytemp-2] |
                  stack[currgen].now[xtemp+2][ytemp-3] | stack[currgen].now[xtemp+1][ytemp-3] |
                  stack[currgen].now[xtemp][ytemp-3]) & LiveBit) break;
               if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
                  stack[currgen].now[xtemp-2][ytemp-1] | stack[currgen].now[xtemp-2][ytemp-2] |
                  stack[currgen].now[xtemp-2][ytemp-3] | stack[currgen].now[xtemp-1][ytemp-3] |
                  stack[currgen].now[xtemp][ytemp-2]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-2] = 1;
               stack[currgen].now[xtemp-1][ytemp-2] = 1;

               stack[currgen].history[xtemp-1][ytemp+1] = 1;
               stack[currgen].history[xtemp][ytemp+1] = 1;
               stack[currgen].history[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].ymax--;
            }
         }
      }
      break;
      /* what's the proper way to do this kind of thing, anyway? */
   }
   while(1);

   if (stack[currgen].xmin<=1 || stack[currgen].xmax>= SZ-2 ||
       stack[currgen].ymin<=1 || stack[currgen].ymax>= SZ-2)
       return 1; //Edge of the board reached.

   stack[currgen+1].xmin=SZ;
   stack[currgen+1].xmax=0;
   stack[currgen+1].ymin=SZ;
   stack[currgen+1].ymax=0;

   //We have to clear additional space around the pattern because it may
   //contain junk from before, and the function placecatalyst() sometimes
   //expands the boundaries without clearing.
   int xstart=stack[currgen].xmin-1-CATSZ,
      ystart=stack[currgen].ymin-1-CATSZ;
   if (xstart<1) xstart=1;
   if (ystart<1) ystart=1;
   for (x=xstart; x<=stack[currgen].xmax+1+CATSZ && x<SZ-1; x++)
      for (y=ystart; y<=stack[currgen].ymax+1+CATSZ && y<SZ-1; y++)
      {
         if (x<stack[currgen].xmin-1 || x>stack[currgen].xmax+1 ||
            y<stack[currgen].ymin-1 || y>stack[currgen].ymax+1)
         {
            stack[currgen+1].now[x][y]= 1<<0;
            stack[currgen+1].history[x][y]= 1<<0;
            continue;
         }

         //Set the cell itself's next generation:
         if(WillLive(stack[currgen].now[x][y]))
            stack[currgen+1].now[x][y] = LiveBit;
         else
            stack[currgen+1].now[x][y] = 0;

         //Count number of the live neighbors the cell will have the next
         //generation:
         int count=0;
         if (WillLive(stack[currgen].now[x-1][y-1])) count++;
         if (WillLive(stack[currgen].now[x-1][y  ])) count++;
         if (WillLive(stack[currgen].now[x-1][y+1])) count++;
         if (WillLive(stack[currgen].now[x  ][y-1])) count++;
         if (WillLive(stack[currgen].now[x  ][y+1])) count++;
         if (WillLive(stack[currgen].now[x+1][y-1])) count++;
         if (WillLive(stack[currgen].now[x+1][y  ])) count++;
         if (WillLive(stack[currgen].now[x+1][y+1])) count++;

         //If it's a fixed cell, check that it will not change 2 generations
         //from now. We assume that it has already been checked that it will
         //not change next generation:
         if (fixed[x][y] &&
            (((stack[currgen].now[x][y] & LiveBit) && count!=2 && count!=3) ||
            ((stack[currgen].now[x][y] & LiveBit)==0 && count==3)))
            return 1;

         //Set the cell's count bit:
         stack[currgen+1].now[x][y] |= 1<<count;

         //Also set the next generation's history board:
         stack[currgen+1].history[x][y] = stack[currgen].history[x][y] |
            stack[currgen].now[x][y];

         //Adjust next gen's boundary variables if necessary.
         //Note that the pattern's boundaries will never become smaller than
         //in a previous generation, because of history[][]:
         if ((stack[currgen+1].now[x][y] & (~1)) ||
            (stack[currgen+1].history[x][y] & (~1)))
         {
            if (x < stack[currgen+1].xmin) stack[currgen+1].xmin=x;
            if (x > stack[currgen+1].xmax) stack[currgen+1].xmax=x;
            if (y < stack[currgen+1].ymin) stack[currgen+1].ymin=y;
            if (y > stack[currgen+1].ymax) stack[currgen+1].ymax=y;
         }
      } //for for

   stack[currgen+1].numgliders=stack[currgen].numgliders;
   currgen++;
   currx=curry=currcatn=currorient=0;
   return 0;
} //advance1gen()

/* This function looks for a place to place a catalyst on this generation's
board, starting from the position specified by the globals currx, curry,
currorient, and currcatn, and going ahead. It makes sure that the
catalyst would not have reacted at any previous generation. It also makes
sure that the catalyst will react now, i.e. next generation.
*/
int placecatalyst()
{
   int catszx, catszy;
   char catcell;

   while (currcatn<NCATS)
   {
   //Skip this catalyst if it is deactivated:
   if (!catisused[currcatn])
   {
      currorient=0;
      currcatn++;
      continue;
   }

   while (currorient<8)
   {
   //Skip some orientations for symmetric patterns:
   if (cata[currcatn].name=='eat2')
      if (currorient>=4)
         break;
   if (cata[currcatn].name=='bloc' || cata[currcatn].name=='_tub')
      if (currorient==1 || currorient==3 || currorient==5 || currorient==7)
      {
         curry=0;
         currorient++;
         continue;
      }

   if (currorient<4)
   {
      catszy=cata[currcatn].y;
      catszx=cata[currcatn].x;
   }
   else
   {
      catszy=cata[currcatn].x;
      catszx=cata[currcatn].y;
   }
   if (curry<stack[currgen].ymin - catszy)
      curry=stack[currgen].ymin - catszy;

   while (curry<SZ-catszy && curry<=stack[currgen].ymax + catszy)
   {
   if (currx<stack[currgen].xmin - catszx)
      currx=stack[currgen].xmin - catszx;

   while (currx<SZ-catszx && currx<=stack[currgen].xmax + catszx)
   {
      int x, y, reacts=0, isgood=1;

      //Check the catalyst in this position cell by cell:
      for (x=0; x<catszx && isgood; x++)
      for (y=0; y<catszy && isgood; y++)
      {
      //Select the cell according to the orientation:
      if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
      else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
      else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
      else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
      else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
      else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
      else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
      else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

      if (catcell=='o' || catcell=='*')
         if ((stack[currgen].history[currx+x][curry+y] != 1<<0) ||
            (fixed[currx+x][curry+y] > 0))
         { isgood=0; continue; }

      if (catcell=='.' || catcell=='1' || catcell=='x')
         if (stack[currgen].history[currx+x][curry+y] &
            (LiveBit | (1<<2) | (1<<3)))
         { isgood=0; continue; }

      if (catcell=='$' || catcell=='%' || catcell=='^')
         if (stack[currgen].history[currx+x][curry+y] & LiveBit)
         { isgood=0; continue; }

      if (catcell==':' || catcell=='2' || catcell=='X')
         if (stack[currgen].history[currx+x][curry+y] &
            (LiveBit | (1<<1) | (1<<3)))
         { isgood=0; continue; }

      if (catcell=='1')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
            reacts=1;

      if (catcell=='2')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
            reacts=1;

      if (catcell=='x')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
         { isgood=0; continue; }

      if (catcell=='X')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
         { isgood=0; continue; }

      //Reject also if the catalyst will cause a birth in a fixed cell:
      if (fixed[currx+x][curry+y] && (catcell=='.' || catcell=='1') &&
         (stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
         { isgood=0; continue; }

      if (fixed[currx+x][curry+y] && (catcell==':' || catcell=='2') &&
         (stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
         { isgood=0; continue; }
      } //for for

      if (isgood && reacts)
      {
         //Save the current values of the boundary variables:
         catplaced[nCatsTotal].oldxmin=stack[currgen].xmin;
         catplaced[nCatsTotal].oldxmax=stack[currgen].xmax;
         catplaced[nCatsTotal].oldymin=stack[currgen].ymin;
         catplaced[nCatsTotal].oldymax=stack[currgen].ymax;
         //Save the current boundaries of boardgen0[][]:
         catplaced[nCatsTotal].g0oldminx=g0minx;
         catplaced[nCatsTotal].g0oldminy=g0miny;
         catplaced[nCatsTotal].g0oldmaxx=g0maxx;
         catplaced[nCatsTotal].g0oldmaxy=g0maxy;

         //Place the catalyst on the board.
         //Also, modify the history board as if the
         //catalyst was always there.
         //Also place the catalyst on boardgen0[][]:
         for (x=0; x<catszx; x++)
         for (y=0; y<catszy; y++)
         {
         if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
         else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
         else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
         else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
         else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
         else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
         else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
         else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

         //Adjust now[][],  history[][], and boardgen0[][]:
         if (catcell=='o')
         {
            stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
            stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
            boardgen0[currx+x][curry+y]=2;
         }
         //It doesn't necessarily have 3 neighbors, but it doesn't matter.

         if (catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
            stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
            boardgen0[currx+x][curry+y]=2;
            fixed[currx+x][curry+y]++;
         }

         //Adjust the boundaries of boardgen0[][]:
         if (catcell=='o' || catcell=='*' || catcell=='x' || catcell=='X')
         {
            if (currx+x < g0minx) g0minx=currx+x;
            if (currx+x > g0maxx) g0maxx=currx+x;
            if (curry+y < g0miny) g0miny=curry+y;
            if (curry+y > g0maxy) g0maxy=curry+y;
         }

         if (catcell=='.' || catcell=='1' || catcell=='x')
         {
            stack[currgen].now[currx+x][curry+y] <<= 1;
            stack[currgen].history[currx+x][curry+y] <<= 1;
         }

         if (catcell==':' || catcell=='2' || catcell=='X')
         {
            stack[currgen].now[currx+x][curry+y] <<= 2;
            stack[currgen].history[currx+x][curry+y] <<= 2;
         }

         if (catcell=='$')
         {
            stack[currgen].now[currx+x][curry+y] <<= 4;
            stack[currgen].history[currx+x][curry+y] <<= 4;
         }

         if (catcell=='%')
         {
            stack[currgen].now[currx+x][curry+y] <<= 5;
            stack[currgen].history[currx+x][curry+y] <<= 5;
         }

         if (catcell=='^')
         {
            stack[currgen].now[currx+x][curry+y] <<= 6;
            stack[currgen].history[currx+x][curry+y] <<= 6;
         }

         if (catcell=='x' || catcell=='X')
            fixed[currx+x][curry+y]++;

         //Adjust the boundary variables if necessary:
         if (catcell != ' ')
         {
            if (currx+x < stack[currgen].xmin)
               stack[currgen].xmin=currx+x;
            if (currx+x > stack[currgen].xmax)
               stack[currgen].xmax=currx+x;
            if (curry+y < stack[currgen].ymin)
               stack[currgen].ymin=curry+y;
            if (curry+y > stack[currgen].ymax)
               stack[currgen].ymax=curry+y;
         }
         } //for for

         //Record the placement:
         catplaced[nCatsTotal].x=currx;
         catplaced[nCatsTotal].y=curry;
         catplaced[nCatsTotal].n=currcatn;
         catplaced[nCatsTotal].orient=currorient;
         catplaced[nCatsTotal].gen=currgen;
         nCatsTotal++;
         stack[currgen].numcats++;

         return 1;
      } //isgood && reacts
      currx++;
   } //while currx
   currx=0;
   curry++;
   } //while curry
   curry=0;
   currorient++;
   } //while currorient
   currorient=0;
   currcatn++;
   } //while currcatn
   return 0; //No way to place a catalyst
} //placecatalyst()

/* This function removes the last catalyst that was placed, according to
the array catplaced[]. It backs up to the generation when that catalyst
was placed. It returns 1 if there are no catalysts to remove.
*/
int removelastcat()
{
   if (nCatsTotal==0)
      return 1;
   nCatsTotal--;

   currgen=catplaced[nCatsTotal].gen;
   currx=catplaced[nCatsTotal].x;
   curry=catplaced[nCatsTotal].y;
   currcatn=catplaced[nCatsTotal].n;
   currorient=catplaced[nCatsTotal].orient;

   stack[currgen].numcats--;

   int catszx, catszy, x, y;
   char catcell;

   if (currorient<4)
   {
      catszy=cata[currcatn].y;
      catszx=cata[currcatn].x;
   }
   else
   {
      catszy=cata[currcatn].x;
      catszx=cata[currcatn].y;
   }

   //Undo the placement of the catalyst, by restoring the values of
   //new[][] and history[][].
   //Also, remove the catalyst from boardgen0[][]:
   for (x=0; x<catszx; x++)
      for (y=0; y<catszy; y++)
      {
         if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
         else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
         else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
         else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
         else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
         else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
         else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
         else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

         if (catcell=='o' || catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]= 1<<0;
            stack[currgen].history[currx+x][curry+y]= 1<<0;
            boardgen0[currx+x][curry+y]=0;
         }

         if (catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]= 1<<0;
            stack[currgen].history[currx+x][curry+y]= 1<<0;
            boardgen0[currx+x][curry+y]=0;
            fixed[currx+x][curry+y]--;
            if (fixed[currx+x][curry+y]<0)
            {
               printf("Error 1 in removelastcat(): fixed[%d][%d] < 0.\n",
                  currx+x, curry+y);
               exit(1);
            }
         }

         if (catcell=='x' || catcell=='X')
         {
            fixed[currx+x][curry+y]--;
            if (fixed[currx+x][curry+y]<0)
            {
               printf("Error 2 in removelastcat(): fixed[%d][%d] < 0.\n",
                  currx+x, curry+y);
               exit(1);
            }
         }

         if (catcell=='.' || catcell=='1' || catcell=='x')
         {
            stack[currgen].now[currx+x][curry+y] >>= 1;
            stack[currgen].history[currx+x][curry+y] >>= 1;
         }

         if (catcell==':' || catcell=='2' || catcell=='X')
         {
            stack[currgen].now[currx+x][curry+y] >>= 2;
            stack[currgen].history[currx+x][curry+y] >>= 2;
         }

         if (catcell=='$')
         {
            stack[currgen].now[currx+x][curry+y] >>= 4;
            stack[currgen].history[currx+x][curry+y] >>= 4;
         }

         if (catcell=='%')
         {
            stack[currgen].now[currx+x][curry+y] >>= 5;
            stack[currgen].history[currx+x][curry+y] >>= 5;
         }

         if (catcell=='^')
         {
            stack[currgen].now[currx+x][curry+y] >>= 6;
            stack[currgen].history[currx+x][curry+y] >>= 6;
         }
      } //for for

   //Restore the boundary variables:
   stack[currgen].xmin=catplaced[nCatsTotal].oldxmin;
   stack[currgen].xmax=catplaced[nCatsTotal].oldxmax;
   stack[currgen].ymin=catplaced[nCatsTotal].oldymin;
   stack[currgen].ymax=catplaced[nCatsTotal].oldymax;
   //Restore boardgen0[][]'s boundaries:
   g0minx=catplaced[nCatsTotal].g0oldminx;
   g0miny=catplaced[nCatsTotal].g0oldminy;
   g0maxx=catplaced[nCatsTotal].g0oldmaxx;
   g0maxy=catplaced[nCatsTotal].g0oldmaxy;

   return 0;
} //removelastcat()

void printgen(int gen)
{
   int x, y;

   for (y=stack[gen].ymin; y<=stack[gen].ymax; y++)
   {
      for (x=stack[gen].xmin; x<=stack[gen].xmax; x++)
         if (stack[gen].now[x][y] & LiveBit)
            if (fixed[x][y]) printf("*");
            else printf("o");
         else
            if (fixed[x][y]) printf(",");
            else printf(".");
      printf("\n");
   }
//   printf("Catalysts placed at gens:");
//   for (int n=0; n<nCatsTotal; n++)
//      printf(" %d", catplaced[n].gen);
   printf("\nGeneration %d\n   -----\n", gen);
} //printgen()

void printboard(FILE *f)
{
   int x, y;
   char ch;

   for (y=g0miny; y<=g0maxy; y++)
   {
      for (x=g0minx; x<=g0maxx; x++)
         //Fix this:
         if (boardgen0[x][y])
            if (fixed[x][y]) fprintf(f, "*");
            else fprintf(f, "o");
         else
            if (fixed[x][y]) fprintf(f, ",");
            else fprintf(f, ".");

      fprintf(f, "\n");
   }

   fprintf(f,"\nFinal position:\n");
   for (y=stack[currgen].ymin; y<=stack[currgen].ymax; y++)
   {
      for (x=stack[currgen].xmin; x<=stack[currgen].xmax; x++)
         if (stack[currgen].now[x][y] & LiveBit) fprintf(f, "*");
         else fprintf(f,".");

      fprintf(f, "\n");
   }
fprintf(f, "\n%d glider%s escaped.",stack[currgen].numgliders, stack[currgen].numgliders==1?"":"s");

for (x=1; x==currgen; x++)
{
   printgen(x);
   do { scanf("%c", &ch); } while (ch!='\n');
}
} //printboard()

void checkboard(FILE *f)
{
   int x, y, z;

   z=0;

   for (y=g0miny; y<=g0maxy; y++)
   {
      // check for any mismatches with target pattern
      for (x=g0minx; x<=g0maxx; x++)
         if (((stack[currgen].now[x][y] & LiveBit) && (targetgen0[x][y] == 1)) ||
            (((stack[currgen].now[x][y] & LiveBit) == 0) && (targetgen0[x][y] == 2))) z=1;

         //if (stack[currgen].now[x][y] & LiveBit)
         //{
         //   if (target[x][y] == 1) z=1;
         //}
         //else if (target[x][y] == 2) z=1;
   }
   if (z == 0)
   {
      fprintf(f, "\nPattern matched target!\n");
      nummatched++;
   }
} //checkboard()

void printinfo(FILE *f)
{
   int n;

   fprintf(f, "%d catalyst%s", nCatsTotal, nCatsTotal==1?"":"s");
   if (nCatsTotal>0)
   {
      fprintf(f, ", which react%s", nCatsTotal==1 ? "s at gen." : " at gens.:");
      for (n=0; n<nCatsTotal; n++)
         fprintf(f, " %d", catplaced[n].gen);
   }
   else
      fprintf(f, ".");
   fprintf(f, "\nGeneration reached: %d\n   -----\n", currgen);
} //printinfo()


void PrintAllBoard(FILE *f)
{
    int x, y;

    for (y=0; y<SZ; y++)
    {
        for (x=0; x<SZ; x++)
            //Fix this:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "A");
                else fprintf(f, "C");
            else if (fixed[x][y]) fprintf(f, ".");
            else fprintf(f, ".");

        fprintf(f, "$");
      //fprintf(f, "\n");
    }

   fprintf(f, "50$");
   fprintf(f, "\n");
} 
The app requires setup.txt in this format:

Code: Select all

reactions.txt
reactions.rle
AAA
..A
AAA!
4
0
startinggen 1
gens 60
catsurvive 100
catstoplace 2
maxfirstgen 10
A python script with exe is attached. Extract to path/to/golly/scripts/python/catgl. Enjoy.


EDIT Newer version of catgl.py with instructions can be found here.
Attachments
Catgl1.0.3.zip
(51.11 KiB) Downloaded 472 times
Last edited by simsim314 on February 26th, 2015, 8:42 am, edited 14 times in total.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 9th, 2014, 4:08 am

simsim314 wrote:Following this: viewtopic.php?p=11858#p11858 I'm starting to work on improving the catalyst search utility by Gabriel Nivasch.
Looks good so far! Just for the record, here's a link to some probably-not-very-competent changes I made to 'catalyst' over a decade ago, when I spent some time hunting for a 90-degree Snark reflector to improve on the 180-degree boojum reflector.

The main changes were some simple pattern-matching options, and a system that checked for and removed gliders around the edges of the bounding box. I seem to recall that gliders were a significant problem in the original: if they weren't caught by an eater or another catalyst, they would eventually overflow the bounding box and stop the search in a lot of promising cases. My 'catgl' version counted and reported the number of escaped gliders for each candidate pattern, and also reported whether each candidate matched a target pattern which could be included as part of the initial input.

It's probably not worth trying to rehabilitate any of that old code, but maybe it will spark an idea for the new project --

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 9th, 2014, 12:28 pm

dvgrn wrote:My 'catgl' version counted and reported the number of escaped gliders for each candidate pattern, and also reported whether each candidate matched a target pattern which could be included as part of the initial input.
Great! I modified it to report RLE and currently catgl is the latest WIP as for now.I think my "parallel" approach could work better than what catalyst is currently doing, yielding deeper and more interesting results. But for now I want to concentrate on catalyst, mainly because most of the work I intend to make is in python, and should be done anyway for many C utilities.

Code: Select all

/* catgl v1.0.1 completed 5/9/2014 by Mciahel Simkin

Based on catgl 5/29/2001 by Dave Greene
Based on catalyst v1.0, released 3/21/2001
   Copyright (c) 2001, Gabriel Nivasch

   Changes between catalyst 1.0 and catgl 1.0:
   - all source code combined into a single catgl.cpp file
   - added '1', '0', '+', '-' target options to input (see readme.txt)
   - glider-catcher code avoids rejecting glider-producing reactions
   
 Changes between catgl 1.0.1 and catgl 1.0:
	- GCC compatible C code.
	- RLE export 
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SZ 70
#define MAXGENS 180
#define MAXCATS 10

#define NCATS 7
#define CATSZ 15

typedef struct
{
    int x, y;
} cell;

typedef struct
{
    char c[CATSZ][CATSZ];
    int x, y, name;
} catalyst;



/* The following array contains the state of the pattern at each
generation from 0 to the current one. For each generation there are two
arrays: now[SZ][SZ] and history[SZ][SZ]. now[][] contains the state of
the pattern at that generation. The format is as follows:
If the cell is alive, then bit #9 (LiveBit) is set.
In addition, exactly one of the bits #0 to #8 is set, indicating the
number of live neighbors the cell has.

history[][] contains the superposition (inclusive "OR") of all the
states each cell has been in, in all the previous generations.

The purpose of history[][] is to know whether the cell has ever been alive,
and if it was never alive, to know all the different neighbor counts it
has had. If bit #9 is set, it means the cell has been alive at some
point, and each of the bits #0 to #8 that is set indicates a neighbor
count the cell has had at some point.

For each generation there are also four variables indicating the current
boundaries of the pattern, the variable numcats, which indicates how
many catalysts have been placed this generation, and the variable numgliders,
which is the total number of gliders created as of this generation.
*/
struct board
{
	int now[SZ][SZ];
	int history[SZ][SZ];
	int xmin, xmax, ymin, ymax;
	int numcats, numgliders;
} stack[MAXGENS];

//Bit values used in now[][] and history[][]:
#define LiveBit 512
#define CountBitMask 511

//This macro function is used in advance1gen():
#define WillLive(x) ((x)==8 || (x)==516 || (x)==520)

/* The following array is used to print out the patterns created.
At the beginning the initial pattern is set in it. In addition, each
time a catalyst is placed on the board at a certain generation, it is
also placed here. When the catalyst is removed from the board, it is
also removed from here.
*/
int boardgen0[SZ][SZ];
int target[SZ][SZ];
int targetgen0[SZ][SZ];
int g0minx=SZ, g0miny=SZ, g0maxx=0, g0maxy=0;

/* This array indicates which cells are fixed, i.e. if they are dead,
they are not allowed to live, and if they are alive, they are not
allowed to die. There are two sources from which fixed cells can come:
From the catalysts (cells marked 'x', 'X', and '*'), or from the input
pattern of the user. Note that cells that were set fixed dead by the
user or by a catalyst, might be set again as fixed dead by another catalyst.
Then, when the second catalyst is removed, the cell should still remain fixed
dead because of the first catalyst or the user's input. Therefore, the
procedure is as follows:
To set a cell as fixed, add 1. To unset it, substract 1. The cell is free
only if the value is 0. If the value is less than 0, it is an error.
*/
int fixed[SZ][SZ];

/* The following array contains info for each catalyst that has
been placed on the board: position, catalyst number, orientation,
generation placed, and the old values of the pattern boundaries, which
are restored when the catalyst is removed.
*/
struct catposition
{
	int x, y, n, orient, gen;
	int oldxmin, oldxmax, oldymin, oldymax; //Used to restore the values
		//after removing the catalyst.
	int g0oldminx, g0oldminy, g0oldmaxx, g0oldmaxy;
		//Used to restore g0minx, etc., after removing the catalyst.
} catplaced[MAXCATS];

struct cell
{ int x, y;};

struct catalyst
{
	char c[CATSZ][CATSZ];
	int x, y, name;
};

const catalyst cata[NCATS]=
{
	{{{' ', ' ', 'x', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', '*', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '$', '%', '*', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  6, 6, 'eat1'},
	{{{' ', ' ', ' ', '.', '.', 'X', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', '$', '*', '$', 'o', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', '*', '*', '$', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '*', '%', '^', '$', '$', '$', '$', 'X', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', '*', '*', '$', '*', '*', '.', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', '%', '*', '^', '*', '$', '.', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ':', '*', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', '.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', '.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  9, 9, 'eat2'},
	{{{'x', '.', ':', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'2', '*', '$', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'X', '*', '*', '$', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'x', ':', ':', ':', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  4, 6, 'snak'},
	{{{'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  4, 4, 'bloc'},
	{{{' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ':', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ':', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', ':', 'o', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', '*', '$', 'o', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '$', '$', '*', 'X', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  11, 8, 'tubw'},
	{{{' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'o', '$', '*', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  5, 5, '_tub'},
	{{{' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'x', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'o', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'X', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  5, 5, 'boat'}
};

int catisused[NCATS]; //The user can select which catalysts to use.

int startinggen, gens, catstoplace, catspergen, catsurvive;
int currgen, currx, curry, currcatn, currorient;
int nCatsTotal=0; //Used to index catplaced[]
int callcount=0;
int nummatched=0;

int advance1gen(void);
int placecatalyst(void);
int removelastcat(void);
void printgen(int);
void printboard(FILE *);
void checkboard(FILE *);
void PrintAllBoard(FILE *);
void printinfo(FILE *);

int main()
{
	clock_t t;
    int initial[SZ][SZ];
    int x, y, g;
    int xmin=SZ, xmax=0, ymin=SZ, ymax=0;
    char ch;
    int tofile;
    FILE *fi, *ft;

	t = clock();

	//Use this commented-out code to print out a catalyst and check that
	//it is correct:
	/*for (y=0; y<cata[6].y; y++)
	{
		for (x=0; x<cata[6].x; x++)
			printf("%c", cata[6].c[x][y]);
		printf("\n");
	}
	if(1)return 0;*/

	for (x=0; x<SZ; x++)
		for (y=0; y<SZ; y++)
		{
			initial[x][y]=0;
			boardgen0[x][y]=0;
			fixed[x][y]=0;

			for (g=0; g<MAXGENS; g++)
			{
				stack[g].now[x][y]= 1<<0;
				stack[g].history[x][y]= 1<<0;
			}
		}

	for (x=0; x<NCATS; x++)
		catisused[x]=0;

	printf("Print output to file (y/n)? ");
	do
	{ scanf("%c", &ch); }
	while (ch!='n' && ch!='N' && ch!='y' && ch!='Y');

	tofile=((ch=='Y') || (ch=='y'));
	if (tofile)
    {
		ft=fopen("reactions.txt", "w");
		fi=fopen("reactions.rle", "w");
		fprintf(fi, "x = 0, y = 0, rule = LifeHistory\n");
	}
	else
		fi=stdout;

	printf("\nPlease enter the pattern. Use ' ' or '.' for regular dead cells,\n"
		"',' for fixed dead cells, '*' for fixed live cells, and any other \n"
		"character for regular live cells. Finish the pattern with '!':\n");

	//Read the pattern entered by the user and store it in initial[][].
	// target[][] will contain a 1 for cells that must be off at end, 2 if must be on
	//Set the fixed cells in fixed[][]:
	x=3;
	y=3;
	char c;
	while ((c=getchar())!='!')
	{
		if (c == '\n' || c == 'n' || c == 'N')
		{
			x=3;
			y++;
			continue;
		}

		if (c != ' ' && c != '.')
		{
			if (x>=SZ-3)
			{ printf("Error: Pattern too wide.\n"); return 1; }
			if (y>=SZ-3)
			{ printf("Error: Pattern too tall.\n"); return 1; }

			if (c == ',')
			{
				initial[x][y]=1; //Fixed dead
				target[x][y]=1;  //Must (obviously) be dead in final pattern
			}
			else if (c == '*')
			{
				initial[x][y]=2; //Fixed live
				target[x][y]=2;  //Must (obviously) be live in final pattern
			}
			else if (c == '1')
				{
					initial[x][y]=3; //Live
					target[x][y]=2;  //Must be live in final pattern
				}
			else if (c == '0')
				target[x][y]=1;   //Must be dead in final pattern
			else if (c == '+')
				target[x][y]=2;   //Must be live in final pattern
			else if (c == '-')
				{
					initial[x][y]=3; //Live
					target[x][y]=1;  //Must be dead in final pattern
				}
			else
				initial[x][y]=3; //Live

			if (x<xmin) xmin=x;
			if (x>xmax) xmax=x;
			if (y<ymin) ymin=y;
			if (y>ymax) ymax=y;
		}

		x++;
	}

	//Initialize fixed[][], stack[0], boardgen0[][], and targetgen0[][],
	//centering the pattern in the middle of the board:
	int xoffset=(SZ-xmax-xmin-2)/2, yoffset=(SZ-ymax-ymin-2)/2;
	for (x=xmin-1; x<=xmax+1; x++)
		for (y=ymin-1; y<=ymax+1; y++)
		{
			stack[0].now[x+xoffset][y+yoffset]=0;

			if (initial[x][y]>1) //2 or 3, which is alive
			{
				stack[0].now[x+xoffset][y+yoffset]=LiveBit;
				//Little hack so that things work properly:
				stack[0].history[x+xoffset][y+yoffset] = 1<<3;

				boardgen0[x+xoffset][y+yoffset]=1;
			}

			if (initial[x][y]==1 || initial[x][y]==2) //Fixed
				fixed[x+xoffset][y+yoffset]=1;

			// copy target pattern to correct offset
			targetgen0[x+xoffset][y+yoffset]=target[x][y];

			//Count the neighbors:
			int count=(initial[x-1][y-1]>1) + (initial[x][y-1]>1) + (initial[x+1][y-1]>1)
			         +(initial[x-1][y  ]>1)                       + (initial[x+1][y  ]>1)
			         +(initial[x-1][y+1]>1) + (initial[x][y+1]>1) + (initial[x+1][y+1]>1);

			if (count<0 || count>8)
			{ printf("Error: count is out of range.\n"); return 1; }

			//Check for inconsistency:
			if ((initial[x][y]==2 && count!=2 && count!=3) ||
				(initial[x][y]==1 && count==3))
			{ printf("Error: Inconsistency in the starting pattern.\n"); return 1; }

			//Set the neighbor count bit:
			stack[0].now[x+xoffset][y+yoffset] |= 1<<count;
		} //for for

	//Set boundary variables:
	stack[0].xmin= g0minx= xmin-1 + xoffset;
	stack[0].xmax= g0maxx= xmax+1 + xoffset;
	stack[0].ymin= g0miny= ymin-1 + yoffset;
	stack[0].ymax= g0maxy= ymax+1 + yoffset;

	stack[0].numcats=0;

	//Repeat the initial pattern to the user:
	printf("\nYou entered:\n");
	if (tofile)
		fprintf(ft, "Initial pattern:\n");
	for (y=ymin; y<=ymax; y++)
	{
		for (x=xmin; x<=xmax; x++)
		{
			switch (initial[x][y])
			{
				case 0: ch='.'; break;
				case 1: ch=','; break;
				case 2: ch='*'; break;
				case 3: ch='o'; break;
			}
			printf("%c", ch);
			if (tofile)
				fprintf(ft, "%c", ch);
		}
		printf("\n");
		if (tofile)
			fprintf(ft, "\n");
	}

	//Also repeat the target grid (temp diagnostic)
	printf("\nYou entered a target pattern of:\n");
	if (tofile)
		fprintf(ft, "Target pattern:\n");
	for (y=ymin; y<=ymax; y++)
	{
		for (x=xmin; x<=xmax; x++)
		{
			switch (target[x][y])
			{
				case 0: ch='.'; break;
				case 1: ch=','; break;
				case 2: ch='*'; break;
			}
			printf("%c", ch);
			if (tofile)
				fprintf(ft, "%c", ch);
		}
		printf("\n");
		if (tofile)
			fprintf(ft, "\n");
	}

	printf("\nCustomize which catalysts to use (y/n)? ");
	do
	{ scanf("%c", &ch); }
	while (ch!='n' && ch!='N' && ch!='y' && ch!='Y');

	if ((ch=='Y') || (ch=='y'))
	{
		printf("These are the available catalysts:\n");
		for (x=0; x<NCATS; x++)
			printf("%2d: %c%c%c%c\n", x+1, cata[x].name>>24, cata[x].name>>16,
				cata[x].name>>8, cata[x].name);
		do
		{
			printf("Enter a number from the above list, or 0 to finish: ");
			scanf("%d", &x);
			if (x==0)
				break;
			if (x>NCATS || x<0)
			{ printf("Invalid number.\n"); continue; }
			x--;
			catisused[x]=1;
			printf("%c%c%c%c selected.\n", cata[x].name>>24, cata[x].name>>16,
				cata[x].name>>8, cata[x].name);
		}
		while(1);
	}
	else
		for (x=0; x<NCATS; x++)
			catisused[x]=1;

	if (tofile)
	{
        fprintf(ft, "\nCatalysts to use:\n");
		for (x=0; x<NCATS; x++)
			if (catisused[x])
                fprintf(ft, "%c%c%c%c\n", cata[x].name>>24, cata[x].name>>16,
					cata[x].name>>8, cata[x].name);
	}

	printf("\nEarliest generation for placing catalysts? ");
	scanf("%d", &startinggen);

	printf("Latest generation for placing catalysts? ");
	scanf("%d", &gens);

	if (tofile)
        fprintf(ft, "\nGenerations for placing catalysts: %d through %d.\n",
			startinggen, gens);

	printf("How many generations must a catalyst survive? ");
	scanf("%d", &catsurvive);

	gens += catsurvive;
	if (gens>=MAXGENS)
	{
		printf("Error: %d exceeds the maximum number of generations, which is %d.\n",
			gens, MAXGENS-1);
		return 1;
	}

	printf("Maximum number of catalysts? ");
	scanf("%d", &catstoplace);
	if (catstoplace>MAXCATS)
	{
		printf("Error: %d exceeds the maximum number of catalysts, which is %d.\n",
			catstoplace, MAXCATS);
		return 1;
	}

	/* The purpose of this option was to allow the user to restrict the number of
	catalysts that can be placed in one generation, in order to reduce the
	huge number of possibilities that occur when catstoplace has a large value.
	However, searching with a large value of catstoplace is inconvenient in any
	case. Therefore, I deactivated this option but still left it in the code.
	To re-activate it, un-comment this code and remove the line
	"catspergen=catstoplace;":
	*/
	/*printf("Maximum number of catalysts in one generation? ");
	scanf("%d", &catspergen);*/
	catspergen=catstoplace;


	printf("\n");

	if (tofile)
	{
        fprintf(ft, "Catalysts must survive %d generation%s.\n", catsurvive,
			catsurvive==1?"":"s");

        fprintf(ft, "%d catalysts maximum (%d maximum in one generation).\n\n",
        	catstoplace, catspergen);
        fprintf(ft, "%d catalyst%s maximum.\n\n", catstoplace,
			catstoplace==1?"":"s");
	}

	currgen=0;
	currx=0;
	curry=0;
	currcatn=0;
	currorient=0;

	int numprinted=0;

	//Main loop:
	while(currgen>=0)
	{
		//Place another catalyst at this generation
		//until you can no more.
		if (currgen >= startinggen && currgen+catsurvive <= gens)
			while (nCatsTotal < catstoplace &&
				stack[currgen].numcats < catspergen &&
				placecatalyst())
			{ }

		if (currgen==gens)
		{
            fprintf(ft, "Pattern # %d:\n", ++numprinted);
            PrintAllBoard(fi);
			printboard(ft);
            printinfo(ft);

			if (removelastcat())
				break; //Finished. Out of main loop.

			//Increment the current position:
			currx++;
			continue;
		}

		//Advance 1 generation.
		if (advance1gen())
		{
			if (nCatsTotal==0 ||
				currgen-catplaced[nCatsTotal-1].gen>=catsurvive)
			{
                fprintf(ft, "Pattern # %d:\n", ++numprinted);
                PrintAllBoard(fi);
				printboard(ft);
                printinfo(ft);
			}

			if (removelastcat())
				break; //Finished. Out of main loop.

			//Increment the current position:
			currx++;
		}

		if (callcount%1000==0)
		{
			printf("%d calls to advance1gen().\n", callcount);
			printinfo(stdout);
			if (tofile)
			{
                fprintf(ft, "%d calls to advance1gen().\n", callcount);
                printinfo(ft);
			}
		}
	}

	printf("The end.\n%d call%s to advance1gen()\n"
		"%d pattern%s printed.\n%d pattern%s matched target.\n",
		callcount, callcount==1?"":"s",
		numprinted, numprinted==1?"":"s",
		nummatched, nummatched==1?"":"s");
	if (tofile)
		fprintf(ft, "The end.\n%d call%s to advance1gen()\n"
			"%d pattern%s printed.\n%d pattern%s matched target.\n",
			callcount, callcount==1?"":"s",
			numprinted, numprinted==1?"":"s",
			nummatched, nummatched==1?"":"s");
	t = clock() - t;
	printf ("Total time: %d miliseconds\n",t);

	if(tofile)
	{
		fclose (ft);
		fclose (fi);
	}
	getch();
	return 0;
} //main()

/* This function advances the pattern 1 generation and checks that nothing
of this occurs:
- A 'fixed' cell changes
- The pattern reaches the edge of the board

If one of these occurs the function returns 1.
*/
int advance1gen(void)
{
	int x, y, xtemp, ytemp;

	callcount++;

	if (currgen==MAXGENS)
	{ printf("Error in advance1gen(): Too many generations.\n"); exit(1); }

	do
	{
		if (stack[currgen].xmin == 1)
		{
			xtemp=2;
			for (ytemp=2; ytemp<SZ-2; ytemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp+1][ytemp] &
						stack[currgen].now[xtemp+2][ytemp+1] &
						stack[currgen].now[xtemp+2][ytemp-1] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp+1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit)) break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
						stack[currgen].now[xtemp+1][ytemp+2] | stack[currgen].now[xtemp+2][ytemp+2] |
						stack[currgen].now[xtemp+3][ytemp+2] | stack[currgen].now[xtemp+3][ytemp+1] |
						stack[currgen].now[xtemp+3][ytemp]) & LiveBit) break;
					if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
						stack[currgen].now[xtemp+1][ytemp-2] | stack[currgen].now[xtemp+2][ytemp-2] |
						stack[currgen].now[xtemp+3][ytemp-2] | stack[currgen].now[xtemp+3][ytemp-1] |
						stack[currgen].now[xtemp+2][ytemp]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp+2][ytemp+1] = 1;
					stack[currgen].now[xtemp+2][ytemp-1] = 1;

					stack[currgen].history[xtemp-1][ytemp-1] = 1;
					stack[currgen].history[xtemp-1][ytemp] = 1;
					stack[currgen].history[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].xmin++;
				}
			}
		}
		if (stack[currgen].xmax == SZ-2)
		{
			xtemp=SZ-3;
			for (ytemp=2; ytemp<SZ-2; ytemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp-1][ytemp] &
						stack[currgen].now[xtemp-2][ytemp+1] &
						stack[currgen].now[xtemp-2][ytemp-1] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp-1][ytemp+1]) & LiveBit)) break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
						stack[currgen].now[xtemp-1][ytemp+2] | stack[currgen].now[xtemp-2][ytemp+2] |
						stack[currgen].now[xtemp-3][ytemp+2] | stack[currgen].now[xtemp-3][ytemp+1] |
						stack[currgen].now[xtemp-3][ytemp]) & LiveBit) break;
					if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
						stack[currgen].now[xtemp-1][ytemp-2] | stack[currgen].now[xtemp-2][ytemp-2] |
						stack[currgen].now[xtemp-3][ytemp-2] | stack[currgen].now[xtemp-3][ytemp-1] |
						stack[currgen].now[xtemp-2][ytemp]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp-2][ytemp+1] = 1;
					stack[currgen].now[xtemp-2][ytemp-1] = 1;

					stack[currgen].history[xtemp+1][ytemp-1] = 1;
					stack[currgen].history[xtemp+1][ytemp] = 1;
					stack[currgen].history[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].xmax--;
				}
			}
		}
		if (stack[currgen].ymin == 1)
		{
			ytemp=2;
			for (xtemp=2; xtemp<SZ-2; xtemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp][ytemp+1] &
						stack[currgen].now[xtemp+1][ytemp+2] &
						stack[currgen].now[xtemp-1][ytemp+2] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp+1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit))
						break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
						stack[currgen].now[xtemp+2][ytemp+1] | stack[currgen].now[xtemp+2][ytemp+2] |
						stack[currgen].now[xtemp+2][ytemp+3] | stack[currgen].now[xtemp+1][ytemp+3] |
						stack[currgen].now[xtemp][ytemp+3]) & LiveBit) break;
					if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
						stack[currgen].now[xtemp-2][ytemp+1] | stack[currgen].now[xtemp-2][ytemp+2] |
						stack[currgen].now[xtemp-2][ytemp+3] | stack[currgen].now[xtemp-1][ytemp+3] |
						stack[currgen].now[xtemp][ytemp+2]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+2] = 1;
					stack[currgen].now[xtemp-1][ytemp+2] = 1;

					stack[currgen].history[xtemp-1][ytemp-1] = 1;
					stack[currgen].history[xtemp][ytemp-1] = 1;
					stack[currgen].history[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].ymin++;
				}
			}
		}
		if (stack[currgen].ymax == SZ-2)
		{
			ytemp=SZ-3;
			for (xtemp=2; xtemp<SZ-2; xtemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp][ytemp-1] &
						stack[currgen].now[xtemp+1][ytemp-2] &
						stack[currgen].now[xtemp-1][ytemp-2] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp-1]) & LiveBit))
						break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
						stack[currgen].now[xtemp+2][ytemp-1] | stack[currgen].now[xtemp+2][ytemp-2] |
						stack[currgen].now[xtemp+2][ytemp-3] | stack[currgen].now[xtemp+1][ytemp-3] |
						stack[currgen].now[xtemp][ytemp-3]) & LiveBit) break;
					if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
						stack[currgen].now[xtemp-2][ytemp-1] | stack[currgen].now[xtemp-2][ytemp-2] |
						stack[currgen].now[xtemp-2][ytemp-3] | stack[currgen].now[xtemp-1][ytemp-3] |
						stack[currgen].now[xtemp][ytemp-2]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-2] = 1;
					stack[currgen].now[xtemp-1][ytemp-2] = 1;

					stack[currgen].history[xtemp-1][ytemp+1] = 1;
					stack[currgen].history[xtemp][ytemp+1] = 1;
					stack[currgen].history[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].ymax--;
				}
			}
		}
		break;
		/* what's the proper way to do this kind of thing, anyway? */
	}
	while(1);

	if (stack[currgen].xmin<=1 || stack[currgen].xmax>= SZ-2 ||
	    stack[currgen].ymin<=1 || stack[currgen].ymax>= SZ-2)
	    return 1; //Edge of the board reached.

	stack[currgen+1].xmin=SZ;
	stack[currgen+1].xmax=0;
	stack[currgen+1].ymin=SZ;
	stack[currgen+1].ymax=0;

	//We have to clear additional space around the pattern because it may
	//contain junk from before, and the function placecatalyst() sometimes
	//expands the boundaries without clearing.
	int xstart=stack[currgen].xmin-1-CATSZ,
		ystart=stack[currgen].ymin-1-CATSZ;
	if (xstart<1) xstart=1;
	if (ystart<1) ystart=1;
	for (x=xstart; x<=stack[currgen].xmax+1+CATSZ && x<SZ-1; x++)
		for (y=ystart; y<=stack[currgen].ymax+1+CATSZ && y<SZ-1; y++)
		{
			if (x<stack[currgen].xmin-1 || x>stack[currgen].xmax+1 ||
				y<stack[currgen].ymin-1 || y>stack[currgen].ymax+1)
			{
				stack[currgen+1].now[x][y]= 1<<0;
				stack[currgen+1].history[x][y]= 1<<0;
				continue;
			}

			//Set the cell itself's next generation:
			if(WillLive(stack[currgen].now[x][y]))
				stack[currgen+1].now[x][y] = LiveBit;
			else
				stack[currgen+1].now[x][y] = 0;

			//Count number of the live neighbors the cell will have the next
			//generation:
			int count=0;
			if (WillLive(stack[currgen].now[x-1][y-1])) count++;
			if (WillLive(stack[currgen].now[x-1][y  ])) count++;
			if (WillLive(stack[currgen].now[x-1][y+1])) count++;
			if (WillLive(stack[currgen].now[x  ][y-1])) count++;
			if (WillLive(stack[currgen].now[x  ][y+1])) count++;
			if (WillLive(stack[currgen].now[x+1][y-1])) count++;
			if (WillLive(stack[currgen].now[x+1][y  ])) count++;
			if (WillLive(stack[currgen].now[x+1][y+1])) count++;

			//If it's a fixed cell, check that it will not change 2 generations
			//from now. We assume that it has already been checked that it will
			//not change next generation:
			if (fixed[x][y] &&
				(((stack[currgen].now[x][y] & LiveBit) && count!=2 && count!=3) ||
				((stack[currgen].now[x][y] & LiveBit)==0 && count==3)))
				return 1;

			//Set the cell's count bit:
			stack[currgen+1].now[x][y] |= 1<<count;

			//Also set the next generation's history board:
			stack[currgen+1].history[x][y] = stack[currgen].history[x][y] |
				stack[currgen].now[x][y];

			//Adjust next gen's boundary variables if necessary.
			//Note that the pattern's boundaries will never become smaller than
			//in a previous generation, because of history[][]:
			if ((stack[currgen+1].now[x][y] & (~1)) ||
				(stack[currgen+1].history[x][y] & (~1)))
			{
				if (x < stack[currgen+1].xmin) stack[currgen+1].xmin=x;
				if (x > stack[currgen+1].xmax) stack[currgen+1].xmax=x;
				if (y < stack[currgen+1].ymin) stack[currgen+1].ymin=y;
				if (y > stack[currgen+1].ymax) stack[currgen+1].ymax=y;
			}
		} //for for

	stack[currgen+1].numgliders=stack[currgen].numgliders;
	currgen++;
	currx=curry=currcatn=currorient=0;
	return 0;
} //advance1gen()

/* This function looks for a place to place a catalyst on this generation's
board, starting from the position specified by the globals currx, curry,
currorient, and currcatn, and going ahead. It makes sure that the
catalyst would not have reacted at any previous generation. It also makes
sure that the catalyst will react now, i.e. next generation.
*/
int placecatalyst()
{
	int catszx, catszy;
	char catcell;

	while (currcatn<NCATS)
	{
	//Skip this catalyst if it is deactivated:
	if (!catisused[currcatn])
	{
		currorient=0;
		currcatn++;
		continue;
	}

	while (currorient<8)
	{
	//Skip some orientations for symmetric patterns:
	if (cata[currcatn].name=='eat2')
		if (currorient>=4)
			break;
	if (cata[currcatn].name=='bloc' || cata[currcatn].name=='_tub')
		if (currorient==1 || currorient==3 || currorient==5 || currorient==7)
		{
			curry=0;
			currorient++;
			continue;
		}

	if (currorient<4)
	{
		catszy=cata[currcatn].y;
		catszx=cata[currcatn].x;
	}
	else
	{
		catszy=cata[currcatn].x;
		catszx=cata[currcatn].y;
	}
	if (curry<stack[currgen].ymin - catszy)
		curry=stack[currgen].ymin - catszy;

	while (curry<SZ-catszy && curry<=stack[currgen].ymax + catszy)
	{
	if (currx<stack[currgen].xmin - catszx)
		currx=stack[currgen].xmin - catszx;

	while (currx<SZ-catszx && currx<=stack[currgen].xmax + catszx)
	{
		int x, y, reacts=0, isgood=1;

		//Check the catalyst in this position cell by cell:
		for (x=0; x<catszx && isgood; x++)
		for (y=0; y<catszy && isgood; y++)
		{
		//Select the cell according to the orientation:
		if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
		else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
		else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
		else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
		else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
		else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
		else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
		else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

		if (catcell=='o' || catcell=='*')
			if ((stack[currgen].history[currx+x][curry+y] != 1<<0) ||
				(fixed[currx+x][curry+y] > 0))
			{ isgood=0; continue; }

		if (catcell=='.' || catcell=='1' || catcell=='x')
			if (stack[currgen].history[currx+x][curry+y] &
				(LiveBit | (1<<2) | (1<<3)))
			{ isgood=0; continue; }

		if (catcell=='$' || catcell=='%' || catcell=='^')
			if (stack[currgen].history[currx+x][curry+y] & LiveBit)
			{ isgood=0; continue; }

		if (catcell==':' || catcell=='2' || catcell=='X')
			if (stack[currgen].history[currx+x][curry+y] &
				(LiveBit | (1<<1) | (1<<3)))
			{ isgood=0; continue; }

		if (catcell=='1')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
				reacts=1;

		if (catcell=='2')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
				reacts=1;

		if (catcell=='x')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
			{ isgood=0; continue; }

		if (catcell=='X')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
			{ isgood=0; continue; }

		//Reject also if the catalyst will cause a birth in a fixed cell:
		if (fixed[currx+x][curry+y] && (catcell=='.' || catcell=='1') &&
			(stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
			{ isgood=0; continue; }

		if (fixed[currx+x][curry+y] && (catcell==':' || catcell=='2') &&
			(stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
			{ isgood=0; continue; }
		} //for for

		if (isgood && reacts)
		{
			//Save the current values of the boundary variables:
			catplaced[nCatsTotal].oldxmin=stack[currgen].xmin;
			catplaced[nCatsTotal].oldxmax=stack[currgen].xmax;
			catplaced[nCatsTotal].oldymin=stack[currgen].ymin;
			catplaced[nCatsTotal].oldymax=stack[currgen].ymax;
			//Save the current boundaries of boardgen0[][]:
			catplaced[nCatsTotal].g0oldminx=g0minx;
			catplaced[nCatsTotal].g0oldminy=g0miny;
			catplaced[nCatsTotal].g0oldmaxx=g0maxx;
			catplaced[nCatsTotal].g0oldmaxy=g0maxy;

			//Place the catalyst on the board.
			//Also, modify the history board as if the
			//catalyst was always there.
			//Also place the catalyst on boardgen0[][]:
			for (x=0; x<catszx; x++)
			for (y=0; y<catszy; y++)
			{
			if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
			else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
			else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
			else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
			else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
			else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
			else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
			else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

			//Adjust now[][],  history[][], and boardgen0[][]:
			if (catcell=='o')
			{
				stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
				stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
				boardgen0[currx+x][curry+y]=2;
			}
			//It doesn't necessarily have 3 neighbors, but it doesn't matter.

			if (catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
				stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
				boardgen0[currx+x][curry+y]=2;
				fixed[currx+x][curry+y]++;
			}

			//Adjust the boundaries of boardgen0[][]:
			if (catcell=='o' || catcell=='*' || catcell=='x' || catcell=='X')
			{
				if (currx+x < g0minx) g0minx=currx+x;
				if (currx+x > g0maxx) g0maxx=currx+x;
				if (curry+y < g0miny) g0miny=curry+y;
				if (curry+y > g0maxy) g0maxy=curry+y;
			}

			if (catcell=='.' || catcell=='1' || catcell=='x')
			{
				stack[currgen].now[currx+x][curry+y] <<= 1;
				stack[currgen].history[currx+x][curry+y] <<= 1;
			}

			if (catcell==':' || catcell=='2' || catcell=='X')
			{
				stack[currgen].now[currx+x][curry+y] <<= 2;
				stack[currgen].history[currx+x][curry+y] <<= 2;
			}

			if (catcell=='$')
			{
				stack[currgen].now[currx+x][curry+y] <<= 4;
				stack[currgen].history[currx+x][curry+y] <<= 4;
			}

			if (catcell=='%')
			{
				stack[currgen].now[currx+x][curry+y] <<= 5;
				stack[currgen].history[currx+x][curry+y] <<= 5;
			}

			if (catcell=='^')
			{
				stack[currgen].now[currx+x][curry+y] <<= 6;
				stack[currgen].history[currx+x][curry+y] <<= 6;
			}

			if (catcell=='x' || catcell=='X')
				fixed[currx+x][curry+y]++;

			//Adjust the boundary variables if necessary:
			if (catcell != ' ')
			{
				if (currx+x < stack[currgen].xmin)
					stack[currgen].xmin=currx+x;
				if (currx+x > stack[currgen].xmax)
					stack[currgen].xmax=currx+x;
				if (curry+y < stack[currgen].ymin)
					stack[currgen].ymin=curry+y;
				if (curry+y > stack[currgen].ymax)
					stack[currgen].ymax=curry+y;
			}
			} //for for

			//Record the placement:
			catplaced[nCatsTotal].x=currx;
			catplaced[nCatsTotal].y=curry;
			catplaced[nCatsTotal].n=currcatn;
			catplaced[nCatsTotal].orient=currorient;
			catplaced[nCatsTotal].gen=currgen;
			nCatsTotal++;
			stack[currgen].numcats++;

			return 1;
		} //isgood && reacts
		currx++;
	} //while currx
	currx=0;
	curry++;
	} //while curry
	curry=0;
	currorient++;
	} //while currorient
	currorient=0;
	currcatn++;
	} //while currcatn
	return 0; //No way to place a catalyst
} //placecatalyst()

/* This function removes the last catalyst that was placed, according to
the array catplaced[]. It backs up to the generation when that catalyst
was placed. It returns 1 if there are no catalysts to remove.
*/
int removelastcat()
{
	if (nCatsTotal==0)
		return 1;
	nCatsTotal--;

	currgen=catplaced[nCatsTotal].gen;
	currx=catplaced[nCatsTotal].x;
	curry=catplaced[nCatsTotal].y;
	currcatn=catplaced[nCatsTotal].n;
	currorient=catplaced[nCatsTotal].orient;

	stack[currgen].numcats--;

	int catszx, catszy, x, y;
	char catcell;

	if (currorient<4)
	{
		catszy=cata[currcatn].y;
		catszx=cata[currcatn].x;
	}
	else
	{
		catszy=cata[currcatn].x;
		catszx=cata[currcatn].y;
	}

	//Undo the placement of the catalyst, by restoring the values of
	//new[][] and history[][].
	//Also, remove the catalyst from boardgen0[][]:
	for (x=0; x<catszx; x++)
		for (y=0; y<catszy; y++)
		{
			if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
			else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
			else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
			else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
			else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
			else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
			else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
			else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

			if (catcell=='o' || catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]= 1<<0;
				stack[currgen].history[currx+x][curry+y]= 1<<0;
				boardgen0[currx+x][curry+y]=0;
			}

			if (catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]= 1<<0;
				stack[currgen].history[currx+x][curry+y]= 1<<0;
				boardgen0[currx+x][curry+y]=0;
				fixed[currx+x][curry+y]--;
				if (fixed[currx+x][curry+y]<0)
				{
					printf("Error 1 in removelastcat(): fixed[%d][%d] < 0.\n",
						currx+x, curry+y);
					exit(1);
				}
			}

			if (catcell=='x' || catcell=='X')
			{
				fixed[currx+x][curry+y]--;
				if (fixed[currx+x][curry+y]<0)
				{
					printf("Error 2 in removelastcat(): fixed[%d][%d] < 0.\n",
						currx+x, curry+y);
					exit(1);
				}
			}

			if (catcell=='.' || catcell=='1' || catcell=='x')
			{
				stack[currgen].now[currx+x][curry+y] >>= 1;
				stack[currgen].history[currx+x][curry+y] >>= 1;
			}

			if (catcell==':' || catcell=='2' || catcell=='X')
			{
				stack[currgen].now[currx+x][curry+y] >>= 2;
				stack[currgen].history[currx+x][curry+y] >>= 2;
			}

			if (catcell=='$')
			{
				stack[currgen].now[currx+x][curry+y] >>= 4;
				stack[currgen].history[currx+x][curry+y] >>= 4;
			}

			if (catcell=='%')
			{
				stack[currgen].now[currx+x][curry+y] >>= 5;
				stack[currgen].history[currx+x][curry+y] >>= 5;
			}

			if (catcell=='^')
			{
				stack[currgen].now[currx+x][curry+y] >>= 6;
				stack[currgen].history[currx+x][curry+y] >>= 6;
			}
		} //for for

	//Restore the boundary variables:
	stack[currgen].xmin=catplaced[nCatsTotal].oldxmin;
	stack[currgen].xmax=catplaced[nCatsTotal].oldxmax;
	stack[currgen].ymin=catplaced[nCatsTotal].oldymin;
	stack[currgen].ymax=catplaced[nCatsTotal].oldymax;
	//Restore boardgen0[][]'s boundaries:
	g0minx=catplaced[nCatsTotal].g0oldminx;
	g0miny=catplaced[nCatsTotal].g0oldminy;
	g0maxx=catplaced[nCatsTotal].g0oldmaxx;
	g0maxy=catplaced[nCatsTotal].g0oldmaxy;

	return 0;
} //removelastcat()

void printgen(int gen)
{
	int x, y;

	for (y=stack[gen].ymin; y<=stack[gen].ymax; y++)
	{
		for (x=stack[gen].xmin; x<=stack[gen].xmax; x++)
			if (stack[gen].now[x][y] & LiveBit)
				if (fixed[x][y]) printf("*");
				else printf("o");
			else
				if (fixed[x][y]) printf(",");
				else printf(".");
		printf("\n");
	}
//	printf("Catalysts placed at gens:");
//	for (int n=0; n<nCatsTotal; n++)
//		printf(" %d", catplaced[n].gen);
	printf("\nGeneration %d\n   -----\n", gen);
} //printgen()

void printboard(FILE *f)
{
	int x, y;
	char ch;

	for (y=g0miny; y<=g0maxy; y++)
	{
		for (x=g0minx; x<=g0maxx; x++)
			//Fix this:
			if (boardgen0[x][y])
				if (fixed[x][y]) fprintf(f, "*");
				else fprintf(f, "o");
			else
				if (fixed[x][y]) fprintf(f, ",");
				else fprintf(f, ".");

		fprintf(f, "\n");
	}

	fprintf(f,"\nFinal position:\n");
	for (y=stack[currgen].ymin; y<=stack[currgen].ymax; y++)
	{
		for (x=stack[currgen].xmin; x<=stack[currgen].xmax; x++)
			if (stack[currgen].now[x][y] & LiveBit) fprintf(f, "*");
			else fprintf(f,".");

		fprintf(f, "\n");
	}
fprintf(f, "\n%d glider%s escaped.",stack[currgen].numgliders, stack[currgen].numgliders==1?"":"s");

for (x=1; x==currgen; x++)
{
	printgen(x);
	do { scanf("%c", &ch); } while (ch!='\n');
}
} //printboard()

void checkboard(FILE *f)
{
	int x, y, z;

	z=0;

	for (y=g0miny; y<=g0maxy; y++)
	{
		// check for any mismatches with target pattern
		for (x=g0minx; x<=g0maxx; x++)
			if (((stack[currgen].now[x][y] & LiveBit) && (targetgen0[x][y] == 1)) ||
				(((stack[currgen].now[x][y] & LiveBit) == 0) && (targetgen0[x][y] == 2))) z=1;

			//if (stack[currgen].now[x][y] & LiveBit)
			//{
			//	if (target[x][y] == 1) z=1;
			//}
			//else if (target[x][y] == 2) z=1;
	}
	if (z == 0)
	{
		fprintf(f, "\nPattern matched target!\n");
		nummatched++;
	}
} //checkboard()

void printinfo(FILE *f)
{
	int n;

	fprintf(f, "%d catalyst%s", nCatsTotal, nCatsTotal==1?"":"s");
	if (nCatsTotal>0)
	{
		fprintf(f, ", which react%s", nCatsTotal==1 ? "s at gen." : " at gens.:");
		for (n=0; n<nCatsTotal; n++)
			fprintf(f, " %d", catplaced[n].gen);
	}
	else
		fprintf(f, ".");
	fprintf(f, "\nGeneration reached: %d\n   -----\n", currgen);
} //printinfo()


void PrintAllBoard(FILE *f)
{
    int x, y;

    for (y=0; y<SZ; y++)
    {
        for (x=0; x<SZ; x++)
            //Fix this:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "A");
                else fprintf(f, "C");
            else if (fixed[x][y]) fprintf(f, ".");
            else fprintf(f, ".");

        fprintf(f, "$");
		//fprintf(f, "\n");
    }

	fprintf(f, "50$");
	fprintf(f, "\n");
} 

By the way, why did you call it c++ application? there is no single class or std usage? (and I'm glad it's this way, because c works faster, but still just wondering).
Last edited by simsim314 on May 9th, 2014, 3:06 pm, edited 1 time in total.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 9th, 2014, 1:24 pm

simsim314 wrote:By the way, why did you call it c++ application? there is no single class or std usage? (and I'm glad it's this way, because c works faster, but still just wondering.)
Just typing too quickly, probably. My usual vague summary is that the common workhorse Conway's Life search utilities are "C/C++".

It's been over a decade since I last looked at that 'catgl' code, so I remember hardly anything about it except for how to use the various parameters when I run the executable.

I didn't do any benchmarking at the time, by the way, so I have no idea if I slowed the search down tremendously with my extra code to remove gliders -- that's a lot of extra monitoring of the boundaries, and I may well not have done it particularly efficiently. All I know is that catgl still seemed to run pretty fast, and produced more of the results I was looking for...!

As I think I mentioned elsewhere, I've definitely been wishing for a version of catgl that can "roll up" all the candidate outputs that are effectively identical (same active pattern at the end, just different catalyst locations). There are often dozens or hundreds of duplicate candidates; even if you get rid of the glider-eating catalysts, there are still many permutations of fishhook-eater <-> eater2, or added blocks that just uselessly catalyze a dying spark. Several times I've started writing a post-processor to display catgl results in this way, but always had to drop the project before getting all the bugs out.

Another detail I'm not sure I've mentioned: have you looked at 'ptbsearch' as an alternate target for improvement? The big difference is that ptbsearch has much better support for transparent catalysts, which statistically speaking are a very large and important part of known stable circuitry.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 9th, 2014, 3:46 pm

dvgrn wrote: Several times I've started writing a post-processor to display catgl results in this way
This where I think python can come along. Actually it's much more easy to write those "post processors" in python. Python is a much more simple, elegant, light weighted, Object Oriented, scripting language than C, so it's just more simple. This is probably not "objective" measure, because people who write years in C will disagree, but for me at least, python is currently the best "fast develop language", and I prefer it if performance are not critical. So taking 100K of potential catalysts setups, coming from catgl, and "analyze" them in golly with python, is just the simplest and fastest developing approach, that costs maybe extra few seconds of performance (few seconds that I can spare :) ).

I also managed to make the current RLE with constant steps between the results, so in python it would be very easy to know where each catalyst starts. Also just using golly, it's manageable to scroll over all the options of catalysts, more or less. Probably with some scripting it could be done even more precisely.
dvgrn wrote:I didn't do any benchmarking at the time, by the way,
Well I looked at the code diff, and I can say I didn't see anything that can cost more than 30% extra.
dvgrn wrote:...have you looked at 'ptbsearch' as an alternate target for improvement?
Well the answer is yes, and there is a several reasons why I still stick to catalyst/catgl.

1. catgl is very simple single file to modify, it's about 300 lines of active code, making it very easy to maintain, even though it's totally spaghetti code. ptbsearch is a whole project, for this level of "investment" I'll prefer to write my own optimizer. I've already wrote some extremely fast Life runner, that work on bit operations. This kind of optimization can make about 10-60 times faster the current catalyst algorithm. I would like to write something of my own on this topic anyway, after my initial analysis of the current catalysts systems.

2. catalyst is much more "user friendly" on my opinion. It took about 10 sec to understand how to write input for catalyst, and I'm still not sure about ptbsearch, it's just a bit more complex and in the place where I want to enter my own code.

3. The python utility is anyway by far more important than the C "base" it runs. So I want to concentrate most of my effort on scripting part, putting there as much possible reusable OO code. Which can be reused with ptbsearch later on, so catalyst is just a place to start being so simple to modify.

4. It's probably not that hard to modify catalyst to search for transparent catalysts. It won't work that fast, but it's possible in pretty low development effort, just inside placecatalyst function one need to "evolve" the current pattern few ticks instead of just removing it "dumbly" because it "failed to survive" in one generation.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 9th, 2014, 6:52 pm

An Update 1.0.2 with all necessary C changes that will allow a python script.

Code: Select all

/* catgl v1.0.2 released 5/10/2014 by Mciahel Simkin

Based on catgl 5/29/2001 by Dave Greene
Based on catalyst v1.0, released 3/21/2001
   Copyright (c) 2001, Gabriel Nivasch

Changes between catalyst 1.0 and catgl 1.0:

   - all source code combined into a single catgl.cpp file
   - added '1', '0', '+', '-' target options to input (see readme.txt)
   - glider-catcher code avoids rejecting glider-producing reactions
   
Changes between catgl 1.0.1 and catgl 1.0:
   - GCC compatible C code.
   - reactions.rle added output in rle format. 
 
Changes between catgl 1.0.2 and catgl 1.0.1:
	- Input parameters are now defined in setup.txt
	- Ouput files are now defined in setup
	- maxfirstgen added to setup, to allow parallel search. 
	  It defined the maximal allowed generation for the first catalyst to be place.

setup.txt sample: 

reactions.txt
reactions.rle
AAA
..A
AAA!
1
4
6
0
startinggen 1
gens 60
catsurvive 100
catstoplace 2
maxfirstgen 10
  
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SZ 180
#define MAXGENS 180
#define MAXCATS 10

#define NCATS 7
#define CATSZ 15

typedef struct
{
    int x, y;
} cell;

typedef struct
{
    char c[CATSZ][CATSZ];
    int x, y, name;
} catalyst;



/* The following array contains the state of the pattern at each
generation from 0 to the current one. For each generation there are two
arrays: now[SZ][SZ] and history[SZ][SZ]. now[][] contains the state of
the pattern at that generation. The format is as follows:
If the cell is alive, then bit #9 (LiveBit) is set.
In addition, exactly one of the bits #0 to #8 is set, indicating the
number of live neighbors the cell has.

history[][] contains the superposition (inclusive "OR") of all the
states each cell has been in, in all the previous generations.

The purpose of history[][] is to know whether the cell has ever been alive,
and if it was never alive, to know all the different neighbor counts it
has had. If bit #9 is set, it means the cell has been alive at some
point, and each of the bits #0 to #8 that is set indicates a neighbor
count the cell has had at some point.

For each generation there are also four variables indicating the current
boundaries of the pattern, the variable numcats, which indicates how
many catalysts have been placed this generation, and the variable numgliders,
which is the total number of gliders created as of this generation.
*/
struct board
{
	int now[SZ][SZ];
	int history[SZ][SZ];
	int xmin, xmax, ymin, ymax;
	int numcats, numgliders;
} stack[MAXGENS];

//Bit values used in now[][] and history[][]:
#define LiveBit 512
#define CountBitMask 511

//This macro function is used in advance1gen():
#define WillLive(x) ((x)==8 || (x)==516 || (x)==520)

/* The following array is used to print out the patterns created.
At the beginning the initial pattern is set in it. In addition, each
time a catalyst is placed on the board at a certain generation, it is
also placed here. When the catalyst is removed from the board, it is
also removed from here.
*/
int boardgen0[SZ][SZ];
int target[SZ][SZ];
int targetgen0[SZ][SZ];
int g0minx=SZ, g0miny=SZ, g0maxx=0, g0maxy=0;

/* This array indicates which cells are fixed, i.e. if they are dead,
they are not allowed to live, and if they are alive, they are not
allowed to die. There are two sources from which fixed cells can come:
From the catalysts (cells marked 'x', 'X', and '*'), or from the input
pattern of the user. Note that cells that were set fixed dead by the
user or by a catalyst, might be set again as fixed dead by another catalyst.
Then, when the second catalyst is removed, the cell should still remain fixed
dead because of the first catalyst or the user's input. Therefore, the
procedure is as follows:
To set a cell as fixed, add 1. To unset it, substract 1. The cell is free
only if the value is 0. If the value is less than 0, it is an error.
*/
int fixed[SZ][SZ];

/* The following array contains info for each catalyst that has
been placed on the board: position, catalyst number, orientation,
generation placed, and the old values of the pattern boundaries, which
are restored when the catalyst is removed.
*/
struct catposition
{
	int x, y, n, orient, gen;
	int oldxmin, oldxmax, oldymin, oldymax; //Used to restore the values
		//after removing the catalyst.
	int g0oldminx, g0oldminy, g0oldmaxx, g0oldmaxy;
		//Used to restore g0minx, etc., after removing the catalyst.
} catplaced[MAXCATS];

struct cell
{ int x, y;};

struct catalyst
{
	char c[CATSZ][CATSZ];
	int x, y, name;
};

const catalyst cata[NCATS]=
{
	{{{' ', ' ', 'x', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', '*', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '$', '%', '*', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  6, 6, 'eat1'},
	{{{' ', ' ', ' ', '.', '.', 'X', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', '$', '*', '$', 'o', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', '*', '*', '$', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '*', '%', '^', '$', '$', '$', '$', 'X', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', '*', '*', '$', '*', '*', '.', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', '%', '*', '^', '*', '$', '.', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ':', '*', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', '.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', '.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  9, 9, 'eat2'},
	{{{'x', '.', ':', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'2', '*', '$', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'X', '*', '*', '$', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'x', ':', ':', ':', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  4, 6, 'snak'},
	{{{'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  4, 4, 'bloc'},
	{{{' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ':', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ':', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', ':', 'o', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', '.', '*', '$', 'o', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '$', '$', '*', 'X', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  11, 8, 'tubw'},
	{{{' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'o', '$', '*', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  5, 5, '_tub'},
	{{{' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'x', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'o', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {'1', 'X', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	  {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
	  5, 5, 'boat'}
};

int catisused[NCATS]; //The user can select which catalysts to use.

int startinggen, gens, catstoplace, catspergen, catsurvive, maxfirstgen;
int currgen, currx, curry, currcatn, currorient;
int nCatsTotal=0; //Used to index catplaced[]
int callcount=0;
int nummatched=0;

int advance1gen(void);
int placecatalyst(void);
int removelastcat(void);
void printgen(int);
void printboard(FILE *);
void checkboard(FILE *);
void PrintAllBoard(FILE *);
void printinfo(FILE *);

int main()
{
	clock_t t;
    int initial[SZ][SZ];
    int x, y, g;
    int xmin=SZ, xmax=0, ymin=SZ, ymax=0;
    char ch;
    int tofile;
    FILE *fi, *ft;
	FILE *fsetup;
	char c;
	char str[100];
    char reactionstxt[256];
    char reactionsrle[256];

	t = clock();

	for (x=0; x<SZ; x++)
		for (y=0; y<SZ; y++)
		{
			initial[x][y]=0;
			boardgen0[x][y]=0;
			fixed[x][y]=0;

			for (g=0; g<MAXGENS; g++)
			{
				stack[g].now[x][y]= 1<<0;
				stack[g].history[x][y]= 1<<0;
			}
		}

	for (x=0; x<NCATS; x++)
		catisused[x]=0;

	tofile=1;
    fsetup = fopen("setup.txt", "r");

    fscanf (fsetup, "%s", reactionstxt);
    fscanf (fsetup, "%s", reactionsrle);

	if (tofile)
    {
		ft=fopen(reactionstxt, "w");
		fi=fopen(reactionsrle, "w");
		fprintf(fi, "x = 0, y = 0, rule = LifeHistory\n");
	}
	else
		fi=stdout;

	x=3;
	y=3;

	while ((c = getc(fsetup)) != '!')
	{
		if (c == '\n' || c == 'n' || c == 'N')
		{
			x=3;
			y++;
			continue;
		}

		if (c != ' ' && c != '.')
		{
			if (x>=SZ-3)
			{ printf("Error: Pattern too wide.\n"); return 1; }
			if (y>=SZ-3)
			{ printf("Error: Pattern too tall.\n"); return 1; }

			if (c == ',')
			{
				initial[x][y]=1; //Fixed dead
				target[x][y]=1;  //Must (obviously) be dead in final pattern
			}
			else if (c == '*')
			{
				initial[x][y]=2; //Fixed live
				target[x][y]=2;  //Must (obviously) be live in final pattern
			}
			else if (c == '1')
				{
					initial[x][y]=3; //Live
					target[x][y]=2;  //Must be live in final pattern
				}
			else if (c == '0')
				target[x][y]=1;   //Must be dead in final pattern
			else if (c == '+')
				target[x][y]=2;   //Must be live in final pattern
			else if (c == '-')
				{
					initial[x][y]=3; //Live
					target[x][y]=1;  //Must be dead in final pattern
				}
			else
				initial[x][y]=3; //Live

			if (x<xmin) xmin=x;
			if (x>xmax) xmax=x;
			if (y<ymin) ymin=y;
			if (y>ymax) ymax=y;
		}

		x++;
	}

	//Initialize fixed[][], stack[0], boardgen0[][], and targetgen0[][],
	//centering the pattern in the middle of the board:
	int xoffset=(SZ-xmax-xmin-2)/2, yoffset=(SZ-ymax-ymin-2)/2;
	for (x=xmin-1; x<=xmax+1; x++)
		for (y=ymin-1; y<=ymax+1; y++)
		{
			stack[0].now[x+xoffset][y+yoffset]=0;

			if (initial[x][y]>1) //2 or 3, which is alive
			{
				stack[0].now[x+xoffset][y+yoffset]=LiveBit;
				//Little hack so that things work properly:
				stack[0].history[x+xoffset][y+yoffset] = 1<<3;

				boardgen0[x+xoffset][y+yoffset]=1;
			}

			if (initial[x][y]==1 || initial[x][y]==2) //Fixed
				fixed[x+xoffset][y+yoffset]=1;

			// copy target pattern to correct offset
			targetgen0[x+xoffset][y+yoffset]=target[x][y];

			//Count the neighbors:
			int count=(initial[x-1][y-1]>1) + (initial[x][y-1]>1) + (initial[x+1][y-1]>1)
			         +(initial[x-1][y  ]>1)                       + (initial[x+1][y  ]>1)
			         +(initial[x-1][y+1]>1) + (initial[x][y+1]>1) + (initial[x+1][y+1]>1);

			if (count<0 || count>8)
			{ printf("Error: count is out of range.\n"); return 1; }

			//Check for inconsistency:
			if ((initial[x][y]==2 && count!=2 && count!=3) ||
				(initial[x][y]==1 && count==3))
			{ printf("Error: Inconsistency in the starting pattern.\n"); return 1; }

			//Set the neighbor count bit:
			stack[0].now[x+xoffset][y+yoffset] |= 1<<count;
		} //for for

	//Set boundary variables:
	stack[0].xmin= g0minx= xmin-1 + xoffset;
	stack[0].xmax= g0maxx= xmax+1 + xoffset;
	stack[0].ymin= g0miny= ymin-1 + yoffset;
	stack[0].ymax= g0maxy= ymax+1 + yoffset;

	stack[0].numcats=0;

	//Repeat the initial pattern to the user:
	printf("\nYou entered:\n");
	if (tofile)
		fprintf(ft, "Initial pattern:\n");
	for (y=ymin; y<=ymax; y++)
	{
		for (x=xmin; x<=xmax; x++)
		{
			switch (initial[x][y])
			{
				case 0: ch='.'; break;
				case 1: ch=','; break;
				case 2: ch='*'; break;
				case 3: ch='o'; break;
			}
			printf("%c", ch);
			if (tofile)
				fprintf(ft, "%c", ch);
		}
		printf("\n");
		if (tofile)
			fprintf(ft, "\n");
	}

	//Also repeat the target grid (temp diagnostic)
	printf("\nYou entered a target pattern of:\n");
	if (tofile)
		fprintf(ft, "Target pattern:\n");
	for (y=ymin; y<=ymax; y++)
	{
		for (x=xmin; x<=xmax; x++)
		{
			switch (target[x][y])
			{
				case 0: ch='.'; break;
				case 1: ch=','; break;
				case 2: ch='*'; break;
			}
			printf("%c", ch);
			if (tofile)
				fprintf(ft, "%c", ch);
		}
		printf("\n");
		if (tofile)
			fprintf(ft, "\n");
	}

	while (1)
	{
		int i = 0;

		fscanf (fsetup, "%d", &i);


		if(i == 0)
			break;

		if (i>NCATS || i<0)
			continue;

		catisused[i - 1]=1;

		printf("The catalyst = %d\n",i);

	}

	if (tofile)
	{
        fprintf(ft, "\nCatalysts to use:\n");
		for (x=0; x<NCATS; x++)
			if (catisused[x])
                fprintf(ft, "%c%c%c%c\n", cata[x].name>>24, cata[x].name>>16,
					cata[x].name>>8, cata[x].name);
	}


	fscanf (fsetup, "%s", str);
	fscanf (fsetup, "%d", &startinggen);
	printf("\nEarliest generation for placing catalysts: %d\n", startinggen);

	fscanf (fsetup, "%s", str);
	fscanf (fsetup, "%d", &gens);
	printf("\nLatest generation for placing catalysts:  %d\n", gens);

	if (tofile)
        fprintf(ft, "\nGenerations for placing catalysts: %d through %d.\n",
			startinggen, gens);

	fscanf (fsetup, "%s", str);
	fscanf (fsetup, "%d", &catsurvive);
	printf("\nHow many generations must a catalyst survive:   %d\n", catsurvive);

	gens += catsurvive;
	if (gens>=MAXGENS)
	{
		printf("Error: %d exceeds the maximum number of generations, which is %d.\n",
			gens, MAXGENS-1);
		return 1;
	}

	fscanf (fsetup, "%s", str);
	fscanf (fsetup, "%d", &catstoplace);
	printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fscanf (fsetup, "%s", str);
	fscanf (fsetup, "%d", &maxfirstgen);
	printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fclose(fsetup);

	if (catstoplace>MAXCATS)
	{
		printf("Error: %d exceeds the maximum number of catalysts, which is %d.\n",
			catstoplace, MAXCATS);
		return 1;
	}

	/* The purpose of this option was to allow the user to restrict the number of
	catalysts that can be placed in one generation, in order to reduce the
	huge number of possibilities that occur when catstoplace has a large value.
	However, searching with a large value of catstoplace is inconvenient in any
	case. Therefore, I deactivated this option but still left it in the code.
	To re-activate it, un-comment this code and remove the line
	"catspergen=catstoplace;":
	*/
	/*printf("Maximum number of catalysts in one generation? ");
	scanf("%d", &catspergen);*/
	catspergen=catstoplace;


	printf("\n");

	if (tofile)
	{
        fprintf(ft, "Catalysts must survive %d generation%s.\n", catsurvive,
			catsurvive==1?"":"s");

        fprintf(ft, "%d catalysts maximum (%d maximum in one generation).\n\n",
        	catstoplace, catspergen);
        fprintf(ft, "%d catalyst%s maximum.\n\n", catstoplace,
			catstoplace==1?"":"s");
	}

	currgen=0;
	currx=0;
	curry=0;
	currcatn=0;
	currorient=0;

	int numprinted=0;

	//Main loop:
	while(currgen>=0)
	{
        if(catplaced[0].gen > maxfirstgen)
            break;
		//Place another catalyst at this generation
		//until you can no more.
		if (currgen >= startinggen && currgen+catsurvive <= gens)
			while (nCatsTotal < catstoplace &&
				stack[currgen].numcats < catspergen &&
				placecatalyst())
			{ }

		if (currgen==gens)
		{
            fprintf(ft, "Pattern # %d:\n", ++numprinted);
            PrintAllBoard(fi);
			printboard(ft);
            printinfo(ft);

			if (removelastcat())
				break; //Finished. Out of main loop.

			//Increment the current position:
			currx++;
			continue;
		}

		//Advance 1 generation.
		if (advance1gen())
		{
			if (nCatsTotal==0 ||
				currgen-catplaced[nCatsTotal-1].gen>=catsurvive)
			{
                fprintf(ft, "Pattern # %d:\n", ++numprinted);
                PrintAllBoard(fi);
				printboard(ft);
                printinfo(ft);
			}

			if (removelastcat())
				break; //Finished. Out of main loop.

			//Increment the current position:
			currx++;
		}

		if (callcount%1000==0)
		{
			printf("%d calls to advance1gen().\n", callcount);
			printinfo(stdout);
			if (tofile)
			{
                fprintf(ft, "%d calls to advance1gen().\n", callcount);
                printinfo(ft);
			}
		}


	}

	printf("The end.\n%d call%s to advance1gen()\n"
		"%d pattern%s printed.\n%d pattern%s matched target.\n",
		callcount, callcount==1?"":"s",
		numprinted, numprinted==1?"":"s",
		nummatched, nummatched==1?"":"s");
	if (tofile)
		fprintf(ft, "The end.\n%d call%s to advance1gen()\n"
			"%d pattern%s printed.\n%d pattern%s matched target.\n",
			callcount, callcount==1?"":"s",
			numprinted, numprinted==1?"":"s",
			nummatched, nummatched==1?"":"s");

	t = clock() - t;
	printf ("Total time: %d miliseconds\n",t);

	if(tofile)
	{
		fclose (ft);
		fclose (fi);
	}
	//getch();
	return 0;
} //main()

/* This function advances the pattern 1 generation and checks that nothing
of this occurs:
- A 'fixed' cell changes
- The pattern reaches the edge of the board

If one of these occurs the function returns 1.
*/
int advance1gen(void)
{
	int x, y, xtemp, ytemp;

	callcount++;

	if (currgen==MAXGENS)
	{ printf("Error in advance1gen(): Too many generations.\n"); exit(1); }

	do
	{
		if (stack[currgen].xmin == 1)
		{
			xtemp=2;
			for (ytemp=2; ytemp<SZ-2; ytemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp+1][ytemp] &
						stack[currgen].now[xtemp+2][ytemp+1] &
						stack[currgen].now[xtemp+2][ytemp-1] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp+1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit)) break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
						stack[currgen].now[xtemp+1][ytemp+2] | stack[currgen].now[xtemp+2][ytemp+2] |
						stack[currgen].now[xtemp+3][ytemp+2] | stack[currgen].now[xtemp+3][ytemp+1] |
						stack[currgen].now[xtemp+3][ytemp]) & LiveBit) break;
					if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
						stack[currgen].now[xtemp+1][ytemp-2] | stack[currgen].now[xtemp+2][ytemp-2] |
						stack[currgen].now[xtemp+3][ytemp-2] | stack[currgen].now[xtemp+3][ytemp-1] |
						stack[currgen].now[xtemp+2][ytemp]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp+2][ytemp+1] = 1;
					stack[currgen].now[xtemp+2][ytemp-1] = 1;

					stack[currgen].history[xtemp-1][ytemp-1] = 1;
					stack[currgen].history[xtemp-1][ytemp] = 1;
					stack[currgen].history[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].xmin++;
				}
			}
		}
		if (stack[currgen].xmax == SZ-2)
		{
			xtemp=SZ-3;
			for (ytemp=2; ytemp<SZ-2; ytemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp-1][ytemp] &
						stack[currgen].now[xtemp-2][ytemp+1] &
						stack[currgen].now[xtemp-2][ytemp-1] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp-1][ytemp+1]) & LiveBit)) break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
						stack[currgen].now[xtemp-1][ytemp+2] | stack[currgen].now[xtemp-2][ytemp+2] |
						stack[currgen].now[xtemp-3][ytemp+2] | stack[currgen].now[xtemp-3][ytemp+1] |
						stack[currgen].now[xtemp-3][ytemp]) & LiveBit) break;
					if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
						stack[currgen].now[xtemp-1][ytemp-2] | stack[currgen].now[xtemp-2][ytemp-2] |
						stack[currgen].now[xtemp-3][ytemp-2] | stack[currgen].now[xtemp-3][ytemp-1] |
						stack[currgen].now[xtemp-2][ytemp]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp-2][ytemp+1] = 1;
					stack[currgen].now[xtemp-2][ytemp-1] = 1;

					stack[currgen].history[xtemp+1][ytemp-1] = 1;
					stack[currgen].history[xtemp+1][ytemp] = 1;
					stack[currgen].history[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].xmax--;
				}
			}
		}
		if (stack[currgen].ymin == 1)
		{
			ytemp=2;
			for (xtemp=2; xtemp<SZ-2; xtemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp][ytemp+1] &
						stack[currgen].now[xtemp+1][ytemp+2] &
						stack[currgen].now[xtemp-1][ytemp+2] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp+1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit))
						break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
						stack[currgen].now[xtemp+2][ytemp+1] | stack[currgen].now[xtemp+2][ytemp+2] |
						stack[currgen].now[xtemp+2][ytemp+3] | stack[currgen].now[xtemp+1][ytemp+3] |
						stack[currgen].now[xtemp][ytemp+3]) & LiveBit) break;
					if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
						stack[currgen].now[xtemp-2][ytemp+1] | stack[currgen].now[xtemp-2][ytemp+2] |
						stack[currgen].now[xtemp-2][ytemp+3] | stack[currgen].now[xtemp-1][ytemp+3] |
						stack[currgen].now[xtemp][ytemp+2]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+2] = 1;
					stack[currgen].now[xtemp-1][ytemp+2] = 1;

					stack[currgen].history[xtemp-1][ytemp-1] = 1;
					stack[currgen].history[xtemp][ytemp-1] = 1;
					stack[currgen].history[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].ymin++;
				}
			}
		}
		if (stack[currgen].ymax == SZ-2)
		{
			ytemp=SZ-3;
			for (xtemp=2; xtemp<SZ-2; xtemp++)
			{
				if (stack[currgen].now[xtemp][ytemp] & LiveBit)
				{
					/* the three cells (besides the leading cell) that must be on in any glider */
					if (!(stack[currgen].now[xtemp][ytemp-1] &
						stack[currgen].now[xtemp+1][ytemp-2] &
						stack[currgen].now[xtemp-1][ytemp-2] & LiveBit)) break;
					/* one of these two cells must be on, depending on glider direction */
					if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp-1]) & LiveBit))
						break;
					/* other neigboring cells must all be off */
					if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
						stack[currgen].now[xtemp+2][ytemp-1] | stack[currgen].now[xtemp+2][ytemp-2] |
						stack[currgen].now[xtemp+2][ytemp-3] | stack[currgen].now[xtemp+1][ytemp-3] |
						stack[currgen].now[xtemp][ytemp-3]) & LiveBit) break;
					if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
						stack[currgen].now[xtemp-2][ytemp-1] | stack[currgen].now[xtemp-2][ytemp-2] |
						stack[currgen].now[xtemp-2][ytemp-3] | stack[currgen].now[xtemp-1][ytemp-3] |
						stack[currgen].now[xtemp][ytemp-2]) & LiveBit) break;
					/* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
					   but good enough.  Clear glider pattern and return a match for this cell */
					stack[currgen].now[xtemp][ytemp] = 1;
					stack[currgen].now[xtemp-1][ytemp] = 1;
					stack[currgen].now[xtemp+1][ytemp] = 1;
					stack[currgen].now[xtemp][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-1] = 1;
					stack[currgen].now[xtemp-1][ytemp-1] = 1;
					stack[currgen].now[xtemp+1][ytemp-2] = 1;
					stack[currgen].now[xtemp-1][ytemp-2] = 1;

					stack[currgen].history[xtemp-1][ytemp+1] = 1;
					stack[currgen].history[xtemp][ytemp+1] = 1;
					stack[currgen].history[xtemp+1][ytemp+1] = 1;
					stack[currgen].now[xtemp-1][ytemp+1] = 1;
					stack[currgen].now[xtemp][ytemp+1] = 1;
					stack[currgen].now[xtemp+1][ytemp+1] = 1;

					stack[currgen].numgliders++;
					stack[currgen].ymax--;
				}
			}
		}
		break;
		/* what's the proper way to do this kind of thing, anyway? */
	}
	while(1);

	if (stack[currgen].xmin<=1 || stack[currgen].xmax>= SZ-2 ||
	    stack[currgen].ymin<=1 || stack[currgen].ymax>= SZ-2)
	    return 1; //Edge of the board reached.

	stack[currgen+1].xmin=SZ;
	stack[currgen+1].xmax=0;
	stack[currgen+1].ymin=SZ;
	stack[currgen+1].ymax=0;

	//We have to clear additional space around the pattern because it may
	//contain junk from before, and the function placecatalyst() sometimes
	//expands the boundaries without clearing.
	int xstart=stack[currgen].xmin-1-CATSZ,
		ystart=stack[currgen].ymin-1-CATSZ;
	if (xstart<1) xstart=1;
	if (ystart<1) ystart=1;
	for (x=xstart; x<=stack[currgen].xmax+1+CATSZ && x<SZ-1; x++)
		for (y=ystart; y<=stack[currgen].ymax+1+CATSZ && y<SZ-1; y++)
		{
			if (x<stack[currgen].xmin-1 || x>stack[currgen].xmax+1 ||
				y<stack[currgen].ymin-1 || y>stack[currgen].ymax+1)
			{
				stack[currgen+1].now[x][y]= 1<<0;
				stack[currgen+1].history[x][y]= 1<<0;
				continue;
			}

			//Set the cell itself's next generation:
			if(WillLive(stack[currgen].now[x][y]))
				stack[currgen+1].now[x][y] = LiveBit;
			else
				stack[currgen+1].now[x][y] = 0;

			//Count number of the live neighbors the cell will have the next
			//generation:
			int count=0;
			if (WillLive(stack[currgen].now[x-1][y-1])) count++;
			if (WillLive(stack[currgen].now[x-1][y  ])) count++;
			if (WillLive(stack[currgen].now[x-1][y+1])) count++;
			if (WillLive(stack[currgen].now[x  ][y-1])) count++;
			if (WillLive(stack[currgen].now[x  ][y+1])) count++;
			if (WillLive(stack[currgen].now[x+1][y-1])) count++;
			if (WillLive(stack[currgen].now[x+1][y  ])) count++;
			if (WillLive(stack[currgen].now[x+1][y+1])) count++;

			//If it's a fixed cell, check that it will not change 2 generations
			//from now. We assume that it has already been checked that it will
			//not change next generation:
			if (fixed[x][y] &&
				(((stack[currgen].now[x][y] & LiveBit) && count!=2 && count!=3) ||
				((stack[currgen].now[x][y] & LiveBit)==0 && count==3)))
				return 1;

			//Set the cell's count bit:
			stack[currgen+1].now[x][y] |= 1<<count;

			//Also set the next generation's history board:
			stack[currgen+1].history[x][y] = stack[currgen].history[x][y] |
				stack[currgen].now[x][y];

			//Adjust next gen's boundary variables if necessary.
			//Note that the pattern's boundaries will never become smaller than
			//in a previous generation, because of history[][]:
			if ((stack[currgen+1].now[x][y] & (~1)) ||
				(stack[currgen+1].history[x][y] & (~1)))
			{
				if (x < stack[currgen+1].xmin) stack[currgen+1].xmin=x;
				if (x > stack[currgen+1].xmax) stack[currgen+1].xmax=x;
				if (y < stack[currgen+1].ymin) stack[currgen+1].ymin=y;
				if (y > stack[currgen+1].ymax) stack[currgen+1].ymax=y;
			}
		} //for for

	stack[currgen+1].numgliders=stack[currgen].numgliders;
	currgen++;
	currx=curry=currcatn=currorient=0;
	return 0;
} //advance1gen()

/* This function looks for a place to place a catalyst on this generation's
board, starting from the position specified by the globals currx, curry,
currorient, and currcatn, and going ahead. It makes sure that the
catalyst would not have reacted at any previous generation. It also makes
sure that the catalyst will react now, i.e. next generation.
*/
int placecatalyst()
{
	int catszx, catszy;
	char catcell;

	while (currcatn<NCATS)
	{
	//Skip this catalyst if it is deactivated:
	if (!catisused[currcatn])
	{
		currorient=0;
		currcatn++;
		continue;
	}

	while (currorient<8)
	{
	//Skip some orientations for symmetric patterns:
	if (cata[currcatn].name=='eat2')
		if (currorient>=4)
			break;
	if (cata[currcatn].name=='bloc' || cata[currcatn].name=='_tub')
		if (currorient==1 || currorient==3 || currorient==5 || currorient==7)
		{
			curry=0;
			currorient++;
			continue;
		}

	if (currorient<4)
	{
		catszy=cata[currcatn].y;
		catszx=cata[currcatn].x;
	}
	else
	{
		catszy=cata[currcatn].x;
		catszx=cata[currcatn].y;
	}
	if (curry<stack[currgen].ymin - catszy)
		curry=stack[currgen].ymin - catszy;

	while (curry<SZ-catszy && curry<=stack[currgen].ymax + catszy)
	{
	if (currx<stack[currgen].xmin - catszx)
		currx=stack[currgen].xmin - catszx;

	while (currx<SZ-catszx && currx<=stack[currgen].xmax + catszx)
	{
		int x, y, reacts=0, isgood=1;

		//Check the catalyst in this position cell by cell:
		for (x=0; x<catszx && isgood; x++)
		for (y=0; y<catszy && isgood; y++)
		{
		//Select the cell according to the orientation:
		if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
		else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
		else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
		else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
		else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
		else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
		else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
		else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

		if (catcell=='o' || catcell=='*')
			if ((stack[currgen].history[currx+x][curry+y] != 1<<0) ||
				(fixed[currx+x][curry+y] > 0))
			{ isgood=0; continue; }

		if (catcell=='.' || catcell=='1' || catcell=='x')
			if (stack[currgen].history[currx+x][curry+y] &
				(LiveBit | (1<<2) | (1<<3)))
			{ isgood=0; continue; }

		if (catcell=='$' || catcell=='%' || catcell=='^')
			if (stack[currgen].history[currx+x][curry+y] & LiveBit)
			{ isgood=0; continue; }

		if (catcell==':' || catcell=='2' || catcell=='X')
			if (stack[currgen].history[currx+x][curry+y] &
				(LiveBit | (1<<1) | (1<<3)))
			{ isgood=0; continue; }

		if (catcell=='1')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
				reacts=1;

		if (catcell=='2')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
				reacts=1;

		if (catcell=='x')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
			{ isgood=0; continue; }

		if (catcell=='X')
			if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
			{ isgood=0; continue; }

		//Reject also if the catalyst will cause a birth in a fixed cell:
		if (fixed[currx+x][curry+y] && (catcell=='.' || catcell=='1') &&
			(stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
			{ isgood=0; continue; }

		if (fixed[currx+x][curry+y] && (catcell==':' || catcell=='2') &&
			(stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
			{ isgood=0; continue; }
		} //for for

		if (isgood && reacts)
		{
			//Save the current values of the boundary variables:
			catplaced[nCatsTotal].oldxmin=stack[currgen].xmin;
			catplaced[nCatsTotal].oldxmax=stack[currgen].xmax;
			catplaced[nCatsTotal].oldymin=stack[currgen].ymin;
			catplaced[nCatsTotal].oldymax=stack[currgen].ymax;
			//Save the current boundaries of boardgen0[][]:
			catplaced[nCatsTotal].g0oldminx=g0minx;
			catplaced[nCatsTotal].g0oldminy=g0miny;
			catplaced[nCatsTotal].g0oldmaxx=g0maxx;
			catplaced[nCatsTotal].g0oldmaxy=g0maxy;

			//Place the catalyst on the board.
			//Also, modify the history board as if the
			//catalyst was always there.
			//Also place the catalyst on boardgen0[][]:
			for (x=0; x<catszx; x++)
			for (y=0; y<catszy; y++)
			{
			if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
			else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
			else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
			else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
			else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
			else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
			else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
			else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

			//Adjust now[][],  history[][], and boardgen0[][]:
			if (catcell=='o')
			{
				stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
				stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
				boardgen0[currx+x][curry+y]=2;
			}
			//It doesn't necessarily have 3 neighbors, but it doesn't matter.

			if (catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
				stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
				boardgen0[currx+x][curry+y]=2;
				fixed[currx+x][curry+y]++;
			}

			//Adjust the boundaries of boardgen0[][]:
			if (catcell=='o' || catcell=='*' || catcell=='x' || catcell=='X')
			{
				if (currx+x < g0minx) g0minx=currx+x;
				if (currx+x > g0maxx) g0maxx=currx+x;
				if (curry+y < g0miny) g0miny=curry+y;
				if (curry+y > g0maxy) g0maxy=curry+y;
			}

			if (catcell=='.' || catcell=='1' || catcell=='x')
			{
				stack[currgen].now[currx+x][curry+y] <<= 1;
				stack[currgen].history[currx+x][curry+y] <<= 1;
			}

			if (catcell==':' || catcell=='2' || catcell=='X')
			{
				stack[currgen].now[currx+x][curry+y] <<= 2;
				stack[currgen].history[currx+x][curry+y] <<= 2;
			}

			if (catcell=='$')
			{
				stack[currgen].now[currx+x][curry+y] <<= 4;
				stack[currgen].history[currx+x][curry+y] <<= 4;
			}

			if (catcell=='%')
			{
				stack[currgen].now[currx+x][curry+y] <<= 5;
				stack[currgen].history[currx+x][curry+y] <<= 5;
			}

			if (catcell=='^')
			{
				stack[currgen].now[currx+x][curry+y] <<= 6;
				stack[currgen].history[currx+x][curry+y] <<= 6;
			}

			if (catcell=='x' || catcell=='X')
				fixed[currx+x][curry+y]++;

			//Adjust the boundary variables if necessary:
			if (catcell != ' ')
			{
				if (currx+x < stack[currgen].xmin)
					stack[currgen].xmin=currx+x;
				if (currx+x > stack[currgen].xmax)
					stack[currgen].xmax=currx+x;
				if (curry+y < stack[currgen].ymin)
					stack[currgen].ymin=curry+y;
				if (curry+y > stack[currgen].ymax)
					stack[currgen].ymax=curry+y;
			}
			} //for for

			//Record the placement:
			catplaced[nCatsTotal].x=currx;
			catplaced[nCatsTotal].y=curry;
			catplaced[nCatsTotal].n=currcatn;
			catplaced[nCatsTotal].orient=currorient;
			catplaced[nCatsTotal].gen=currgen;
			nCatsTotal++;
			stack[currgen].numcats++;

			return 1;
		} //isgood && reacts
		currx++;
	} //while currx
	currx=0;
	curry++;
	} //while curry
	curry=0;
	currorient++;
	} //while currorient
	currorient=0;
	currcatn++;
	} //while currcatn
	return 0; //No way to place a catalyst
} //placecatalyst()

/* This function removes the last catalyst that was placed, according to
the array catplaced[]. It backs up to the generation when that catalyst
was placed. It returns 1 if there are no catalysts to remove.
*/
int removelastcat()
{
	if (nCatsTotal==0)
		return 1;
	nCatsTotal--;

	currgen=catplaced[nCatsTotal].gen;
	currx=catplaced[nCatsTotal].x;
	curry=catplaced[nCatsTotal].y;
	currcatn=catplaced[nCatsTotal].n;
	currorient=catplaced[nCatsTotal].orient;

	stack[currgen].numcats--;

	int catszx, catszy, x, y;
	char catcell;

	if (currorient<4)
	{
		catszy=cata[currcatn].y;
		catszx=cata[currcatn].x;
	}
	else
	{
		catszy=cata[currcatn].x;
		catszx=cata[currcatn].y;
	}

	//Undo the placement of the catalyst, by restoring the values of
	//new[][] and history[][].
	//Also, remove the catalyst from boardgen0[][]:
	for (x=0; x<catszx; x++)
		for (y=0; y<catszy; y++)
		{
			if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
			else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
			else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
			else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
			else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
			else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
			else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
			else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

			if (catcell=='o' || catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]= 1<<0;
				stack[currgen].history[currx+x][curry+y]= 1<<0;
				boardgen0[currx+x][curry+y]=0;
			}

			if (catcell=='*')
			{
				stack[currgen].now[currx+x][curry+y]= 1<<0;
				stack[currgen].history[currx+x][curry+y]= 1<<0;
				boardgen0[currx+x][curry+y]=0;
				fixed[currx+x][curry+y]--;
				if (fixed[currx+x][curry+y]<0)
				{
					printf("Error 1 in removelastcat(): fixed[%d][%d] < 0.\n",
						currx+x, curry+y);
					exit(1);
				}
			}

			if (catcell=='x' || catcell=='X')
			{
				fixed[currx+x][curry+y]--;
				if (fixed[currx+x][curry+y]<0)
				{
					printf("Error 2 in removelastcat(): fixed[%d][%d] < 0.\n",
						currx+x, curry+y);
					exit(1);
				}
			}

			if (catcell=='.' || catcell=='1' || catcell=='x')
			{
				stack[currgen].now[currx+x][curry+y] >>= 1;
				stack[currgen].history[currx+x][curry+y] >>= 1;
			}

			if (catcell==':' || catcell=='2' || catcell=='X')
			{
				stack[currgen].now[currx+x][curry+y] >>= 2;
				stack[currgen].history[currx+x][curry+y] >>= 2;
			}

			if (catcell=='$')
			{
				stack[currgen].now[currx+x][curry+y] >>= 4;
				stack[currgen].history[currx+x][curry+y] >>= 4;
			}

			if (catcell=='%')
			{
				stack[currgen].now[currx+x][curry+y] >>= 5;
				stack[currgen].history[currx+x][curry+y] >>= 5;
			}

			if (catcell=='^')
			{
				stack[currgen].now[currx+x][curry+y] >>= 6;
				stack[currgen].history[currx+x][curry+y] >>= 6;
			}
		} //for for

	//Restore the boundary variables:
	stack[currgen].xmin=catplaced[nCatsTotal].oldxmin;
	stack[currgen].xmax=catplaced[nCatsTotal].oldxmax;
	stack[currgen].ymin=catplaced[nCatsTotal].oldymin;
	stack[currgen].ymax=catplaced[nCatsTotal].oldymax;
	//Restore boardgen0[][]'s boundaries:
	g0minx=catplaced[nCatsTotal].g0oldminx;
	g0miny=catplaced[nCatsTotal].g0oldminy;
	g0maxx=catplaced[nCatsTotal].g0oldmaxx;
	g0maxy=catplaced[nCatsTotal].g0oldmaxy;

	return 0;
} //removelastcat()

void printgen(int gen)
{
	int x, y;

	for (y=stack[gen].ymin; y<=stack[gen].ymax; y++)
	{
		for (x=stack[gen].xmin; x<=stack[gen].xmax; x++)
			if (stack[gen].now[x][y] & LiveBit)
				if (fixed[x][y]) printf("*");
				else printf("o");
			else
				if (fixed[x][y]) printf(",");
				else printf(".");
		printf("\n");
	}
//	printf("Catalysts placed at gens:");
//	for (int n=0; n<nCatsTotal; n++)
//		printf(" %d", catplaced[n].gen);
	printf("\nGeneration %d\n   -----\n", gen);
} //printgen()

void printboard(FILE *f)
{
	int x, y;
	char ch;

	for (y=g0miny; y<=g0maxy; y++)
	{
		for (x=g0minx; x<=g0maxx; x++)
			//Fix this:
			if (boardgen0[x][y])
				if (fixed[x][y]) fprintf(f, "*");
				else fprintf(f, "o");
			else
				if (fixed[x][y]) fprintf(f, ",");
				else fprintf(f, ".");

		fprintf(f, "\n");
	}

	fprintf(f,"\nFinal position:\n");
	for (y=stack[currgen].ymin; y<=stack[currgen].ymax; y++)
	{
		for (x=stack[currgen].xmin; x<=stack[currgen].xmax; x++)
			if (stack[currgen].now[x][y] & LiveBit) fprintf(f, "*");
			else fprintf(f,".");

		fprintf(f, "\n");
	}
fprintf(f, "\n%d glider%s escaped.",stack[currgen].numgliders, stack[currgen].numgliders==1?"":"s");

for (x=1; x==currgen; x++)
{
	printgen(x);
	do { scanf("%c", &ch); } while (ch!='\n');
}
} //printboard()

void checkboard(FILE *f)
{
	int x, y, z;

	z=0;

	for (y=g0miny; y<=g0maxy; y++)
	{
		// check for any mismatches with target pattern
		for (x=g0minx; x<=g0maxx; x++)
			if (((stack[currgen].now[x][y] & LiveBit) && (targetgen0[x][y] == 1)) ||
				(((stack[currgen].now[x][y] & LiveBit) == 0) && (targetgen0[x][y] == 2))) z=1;

			//if (stack[currgen].now[x][y] & LiveBit)
			//{
			//	if (target[x][y] == 1) z=1;
			//}
			//else if (target[x][y] == 2) z=1;
	}
	if (z == 0)
	{
		fprintf(f, "\nPattern matched target!\n");
		nummatched++;
	}
} //checkboard()

void printinfo(FILE *f)
{
	int n;

	fprintf(f, "%d catalyst%s", nCatsTotal, nCatsTotal==1?"":"s");
	if (nCatsTotal>0)
	{
		fprintf(f, ", which react%s", nCatsTotal==1 ? "s at gen." : " at gens.:");
		for (n=0; n<nCatsTotal; n++)
			fprintf(f, " %d", catplaced[n].gen);
	}
	else
		fprintf(f, ".");
	fprintf(f, "\nGeneration reached: %d\n   -----\n", currgen);
} //printinfo()


void PrintAllBoard(FILE *f)
{
    int x, y;

    for (y=0; y<SZ; y++)
    {
        for (x=0; x<SZ; x++)
            //Fix this:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "A");
                else fprintf(f, "C");
            else if (fixed[x][y]) fprintf(f, ".");
            else fprintf(f, ".");

        fprintf(f, "$");
		//fprintf(f, "\n");
    }

	fprintf(f, "50$");
	fprintf(f, "\n");
}
seup.txt example (should be places in the same folder:

Code: Select all

reactions.txt
reactions.rle
AAA
..A
AAA!
4
0
startinggen 1
gens 60
catsurvive 100
catstoplace 2
maxfirstgen 10
Last edited by simsim314 on May 10th, 2014, 5:42 pm, edited 1 time in total.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 10th, 2014, 9:38 am

simsim314 wrote:4. It's probably not that hard to modify catalyst to search for transparent catalysts. It won't work that fast, but it's possible in pretty low development effort, just inside placecatalyst function one need to "evolve" the current pattern few ticks instead of just removing it "dumbly" because it "failed to survive" in one generation.
Well, yes and no. "It won't work that fast" I'll definitely agree with: ptbsearch searches get exponentially slower as the number of allowed transparent objects goes up -- for obvious reasons: you have to continue every branch of the search for much longer before you know for certain that it's a dead end. As long as there's an active pattern near the transparent catalyst's location, and it hasn't done any permanent damage to nearby non-transparent catalysts, it's still perfectly possible that it will settle back to exactly the object you want.

Evolving the pattern a "few ticks" I'm not so sure about. For each transparent catalyst you place, you're really doing a fairly huge speculative search, looking for stabilizations that happen to restore the object. It might be worth looking at all possible placements of two or three non-transparent catalysts, for example, up to dozens of ticks after the transparent object is destroyed. ptbsearch allows multiple simultaneous transparent objects, making the search problem exponentially worse.

-- I'm not sure any circuits have actually been found yet that have more than one transparent object active at a time. Possibly as a practical matter it makes sense to support just one transparent object: if you've placed an object and it has disappeared, you have to resolve that problem or give up on that branch of the search tree.

In other words... the initial basis of a 'catalyst' search is often a transparent object, e.g., an initial block+glider interaction. Once a branch has been found where that initial block is restored, that's a big discovery. It might then make sense to work extra hard to find a resolution of the remaining active reaction, to the point of throwing in another transparent object to try to settle it into something useful. But in the interests of completing searches within days rather than decades, it might make sense to avoid speculative multi-transparent branches of the tree.

Some statistics on known transparent objects: the Fx77 conduit takes 35 ticks to rebuild its transparent block, and that's a quick recovery -- 53 ticks for the F166 conduit, and 54 ticks for the F117 and other similar block+snake R-to-H conversions, is probably a more usual number. The second transparent block in the boojum reflector reappears in only 24 ticks, but the first block takes over 100 ticks.

The F77 and F166 blocks are simple transparent catalysts -- you add the block and run the pattern, and a block magically reappears in the same place, with the rest of the active reaction at a safe distance that can be separately catalyzed into something useful. The other cases are "assisted" reactions: additional catalysts are needed after the transparent-object interaction to encourage it to reappear. For the snake+block catalyst, if you don't put the snake in just the right place, you don't get the block back. In the boojum reflector, you again need an eater to catalyze the reappearance of the first block, and a second eater to protect the first one from a destructive spark.

Transparent beehives are similar -- there are known examples, some simple and some assisted, with 45, 44, 41, 33, and 18-tick recovery times. There's a 54-tick simple transparent loaf, of all things. Seems as if there should be more transparent boats out there, but other objects are rare enough in random ash that those reactions will be really hard to find. Even a transparent pond or tub would be very impressive, and less symmetrical transparent objects would be correspondingly rarer. No doubt there are piles of transparent-blinker reactions, of course, but that would limit the circuit to P2 so nobody's been too interested in hunting for them.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 10th, 2014, 3:55 pm

Here is the python GUI script (place catgl.exe in python scripts folder together with this script):

Code: Select all

#python GUI for Catgl. V0.1 released 5/10/2014 by Mciahel Simkin
#Runs catgl.exe with current golly pattern. 
#saves/load the latest settings. 
#creates setup.txt and waits for catgl to finish running. 
#opens rle report from catgl
# - multiproccess is not implemented yet

import golly as g 
import sys, os
from Tkinter import *
from tkFileDialog import askopenfilename
import tkMessageBox
import os.path
import time
import subprocess

class CatglForm(Frame):
   def __init__(self, master):
      Frame.__init__(self, master)
      
      self.master = master
      
      master.wm_attributes("-topmost", 1)
      master.geometry("400x400")
      master.title("Catalyst Search")

      #self.pathlabel = Label(master, height=1, width=100,text="Path to patgl.exe:")
      #self.pathlabel.place(x=-295, y=0)

      #self.fileChooserButton = Button(master, width=2, text ="...", command = self.ChooseCatglFile)
      #self.fileChooserButton.place(x=372, y=14)

      self.filePath = StringVar()
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python"),"catgl.exe"))
      
      #self.fileEntry = Entry(master, width=58, textvariable=self.filePath)
      #self.fileEntry.place(x=10, y=20)
    
      self.catalystsLabels = ["Eater 1", "Eater 2", "Snake", "Block", "Block and tub with tail", "Tub", "Boat"]
      self.catalystUse = []
      self.lasty = 0
      
      for i in range(len(self.catalystsLabels)):
         self.catalystUse.append(IntVar())
         l = Checkbutton(master, text=self.catalystsLabels[i], variable=self.catalystUse[i])
         self.lasty = 20 * i 
         l.place(x=10, y=self.lasty)
      
      self.lasty += 30
      dx = 280
      
      self.firstgen = StringVar()
      
      temp = Label(master, height=1, width=100,text="First Generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.firstgen)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.gens = StringVar()
      
      temp = Label(master, height=1, width=100,text="Last generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.gens)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.survive = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of generation for catalyst to survive:")
      temp.place(x=-220, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.survive)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.cats = StringVar()
      
      temp = Label(master, height=1, width=100,text="Maximal number of catalyst to place:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.cats)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.threads = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of parallel processes to run:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.threads)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 40

      
      self.startButton = Button(master, width=20, text ="Start", command = self.Start)
      self.startButton.place(x=120, y=self.lasty)
      
      self.Load()
      
    
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python"),"catgl.exe"))
      
   def ChooseCatglFile(self):
      filename = askopenfilename()
      self.filePath.set(filename)
   
   def Start(self):
   
      if self.filePath.get() == "": 
         tkMessageBox.showinfo("","Please choose path to catgl.exe")
         return
      
      catalystList = ""
      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"
      
      if catalystList == "": 
         tkMessageBox.showinfo("","Please choose at least one catalyst")
         return
      else: 
         catalystList += "0"
         
         
      if not self.firstgen.get().isdigit():
         tkMessageBox.showinfo("","First generation parameter must be integer")
         return
         
         
      if not self.gens.get().isdigit():
         tkMessageBox.showinfo("","Last generation parameter must be integer")
         return
         
      if not self.survive.get().isdigit():
         tkMessageBox.showinfo("","Generations to survive parameter must be integer")
         return
         
      if not self.cats.get().isdigit():
         tkMessageBox.showinfo("","Number of catalyst parameter must be integer")
         return
         
      if not self.threads.get().isdigit():
         tkMessageBox.showinfo("","Number of process to run parameter must be integer")
         return
      

     
      with open(os.path.join(os.path.dirname(self.filePath.get()),"setup.txt"), "w") as text_file:
         text_file.write("reactions.txt\n")
         text_file.write("reactions.rle\n")
         
         rect = g.getrect(); 
         
         for i in xrange(rect[0], rect[0] + rect[2] + 1):
            for j in xrange(rect[1], rect[1] + rect[3] + 1):   
               if g.getcell(i, j) == 0: 
                  text_file.write(".")
               else:
                  text_file.write("A")
            
            if i == rect[0] + rect[2]:
               text_file.write("!")
            else:
               text_file.write("\n")
         
         text_file.write("\n")
         text_file.write(catalystList)
         
         text_file.write("\nstartinggen " + self.firstgen.get())
         text_file.write("\ngens " + self.gens.get())
         text_file.write("\ncatsurvive " + self.survive.get())
         text_file.write("\ncatstoplace " + self.cats.get())
         text_file.write("\nmaxfirstgen " + self.gens.get())
         
      self.Save()


      p = subprocess.Popen([self.filePath.get()])

      while p.poll() is None:
         time.sleep(0.5)

      g.open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"))
      
      self.master.quit()
      self.master.destroy()
      g.exit("")

   def Save(self):
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
   
      with open(fname, "w") as text_file:
         
         catalystList = ""
         
         for i in range(len(self.catalystUse)):
            if self.catalystUse[i].get() == 1:
               catalystList += str(i + 1) + "\n"
         
         catalystList += "0"
         text_file.write(catalystList)
         text_file.write("\n" + self.filePath.get())
         text_file.write("\n" + self.firstgen.get())
         text_file.write("\n" + self.gens.get())
         text_file.write("\n" + self.survive.get())
         text_file.write("\n" + self.cats.get())
         text_file.write("\n" + self.threads.get())
         
   def Load(self):
      
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
      
      if not os.path.isfile(fname):
         return
         
      with open(fname, "r") as text_file:
      
         while True: 
            line = text_file.readline()
            
            if line.strip() == "0":
               break
            
            self.catalystUse[int(line) - 1].set(1)
            
         self.filePath.set(text_file.readline().strip())
         self.firstgen.set(text_file.readline().strip())
         self.gens.set(text_file.readline().strip())
         self.survive.set(text_file.readline().strip())
         self.cats.set(text_file.readline().strip())
         self.threads.set(text_file.readline().strip())
         
root = Tk()
app = CatglForm(root)
root.mainloop()
Notice: a small modification made to catgl 1.0.2 source (getch() removed in the end).
Notice: Multiprocessing and analysis is not implemented yet. It's only a GUI shell for catgl now. It's still more pleasant to run this script directly from golly.
Last edited by simsim314 on May 11th, 2014, 3:29 am, edited 2 times in total.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 10th, 2014, 6:45 pm

dvgrn wrote: For each transparent catalyst you place, you're really doing a fairly huge speculative search, looking for stabilization that happen to restore the object. It might be worth looking at all possible placements of two or three non-transparent catalysts, for example, up to dozens of ticks after the transparent object is destroyed.
Well this case is definitely slows down the search significantly. I meant just to look few ticks ahead for transparent catalysts without any "support". Those cases are not so significantly slowing down the system, on the other hand they can introduce some new findings. From the cases you mentioned, around 50% are "self sustainable" transparent catalyst, so I would focus on them first.

Another important detail: when looking at Herschel conduits it's better to approach the search as sequence of "influences", concentrating on those interactions that "throw the action further away", so one can "split" the search into small batches (exactly the opposite of G->H search where we want the action to be concentrated around the starting block). So although some of the conduits have 5-6 SLs the search of transparent interaction can be reduced to only one "single helper catalyst" making the search pretty fast, even two helpers per transparent block will perform reasonably well. The main thing to notice here is that conduits are working as long as they "transfer the action further". Herschel is just one of the more common possibilities, but one can "ignore" the type of the "internal mess" and concentrate purely on "transferring the action forward".

Also conduits are much less "headache", and we have pretty nice collection of them to built whatever we need. The main issue is G->H. Although on the other hand, with tandem approach we can reach some pretty fast recovery time mechanisms, and the battle neck will probably be the Herschel splitters. Anyway fast single G->H will solve a lot of problems, for example there would be no reason to use splitters at all. It's also the most potential field of research, because one can recognize the last improvement on the subject was at late 90s, and it's performance is pretty poor, having only the simplest and the most "simple to find" converters, that have problems and require a long recovery time (usually to get rid of extra mess of these "bad" reactions). So concentrating the search on G->H is crucial in my opinion. On the way we create nice tools that can help with conduits and H->G or whatever.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 12th, 2014, 8:34 am

simsim314 wrote:Here is the python GUI script (place catgl.exe in python scripts folder together with this script)...
Notice: Multiprocessing and analysis is not implemented yet. It's only a GUI shell for catgl now. It's still more pleasant to run this script directly from golly.
Looks very nice! Reloads the last settings after a successful run, too.

I had to change "-252" and similar coordinates to "-200", to keep the left edges of the labels from being cut off by the left edge of the form. Also, to make the whole package more portable, I used

Code: Select all

self.filePath.set(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])),"catgl.exe"))
after the form's self.Load(). Seemed to work fine. That way I can hide the script and executable in their own "catgl" folder, which can be moved anywhere without breaking anything.

Other than that, the only odd thing I noticed was that there was no text entry option for "maxfirstgen" from the sample setup.txt.

Ctrl+C in the catgl.exe window appears to cancel an ongoing search without losing any work. As soon as the command window closes, the script displays whatever results have been generated up to that point.

-- I wonder how this kind of GUI shell could be made Mac- and Linux-compatible...

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 12th, 2014, 9:53 am

dvgrn wrote:I had to change "-252" and similar coordinates to "-200",
Hmm this is pretty annoying, tkinter GUI is very unpleasant. There are ways to avoid those kind of things using some other packing options, which themselves have some annoying limitation, although probably for this application it wouldn't really matter, I just so much don't like to work with predefined layouts which limit me. Anyway I'll probably make the form more portable in this sense, so the feedback was helpful.
dvgrn wrote: That way I can hide the script and executable in their own "catgl" folder, which can be moved anywhere without breaking anything.
How can you place a script in some other folder other than scripts/python?
dvgrn wrote: there was no text entry option for "maxfirstgen" from the sample setup.txt
The maxfirstgen is not user option, but an option that allows parallel search. Say you want to search 1-50 generations. So you make two processes:
1. searches 1-50 with maxfirstgen 25
2. startgen 26 and last gen 50.

Those processes run in parallel and cover the whole search space. This is of course simplification, in reality I'll make smaller steps, and in case some process had finished I'll assign to it the "next search space".

Currently I didn't implement parallel searching, so this option is pretty useless in this sense, although I guess you can find it somehow useful, but I don't want to overwhelm the user with too much definitions.

----

Anyway I'm a bit more concerned now with python "post analysis script", and I'm seriously thinking of writing my own C++ utility (which I discovered works with same speed as C if you don't use STL).

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 12th, 2014, 10:31 am

simsim314 wrote:
dvgrn wrote: That way I can hide the script and executable in their own "catgl" folder, which can be moved anywhere without breaking anything.
How can you place a script in some other folder other than scripts/python?
The particular case I was thinking of was a "catgl" subfolder under the Scripts folder; I keep a lot of utility scripts for editing in the main Scripts folder, and don't want to have to scroll through too many scripts to find the ones I use all the time.

It's also perfectly possible to put a script in any other folder you want, though. There's one mixed in with the patterns in Patterns/Life/Oscillators, for example. I'll often have Golly run a script from the Desktop folder, especially if it's some kind of data-processing task that needs a lot of subsidiary lookup tables or text files or whatever -- no point in cluttering up Scripts with those. You can also define keyboard shortcuts to run any script anywhere, which gives you instant access from Golly.

Another question just occurred to me: it looks as if you included all of my strange optional characters for defining target patterns, in your current 1.0.2 source -- '+' = dead, must be live in final pattern; '-' = live, must be dead in final pattern, etc. But the input format is now just .'s and A's. Do you have any plans to allow some kind of LifeHistory multi-state input to use my pattern-matching options? Search for "Pattern matched target!" in the code to find the very simple pattern matching system that I added, with an extra target[] array.

More likely, will you just be starting over as far as implementing post-processing and pattern-matching?

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 12th, 2014, 1:17 pm

dvgrn wrote: Do you have any plans to allow some kind of LifeHistory multi-state input to use my pattern-matching options? Search for "Pattern matched target!"
This is kind of question to you: what is the status of target pattern matching? is it "nice to have"? or you don't report at all stuff that doesn't match?

This target matching is a must, because when you do "second iteration" catgl will generate also catalyst that destroy other catalysts from the input pattern, so the target matching comes handy. But as I understand if pattern doesn't matches target, it still reports it. So we maybe need some additional flags for it.

Anyway those kind of things can be analyzed inside the python script pretty straight forward as well, so I'm not sure if I should invest some extra time on this.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » May 12th, 2014, 3:22 pm

-- Forgot to mention: the Python script could use a test for g.getrect==[] {universe is empty}, with a message explaining to the user how to set up a test pattern. Otherwise you can click Start multiple times, and not see any error messages until you give up and close the Tkinter form. Then you get a whole pile of errors, and occasionally a Golly crash it seems.
simsim314 wrote:This is kind of question to you: what is the status of target pattern matching? is it "nice to have"? or you don't report at all stuff that doesn't match?
My additional target matching options were always a little bit kludgey. I ran a lot of searches using those options over the years, and found some minor new thing every now and then. The 4H->G converter used in the linear Life replicator was partly a catgl find, if I recall correctly.

My general method was to run a search with the target defined as exactly the output I was hoping for -- block restored to the right place, or whatever was required for that specific search. At the end of a run I'd do a text search for "Pattern matched target" -- and would be really pleasantly surprised if there was anything there!

An exact match was usually set up to be a very unlikely event; more often I'd get no matches at all. Then I'd have to look through the stuff that didn't match, and pick out the ones with the best active regions, that traveled well and left the least debris, let's say. If I could find something that looked as if a solution might still be possible with a few more catalysts, then I'd do a new run with that as a starting point.

The glider-catching code turned out to be particularly useful in searches. You can pretty much always add an eater if you don't really want the glider output. I turned up a few new H-to-G converters by searching result files for the optional extra line that reports the number of gliders that escaped.

For the record, here's the relevant section from ReadMe.txt about changes going from catalyst to catgl:

Code: Select all

In the input pattern, four more characters are now considered legal:
 
   '1' means "ON at the start, ON at the end, don't care what happens in between"
   '0' means "OFF at the start, OFF at the end, don't care what happens in between"
   '+' means "OFF at the start, ON at the end, don't care what happens in between"
   '-' means "ON at the start, OFF at the end, don't care what happens in between".
 
The '1' and '0' made sense at the time, though they may be vaguely confusing if you know about the (unrelated) 1's and 2's in the catalyst definitions.  
 
The program does not suppress non-matching output -- it just prints an extra line saying

     Pattern matched target!
 
when it does match.  A text search for 'matched' in reactions.txt will quickly tell you if anything really interesting has turned up.
 
In 'catgl' there's also a count after each pattern of the number of gliders that were suppressed along the edges of the reaction area, where they otherwise would have escaped.  The glider-catcher code was added when 'catalyst' was found to reject perfectly good solutions just because they threw off an extra glider that moved outside the allowed bounding box during the search period.
simsim314 wrote:This target matching is a must, because when you do "second iteration" catgl will generate also catalyst that destroy other catalysts from the input pattern, so the target matching comes handy. But as I understand if pattern doesn't matches target, it still reports it. So we maybe need some additional flags for it.
The original catalyst utility included ways to specify cells that must remain OFF -- ',' -- and must remain ON -- '*'. I just added a few more optional extra target cell types, as described above. The ones you really need to support are the always-OFF and always-ON ones. You could do that in LifeHistory using states 3 and 4, I think.

State 2 would be useful for forbidding catalyst placements in a given region. The way to do that in the original catalyst was to include a scattering single ON cells in the starting pattern, lining the forbidden region thickly enough that catalysts could not fit between the ON cells -- that area would count as "already used" and not suitable for new placements. The History state is supposed to convey the same information.

It should be fairly easy to allow B cell types as well as C and D ones, and set up the history[] array accordingly from T=0, instead of effectively doing the same thing at T=1. State 3 "C" cells are already there in the current output, but those should probably be plain A cells. The C (always-ON) cells are the ones in the catalyst that are never supposed to turn off. The D cells (always-OFF) are equally useful to have in the output, to transfer into new searches.

Supporting 1, 0, + and - cells in your Python GUI shell would require setting up initial patterns in a new LifeCatGl rule with more states, not trivially convertible to LifeHistory or Life. I don't think I had enough success with those extra options to make them worth supporting here -- especially if you're going to end up with equivalent or better tools to post-process the results.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 13th, 2014, 8:16 am

dvgrn wrote:...You could do that in LifeHistory using states 3 and 4, I think. State 2 would be useful for forbidding catalyst placements in a given region...
OOps... I actually sensed the need for those but simply missed the point of those states (I'm new to catalyst searches). It's small fix and I already written the code. I added the following input options:

B - Set dead history
C - Always live
D - Always dead

Have some bug in it, so be tuned for update.

I also fixed some small bugs:

1. The orientation is fixed and as in original.
2. Empty state, will alert and show the rules.

Will post soon.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 13th, 2014, 10:29 am

OK seems to be working...

Here is Catgl.c

Code: Select all

/* catgl v1.0.3 released 5/13/2014 by Mciahel Simkin

Based on catgl 5/29/2001 by Dave Greene
Based on catalyst v1.0, released 3/21/2001
   Copyright (c) 2001, Gabriel Nivasch

Changes between catalyst 1.0 and catgl 1.0:

   - all source code combined into a single catgl.cpp file
   - added '1', '0', '+', '-' target options to input (see readme.txt)
   - glider-catcher code avoids rejecting glider-producing reactions
   
Changes between catgl 1.0.1 and catgl 1.0:
   - GCC compatible C code.
   - reactions.rle added output in rle format. 

Changes between catgl 1.0.2 and catgl 1.0.1:
   - Input parameters are now defined in setup.txt
   - Ouput files are now defined in setup
   - maxfirstgen added to setup, to allow parallel search. 
     It defines the maximal allowed generation for the first catalyst to be place.

Changes between catgl 1.0.3 and catgl 1.0.2:
    - New inpit states added B,C,D.
	- B sets history to 3 dead cells
	- C same as '*' - always alive. 
	- D same as ',' - always dead. 
	
setup.txt sample: 

reactions.txt
reactions.rle
AAA
..A
AAA!
1
4
6
0
startinggen 1
gens 60
catsurvive 100
catstoplace 2
maxfirstgen 10
  
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SZ 180
#define MAXGENS 250
#define MAXCATS 10

#define NCATS 7
#define CATSZ 15

typedef struct
{
    int x, y;
} cell;

typedef struct
{
    char c[CATSZ][CATSZ];
    int x, y, name;
} catalyst;



/* The following array contains the state of the pattern at each
generation from 0 to the current one. For each generation there are two
arrays: now[SZ][SZ] and history[SZ][SZ]. now[][] contains the state of
the pattern at that generation. The format is as follows:
If the cell is alive, then bit #9 (LiveBit) is set.
In addition, exactly one of the bits #0 to #8 is set, indicating the
number of live neighbors the cell has.

history[][] contains the superposition (inclusive "OR") of all the
states each cell has been in, in all the previous generations.

The purpose of history[][] is to know whether the cell has ever been alive,
and if it was never alive, to know all the different neighbor counts it
has had. If bit #9 is set, it means the cell has been alive at some
point, and each of the bits #0 to #8 that is set indicates a neighbor
count the cell has had at some point.

For each generation there are also four variables indicating the current
boundaries of the pattern, the variable numcats, which indicates how
many catalysts have been placed this generation, and the variable numgliders,
which is the total number of gliders created as of this generation.
*/
struct board
{
   int now[SZ][SZ];
   int history[SZ][SZ];
   int xmin, xmax, ymin, ymax;
   int numcats, numgliders;
} stack[MAXGENS];

//Bit values used in now[][] and history[][]:
#define LiveBit 512
#define CountBitMask 511

//This macro function is used in advance1gen():
#define WillLive(x) ((x)==8 || (x)==516 || (x)==520)

/* The following array is used to print out the patterns created.
At the beginning the initial pattern is set in it. In addition, each
time a catalyst is placed on the board at a certain generation, it is
also placed here. When the catalyst is removed from the board, it is
also removed from here.
*/
int boardgen0[SZ][SZ];
int target[SZ][SZ];
int targetgen0[SZ][SZ];
int g0minx=SZ, g0miny=SZ, g0maxx=0, g0maxy=0;

/* This array indicates which cells are fixed, i.e. if they are dead,
they are not allowed to live, and if they are alive, they are not
allowed to die. There are two sources from which fixed cells can come:
From the catalysts (cells marked 'x', 'X', and '*'), or from the input
pattern of the user. Note that cells that were set fixed dead by the
user or by a catalyst, might be set again as fixed dead by another catalyst.
Then, when the second catalyst is removed, the cell should still remain fixed
dead because of the first catalyst or the user's input. Therefore, the
procedure is as follows:
To set a cell as fixed, add 1. To unset it, substract 1. The cell is free
only if the value is 0. If the value is less than 0, it is an error.
*/
int fixed[SZ][SZ];

/* The following array contains info for each catalyst that has
been placed on the board: position, catalyst number, orientation,
generation placed, and the old values of the pattern boundaries, which
are restored when the catalyst is removed.
*/
struct catposition
{
   int x, y, n, orient, gen;
   int oldxmin, oldxmax, oldymin, oldymax; //Used to restore the values
      //after removing the catalyst.
   int g0oldminx, g0oldminy, g0oldmaxx, g0oldmaxy;
      //Used to restore g0minx, etc., after removing the catalyst.
} catplaced[MAXCATS];

struct cell
{ int x, y;};

struct catalyst
{
   char c[CATSZ][CATSZ];
   int x, y, name;
};

const catalyst cata[NCATS]=
{
   {{{' ', ' ', 'x', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', '*', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '$', '%', '*', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     6, 6, 'eat1'},
   {{{' ', ' ', ' ', '.', '.', 'X', ':', '2', '1', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', '$', '*', '$', 'o', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', '*', '*', '$', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '*', '%', '^', '$', '$', '$', '$', 'X', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', '*', '*', '$', '*', '*', '.', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', '%', '*', '^', '*', '$', '.', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ':', '*', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', '.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', '.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     9, 9, 'eat2'},
   {{{'x', '.', ':', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'2', '*', '$', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'X', '*', '*', '$', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'x', ':', ':', ':', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     4, 6, 'snak'},
   {{{'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', 'o', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', 'X', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     4, 4, 'bloc'},
   {{{' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ':', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ':', 'o', 'o', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', ':', 'o', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', '.', '*', '$', 'o', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '$', '$', '*', 'X', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '*', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {':', '*', '$', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     11, 8, 'tubw'},
   {{{' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'o', '$', '*', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'.', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', 'x', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     5, 5, '_tub'},
   {{{' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'x', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'o', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'X', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     5, 5, 'boat'}
};

int catisused[NCATS]; //The user can select which catalysts to use.

int startinggen, gens, catstoplace, catspergen, catsurvive, maxfirstgen;
int currgen, currx, curry, currcatn, currorient;
int nCatsTotal=0; //Used to index catplaced[]
int callcount=0;
int nummatched=0;

int advance1gen(void);
int placecatalyst(void);
int removelastcat(void);
void printgen(int);
void printboard(FILE *);
void checkboard(FILE *);
void PrintAllBoard(FILE *);
void printinfo(FILE *);

int main()
{
   clock_t t;
    int initial[SZ][SZ];
    int x, y, g;
    int xmin=SZ, xmax=0, ymin=SZ, ymax=0;
    char ch;
    int tofile;
    FILE *fi, *ft;
   FILE *fsetup;
   char c;
   char str[100];
    char reactionstxt[256];
    char reactionsrle[256];

   t = clock();

   for (x=0; x<SZ; x++)
      for (y=0; y<SZ; y++)
      {
         initial[x][y]=0;
         boardgen0[x][y]=0;
         fixed[x][y]=0;

         for (g=0; g<MAXGENS; g++)
         {
            stack[g].now[x][y]= 1<<0;
            stack[g].history[x][y]= 1<<0;
         }
      }

   for (x=0; x<NCATS; x++)
      catisused[x]=0;

   tofile=1;
    fsetup = fopen("setup.txt", "r");

    fscanf (fsetup, "%s", reactionstxt);
    fscanf (fsetup, "%s", reactionsrle);

   if (tofile)
    {
      ft=fopen(reactionstxt, "w");
      fi=fopen(reactionsrle, "w");
      fprintf(fi, "x = 0, y = 0, rule = LifeHistory\n");
   }
   else
      fi=stdout;

   x=3;
   y=3;

   while ((c = getc(fsetup)) != '!')
   {
      if (c == '\n' || c == 'n' || c == 'N')
      {
         x=3;
         y++;
         continue;
      }

      if (c != ' ' && c != '.')
      {
         if (x>=SZ-3)
         { printf("Error: Pattern too wide.\n"); return 1; }
         if (y>=SZ-3)
         { printf("Error: Pattern too tall.\n"); return 1; }

         if (c == ',' || c == 'D')
         {
            initial[x][y]=1; //Fixed dead
            target[x][y]=1;  //Must (obviously) be dead in final pattern
         }
         else if (c == '*' || c == 'C')
         {
            initial[x][y]=2; //Fixed live
            target[x][y]=2;  //Must (obviously) be live in final pattern
         }
		 else if (c == 'B')
		 {
			 initial[x][y]=-1; //History Dead
		 }
         else if (c == '1')
            {
               initial[x][y]=3; //Live
               target[x][y]=2;  //Must be live in final pattern
            }
         else if (c == '0')
            target[x][y]=1;   //Must be dead in final pattern
         else if (c == '+')
            target[x][y]=2;   //Must be live in final pattern
         else if (c == '-')
            {
               initial[x][y]=3; //Live
               target[x][y]=1;  //Must be dead in final pattern
            }
         else 
            initial[x][y]=3; //Live

         if (x<xmin) xmin=x;
         if (x>xmax) xmax=x;
         if (y<ymin) ymin=y;
         if (y>ymax) ymax=y;
      }

      x++;
   }

   //Initialize fixed[][], stack[0], boardgen0[][], and targetgen0[][],
   //centering the pattern in the middle of the board:
   int xoffset=(SZ-xmax-xmin-2)/2, yoffset=(SZ-ymax-ymin-2)/2;
   for (x=xmin-1; x<=xmax+1; x++)
      for (y=ymin-1; y<=ymax+1; y++)
      {
         stack[0].now[x+xoffset][y+yoffset]=0;
		
		if (initial[x][y]==-1) //History Dead
		{
			 stack[0].history[x+xoffset][y+yoffset] = 1<<3;
		}
        
         if (initial[x][y]>1) //2 or 3, which is alive
         {
            stack[0].now[x+xoffset][y+yoffset]=LiveBit;
            //Little hack so that things work properly:
            stack[0].history[x+xoffset][y+yoffset] = 1<<3;

            boardgen0[x+xoffset][y+yoffset]=1;
         }

         if (initial[x][y]==1 || initial[x][y]==2) //Fixed
            fixed[x+xoffset][y+yoffset]=1;

         // copy target pattern to correct offset
         targetgen0[x+xoffset][y+yoffset]=target[x][y];

         //Count the neighbors:
         int count=(initial[x-1][y-1]>1) + (initial[x][y-1]>1) + (initial[x+1][y-1]>1)
                  +(initial[x-1][y  ]>1)                       + (initial[x+1][y  ]>1)
                  +(initial[x-1][y+1]>1) + (initial[x][y+1]>1) + (initial[x+1][y+1]>1);

         if (count<0 || count>8)
         { printf("Error: count is out of range.\n"); return 1; }

         //Check for inconsistency:
         if ((initial[x][y]==2 && count!=2 && count!=3) ||
            (initial[x][y]==1 && count==3))
         { printf("Error: Inconsistency in the starting pattern.\n"); return 1; }

         //Set the neighbor count bit:
         stack[0].now[x+xoffset][y+yoffset] |= 1<<count;
      } //for for

   //Set boundary variables:
   stack[0].xmin= g0minx= xmin-1 + xoffset;
   stack[0].xmax= g0maxx= xmax+1 + xoffset;
   stack[0].ymin= g0miny= ymin-1 + yoffset;
   stack[0].ymax= g0maxy= ymax+1 + yoffset;

   stack[0].numcats=0;

   //Repeat the initial pattern to the user:
   printf("\nYou entered:\n");
   if (tofile)
      fprintf(ft, "Initial pattern:\n");
   for (y=ymin; y<=ymax; y++)
   {
      for (x=xmin; x<=xmax; x++)
      {
         switch (initial[x][y])
         {
            case 0: ch='.'; break;
            case 1: ch=','; break;
            case 2: ch='*'; break;
            case 3: ch='o'; break;
         }
         printf("%c", ch);
         if (tofile)
            fprintf(ft, "%c", ch);
      }
      printf("\n");
      if (tofile)
         fprintf(ft, "\n");
   }

   //Also repeat the target grid (temp diagnostic)
   printf("\nYou entered a target pattern of:\n");
   if (tofile)
      fprintf(ft, "Target pattern:\n");
   for (y=ymin; y<=ymax; y++)
   {
      for (x=xmin; x<=xmax; x++)
      {
         switch (target[x][y])
         {
            case 0: ch='.'; break;
            case 1: ch=','; break;
            case 2: ch='*'; break;
         }
         printf("%c", ch);
         if (tofile)
            fprintf(ft, "%c", ch);
      }
      printf("\n");
      if (tofile)
         fprintf(ft, "\n");
   }

   while (1)
   {
      int i = 0;

      fscanf (fsetup, "%d", &i);


      if(i == 0)
         break;

      if (i>NCATS || i<0)
         continue;

      catisused[i - 1]=1;

      printf("The catalyst = %d\n",i);

   }

   if (tofile)
   {
        fprintf(ft, "\nCatalysts to use:\n");
      for (x=0; x<NCATS; x++)
         if (catisused[x])
                fprintf(ft, "%c%c%c%c\n", cata[x].name>>24, cata[x].name>>16,
               cata[x].name>>8, cata[x].name);
   }


   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &startinggen);
   printf("\nEarliest generation for placing catalysts: %d\n", startinggen);

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &gens);
   printf("\nLatest generation for placing catalysts:  %d\n", gens);

   if (tofile)
        fprintf(ft, "\nGenerations for placing catalysts: %d through %d.\n",
         startinggen, gens);

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &catsurvive);
   printf("\nHow many generations must a catalyst survive:   %d\n", catsurvive);

   gens += catsurvive;
   if (gens>=MAXGENS)
   {
      printf("Error: %d exceeds the maximum number of generations, which is %d.\n",
         gens, MAXGENS-1);
      return 1;
   }

   fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &catstoplace);
   printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fscanf (fsetup, "%s", str);
   fscanf (fsetup, "%d", &maxfirstgen);
   printf("\nMaximum number of catalysts:   %d\n", catstoplace);

    fclose(fsetup);

   if (catstoplace>MAXCATS)
   {
      printf("Error: %d exceeds the maximum number of catalysts, which is %d.\n",
         catstoplace, MAXCATS);
      return 1;
   }

   /* The purpose of this option was to allow the user to restrict the number of
   catalysts that can be placed in one generation, in order to reduce the
   huge number of possibilities that occur when catstoplace has a large value.
   However, searching with a large value of catstoplace is inconvenient in any
   case. Therefore, I deactivated this option but still left it in the code.
   To re-activate it, un-comment this code and remove the line
   "catspergen=catstoplace;":
   */
   /*printf("Maximum number of catalysts in one generation? ");
   scanf("%d", &catspergen);*/
   catspergen=catstoplace;


   printf("\n");

   if (tofile)
   {
        fprintf(ft, "Catalysts must survive %d generation%s.\n", catsurvive,
         catsurvive==1?"":"s");

        fprintf(ft, "%d catalysts maximum (%d maximum in one generation).\n\n",
           catstoplace, catspergen);
        fprintf(ft, "%d catalyst%s maximum.\n\n", catstoplace,
         catstoplace==1?"":"s");
   }

   currgen=0;
   currx=0;
   curry=0;
   currcatn=0;
   currorient=0;

   int numprinted=0;

   //Main loop:
   while(currgen>=0)
   {
        if(catplaced[0].gen > maxfirstgen)
            break;
      //Place another catalyst at this generation
      //until you can no more.
      if (currgen >= startinggen && currgen+catsurvive <= gens)
         while (nCatsTotal < catstoplace &&
            stack[currgen].numcats < catspergen &&
            placecatalyst())
         { }

      if (currgen==gens)
      {
            fprintf(ft, "Pattern # %d:\n", ++numprinted);
            PrintAllBoard(fi);
         printboard(ft);
            printinfo(ft);

         if (removelastcat())
            break; //Finished. Out of main loop.

         //Increment the current position:
         currx++;
         continue;
      }

      //Advance 1 generation.
      if (advance1gen())
      {
         if (nCatsTotal==0 ||
            currgen-catplaced[nCatsTotal-1].gen>=catsurvive)
         {
                fprintf(ft, "Pattern # %d:\n", ++numprinted);
                PrintAllBoard(fi);
            printboard(ft);
                printinfo(ft);
         }

         if (removelastcat())
            break; //Finished. Out of main loop.

         //Increment the current position:
         currx++;
      }

      if (callcount%1000==0)
      {
         printf("%d calls to advance1gen().\n", callcount);
         printinfo(stdout);
         if (tofile)
         {
                fprintf(ft, "%d calls to advance1gen().\n", callcount);
                printinfo(ft);
         }
      }


   }

   printf("The end.\n%d call%s to advance1gen()\n"
      "%d pattern%s printed.\n%d pattern%s matched target.\n",
      callcount, callcount==1?"":"s",
      numprinted, numprinted==1?"":"s",
      nummatched, nummatched==1?"":"s");
   if (tofile)
      fprintf(ft, "The end.\n%d call%s to advance1gen()\n"
         "%d pattern%s printed.\n%d pattern%s matched target.\n",
         callcount, callcount==1?"":"s",
         numprinted, numprinted==1?"":"s",
         nummatched, nummatched==1?"":"s");

   t = clock() - t;
   printf ("Total time: %d miliseconds\n",t);

   if(tofile)
   {
      fclose (ft);
      fclose (fi);
   }
   //getch();
   return 0;
} //main()

/* This function advances the pattern 1 generation and checks that nothing
of this occurs:
- A 'fixed' cell changes
- The pattern reaches the edge of the board

If one of these occurs the function returns 1.
*/
int advance1gen(void)
{
   int x, y, xtemp, ytemp;

   callcount++;

   if (currgen==MAXGENS)
   { printf("Error in advance1gen(): Too many generations.\n"); exit(1); }

   do
   {
      if (stack[currgen].xmin == 1)
      {
         xtemp=2;
         for (ytemp=2; ytemp<SZ-2; ytemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp+1][ytemp] &
                  stack[currgen].now[xtemp+2][ytemp+1] &
                  stack[currgen].now[xtemp+2][ytemp-1] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp+1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit)) break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
                  stack[currgen].now[xtemp+1][ytemp+2] | stack[currgen].now[xtemp+2][ytemp+2] |
                  stack[currgen].now[xtemp+3][ytemp+2] | stack[currgen].now[xtemp+3][ytemp+1] |
                  stack[currgen].now[xtemp+3][ytemp]) & LiveBit) break;
               if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
                  stack[currgen].now[xtemp+1][ytemp-2] | stack[currgen].now[xtemp+2][ytemp-2] |
                  stack[currgen].now[xtemp+3][ytemp-2] | stack[currgen].now[xtemp+3][ytemp-1] |
                  stack[currgen].now[xtemp+2][ytemp]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp+2][ytemp+1] = 1;
               stack[currgen].now[xtemp+2][ytemp-1] = 1;

               stack[currgen].history[xtemp-1][ytemp-1] = 1;
               stack[currgen].history[xtemp-1][ytemp] = 1;
               stack[currgen].history[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].xmin++;
            }
         }
      }
      if (stack[currgen].xmax == SZ-2)
      {
         xtemp=SZ-3;
         for (ytemp=2; ytemp<SZ-2; ytemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp-1][ytemp] &
                  stack[currgen].now[xtemp-2][ytemp+1] &
                  stack[currgen].now[xtemp-2][ytemp-1] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp-1][ytemp+1]) & LiveBit)) break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp][ytemp+1] | stack[currgen].now[xtemp][ytemp+2] |
                  stack[currgen].now[xtemp-1][ytemp+2] | stack[currgen].now[xtemp-2][ytemp+2] |
                  stack[currgen].now[xtemp-3][ytemp+2] | stack[currgen].now[xtemp-3][ytemp+1] |
                  stack[currgen].now[xtemp-3][ytemp]) & LiveBit) break;
               if ((stack[currgen].now[xtemp][ytemp-1] | stack[currgen].now[xtemp][ytemp-2] |
                  stack[currgen].now[xtemp-1][ytemp-2] | stack[currgen].now[xtemp-2][ytemp-2] |
                  stack[currgen].now[xtemp-3][ytemp-2] | stack[currgen].now[xtemp-3][ytemp-1] |
                  stack[currgen].now[xtemp-2][ytemp]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp-2][ytemp+1] = 1;
               stack[currgen].now[xtemp-2][ytemp-1] = 1;

               stack[currgen].history[xtemp+1][ytemp-1] = 1;
               stack[currgen].history[xtemp+1][ytemp] = 1;
               stack[currgen].history[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].xmax--;
            }
         }
      }
      if (stack[currgen].ymin == 1)
      {
         ytemp=2;
         for (xtemp=2; xtemp<SZ-2; xtemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp][ytemp+1] &
                  stack[currgen].now[xtemp+1][ytemp+2] &
                  stack[currgen].now[xtemp-1][ytemp+2] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp+1] ^ stack[currgen].now[xtemp+1][ytemp+1]) & LiveBit))
                  break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
                  stack[currgen].now[xtemp+2][ytemp+1] | stack[currgen].now[xtemp+2][ytemp+2] |
                  stack[currgen].now[xtemp+2][ytemp+3] | stack[currgen].now[xtemp+1][ytemp+3] |
                  stack[currgen].now[xtemp][ytemp+3]) & LiveBit) break;
               if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
                  stack[currgen].now[xtemp-2][ytemp+1] | stack[currgen].now[xtemp-2][ytemp+2] |
                  stack[currgen].now[xtemp-2][ytemp+3] | stack[currgen].now[xtemp-1][ytemp+3] |
                  stack[currgen].now[xtemp][ytemp+2]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+2] = 1;
               stack[currgen].now[xtemp-1][ytemp+2] = 1;

               stack[currgen].history[xtemp-1][ytemp-1] = 1;
               stack[currgen].history[xtemp][ytemp-1] = 1;
               stack[currgen].history[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].ymin++;
            }
         }
      }
      if (stack[currgen].ymax == SZ-2)
      {
         ytemp=SZ-3;
         for (xtemp=2; xtemp<SZ-2; xtemp++)
         {
            if (stack[currgen].now[xtemp][ytemp] & LiveBit)
            {
               /* the three cells (besides the leading cell) that must be on in any glider */
               if (!(stack[currgen].now[xtemp][ytemp-1] &
                  stack[currgen].now[xtemp+1][ytemp-2] &
                  stack[currgen].now[xtemp-1][ytemp-2] & LiveBit)) break;
               /* one of these two cells must be on, depending on glider direction */
               if (!((stack[currgen].now[xtemp-1][ytemp-1] ^ stack[currgen].now[xtemp+1][ytemp-1]) & LiveBit))
                  break;
               /* other neigboring cells must all be off */
               if ((stack[currgen].now[xtemp+1][ytemp] | stack[currgen].now[xtemp+2][ytemp] |
                  stack[currgen].now[xtemp+2][ytemp-1] | stack[currgen].now[xtemp+2][ytemp-2] |
                  stack[currgen].now[xtemp+2][ytemp-3] | stack[currgen].now[xtemp+1][ytemp-3] |
                  stack[currgen].now[xtemp][ytemp-3]) & LiveBit) break;
               if ((stack[currgen].now[xtemp-1][ytemp] | stack[currgen].now[xtemp-2][ytemp] |
                  stack[currgen].now[xtemp-2][ytemp-1] | stack[currgen].now[xtemp-2][ytemp-2] |
                  stack[currgen].now[xtemp-2][ytemp-3] | stack[currgen].now[xtemp-1][ytemp-3] |
                  stack[currgen].now[xtemp][ytemp-2]) & LiveBit) break;
               /* pattern matches a glider with 1-cell empty boundary -- not quite certain to persist,
                  but good enough.  Clear glider pattern and return a match for this cell */
               stack[currgen].now[xtemp][ytemp] = 1;
               stack[currgen].now[xtemp-1][ytemp] = 1;
               stack[currgen].now[xtemp+1][ytemp] = 1;
               stack[currgen].now[xtemp][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-1] = 1;
               stack[currgen].now[xtemp-1][ytemp-1] = 1;
               stack[currgen].now[xtemp+1][ytemp-2] = 1;
               stack[currgen].now[xtemp-1][ytemp-2] = 1;

               stack[currgen].history[xtemp-1][ytemp+1] = 1;
               stack[currgen].history[xtemp][ytemp+1] = 1;
               stack[currgen].history[xtemp+1][ytemp+1] = 1;
               stack[currgen].now[xtemp-1][ytemp+1] = 1;
               stack[currgen].now[xtemp][ytemp+1] = 1;
               stack[currgen].now[xtemp+1][ytemp+1] = 1;

               stack[currgen].numgliders++;
               stack[currgen].ymax--;
            }
         }
      }
      break;
      /* what's the proper way to do this kind of thing, anyway? */
   }
   while(1);

   if (stack[currgen].xmin<=1 || stack[currgen].xmax>= SZ-2 ||
       stack[currgen].ymin<=1 || stack[currgen].ymax>= SZ-2)
       return 1; //Edge of the board reached.

   stack[currgen+1].xmin=SZ;
   stack[currgen+1].xmax=0;
   stack[currgen+1].ymin=SZ;
   stack[currgen+1].ymax=0;

   //We have to clear additional space around the pattern because it may
   //contain junk from before, and the function placecatalyst() sometimes
   //expands the boundaries without clearing.
   int xstart=stack[currgen].xmin-1-CATSZ,
      ystart=stack[currgen].ymin-1-CATSZ;
   if (xstart<1) xstart=1;
   if (ystart<1) ystart=1;
   for (x=xstart; x<=stack[currgen].xmax+1+CATSZ && x<SZ-1; x++)
      for (y=ystart; y<=stack[currgen].ymax+1+CATSZ && y<SZ-1; y++)
      {
         if (x<stack[currgen].xmin-1 || x>stack[currgen].xmax+1 ||
            y<stack[currgen].ymin-1 || y>stack[currgen].ymax+1)
         {
            stack[currgen+1].now[x][y]= 1<<0;
            stack[currgen+1].history[x][y]= 1<<0;
            continue;
         }

         //Set the cell itself's next generation:
         if(WillLive(stack[currgen].now[x][y]))
            stack[currgen+1].now[x][y] = LiveBit;
         else
            stack[currgen+1].now[x][y] = 0;

         //Count number of the live neighbors the cell will have the next
         //generation:
         int count=0;
         if (WillLive(stack[currgen].now[x-1][y-1])) count++;
         if (WillLive(stack[currgen].now[x-1][y  ])) count++;
         if (WillLive(stack[currgen].now[x-1][y+1])) count++;
         if (WillLive(stack[currgen].now[x  ][y-1])) count++;
         if (WillLive(stack[currgen].now[x  ][y+1])) count++;
         if (WillLive(stack[currgen].now[x+1][y-1])) count++;
         if (WillLive(stack[currgen].now[x+1][y  ])) count++;
         if (WillLive(stack[currgen].now[x+1][y+1])) count++;

         //If it's a fixed cell, check that it will not change 2 generations
         //from now. We assume that it has already been checked that it will
         //not change next generation:
         if (fixed[x][y] &&
            (((stack[currgen].now[x][y] & LiveBit) && count!=2 && count!=3) ||
            ((stack[currgen].now[x][y] & LiveBit)==0 && count==3)))
            return 1;

         //Set the cell's count bit:
         stack[currgen+1].now[x][y] |= 1<<count;

         //Also set the next generation's history board:
         stack[currgen+1].history[x][y] = stack[currgen].history[x][y] |
            stack[currgen].now[x][y];

         //Adjust next gen's boundary variables if necessary.
         //Note that the pattern's boundaries will never become smaller than
         //in a previous generation, because of history[][]:
         if ((stack[currgen+1].now[x][y] & (~1)) ||
            (stack[currgen+1].history[x][y] & (~1)))
         {
            if (x < stack[currgen+1].xmin) stack[currgen+1].xmin=x;
            if (x > stack[currgen+1].xmax) stack[currgen+1].xmax=x;
            if (y < stack[currgen+1].ymin) stack[currgen+1].ymin=y;
            if (y > stack[currgen+1].ymax) stack[currgen+1].ymax=y;
         }
      } //for for

   stack[currgen+1].numgliders=stack[currgen].numgliders;
   currgen++;
   currx=curry=currcatn=currorient=0;
   return 0;
} //advance1gen()

/* This function looks for a place to place a catalyst on this generation's
board, starting from the position specified by the globals currx, curry,
currorient, and currcatn, and going ahead. It makes sure that the
catalyst would not have reacted at any previous generation. It also makes
sure that the catalyst will react now, i.e. next generation.
*/
int placecatalyst()
{
   int catszx, catszy;
   char catcell;

   while (currcatn<NCATS)
   {
   //Skip this catalyst if it is deactivated:
   if (!catisused[currcatn])
   {
      currorient=0;
      currcatn++;
      continue;
   }

   while (currorient<8)
   {
   //Skip some orientations for symmetric patterns:
   if (cata[currcatn].name=='eat2')
      if (currorient>=4)
         break;
   if (cata[currcatn].name=='bloc' || cata[currcatn].name=='_tub')
      if (currorient==1 || currorient==3 || currorient==5 || currorient==7)
      {
         curry=0;
         currorient++;
         continue;
      }

   if (currorient<4)
   {
      catszy=cata[currcatn].y;
      catszx=cata[currcatn].x;
   }
   else
   {
      catszy=cata[currcatn].x;
      catszx=cata[currcatn].y;
   }
   if (curry<stack[currgen].ymin - catszy)
      curry=stack[currgen].ymin - catszy;

   while (curry<SZ-catszy && curry<=stack[currgen].ymax + catszy)
   {
   if (currx<stack[currgen].xmin - catszx)
      currx=stack[currgen].xmin - catszx;

   while (currx<SZ-catszx && currx<=stack[currgen].xmax + catszx)
   {
      int x, y, reacts=0, isgood=1;

      //Check the catalyst in this position cell by cell:
      for (x=0; x<catszx && isgood; x++)
      for (y=0; y<catszy && isgood; y++)
      {
      //Select the cell according to the orientation:
      if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
      else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
      else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
      else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
      else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
      else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
      else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
      else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

      if (catcell=='o' || catcell=='*')
         if ((stack[currgen].history[currx+x][curry+y] != 1<<0) ||
            (fixed[currx+x][curry+y] > 0))
         { isgood=0; continue; }

      if (catcell=='.' || catcell=='1' || catcell=='x')
         if (stack[currgen].history[currx+x][curry+y] &
            (LiveBit | (1<<2) | (1<<3)))
         { isgood=0; continue; }

      if (catcell=='$' || catcell=='%' || catcell=='^')
         if (stack[currgen].history[currx+x][curry+y] & LiveBit)
         { isgood=0; continue; }

      if (catcell==':' || catcell=='2' || catcell=='X')
         if (stack[currgen].history[currx+x][curry+y] &
            (LiveBit | (1<<1) | (1<<3)))
         { isgood=0; continue; }

      if (catcell=='1')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
            reacts=1;

      if (catcell=='2')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
            reacts=1;

      if (catcell=='x')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
         { isgood=0; continue; }

      if (catcell=='X')
         if ((stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
         { isgood=0; continue; }

      //Reject also if the catalyst will cause a birth in a fixed cell:
      if (fixed[currx+x][curry+y] && (catcell=='.' || catcell=='1') &&
         (stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<2))
         { isgood=0; continue; }

      if (fixed[currx+x][curry+y] && (catcell==':' || catcell=='2') &&
         (stack[currgen].now[currx+x][curry+y] & CountBitMask) == (1<<1))
         { isgood=0; continue; }
      } //for for

      if (isgood && reacts)
      {
         //Save the current values of the boundary variables:
         catplaced[nCatsTotal].oldxmin=stack[currgen].xmin;
         catplaced[nCatsTotal].oldxmax=stack[currgen].xmax;
         catplaced[nCatsTotal].oldymin=stack[currgen].ymin;
         catplaced[nCatsTotal].oldymax=stack[currgen].ymax;
         //Save the current boundaries of boardgen0[][]:
         catplaced[nCatsTotal].g0oldminx=g0minx;
         catplaced[nCatsTotal].g0oldminy=g0miny;
         catplaced[nCatsTotal].g0oldmaxx=g0maxx;
         catplaced[nCatsTotal].g0oldmaxy=g0maxy;

         //Place the catalyst on the board.
         //Also, modify the history board as if the
         //catalyst was always there.
         //Also place the catalyst on boardgen0[][]:
         for (x=0; x<catszx; x++)
         for (y=0; y<catszy; y++)
         {
         if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
         else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
         else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
         else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
         else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
         else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
         else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
         else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

         //Adjust now[][],  history[][], and boardgen0[][]:
         if (catcell=='o')
         {
            stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
            stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
            boardgen0[currx+x][curry+y]=2;
         }
         //It doesn't necessarily have 3 neighbors, but it doesn't matter.

         if (catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]=LiveBit+(1<<3);
            stack[currgen].history[currx+x][curry+y]=LiveBit+(1<<3);
            boardgen0[currx+x][curry+y]=2;
            fixed[currx+x][curry+y]++;
         }

         //Adjust the boundaries of boardgen0[][]:
         if (catcell=='o' || catcell=='*' || catcell=='x' || catcell=='X')
         {
            if (currx+x < g0minx) g0minx=currx+x;
            if (currx+x > g0maxx) g0maxx=currx+x;
            if (curry+y < g0miny) g0miny=curry+y;
            if (curry+y > g0maxy) g0maxy=curry+y;
         }

         if (catcell=='.' || catcell=='1' || catcell=='x')
         {
            stack[currgen].now[currx+x][curry+y] <<= 1;
            stack[currgen].history[currx+x][curry+y] <<= 1;
         }

         if (catcell==':' || catcell=='2' || catcell=='X')
         {
            stack[currgen].now[currx+x][curry+y] <<= 2;
            stack[currgen].history[currx+x][curry+y] <<= 2;
         }

         if (catcell=='$')
         {
            stack[currgen].now[currx+x][curry+y] <<= 4;
            stack[currgen].history[currx+x][curry+y] <<= 4;
         }

         if (catcell=='%')
         {
            stack[currgen].now[currx+x][curry+y] <<= 5;
            stack[currgen].history[currx+x][curry+y] <<= 5;
         }

         if (catcell=='^')
         {
            stack[currgen].now[currx+x][curry+y] <<= 6;
            stack[currgen].history[currx+x][curry+y] <<= 6;
         }

         if (catcell=='x' || catcell=='X')
            fixed[currx+x][curry+y]++;

         //Adjust the boundary variables if necessary:
         if (catcell != ' ')
         {
            if (currx+x < stack[currgen].xmin)
               stack[currgen].xmin=currx+x;
            if (currx+x > stack[currgen].xmax)
               stack[currgen].xmax=currx+x;
            if (curry+y < stack[currgen].ymin)
               stack[currgen].ymin=curry+y;
            if (curry+y > stack[currgen].ymax)
               stack[currgen].ymax=curry+y;
         }
         } //for for

         //Record the placement:
         catplaced[nCatsTotal].x=currx;
         catplaced[nCatsTotal].y=curry;
         catplaced[nCatsTotal].n=currcatn;
         catplaced[nCatsTotal].orient=currorient;
         catplaced[nCatsTotal].gen=currgen;
         nCatsTotal++;
         stack[currgen].numcats++;

         return 1;
      } //isgood && reacts
      currx++;
   } //while currx
   currx=0;
   curry++;
   } //while curry
   curry=0;
   currorient++;
   } //while currorient
   currorient=0;
   currcatn++;
   } //while currcatn
   return 0; //No way to place a catalyst
} //placecatalyst()

/* This function removes the last catalyst that was placed, according to
the array catplaced[]. It backs up to the generation when that catalyst
was placed. It returns 1 if there are no catalysts to remove.
*/
int removelastcat()
{
   if (nCatsTotal==0)
      return 1;
   nCatsTotal--;

   currgen=catplaced[nCatsTotal].gen;
   currx=catplaced[nCatsTotal].x;
   curry=catplaced[nCatsTotal].y;
   currcatn=catplaced[nCatsTotal].n;
   currorient=catplaced[nCatsTotal].orient;

   stack[currgen].numcats--;

   int catszx, catszy, x, y;
   char catcell;

   if (currorient<4)
   {
      catszy=cata[currcatn].y;
      catszx=cata[currcatn].x;
   }
   else
   {
      catszy=cata[currcatn].x;
      catszx=cata[currcatn].y;
   }

   //Undo the placement of the catalyst, by restoring the values of
   //new[][] and history[][].
   //Also, remove the catalyst from boardgen0[][]:
   for (x=0; x<catszx; x++)
      for (y=0; y<catszy; y++)
      {
         if      (currorient==0) catcell=cata[currcatn].c[         x][         y];
         else if (currorient==1) catcell=cata[currcatn].c[catszx-1-x][         y];
         else if (currorient==2) catcell=cata[currcatn].c[         x][catszy-1-y];
         else if (currorient==3) catcell=cata[currcatn].c[catszx-1-x][catszy-1-y];
         else if (currorient==4) catcell=cata[currcatn].c[         y][         x];
         else if (currorient==5) catcell=cata[currcatn].c[catszy-1-y][         x];
         else if (currorient==6) catcell=cata[currcatn].c[         y][catszx-1-x];
         else                    catcell=cata[currcatn].c[catszy-1-y][catszx-1-x];

         if (catcell=='o' || catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]= 1<<0;
            stack[currgen].history[currx+x][curry+y]= 1<<0;
            boardgen0[currx+x][curry+y]=0;
         }

         if (catcell=='*')
         {
            stack[currgen].now[currx+x][curry+y]= 1<<0;
            stack[currgen].history[currx+x][curry+y]= 1<<0;
            boardgen0[currx+x][curry+y]=0;
            fixed[currx+x][curry+y]--;
            if (fixed[currx+x][curry+y]<0)
            {
               printf("Error 1 in removelastcat(): fixed[%d][%d] < 0.\n",
                  currx+x, curry+y);
               exit(1);
            }
         }

         if (catcell=='x' || catcell=='X')
         {
            fixed[currx+x][curry+y]--;
            if (fixed[currx+x][curry+y]<0)
            {
               printf("Error 2 in removelastcat(): fixed[%d][%d] < 0.\n",
                  currx+x, curry+y);
               exit(1);
            }
         }

         if (catcell=='.' || catcell=='1' || catcell=='x')
         {
            stack[currgen].now[currx+x][curry+y] >>= 1;
            stack[currgen].history[currx+x][curry+y] >>= 1;
         }

         if (catcell==':' || catcell=='2' || catcell=='X')
         {
            stack[currgen].now[currx+x][curry+y] >>= 2;
            stack[currgen].history[currx+x][curry+y] >>= 2;
         }

         if (catcell=='$')
         {
            stack[currgen].now[currx+x][curry+y] >>= 4;
            stack[currgen].history[currx+x][curry+y] >>= 4;
         }

         if (catcell=='%')
         {
            stack[currgen].now[currx+x][curry+y] >>= 5;
            stack[currgen].history[currx+x][curry+y] >>= 5;
         }

         if (catcell=='^')
         {
            stack[currgen].now[currx+x][curry+y] >>= 6;
            stack[currgen].history[currx+x][curry+y] >>= 6;
         }
      } //for for

   //Restore the boundary variables:
   stack[currgen].xmin=catplaced[nCatsTotal].oldxmin;
   stack[currgen].xmax=catplaced[nCatsTotal].oldxmax;
   stack[currgen].ymin=catplaced[nCatsTotal].oldymin;
   stack[currgen].ymax=catplaced[nCatsTotal].oldymax;
   //Restore boardgen0[][]'s boundaries:
   g0minx=catplaced[nCatsTotal].g0oldminx;
   g0miny=catplaced[nCatsTotal].g0oldminy;
   g0maxx=catplaced[nCatsTotal].g0oldmaxx;
   g0maxy=catplaced[nCatsTotal].g0oldmaxy;

   return 0;
} //removelastcat()

void printgen(int gen)
{
   int x, y;

   for (y=stack[gen].ymin; y<=stack[gen].ymax; y++)
   {
      for (x=stack[gen].xmin; x<=stack[gen].xmax; x++)
         if (stack[gen].now[x][y] & LiveBit)
            if (fixed[x][y]) printf("*");
            else printf("o");
         else
            if (fixed[x][y]) printf(",");
            else printf(".");
      printf("\n");
   }
//   printf("Catalysts placed at gens:");
//   for (int n=0; n<nCatsTotal; n++)
//      printf(" %d", catplaced[n].gen);
   printf("\nGeneration %d\n   -----\n", gen);
} //printgen()

void printboard(FILE *f)
{
   int x, y;
   char ch;

   for (y=g0miny; y<=g0maxy; y++)
   {
      for (x=g0minx; x<=g0maxx; x++)
         //Fix this:
         if (boardgen0[x][y])
            if (fixed[x][y]) fprintf(f, "*");
            else fprintf(f, "o");
         else
            if (fixed[x][y]) fprintf(f, ",");
            else fprintf(f, ".");

      fprintf(f, "\n");
   }

   fprintf(f,"\nFinal position:\n");
   for (y=stack[currgen].ymin; y<=stack[currgen].ymax; y++)
   {
      for (x=stack[currgen].xmin; x<=stack[currgen].xmax; x++)
         if (stack[currgen].now[x][y] & LiveBit) fprintf(f, "*");
         else fprintf(f,".");

      fprintf(f, "\n");
   }
fprintf(f, "\n%d glider%s escaped.",stack[currgen].numgliders, stack[currgen].numgliders==1?"":"s");

for (x=1; x==currgen; x++)
{
   printgen(x);
   do { scanf("%c", &ch); } while (ch!='\n');
}
} //printboard()

void checkboard(FILE *f)
{
   int x, y, z;

   z=0;

   for (y=g0miny; y<=g0maxy; y++)
   {
      // check for any mismatches with target pattern
      for (x=g0minx; x<=g0maxx; x++)
         if (((stack[currgen].now[x][y] & LiveBit) && (targetgen0[x][y] == 1)) ||
            (((stack[currgen].now[x][y] & LiveBit) == 0) && (targetgen0[x][y] == 2))) z=1;

         //if (stack[currgen].now[x][y] & LiveBit)
         //{
         //   if (target[x][y] == 1) z=1;
         //}
         //else if (target[x][y] == 2) z=1;
   }
   if (z == 0)
   {
      fprintf(f, "\nPattern matched target!\n");
      nummatched++;
   }
} //checkboard()

void printinfo(FILE *f)
{
   int n;

   fprintf(f, "%d catalyst%s", nCatsTotal, nCatsTotal==1?"":"s");
   if (nCatsTotal>0)
   {
      fprintf(f, ", which react%s", nCatsTotal==1 ? "s at gen." : " at gens.:");
      for (n=0; n<nCatsTotal; n++)
         fprintf(f, " %d", catplaced[n].gen);
   }
   else
      fprintf(f, ".");
   fprintf(f, "\nGeneration reached: %d\n   -----\n", currgen);
} //printinfo()


void PrintAllBoard(FILE *f)
{
    int x, y;

    for (y=0; y<SZ; y++)
    {
        for (x=0; x<SZ; x++)
            //Fix this:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "A");
                else fprintf(f, "C");
            else if (fixed[x][y]) fprintf(f, ".");
            else fprintf(f, ".");

        fprintf(f, "$");
      //fprintf(f, "\n");
    }

   fprintf(f, "50$");
   fprintf(f, "\n");
}

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 13th, 2014, 10:33 am

Here is Catgl.py V0.2

Moved to Catgl folder under scripts/python. (i.e. place in scripts/python/catgl).
-Support for stated 1-2-3-4 was added. 1 - live, 2 - dead history, 3 - always live, 4 - always dead.
-Orientation preserved.
-Empty universe in golly, shows message with simple setup instructions.

Code: Select all

#python GUI for Catgl. V0.2 released 5/13/2014 by Michael Simkin
#Runs catgl.exe with current golly pattern. Supports catgl V1.0.3
#saves/load the latest settings. 
#creates setup.txt and waits for catgl to finish running. 
#opens rle report from catgl
#Support for stated 1-2-3-4 was added. 1 - live, 2 - dead history, 3 - always live, 4 - always dead. 
# - multiprocessing is not implemented yet

import golly as g 
import sys, os
from Tkinter import *
from tkFileDialog import askopenfilename
import tkMessageBox
import os.path
import time
import subprocess

class CatglForm(Frame):
   def __init__(self, master):
      Frame.__init__(self, master)
      
      self.master = master
      
      master.wm_attributes("-topmost", 1)
      master.geometry("400x400")
      master.title("Catalyst Search")

      #self.pathlabel = Label(master, height=1, width=100,text="Path to patgl.exe:")
      #self.pathlabel.place(x=-295, y=0)

      #self.fileChooserButton = Button(master, width=2, text ="...", command = self.ChooseCatglFile)
      #self.fileChooserButton.place(x=372, y=14)

      self.filePath = StringVar()
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python\Catgl"),"catgl.exe"))
      
      #self.fileEntry = Entry(master, width=58, textvariable=self.filePath)
      #self.fileEntry.place(x=10, y=20)
    
      self.catalystsLabels = ["Eater 1", "Eater 2", "Snake", "Block", "Block and tub with tail", "Tub", "Boat"]
      self.catalystUse = []
      self.lasty = 0
      
      for i in range(len(self.catalystsLabels)):
         self.catalystUse.append(IntVar())
         l = Checkbutton(master, text=self.catalystsLabels[i], variable=self.catalystUse[i])
         self.lasty = 20 * i 
         l.place(x=10, y=self.lasty)
      
      self.lasty += 30
      dx = 280
      
      self.firstgen = StringVar()
      
      temp = Label(master, height=1, width=100,text="First Generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.firstgen)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.gens = StringVar()
      
      temp = Label(master, height=1, width=100,text="Last generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.gens)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.survive = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of generation for catalyst to survive:")
      temp.place(x=-220, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.survive)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.cats = StringVar()
      
      temp = Label(master, height=1, width=100,text="Maximal number of catalyst to place:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.cats)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.threads = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of parallel processes to run:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.threads)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 40

      
      self.startButton = Button(master, width=20, text ="Start", command = self.Start)
      self.startButton.place(x=120, y=self.lasty)
      
      self.Load()
      
    
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python\Catgl"),"catgl.exe"))
      
   def ChooseCatglFile(self):
      filename = askopenfilename()
      self.filePath.set(filename)
   
   def Start(self):
   
      if self.filePath.get() == "": 
         tkMessageBox.showinfo("","Please choose path to catgl.exe")
         return
      
      catalystList = ""
      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"
      
      if catalystList == "": 
         tkMessageBox.showinfo("","Please choose at least one catalyst")
         return
      else: 
         catalystList += "0"
         
         
      if not self.firstgen.get().isdigit():
         tkMessageBox.showinfo("","First generation parameter must be integer")
         return
         
         
      if not self.gens.get().isdigit():
         tkMessageBox.showinfo("","Last generation parameter must be integer")
         return
         
      if not self.survive.get().isdigit():
         tkMessageBox.showinfo("","Generations to survive parameter must be integer")
         return
         
      if not self.cats.get().isdigit():
         tkMessageBox.showinfo("","Number of catalyst parameter must be integer")
         return
         
      if not self.threads.get().isdigit():
         tkMessageBox.showinfo("","Number of process to run parameter must be integer")
         return
      

     
      with open(os.path.join(os.path.dirname(self.filePath.get()),"setup.txt"), "w") as text_file:
         text_file.write("reactions.txt\n")
         text_file.write("reactions.rle\n")
         
         rect = g.getrect(); 
         
         for j in xrange(rect[1], rect[1] + rect[3] + 1):
            for i in xrange(rect[0], rect[0] + rect[2] + 1):   
               if g.getcell(i, j) == 0: 
                  text_file.write(".")
               if g.getcell(i, j) == 1: 
                  text_file.write("A")
               if g.getcell(i, j) == 2: 
                  text_file.write("B")
               if g.getcell(i, j) == 3: 
                  text_file.write("C")
               if g.getcell(i, j) == 4: 
                  text_file.write("D")
               
            if j == rect[1] + rect[3]:
               text_file.write("!")
            else:
               text_file.write("\n")
         
         text_file.write("\n")
         text_file.write(catalystList)
         
         text_file.write("\nstartinggen " + self.firstgen.get())
         text_file.write("\ngens " + self.gens.get())
         text_file.write("\ncatsurvive " + self.survive.get())
         text_file.write("\ncatstoplace " + self.cats.get())
         text_file.write("\nmaxfirstgen " + self.gens.get())
         
      self.Save()


      p = subprocess.Popen([self.filePath.get()])

      while p.poll() is None:
         time.sleep(0.5)

      g.open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"))
      
      self.master.quit()
      self.master.destroy()
      g.exit("")

   def Save(self):
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
   
      with open(fname, "w") as text_file:
         
         catalystList = ""
         
         for i in range(len(self.catalystUse)):
            if self.catalystUse[i].get() == 1:
               catalystList += str(i + 1) + "\n"
         
         catalystList += "0"
         text_file.write(catalystList)
         text_file.write("\n" + self.filePath.get())
         text_file.write("\n" + self.firstgen.get())
         text_file.write("\n" + self.gens.get())
         text_file.write("\n" + self.survive.get())
         text_file.write("\n" + self.cats.get())
         text_file.write("\n" + self.threads.get())
         
   def Load(self):
      
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
      
      if not os.path.isfile(fname):
         return
         
      with open(fname, "r") as text_file:
      
         while True: 
            line = text_file.readline()
            
            if line.strip() == "0":
               break
            
            self.catalystUse[int(line) - 1].set(1)
            
         self.filePath.set(text_file.readline().strip())
         self.firstgen.set(text_file.readline().strip())
         self.gens.set(text_file.readline().strip())
         self.survive.set(text_file.readline().strip())
         self.cats.set(text_file.readline().strip())
         self.threads.set(text_file.readline().strip())

if g.getrect() == []: 
	g.exit("Please setup an initial pattern. State 1 for live, state 2 for dead history, state 3 for always live, state 4 for always dead") 
	
root = Tk()
app = CatglForm(root)
root.mainloop()
Attached the whole package.
Attachments
Catgl1.0.3.zip
(51.11 KiB) Downloaded 341 times
Last edited by simsim314 on May 13th, 2014, 5:30 pm, edited 1 time in total.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 13th, 2014, 5:28 pm

OK finally multiprocessing is supported (modern computers many times have multiple cores, mine has 8 physical threads, so this multiprocessing feature can really boost performance).

Code: Select all


#python GUI for Catgl. V0.3 released 5/14/2014 by Michael Simkin
#Runs catgl.exe with current golly pattern. Supports catgl V1.0.3
#saves/load the latest settings. 
#creates setup.txt and waits for catgl to finish running. 
#opens rle report from catgl
#Support for stated 1-2-3-4 was added. 1 - live, 2 - dead history, 3 - always live, 4 - always dead. 
#Multiprocessing is now implemented

import golly as g 
import sys, os
from Tkinter import *
from tkFileDialog import askopenfilename
import tkMessageBox
import os.path
import time
import subprocess

class CatglForm(Frame):
   def __init__(self, master):
      Frame.__init__(self, master)
      
      self.master = master
      
      master.wm_attributes("-topmost", 1)
      master.geometry("400x400")
      master.title("Catalyst Search")

      #self.pathlabel = Label(master, height=1, width=100,text="Path to patgl.exe:")
      #self.pathlabel.place(x=-295, y=0)

      #self.fileChooserButton = Button(master, width=2, text ="...", command = self.ChooseCatglFile)
      #self.fileChooserButton.place(x=372, y=14)

      self.filePath = StringVar()
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python\Catgl"),"catgl.exe"))
      
      #self.fileEntry = Entry(master, width=58, textvariable=self.filePath)
      #self.fileEntry.place(x=10, y=20)
    
      self.catalystsLabels = ["Eater 1", "Eater 2", "Snake", "Block", "Block and tub with tail", "Tub", "Boat"]
      self.catalystUse = []
      self.lasty = 0
      
      for i in range(len(self.catalystsLabels)):
         self.catalystUse.append(IntVar())
         l = Checkbutton(master, text=self.catalystsLabels[i], variable=self.catalystUse[i])
         self.lasty = 20 * i 
         l.place(x=10, y=self.lasty)
      
      self.lasty += 30
      dx = 280
      
      self.firstgen = StringVar()
      
      temp = Label(master, height=1, width=100,text="First Generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.firstgen)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.gens = StringVar()
      
      temp = Label(master, height=1, width=100,text="Last generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.gens)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.survive = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of generation for catalyst to survive:")
      temp.place(x=-220, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.survive)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.cats = StringVar()
      
      temp = Label(master, height=1, width=100,text="Maximal number of catalyst to place:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.cats)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.threads = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of parallel processes to run:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.threads)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 40

      
      self.startButton = Button(master, width=20, text ="Start", command = self.Start)
      self.startButton.place(x=120, y=self.lasty)
      
      self.Load()
      
    
      self.filePath.set(os.path.join( os.path.join(g.getdir("scripts"),"Python\Catgl"),"catgl.exe"))
      
   def ChooseCatglFile(self):
      filename = askopenfilename()
      self.filePath.set(filename)
   
   def Start(self):
   
      if self.filePath.get() == "": 
         tkMessageBox.showinfo("","Please choose path to catgl.exe")
         return
      
      catalystList = ""
      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"
      
      if catalystList == "": 
         tkMessageBox.showinfo("","Please choose at least one catalyst")
         return
      else: 
         catalystList += "0"
         
         
      if not self.firstgen.get().isdigit():
         tkMessageBox.showinfo("","First generation parameter must be integer")
         return
         
         
      if not self.gens.get().isdigit():
         tkMessageBox.showinfo("","Last generation parameter must be integer")
         return
         
      if not self.survive.get().isdigit():
         tkMessageBox.showinfo("","Generations to survive parameter must be integer")
         return
         
      if not self.cats.get().isdigit():
         tkMessageBox.showinfo("","Number of catalyst parameter must be integer")
         return
         
      if not self.threads.get().isdigit():
         tkMessageBox.showinfo("","Number of process to run parameter must be integer")
         return
      
      self.SaveForm()
      idx = self.RunProcs()
      self.CombineReactionFiles(idx)
	  
      g.open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"))
      
      self.master.quit()
      self.master.destroy()
      g.exit("")
	  
   def RunProcs(self):
	
      idx = 0 
      procs = [] 
	  
      while int(self.firstgen.get()) < int(self.gens.get()): 
		if len(procs) < int(self.threads.get()): 
			self.SaveSetup(idx, 5)
			procs.append(subprocess.Popen([self.filePath.get()]))
			time.sleep(0.2)
			idx += 1
		
		for p in procs: 
			if not (p.poll() is None):
				procs.remove(p)
				break
				
      while len(procs) > 0: 
	  
		for p in procs: 
			if not (p.poll() is None):
				procs.remove(p)
				break
				
      return idx
		
   def CombineReactionFiles(self, idx):
      text = ""
      firstSet = False
      for i in xrange(0, idx):
		first = True
		fname = os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(i) + ".rle")
		
		if os.path.isfile(fname):
			with open(fname, "r") as text_file:
				for line in text_file:
					if not(first and firstSet): 
						text += line
						firstSet = True
						
					first = False	
      with open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"), "w") as text_file:
		text_file.write(text)
			

   def SaveSetup(self, idx, genStep):
		
      with open(os.path.join(os.path.dirname(self.filePath.get()),"setup.txt"), "w") as text_file:
	  
         if os.path.isfile(os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(idx) + ".rle")):
			os.remove(os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(idx) + ".rle"))
			
         text_file.write("reactions" + str(idx) + ".txt\n")
         text_file.write("reactions" + str(idx) + ".rle\n")
         
         rect = g.getrect(); 
         
         for j in xrange(rect[1], rect[1] + rect[3] + 1):
            for i in xrange(rect[0], rect[0] + rect[2] + 1):   
               if g.getcell(i, j) == 0: 
                  text_file.write(".")
               if g.getcell(i, j) == 1: 
                  text_file.write("A")
               if g.getcell(i, j) == 2: 
                  text_file.write("B")
               if g.getcell(i, j) == 3: 
                  text_file.write("C")
               if g.getcell(i, j) == 4: 
                  text_file.write("D")
               
            if j == rect[1] + rect[3]:
               text_file.write("!")
            else:
               text_file.write("\n")
         
         text_file.write("\n")
         catalystList = self.CatalystList()
         text_file.write(catalystList)
         
         nextF = int(self.firstgen.get()) + genStep
		 
         text_file.write("\nstartinggen " + self.firstgen.get())
         text_file.write("\ngens " + self.gens.get())
         text_file.write("\ncatsurvive " + self.survive.get())
         text_file.write("\ncatstoplace " + self.cats.get())
         text_file.write("\nmaxfirstgen " + str(nextF - 1))
         
		 
         self.firstgen.set(str(nextF))
		
			
   def CatalystList(self):
	catalystList = ""

	for i in range(len(self.catalystUse)):
		if self.catalystUse[i].get() == 1:
			catalystList += str(i + 1) + "\n"

	catalystList += "0"
	
	return catalystList
	
   def SaveForm(self):
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
   
      with open(fname, "w") as text_file:
         
         catalystList = self.CatalystList()
         text_file.write(catalystList)
         text_file.write("\n" + self.filePath.get())
         text_file.write("\n" + self.firstgen.get())
         text_file.write("\n" + self.gens.get())
         text_file.write("\n" + self.survive.get())
         text_file.write("\n" + self.cats.get())
         text_file.write("\n" + self.threads.get())
         
   def Load(self):
      
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
      
      if not os.path.isfile(fname):
         return
         
      with open(fname, "r") as text_file:
      
         while True: 
            line = text_file.readline()
            
            if line.strip() == "0":
               break
            
            self.catalystUse[int(line) - 1].set(1)
            
         self.filePath.set(text_file.readline().strip())
         self.firstgen.set(text_file.readline().strip())
         self.gens.set(text_file.readline().strip())
         self.survive.set(text_file.readline().strip())
         self.cats.set(text_file.readline().strip())
         self.threads.set(text_file.readline().strip())

if g.getrect() == []: 
   g.exit("Please setup an initial pattern. State 1 for live, state 2 for dead history, state 3 for always live, state 4 for always dead") 
   
root = Tk()
app = CatglForm(root)
root.mainloop()

Now we left with post processing.

By the way it's even hard to call alpha version, so any comments are very appreciated. I don't think I'm interested in stabilizing this script in level of commercial product, but I do want it to be "pleasant user experience" (this is why I implemented the "save last options" feature - without it it's just horrible to fill all the fields again and again).
Last edited by simsim314 on May 14th, 2014, 5:10 pm, edited 2 times in total.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » May 14th, 2014, 11:17 am

One can approach the results that come from catgl utility as "database". This database needs to have special utility for queries. This is the first "query" script on this matter, that I call TargetFilter.

Code: Select all

import golly as g 
from glife.text import *

clickedStates = []

def GetClick():
	global clickedStates
	
	while True:
		event = g.getevent()
		if event.startswith("click"):
			# event is a string like "click 10 20 left altctrlshift"
			evt, xstr, ystr, butt, mods = event.split()
			
			x = int(xstr)
			y = int(ystr)
			
			clickedStates.append((x, y, g.getcell(x, y)))
			
			return (x, y, butt)
			
def RevertClicked():
	for s in clickedStates:
		g.setcell(s[0], s[1], s[2])
		

g.show("click on cell in initial pattern") 
up = GetClick()
g.setcell(up[0], up[1], 5)
g.update()

g.show("Now click on same cell in next pattern") 

down = GetClick()
g.setcell(down[0], down[1], 5)
g.update()

g.show("Now select target pattern. Left click - target = 1, right - target = 0, middle finish".format(down[0], down[1])) 

dy = down[1] - up[1]
target = []

while True:
	click = GetClick()
	
	if click[2] == "middle":
		break
	
	if click[2] == "left":
		target.append((click[0], click[1], 1))
		g.setcell(click[0], click[1], 5)

	else: 
		target.append((click[0], click[1], 0))
		g.setcell(click[0], click[1], 4)
		
	g.update()

RevertClicked()
g.update()
		
results = []
gens = []

for i in xrange(0, 250): 
	
	curY = up[1]
	while g.getcell(up[0], curY) > 0: 
		targetfound = True 
		
		for xy in target:
			x = xy[0]
			y = xy[1] + curY - up[1]
			
			if g.getcell(x, y) % 2 != xy[2]: 
				targetfound = False
				break
		
		if targetfound and not (curY in results): 
			results.append(curY)
			gens.append(i)
		
		curY += dy
		
	g.run(1)

i = 0 
g.reset()

for y in results:
	cells = g.getcells([up[0] - 50, y - 50, 100, 100])
	g.putcells(cells,-100, up[1] + i * dy - y)
	
	gen = gens[i]
	
	periodText = make_text (str(gen))
	g.putcells(periodText,up[0] - 190, up[1] + i * dy) 
	i += 1
		
		
		
		
It works as gollows:
1. click on the first pattern (state 3 cell that exist in all patterns that came from catgl).
2. click on same cell in next pattern.

This will calculate the DY of the pattern.

3. Next draw a target pattern.
- Left click: target cell on.
- Right click: target cell off.
- Middle click: finish the setup and start the search.

----

Future improvements:

1. Save the dy once, and allow to change it as setting.
2. Allow saving target patterns into browsable library.
3. Placing/Stepping/Sizing options export to settings.
4. Work only while catalyst are alive.
5. Add time survival of target parameter.

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » January 31st, 2015, 11:24 am

Turns out that os.path.dirname(self.filePath.get()) does something unexpected when Golly's script folder is set somewhere other than the Scripts folder. Catgl 1.0.3 locks up with an error message that only shows up after you give up and close the dialog box. Easy to work around, but it might be worth displaying the target directory for setup.txt and reactions.rle and so forth, in the dialog box somewhere. (Looks like there's some commented-out code along those lines in the Python script.)

See the walkthrough I just posted for a trial Catgl run. I'll post some notes on possible improvements later today.

EDIT: One obvious item: the reactions.rle output should probably include the red always-OFF cells, so that a promising output can be copied directly to a new input without editing.

Also, it looks like the output colors are inverted -- the always-ON cells should be regular-ON, and vice versa.

Finally, catgl 1.0.3 is still compiled with #define MAXGENS 250, which means that some possible recently-published searches mysteriously don't work. If I recall correctly, if "maximum generations to place catalyst" plus "number of generations for catalyst to survive" adds up to more than 250, then the search will instantly fail, rather mysteriously.

Recompiling with a higher maximum, maybe 512 or so (?), is probably worthwhile -- 250 was a reasonable limit for over a decade ago when the original 'catalyst' was written, but now it's very easy to do searches that run up against this limit.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » February 1st, 2015, 5:11 am

MAXGENS can be calculated similar to bellman.

I also wanted to make it run and forget, to allow further work in golly. And when the search is finished one can open the result file using another script. It's also can be nice to report the intermediate results, for searches that run few days.

Probably would be nice to expand the catalyst list as well. I think they're hard-coded inside the .c aren't they?

Will probably take me a while to come back to this one...

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 1st, 2015, 6:54 pm

simsim314 wrote:I also wanted to make it run and forget, to allow further work in golly. And when the search is finished one can open the result file using another script. It's also can be nice to report the intermediate results, for searches that run few days.
That should be a fairly easy change. Catgl writes to reactions.txt every time it finds something. I've often made a copy of a half-finished results.txt and looked through it while the search was still running.
simsim314 wrote:Probably would be nice to expand the catalyst list as well. I think they're hard-coded inside the .c aren't they?
Yes. It's a painfully awkward format, but it would be fairly easy to define new catalysts, up to 15x15 at least. Here's the actual definition for a boat:

Code: Select all

   {{{' ', 'x', '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'x', ':', '*', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'o', '%', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {'1', 'X', '*', '*', ':', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', '.', ':', ':', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
     {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}},
     5, 5, 'boat'}
-- but maybe we should adjust the boat definition according to Scorbie's suggestion. Here's a minimal version of all the current catalyst definitions, including the boat:

Code: Select all

  x:21         
  .*o:         
.:$%*X         
:***:.         
:*$:.          
...            
6, 6, 'eat1'


   ..X:21      
 .:$*$oo2      
.:***$oo:      
.*%^$$$$X      
.:***$**.      
 .:%*^*$.      
   :*%*:       
   .:*:.       
    ...        
9, 9, 'eat2'


x.:::.         
2*$**:         
X**$*:         
x:::..         
4, 6, 'snak'


.X:.           
:*o2           
:*o2           
.X:.           
4, 4, 'bloc'


    .::.       
    :**:       
    :oo:       
    .::.       
   x..         
  .:o:.        
  .*$o1        
.:$$*X1        
:***:.         
:*$:.          
...            
11, 8, 'tubw'


 .x.           
.:*:.          
1o$*.          
.:*:.          
 .x.           
5, 5, '_tub'


 x..           
x:*:.          
1o%*:          
1X**:          
 .::.          
5, 5, 'boat'
A period means one neighbor, a colon means two neighbors, $ means four neighbors, % means five neighbors. '1' and '2' are locations adjacent to 1 or 2 cells of the active reaction, respectively. An 'o' means that cell of the catalyst can turn off temporarily and then be restored; an asterisk means the cell should never turn off, and an 'X' means the cell must never turn on. (I.e., an ON cell in an 'X' location means the catalyst has failed.)

I'll work on building catgl-compatible definitions of all the other likely catalysts that I know about, including Kazyan's new Bellman-generated still lifes. It would be nice to have the option to add them to ptbsearch as well.

User avatar
Scorbie
Posts: 1692
Joined: December 7th, 2013, 1:05 am

Re: Catalyst improvements WIP

Post by Scorbie » February 1st, 2015, 10:21 pm

dvgrn wrote:but maybe we should adjust the boat definition according to Scorbie's suggestion.
Nothing much, but I think you meant Sokwe... right?

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 2nd, 2015, 12:20 am

Scorbie wrote:
dvgrn wrote:but maybe we should adjust the boat definition according to Scorbie's suggestion.
Nothing much, but I think you meant Sokwe... right?
Hmm, yes, sorry about the typo -- I meant to go back and fix that before posting, but then forgot. The reference I was thinking of is actually from over a year and a half ago -- Sokwe mentioned a fairly large collection of potentially useful catalysts to be added to ptbsearch. They would do just about as well as additions to catgl, I would think.

It's probably time to do a careful comparison between ptbsearch and catgl, and see if there's a reason to use one or the other. Ptbsearch can handle transparent-junk catalysts, but they slow it down quite a lot of course. Is there anything that catgl can do that ptbsearch can't -- catch and count output gliders, maybe, when just one catalyst is added at a time?

User avatar
codeholic
Moderator
Posts: 1147
Joined: September 13th, 2011, 8:23 am
Location: Hamburg, Germany

Re: Catalyst improvements WIP

Post by codeholic » February 2nd, 2015, 4:16 am

dvgrn wrote:Is there anything that catgl can do that ptbsearch can't -- catch and count output gliders, maybe, when just one catalyst is added at a time?
That's in fact the most annoying feature of ptbsearch, that if there's an escaping glider, it will enjoy to find all possible catalysts on each further glider's step, slowing down the search significantly and making it harder to sort out the search results. I wish there were a way to take the best of two programs.
Ivan Fomichev

Post Reply