You can find version 1.5 in my sandbox.
Version 2.2.4 (It's C++ so definitely it's not a Golly script):
Code: Select all
/*
* TarRuleSrc2.cpp -- Search for rules.
* Version 2.2.4
*
* islptng and EvinZL, 2025
*
* Thanks to FWKnightship for parseRLE(), ComparePattern(), and GridPtr.
*/
#include<iostream>
#include<fstream>
#include<cstring>
#include<string>
#include<random>
#include<bitset>
using namespace std;
const string Hensel[51] = { "0","1c","1e","2a","2c","2e","2i","2k","2n","3a","3c","3e","3i","3j","3k","3n","3q","3r","3y",
"4a","4c","4e","4i","4j","4k","4n","4q","4r","4t","4w","4y","4z","5a","5c","5e","5i","5j","5k","5n","5q","5r","5y",
"6a","6c","6e","6i","6k","6n","7c","7e","8" };
const int HenselCnt[9] = { 1,2,6,10,13,10,6,2,1 };
char NLookup[512] = {
'\x00','\x01','\x02','\x03','\x01','\x04','\x03','\x0c','\x02','\x03','\x05','\x09','\x07','\x0f','\x0d','\x13',
'\x00','\x01','\x02','\x03','\x01','\x04','\x03','\x0c','\x02','\x03','\x05','\x09','\x07','\x0f','\x0d','\x13',
'\x02','\x07','\x05','\x0d','\x03','\x0f','\x09','\x13','\x06','\x11','\x0b','\x1b','\x11','\x16','\x1b','\x23',
'\x02','\x07','\x05','\x0d','\x03','\x0f','\x09','\x13','\x06','\x11','\x0b','\x1b','\x11','\x16','\x1b','\x23',
'\x01','\x04','\x07','\x0f','\x08','\x0a','\x10','\x19','\x03','\x0c','\x0d','\x13','\x10','\x19','\x1d','\x20',
'\x01','\x04','\x07','\x0f','\x08','\x0a','\x10','\x19','\x03','\x0c','\x0d','\x13','\x10','\x19','\x1d','\x20',
'\x07','\x12','\x0e','\x18','\x10','\x1e','\x1a','\x24','\x11','\x1c','\x17','\x26','\x1f','\x28','\x27','\x2a',
'\x07','\x12','\x0e','\x18','\x10','\x1e','\x1a','\x24','\x11','\x1c','\x17','\x26','\x1f','\x28','\x27','\x2a',
'\x02','\x07','\x06','\x11','\x07','\x12','\x11','\x1c','\x05','\x0d','\x0b','\x1b','\x0e','\x18','\x17','\x26',
'\x02','\x07','\x06','\x11','\x07','\x12','\x11','\x1c','\x05','\x0d','\x0b','\x1b','\x0e','\x18','\x17','\x26',
'\x05','\x0e','\x0b','\x17','\x0d','\x18','\x1b','\x26','\x0b','\x17','\x15','\x21','\x17','\x29','\x21','\x2b',
'\x05','\x0e','\x0b','\x17','\x0d','\x18','\x1b','\x26','\x0b','\x17','\x15','\x21','\x17','\x29','\x21','\x2b',
'\x03','\x0f','\x11','\x16','\x10','\x1e','\x1f','\x28','\x09','\x13','\x1b','\x23','\x1a','\x24','\x27','\x2a',
'\x03','\x0f','\x11','\x16','\x10','\x1e','\x1f','\x28','\x09','\x13','\x1b','\x23','\x1a','\x24','\x27','\x2a',
'\x0d','\x18','\x17','\x29','\x1d','\x25','\x27','\x2e','\x1b','\x26','\x21','\x2b','\x27','\x2e','\x2f','\x30',
'\x0d','\x18','\x17','\x29','\x1d','\x25','\x27','\x2e','\x1b','\x26','\x21','\x2b','\x27','\x2e','\x2f','\x30',
'\x01','\x08','\x07','\x10','\x04','\x0a','\x0f','\x19','\x07','\x10','\x0e','\x1a','\x12','\x1e','\x18','\x24',
'\x01','\x08','\x07','\x10','\x04','\x0a','\x0f','\x19','\x07','\x10','\x0e','\x1a','\x12','\x1e','\x18','\x24',
'\x03','\x10','\x0d','\x1d','\x0c','\x19','\x13','\x20','\x11','\x1f','\x17','\x27','\x1c','\x28','\x26','\x2a',
'\x03','\x10','\x0d','\x1d','\x0c','\x19','\x13','\x20','\x11','\x1f','\x17','\x27','\x1c','\x28','\x26','\x2a',
'\x04','\x0a','\x12','\x1e','\x0a','\x14','\x1e','\x22','\x0f','\x19','\x18','\x24','\x1e','\x22','\x25','\x2c',
'\x04','\x0a','\x12','\x1e','\x0a','\x14','\x1e','\x22','\x0f','\x19','\x18','\x24','\x1e','\x22','\x25','\x2c',
'\x0f','\x1e','\x18','\x25','\x19','\x22','\x24','\x2c','\x16','\x28','\x29','\x2e','\x28','\x2d','\x2e','\x31',
'\x0f','\x1e','\x18','\x25','\x19','\x22','\x24','\x2c','\x16','\x28','\x29','\x2e','\x28','\x2d','\x2e','\x31',
'\x03','\x10','\x11','\x1f','\x0f','\x1e','\x16','\x28','\x0d','\x1d','\x17','\x27','\x18','\x25','\x29','\x2e',
'\x03','\x10','\x11','\x1f','\x0f','\x1e','\x16','\x28','\x0d','\x1d','\x17','\x27','\x18','\x25','\x29','\x2e',
'\x09','\x1a','\x1b','\x27','\x13','\x24','\x23','\x2a','\x1b','\x27','\x21','\x2f','\x26','\x2e','\x2b','\x30',
'\x09','\x1a','\x1b','\x27','\x13','\x24','\x23','\x2a','\x1b','\x27','\x21','\x2f','\x26','\x2e','\x2b','\x30',
'\x0c','\x19','\x1c','\x28','\x19','\x22','\x28','\x2d','\x13','\x20','\x26','\x2a','\x24','\x2c','\x2e','\x31',
'\x0c','\x19','\x1c','\x28','\x19','\x22','\x28','\x2d','\x13','\x20','\x26','\x2a','\x24','\x2c','\x2e','\x31',
'\x13','\x24','\x26','\x2e','\x20','\x2c','\x2a','\x31','\x23','\x2a','\x2b','\x30','\x2a','\x31','\x30','\x32',
'\x13','\x24','\x26','\x2e','\x20','\x2c','\x2a','\x31','\x23','\x2a','\x2b','\x30','\x2a','\x31','\x30','\x32' };
int HenselToOrder(string st)
{
for (int i = 0; i < 51; i++)
{
if (Hensel[i] == st) return i;
}
return -1;
}
mt19937 rng(random_device{}());
/*
int randfactor = 0xdeadbeef - 0x5f3759df;
int rng()
{
randfactor = rand();
return rand();
}*/
int CondToOrder(int st)
{
return NLookup[st];
}
struct Rule
{
bool b[51];
bool s[51];
Rule() { for (int i = 0; i < 51; i++) { b[i] = false; s[i] = false; } }
Rule(string st)
{
for (int i = 0; i < 51; i++) { b[i] = false; s[i] = false; }
char currentNumber = '0';
bool inMinus = false;
bool inBirth = true;
st += "S";
for (int i = 0; i < st.size(); i++)
{
if ('0' <= st[i] && st[i] <= '8')
{
currentNumber = st[i];
inMinus = false;
if (st[i + 1] == '/' || st[i + 1] == 'S' || st[i + 1] == 's' || ('0' <= st[i + 1] && st[i + 1] <= '8'))
{
st[i] = '-';
}
else continue;
}
if (st[i] == 'S' || st[i] == 's') { inBirth = false; continue; }
if (st[i] == 'B' || st[i] == 'b' || st[i] == '/') continue;
if (st[i] == '-')
{
inMinus = true;
for (int j = 0; j < 51; j++)
{
if (Hensel[j][0] == currentNumber)
if (inBirth) b[j] = true;
else s[j] = true;
}
continue;
}
string currenth = ""; currenth += currentNumber; currenth += st[i];
int index = HenselToOrder(currenth);
if (inBirth) b[index] = !inMinus;
else s[index] = !inMinus;
}
}
bool evolve(int conds) const
{
if (conds & 16) return s[CondToOrder(conds)];
else return b[CondToOrder(conds)];
}
};
string toString(Rule a)
{
string res = "B";
char currentNumber = 'a';
for (int i = 0; i < 51; i++)
{
if (a.b[i])
{
if (currentNumber != Hensel[i][0])
{
res += Hensel[i][0];
currentNumber = Hensel[i][0];
}
if (Hensel[i] != "0" && Hensel[i] != "8") res += Hensel[i][1];
}
}
currentNumber = 'a';
res += "/S";
for (int i = 0; i < 51; i++)
{
if (a.s[i])
{
if (currentNumber != Hensel[i][0])
{
res += Hensel[i][0];
currentNumber = Hensel[i][0];
}
if (Hensel[i] != "0" && Hensel[i] != "8") res += Hensel[i][1];
}
}
return res;
}
Rule operator|(Rule a, Rule b)
{
Rule res;
for (int i = 0; i < 51; i++)
{
res.b[i] = a.b[i] || b.b[i];
res.s[i] = a.s[i] || b.s[i];
}
return res;
}
Rule operator&(Rule a, Rule b)
{
Rule res;
for (int i = 0; i < 51; i++)
{
res.b[i] = a.b[i] && b.b[i];
res.s[i] = a.s[i] && b.s[i];
}
return res;
}
bool operator==(Rule a, Rule b)
{
for (int i = 0; i < 51; i++)
{
if (a.b[i] != b.b[i]) return false;
if (a.s[i] != b.s[i]) return false;
}
return true;
}
int diff(Rule a, Rule b)
{
int cnt = 0;
for (int i = 0; i < 51; i++)
{
if (a.b[i] != b.b[i]) cnt++;
if (a.s[i] != b.s[i]) cnt++;
}
return cnt;
}
Rule randomRule(Rule minrule, Rule maxrule)
{
long long randb = (long long)rng() << 32 | rng();
long long rands = (long long)rng() << 32 | rng();
Rule res;
for (int i = 0; i < 51; i++)
{
if (minrule.b[i]) res.b[i] = true;
else if (!maxrule.b[i]) res.b[i] = false;
else res.b[i] = (randb >> i & 0x01);
if (minrule.s[i]) res.s[i] = true;
else if (!maxrule.s[i]) res.s[i] = false;
else res.s[i] = (rands >> i & 0x01);
}
return res;
}
const int GRIDSIZE = 64;
typedef bitset<GRIDSIZE> Grid[GRIDSIZE], *GridPtr;
const int PATTPOS = GRIDSIZE / 2 - 2;
// return false if the RLE is empty or represents an empty pattern, true otherwise.
// by FWKnightship and modified by islptng
bool parseRLE(const string RLE, GridPtr grid)
{
int num = 0, start_x = PATTPOS, start_y = PATTPOS;
int x = 0, y = 0;
bool empty = 1;
for (unsigned i = 0; i < RLE.size(); ++i)
{
char c = RLE[i];
if (c == 'r')
{
start_y -= num / 2;
if (start_y < 0)
{
cerr << "ERROR: RLE size (" << num
<< ") is larger than grid size (" << num
<< ").\n";
exit(1);
}
num = 0;
}
else if (isdigit(c))
{
num = num * 10 + c - '0';
}
else if (isalpha(c))
{
if (c > 'A' && c <= 'Z')
{
cerr << "ERROR: State out of range, only 2 states are supported.\n";
exit(1);
}
else if (c == 'b')
{
x += num == 0 ? 1 : num;
}
else if (c == 'o' || c <= 'C')
{
int state = c == 'o' ? 1 : c - 'A' + 1;
for (int j = 0; j < (num == 0 ? 1 : num); ++j)
{
if (start_x + x >= GRIDSIZE)
{
cerr << "ERROR: RLE contains too large pattern.(x)\n";
exit(1);
}
grid[start_x + x][start_y + y] = state;
++x;
}
empty = 0;
}
num = 0;
}
else if (c == '.')
{
x += num == 0 ? 1 : num;
num = 0;
}
else if (c == '$')
{
y += num == 0 ? 1 : num;
x = 0;
if (start_y + y >= GRIDSIZE)
{
std::cerr << "ERROR: RLE contains too large pattern.(y)\n";
exit(1);
}
num = 0;
}
else if (c == '!')
{
break;
}
else if (isgraph(c) && c != '=' && c != ',')
{
std::cerr << "ERROR: Invalid character \'" << c << "\'.\n";
exit(1);
}
}
return !empty;
}
// by FWKnightship and modified by islptng and EvinZL
struct cell
{
int x, y;
} aa[GRIDSIZE * GRIDSIZE], bb[GRIDSIZE * GRIDSIZE];
int ComparePattern_dx, ComparePattern_dy;
bool ComparePattern(GridPtr a, GridPtr b)
{
int aaa = 0, bbb = 0;
for (int i = 0; i < GRIDSIZE; ++i)
{
if (a[i].any())
for (int j = 0; j < GRIDSIZE; ++j)
{
if (a[i][j] != 0)
{
aa[aaa].x = i;
aa[aaa].y = j;
++aaa;
}
}
if (b[i].any())
for (int j = 0; j < GRIDSIZE; ++j)
{
if (b[i][j] != 0)
{
bb[bbb].x = i;
bb[bbb].y = j;
++bbb;
}
}
}
if (aaa != bbb) return 0;
if (aaa == 0) return 1;
ComparePattern_dx = bb[0].x - aa[0].x;
ComparePattern_dy = bb[0].y - aa[0].y;
for (int i = 0; i < aaa; ++i)
{
if (bb[i].x - aa[i].x != ComparePattern_dx) return 0;
if (bb[i].y - aa[i].y != ComparePattern_dy) return 0;
}
return 1;
}
void gridcpy(GridPtr from, GridPtr to)
{
for (int i = 0; i < GRIDSIZE; i++) to[i] = from[i];
}
int gridgcl(GridPtr grid, int x, int y)
{
if (x < 0 || x >= GRIDSIZE) return false;
if (y < 0 || y >= GRIDSIZE) return false;
return grid[x][y];
}
const int deltax[] = { -1,-1,0,1,1,1,0,-1 };
const int deltay[] = { 0,1,1,1,0,-1,-1,-1 };
// return false if pattern touches the boundary or becomes empty.
uint64_t shr(uint64_t x, int s) {
return (s > 0) ? (x >> s) : (x << -s);
}
bool evolve(GridPtr from, GridPtr to, Rule r)
{
bool empty = true;
for (int x = 0; x < GRIDSIZE; x++)
{
to[x].reset();
if ((x == 0 ? false : from[x-1].any()) || from[x].any() || ((x == GRIDSIZE-1) ? false : from[x+1].any()))
for (int y = 0; y < GRIDSIZE; y++)
{
int n0 = shr(x == 0 ? 0ull : from[x - 1].to_ullong(), y-1) & 7;
int n1 = shr( from[x ].to_ullong(), y-1) & 7;
int n2 = shr(x == GRIDSIZE-1 ? 0ull : from[x + 1].to_ullong(), y-1) & 7;
int neighbors = n0 | (n1 << 3) | (n2 << 6);
to[x][y] = r.evolve(neighbors);
if (to[x][y])
{
empty = false;
if (x == 0 || x == GRIDSIZE - 1 || y == 0 || y == GRIDSIZE - 1) return false;
}
}
}
return !empty;
}
Rule evolveRulespace_min, evolveRulespace_max;
void evolve_rulespace(GridPtr from, GridPtr to, Rule r)
{
evolveRulespace_min = Rule("B/S");
evolveRulespace_max = Rule("B012345678/S012345678");
for (int x = 0; x < GRIDSIZE; x++)
for (int y = 0; y < GRIDSIZE; y++)
{
int n0 = shr(x == 0 ? 0ull : from[x - 1].to_ullong(), y - 1) & 7;
int n1 = shr( from[x ].to_ullong(), y - 1) & 7;
int n2 = shr(x == GRIDSIZE - 1 ? 0ull : from[x + 1].to_ullong(), y - 1) & 7;
int neighbors = n0 | (n1 << 3) | (n2 << 6);
to[x][y] = r.evolve(neighbors);
if (to[x][y])
if (from[x][y]) evolveRulespace_min.s[CondToOrder(neighbors)] = true;
else evolveRulespace_min.b[CondToOrder(neighbors)] = true;
else
if (from[x][y]) evolveRulespace_max.s[CondToOrder(neighbors)] = false;
else evolveRulespace_max.b[CondToOrder(neighbors)] = false;
}
}
Rule calcRulespace_min, calcRulespace_max;
void calc_rulespace(GridPtr grid, GridPtr temp, Rule r, int gens)
{
calcRulespace_min = Rule("B/S");
calcRulespace_max = Rule("B012345678/S012345678");
for (int g = 0; g < gens; g++)
{
evolve_rulespace(grid, temp, r);
calcRulespace_min = calcRulespace_min | evolveRulespace_min;
calcRulespace_max = calcRulespace_max & evolveRulespace_max;
gridcpy(temp, grid);
}
}
Grid startpatt, endpatt, grid, grid2;
int minx, miny, maxx, maxy, minp, maxp;
string minrstr, maxrstr;
Rule minr, maxr;
string startrle, endrle;
bool readrequire()
{
string strtemp;
ifstream fin;
fin.open("requirements.txt");
fin >> strtemp >> minrstr >> maxrstr;
if (strtemp != "r:") { cerr << "Missing R"; return false; }
fin >> strtemp >> minp >> maxp;
if (strtemp != "p:") { cerr << "Missing P"; return false; }
if (maxp == -1) { cerr << ""; return false; }
fin >> strtemp >> minx >> maxx;
if (strtemp != "x:") { cerr << "Missing X"; return false; }
fin >> strtemp >> miny >> maxy;
if (strtemp != "y:") { cerr << "Missing Y"; return false; }
fin >> strtemp; if (strtemp != "==start") { cerr << "Missing start pattern"; return false; }
strtemp = "?"; startrle = "";
while (strtemp[0] != '=')
{
if (strtemp != "?") startrle += strtemp;
fin >> strtemp;
}
if (strtemp == "==end")
{
endrle = startrle;
fin.close();
return true;
}
if (strtemp != "==target") { cerr << "Missing end tag or target pattern"; return false; }
strtemp = "?"; endrle = "";
while (strtemp[0] != '=')
{
if (strtemp != "?") endrle += strtemp;
fin >> strtemp;
}
if (strtemp != "==end") { cerr << "Missing end tag"; return false; }
fin.close();
return true;
}
Rule rresults[5000]; int cntres = 0;
bool inrange(int n, int from, int to)
{
if (n < from && from != -1) return false;
if (n > to && to != -1) return false;
return true;
}
void swapdxy()
{
if (ComparePattern_dx < 0) ComparePattern_dx *= -1;
if (ComparePattern_dy < 0) ComparePattern_dy *= -1;
if (ComparePattern_dx < ComparePattern_dy)
{
int t = ComparePattern_dx;
ComparePattern_dx = ComparePattern_dy;
ComparePattern_dy = t;
}
}
void initres()
{
ofstream fout;
fout.open("TarRuleSrc_result.txt");
fout << "TarRuleSrc Results:\n\n";
fout.close();
}
void putres(int dx, int dy, int period, int optn, Rule r, Rule r2)
{
ofstream fout;
fout.open("TarRuleSrc_result.txt", ios::app);
if (startrle == endrle)
{
if (dx == dy && dy == 0)
fout << " | p";
else if (dy == 0) fout << " orth | " << dx << "c/";
else if (dy == dx) fout << " diag | " << dx << "c/";
else fout << "obli |(" << dx << "," << dy << ")c/";
}
else fout << "dx=" << dx << "\tdy=" << dy << "\tT=";
fout << period << "\t | space 2^" << optn << "\t | " << toString(r) << " " << toString(r2) << endl;
fout.close();
}
bool checkfound(Rule r)
{
for (int i = 0; i < cntres; i++)
{
if (rresults[i] == r) return true;
}
return false;
}
void printGrid(GridPtr g) {
for (int y = 0; y < GRIDSIZE; y++) {
for (int x = 0; x < GRIDSIZE; x++)
std::cout << (g[y][x] ? '.' : ' ');
std::cout << "\n";
}
}
int main()
{
if (!readrequire()) return -1;
minr = Rule(minrstr);
maxr = Rule(maxrstr);
if (!parseRLE(startrle, startpatt)) return -1;
if (!parseRLE(endrle, endpatt)) return -1;
initres();
int cnt = 0;
while (true)
{
Rule r = randomRule(minr, maxr);
gridcpy(startpatt, grid);
int gen = 0;
while (gen <= maxp)
{
gen++;
bool flag = evolve(grid, grid2, r);
gridcpy(grid2, grid);
if (!flag) break;
if (gen < minp) continue;
flag = ComparePattern(grid, endpatt);
if (flag)
{
swapdxy();
if (!inrange(gen, minp, maxp)) break;
if (!inrange(ComparePattern_dx, minx, maxx)) break;
if (!inrange(ComparePattern_dy, miny, maxy)) break;
gridcpy(startpatt, grid);
calc_rulespace(grid, grid2, r, gen);
Rule tmir = minr | calcRulespace_min;
if (checkfound(tmir)) break;
Rule tmar = maxr & calcRulespace_max;
putres(ComparePattern_dx, ComparePattern_dy, gen, diff(tmir, tmar), tmir, tmar);
rresults[cntres] = tmir;
cntres++;
break;
}
}
cnt++;
if (cnt % 16384 == 0) cout << cnt << " rules tested, " << cntres << " results found\n";
if(cntres == 5000)
{
cout << "5000 results found. Terminating..." << endl;
break;
}
}
return 0;
}
Code: Select all
r: <minrule> <maxrule>
p: <smallest period> <biggest period>
x: <smallest dx> <biggest dx, or -1 if unlimited>
y: <smallest dy> <biggest dy, or -1 if unlimited>
==start
<RLE of the start pattern>
==target
<RLE of the target pattern, can be omitted together with "==target" tag>
==endCode: Select all
r: B/S B2-a345678/S12345678
p: 1 30
x: 1 -1
y: 0 -1
==start
bo$3o!
==end