Re: Thread for your script-related questions
Posted: August 24th, 2025, 8:51 am
Is there a python version of gencols?
Forums for Conway's Game of Life
https://conwaylife.com/forums/
Probably not, and anyway, it is usually better to pipe the output of shipcolls into an apgsearch stdin symmetry than to use gencols, since apgsearch fully censuses collisions and shipcolls works with INT rules.
Probably, but to make efficient guns, you would need a complex script.Resu wrote: August 30th, 2025, 12:55 pm It is possible to make a script that makes guns from a glider syntheses?
Yup, this question comes up every now and then, and efficiency is definitely the key.
slmake doesn't have a feature to specify the parity or color of gliders, but pslmake does have a way to specify the color: there is an argument that can be passed to pslmake like [-c {any,black,white}].PK22 wrote: September 6th, 2025, 4:44 am Is there a way to use slmake to create recipes that only use gliders of a single parity and/or colour?
You should be able to set the full path to the Python library in the GollyPrefs file. On Linux this should be at "~/.golly/GollyPrefs". On Windows, it's atLaundryPizza03 wrote: September 6th, 2025, 8:02 pm Is there a way to get Golly to recognize a default Python library? I specifically want to reference a Conda virtual environment every time I start up.
Code: Select all
C:\Users\<username>\AppData\Roaming\Golly\GollyPrefs
It does indeed work.
Code: Select all
Benchmark results
The number in the parenthesis is the estimated difficulty factor.
c/8 with symmetry D2|
(24) 5x32: 17ms vs. 15.3 with backjumping
(32) 7x32: 1.0371s vs. 568.5ms with backjumping
(40) 9x32: 59.2186s vs. 27.41s with backjumping
c/7 with symmetry D2|
(35) 9x32: 4.5045s vs. 2.7035s with backjumping
3c/8 with symmetry D2|
(40) 9x32: 365.2ms vs. 319.1ms with backjumping
(48) 11x32: 4.5017s vs. 3.5168s with backjumping
c/4 with symmetry D2|
(24) 11x32: 68ms vs. 128.7ms with backjumping
c/5 with symmetry D2|
(30) 11x32: 387ms vs. 470.8ms with backjumping
(35) 13x32: 6.336s vs. 6.9899s with backjumping
c/5d with symmetry D2\
(??) 32x32 w10: 1.0055s vs. 1.5706s with backjumping
c/8d with symmetry D2\
(??) 32x32 w7: 6.0433s vs. 6.0846s with backjumpingCode: Select all
x = 27, y = 24, rule = B3/S23-a5
...........................$
...........................$
........oo.o...o.oo........$
........oo...o...oo........$
.......oo..ooooo..oo.......$
...........ooooo...........$
........oo.......oo........$
...........................$
.....oo.............oo.....$
.......ooo.......ooo.......$
.......oooooo.oooooo.......$
.........o.o...o.o.........$
.....o.o...oo.oo...o.o.....$
......oo...oo.oo...oo......$
.....o...o.o.o.o.o...o.....$
..........o.ooo.o..........$
.....oo...o.....o...oo.....$
.....oo.....o.o.....oo.....$
....o....o..ooo..o....o....$
....o..oo.o.....o.oo..o....$
............o.o............$
........o..o...o..o........$
.....oo...oo...oo...oo.....$
....o.o.............o.o....$
...oo?????????????????oo...!See this post in Basic Questions for the last time this question was answered, with some vaguely related discussion here and here.PK22 wrote: October 8th, 2025, 12:37 pm What program was/is used to find the single-channel recipe toolkit, and how hard would it be to write a program that can find a similar toolkit in INT rules using similar design principles?
dvgrn wrote: April 28th, 2025, 7:11 am An OCA-supporting single-channel search program would be a big step forward. But it might be just as easy to write something like that from scratch using lifelib (which wasn't available when simeks was doing single-channel searches -- it showed up in 2018). Seems like it would be tricky to rework Gol-search to support OCA, and/or to remove its other limitations.
Code: Select all
#pragma once
#include "pattern2.h"
#include "incubator.h"
#include <unordered_map>
#include <map>
#include <set>
#include "dsds.h"
/*
* This contains code from apgmera, minus the dependence on Golly, for
* separating and classifying objects.
*/
namespace apg {
template<int M>
class base_classifier {
public:
bool b0;
uint64_t gmax;
uint8_t transtable[512];
std::string rule;
std::string zoi;
lifetree_abstract<uint32_t>* lab;
lifetree<uint32_t, M + 1> lh;
std::unordered_map<uint64_t, std::string> bitcache;
std::unordered_map<std::string, std::vector<std::string> > decompositions;
bool diagbirth() {
/*
* Does birth occur in either of the following situations?
* o.. ..o
* ... or ...
* ..o o..
*/
return (transtable[257] || transtable[68] || b0);
}
std::vector<std::vector<apg::pattern>> get_patterns(dsds &uf, std::unordered_map<uint64_t, uint64_t> &umap, std::vector<apg::pattern> &phases, std::vector<int> *short_connections) {
uint64_t islcount = 0;
std::unordered_map<uint64_t, uint64_t> cmap;
std::vector<uint64_t> invcmap;
for (auto it = umap.begin(); it != umap.end(); ++it) {
uint64_t cid = uf.find(it->second);
if (cmap.count(cid) == 0) {
cmap[cid] = (islcount++);
invcmap.push_back(cid);
}
}
// Create empty array of patterns:
std::vector<std::vector<apg::pattern>> subpats(islcount);
for (uint64_t c = 0; c < islcount; c++) {
for (uint64_t t = 0; t < phases.size(); t++) {
subpats[c].emplace_back(lab, "", rule);
}
}
// Populate patterns:
for (auto it = umap.begin(); it != umap.end(); ++it) {
uint64_t cid = uf.find(it->second);
uint64_t c = cmap[cid];
uint64_t t = it->first >> 48;
subpats[c][t] += phases[t].subrect((it->first & 0xffffff), ((it->first >> 24) & 0xffffff), 1, 1);
}
if (short_connections == nullptr) { return subpats; }
for (uint64_t t = 0; t < phases.size() - 1; t++) {
for (uint64_t i = 0; i < islcount; i++) {
auto successor = subpats[i][t][1];
if (successor == subpats[i][t+1]) {
continue; // island is stable in this generation
}
auto extrabirths = successor - subpats[i][t+1];
std::vector<int64_t> celllist(extrabirths.totalPopulation() * 2);
extrabirths.get_coords(&(celllist[0]));
for (uint64_t ii = 0; ii < celllist.size(); ii += 2) {
uint64_t coord = (t << 48) + (celllist[ii+1] << 24) + celllist[ii];
std::set<uint64_t> friends;
for (auto&& x : (*short_connections)) {
uint64_t other = coord + x;
if (umap.count(other)) {
uint64_t j = cmap[uf.find(umap[other])];
if (j != i) { friends.insert(j); }
}
}
if (friends.size() == 1) {
uint64_t j = *(friends.begin());
uf.merge(invcmap[i], invcmap[j]);
return subpats; // improvement made
} else if (friends.size() == 0) {
std::cerr << "Unreachable code!!!" << std::endl;
}
}
}
}
return subpats;
}
/**
* Complete object separation.
*/
std::vector<std::string> full_3dsep(std::string parent) {
// cache:
{
auto it = decompositions.find(parent);
if (it != decompositions.end()) {
return it->second;
}
}
std::vector<std::string> elements;
#define RETURN_SINGLETON() elements.push_back(parent); decompositions[parent] = elements; return elements
if (parent[0] != 'x') { RETURN_SINGLETON(); }
apg::pattern pat(lab, parent, rule);
uint64_t period = pat.ascertain_period();
if ((pat.empty()) || (period == 0) || (period > 30000)) { RETURN_SINGLETON(); }
int64_t dy = pat.dy;
int64_t dx = pat.dx;
pat = pat.shift(262144, 262144);
int64_t bbox[4] = {0};
pat.getrect(bbox);
if ((bbox[2] > 100000) || (bbox[3] > 100000)) { RETURN_SINGLETON(); }
std::unordered_map<uint64_t, uint64_t> umap;
uint64_t id = 0;
std::vector<apg::pattern> phases;
for (uint64_t t = 0; t <= period; t++) {
phases.emplace_back(lab, "", rule);
phases[t] += pat;
std::vector<int64_t> celllist(pat.totalPopulation() * 2);
pat.get_coords(&(celllist[0]));
for (uint64_t i = 0; i < celllist.size(); i += 2) {
uint64_t coord = (t << 48) + (celllist[i+1] << 24) + celllist[i];
umap[coord] = id; id += 1;
}
if (t != period) { pat = pat[1]; }
}
std::vector<uint64_t> connections;
std::vector<int> short_connections;
connections.push_back((period << 48) + (dy << 24) + dx);
int range = zoi.size() >> 1;
for (int i = -range; i <= range; i++) {
for (int j = -range; j <= range; j++) {
int k = (i << 24) + j;
if (k > 0) { connections.push_back(k); }
connections.push_back((1ull << 48) + k);
if (k != 0) { short_connections.push_back(k); }
}
}
// disjoint-set data structure:
dsds uf(id);
dsds uf2(id);
std::map<uint64_t, std::vector<uint64_t>> suppressed;
for (auto it = umap.begin(); it != umap.end(); ++it) {
for (auto&& x : connections) {
uint64_t other = it->first + x;
if (umap.count(other)) {
uf.merge(it->second, umap[other]);
uf2.merge(it->second, umap[other]);
}
}
for (auto&& x : short_connections) {
uint64_t other = it->first + x;
if (!(umap.count(other))) {
suppressed[other].push_back(it->second);
}
}
}
for (auto it = suppressed.begin(); it != suppressed.end(); ++it) {
uint64_t l = it->second.size();
if (l >= 3) {
for (uint64_t i = 1; i < l; i++) {
uf2.merge(it->second[i-1], it->second[i]);
}
}
}
if (uf2.count() > 1) {
// fast separation:
auto subpats = get_patterns(uf2, umap, phases, nullptr);
for (auto&& y : subpats) {
std::string apgcode = y[0].apgcode();
auto new_elems = full_3dsep(apgcode);
for (auto&& x : new_elems) { elements.push_back(x); }
}
decompositions[parent] = elements;
return elements;
}
std::vector<std::vector<apg::pattern>> subpats;
// uint64_t isize = uf.count();
while (subpats.size() != uf.count()) {
subpats = get_patterns(uf, umap, phases, &short_connections);
}
uint64_t islcount = subpats.size();
/*
if (islcount != isize) {
std::cout << parent << " improved from " << isize << " islands to " << islcount << std::endl;
}
*/
// Only one island; terminate.
if (islcount == 1) { RETURN_SINGLETON(); }
bool all_standalone = true;
for (uint64_t i = 0; i < islcount; i++) {
for (uint64_t t = 0; t < period; t++) {
if (subpats[i][t][1] != subpats[i][t+1]) {
all_standalone = false;
}
}
}
if (all_standalone) {
for (uint64_t i = 0; i < islcount; i++) {
std::string apgcode = subpats[i][0].apgcode();
auto new_elems = full_3dsep(apgcode);
for (auto&& x : new_elems) { elements.push_back(x); }
}
decompositions[parent] = elements;
return elements;
}
std::set<uint64_t> invalid_cache;
bool low_period_osc = (period <= 3) && (dx == 0) && (dy == 0);
for (uint64_t n = 2; n <= islcount; n++) {
if (low_period_osc) {
if ((n > 4) && (zoi == "95")) {
// unnecessary by Four Colour Theorem
break;
} else if ((n > 7) && (zoi == "99")) {
break;
}
}
uint64_t cost = 1;
for (uint64_t i = 0; i < islcount; i++) {
cost *= n;
if (cost > 1048576) { break; }
}
if ((cost > 1048576) || (islcount > 16)) {
// too expensive; give up
std::cerr << "Gave up attempting to separate " << parent << " (" << islcount << " islands) into " << n << " subobjects." << std::endl;
break;
}
// std::cerr << "Separating " << parent << " (" << islcount << " islands) into " << n << " subobjects." << std::endl;
std::vector<uint8_t> currstack(islcount);
std::vector<uint8_t> maxstack(islcount);
currstack[0] = 0;
currstack[1] = 0;
maxstack[0] = 0;
uint64_t focus = 1;
while (focus) {
uint8_t limit = maxstack[focus - 1] + 1;
limit = (limit >= n) ? (n - 1) : limit;
if (currstack[focus] > limit) {
focus -= 1;
currstack[focus] += 1;
} else {
maxstack[focus] = maxstack[focus - 1];
if (maxstack[focus] < currstack[focus]) { maxstack[focus] = currstack[focus]; }
if (focus < (islcount - 1)) {
focus += 1;
currstack[focus] = 0;
} else {
if (maxstack[focus] == n - 1) {
// We have a n-colouring which uses all n colours:
std::vector<uint64_t> bitmasks(n);
for (uint64_t i = 0; i < islcount; i++) {
bitmasks[currstack[i]] |= (1ull << i);
}
bool valid = true;
for (uint64_t i = 0; i < n; i++) {
if (invalid_cache.count(bitmasks[i])) { valid = false; }
}
if (valid) {
std::vector<std::vector<apg::pattern>> unions(n);
for (uint64_t i = 0; i < n; i++) {
for (uint64_t t = 0; t <= period; t++) {
unions[i].push_back(pattern(lab, "", rule));
}
}
for (uint64_t i = 0; i < islcount; i++) {
for (uint64_t t = 0; t <= period; t++) {
unions[currstack[i]][t] |= subpats[i][t];
}
}
bool faithful = true;
for (uint64_t i = 0; i < n; i++) {
for (uint64_t t = 0; t < period; t++) {
if (unions[i][t][1] != unions[i][t+1]) {
faithful = false;
invalid_cache.insert(bitmasks[i]);
}
}
}
if (faithful) {
// We have a decomposition into non-interacting pieces!
for (uint64_t i = 0; i < n; i++) {
std::string apgcode = unions[i][0].apgcode();
auto new_elems = full_3dsep(apgcode);
for (auto&& x : new_elems) { elements.push_back(x); }
}
decompositions[parent] = elements;
return elements;
}
}
}
currstack[focus] += 1;
}
}
}
}
RETURN_SINGLETON();
}
std::vector<std::string> pseudoBangBang(pattern pat, std::vector<bitworld> *clvec) {
/*
* Borrowed from apgmera, and upgraded.
*/
uint64_t period = pat.ascertain_period();
bool isOscillator = ((pat.dx == 0) && (pat.dy == 0));
pattern hist(&lh, "", rule + "History");
hist += pat;
hist = hist[period + 2];
bitworld lrem = hist.flatlayer(0);
bitworld env = hist.flatlayer(1);
// If we have a moving object, do not reiterate:
bool reiterate = isOscillator && (zoi.length() <= 2);
std::map<std::pair<int64_t, int64_t>, uint64_t> geography;
uint64_t label = 0;
while (lrem.population() != 0) {
bitworld cluster = grow_cluster(lrem.get1cell(), env, reiterate ? "9" : zoi);
lrem -= cluster;
label += 1;
std::vector<std::pair<int64_t, int64_t> > celllist = cluster.getcells();
for (uint64_t i = 0; i < celllist.size(); i++) {
geography[celllist[i]] = label;
}
}
while (reiterate) {
reiterate = false;
for (uint64_t i = 0; i < period; i++) {
hist = hist[1];
bitworld lcurr = hist.flatlayer(0);
bitworld dcurr = bleed(lcurr, "9");
dcurr -= env;
std::vector<std::pair<int64_t, int64_t> > liberties = dcurr.getcells();
for (uint64_t j = 0; j < liberties.size(); j++) {
int64_t ix = liberties[j].first;
int64_t iy = liberties[j].second;
std::map<uint64_t, uint64_t> tally;
for (int64_t ux = 0; ux <= 2; ux++) {
for (int64_t uy = 0; uy <= 2; uy++) {
int value = geography[std::pair<int64_t, int64_t>(ux + ix - 1, uy + iy - 1)];
if (lcurr.getcell(ux + ix - 1, uy + iy - 1)) {
tally[value] = tally[value] + (1 << (uy * 3 + ux));
}
}
}
uint64_t dominantColour = 0;
std::map<uint64_t, uint64_t>::iterator it2;
for (it2 = tally.begin(); it2 != tally.end(); it2++) {
int colour = it2->first;
uint64_t count = it2->second;
if (transtable[count]) { dominantColour = colour; }
// if (__builtin_popcountll(count) == 3) { dominantColour = colour; }
}
// Resolve dependencies:
if (dominantColour != 0) {
std::map<std::pair<int64_t, int64_t>, uint64_t>::iterator it3;
for (it3 = geography.begin(); it3 != geography.end(); it3++) {
std::pair<int64_t, int64_t> coords = it3->first;
uint64_t colour = it3->second;
if (tally[colour] > 0) {
geography[coords] = dominantColour;
if (colour != dominantColour) {
// A change has occurred; keep iterating until we achieve stability:
reiterate = true;
}
}
}
}
}
}
}
bitworld lcurr = (isOscillator ? pat.flatlayer(0) : hist.flatlayer(0));
std::vector<bitworld> cbs(label+1);
std::map<std::pair<int64_t, int64_t>, uint64_t>::iterator it3;
for (it3 = geography.begin(); it3 != geography.end(); it3++) {
std::pair<int64_t, int64_t> coords = it3->first;
uint64_t colour = it3->second;
cbs[colour].setcell(coords.first, coords.second, 1);
}
std::vector<std::string> components;
for (uint64_t l = 1; l <= label; l++) {
cbs[l] &= lcurr;
if (cbs[l].population() > 0) {
if (clvec != 0) { clvec->push_back(cbs[l]); }
apg::pattern ppart(lab, lab->demorton(cbs[l], 1), rule);
components.push_back(ppart.apgcode());
}
}
for (auto it = components.begin(); it != components.end(); ++it) {
if ((*it) == "PATHOLOGICAL") {
// invalid separation
std::vector<std::string> newcomps;
newcomps.push_back(pat.apgcode());
return newcomps;
}
}
return components;
}
std::vector<bitworld> getclusters(bitworld &live, bitworld &env, bool rigorous) {
bitworld lrem = live;
std::vector<bitworld> clusters;
while (lrem.population() != 0) {
// Obtain cluster:
bitworld cluster = grow_cluster(lrem.get1cell(), env, zoi);
cluster &= lrem;
lrem -= cluster;
if (rigorous) {
pattern ppart(lab, lab->demorton(cluster, 1), rule);
pseudoBangBang(ppart, &clusters);
} else {
clusters.push_back(cluster);
}
}
return clusters;
}
// Forward declaration for co-recursive function:
// std::map<std::string, int64_t> census(pattern pat, int numgens, std::string (*adv)(pattern), bool recurse);
std::pair<bool, std::vector<std::string> > identify(uint64_t bb, std::vector<bitworld> &cplanes, bool recurse) {
std::string repr;
std::vector<std::string> elements;
if ((bb != 0) && (bitcache.find(bb) != bitcache.end())) {
repr = bitcache[bb];
elements = decompositions[repr];
return std::pair<bool, std::vector<std::string> >(true, elements);
}
if (cplanes.size() == 0) {
cplanes.resize(1);
cplanes[0].world.emplace(std::pair<int32_t, int32_t>(0, 0), bb);
}
apg::pattern cl2(lab, cplanes, rule);
cl2.pdetect(gmax); // Restrict period.
if (cl2.dt == 0) {
return std::pair<bool, std::vector<std::string> >(false, elements);
}
uint64_t period = cl2.ascertain_period();
repr = cl2.apgcode();
if ((repr[0] == 'x') && (period <= 10000)) {
elements = full_3dsep(repr);
} else if (recurse) {
std::map<std::string, int64_t> rc = census(cl2, period << 3, 0, false);
for (auto it2 = rc.begin(); it2 != rc.end(); ++it2) {
if (it2->second > 0) {
for (int64_t i = 0; i < it2->second; i++) {
elements.push_back(it2->first);
}
}
}
} else {
elements.push_back(repr);
}
if ((repr[0] == 'x') && (bb != 0)) {
bitcache.emplace(bb, repr);
}
return std::pair<bool, std::vector<std::string> >(true, elements);
}
template<int H>
void deeppurge(std::map<std::string, int64_t> &cm, incubator<56, H> &icb, std::string (*adv)(pattern),
bool remove_annoyances, bool remove_gliders) {
uint64_t excess[8] = {0ull};
for (auto it = icb.tiles.begin(); it != icb.tiles.end(); ++it) {
Incube<56, H>* sqt = &(it->second);
for (int y = 0; y < H; y++) {
uint64_t r = sqt->d[y];
while (r != 0) {
uint64_t x = __builtin_ctzll(r);
int annoyance = (remove_annoyances ? icb.isAnnoyance(sqt, x, y) : 0);
if (annoyance > 0) {
excess[annoyance] += 1;
} else if ((!remove_gliders) || (icb.isGlider(sqt, x, y) == 0)) {
// TODO: Identify unknown object
auto intList = icb.get_component(sqt, x, y);
int population = intList.back();
int ll = intList.size() - 1;
if (population > 0) {
if ((remove_annoyances) && (population == 3)) {
excess[3] += 1;
} else {
std::vector<int> celllist(population*2);
int i = 0;
for (int j = 0; j < ll; j += 3) {
if (intList[j + 2] == 1) {
celllist[i++] = intList[j];
celllist[i++] = intList[j+1];
}
}
int left = celllist[0];
int top = celllist[1];
int right = celllist[0];
int bottom = celllist[1];
for (int i = 0; i < (population*2); i += 2) {
if (left > celllist[i]) { left = celllist[i]; }
if (right < celllist[i]) { right = celllist[i]; }
if (top > celllist[i+1]) { top = celllist[i+1]; }
if (bottom < celllist[i+1]) { bottom = celllist[i+1]; }
}
for (int i = 0; i < (population*2); i += 2) {
celllist[i] -= left;
celllist[i+1] -= top;
}
right -= left;
bottom -= top;
uint64_t bitstring = 0;
std::vector<bitworld> cplanes;
if (right <= 7 && bottom <= 7) {
for (int i = 0; i < (population*2); i += 2) {
bitstring |= (1ull << (celllist[i] + 8*celllist[i+1]));
}
} else {
cplanes.resize(1);
for (int i = 0; i < (population*2); i += 2) {
cplanes[0].setcell(celllist[i], celllist[i+1], 1);
}
}
std::pair<bool, std::vector<std::string> > res = identify(bitstring, cplanes, true);
if (!(res.first)) {
apg::pattern cl2(lab, cplanes, rule);
std::string diagnosed = "PATHOLOGICAL";
if (adv != 0) { diagnosed = (*adv)(cl2); }
res.second.push_back(diagnosed);
}
// Enter elements into tally:
for (uint64_t i = 0; i < res.second.size(); i++) {
cm[res.second[i]] += 1;
}
}
}
}
r ^= (1ull << x);
r &= sqt->d[y];
}
}
}
if (excess[3] > 0) { cm["xp2_7"] += excess[3]; }
if (excess[4] > 0) { cm["xs4_33"] += excess[4]; }
if (excess[5] > 0) { cm["xq4_153"] += excess[5]; }
if (excess[6] > 0) { cm["xs6_696"] += excess[6]; }
if (excess[7] > 0) { cm["xs5_253"] += excess[7]; }
}
void census(std::map<std::string, int64_t> &tally, std::vector<bitworld> &planes, std::string (*adv)(pattern), bool recurse) {
bitworld lrem = planes[0];
for (uint64_t i = 1; i < M; i++) {
lrem += planes[i];
}
bitworld env = lrem;
env += planes[M];
bool glider_plane = ((M == 1) && (planes.size() == 3));
bitworld lrem2 = lrem;
if (glider_plane) { lrem2 -= planes[2]; }
while (lrem2.population() != 0) {
// Obtain cluster:
bitworld cluster = grow_cluster(lrem2.get1cell(), env, zoi);
cluster &= lrem;
lrem -= cluster;
lrem2 -= cluster;
uint64_t bb = 0;
if (M == 1) {
if (cluster.world.size() > 1) { cluster = fix_topleft(cluster); }
if (cluster.world.size() == 1) {
// We use a bitcache for fast lookup of small objects:
auto it = cluster.world.begin(); bb = it->second;
}
}
std::vector<bitworld> cplanes;
if (bb == 0) {
for (uint64_t i = 0; i < M; i++) {
cplanes.push_back(cluster);
if (M != 1) { cplanes.back() &= planes[i]; }
}
}
std::pair<bool, std::vector<std::string> > res = identify(bb, cplanes, recurse);
if (!(res.first)) {
apg::pattern cl2(lab, cplanes, rule);
std::string diagnosed = "PATHOLOGICAL";
if (adv != 0) { diagnosed = (*adv)(cl2); }
res.second.push_back(diagnosed);
}
// Enter elements into tally:
for (uint64_t i = 0; i < res.second.size(); i++) {
tally[res.second[i]] += 1;
}
}
if (lrem.population() > 0) { tally["xq4_153"] += (lrem.population() / 5); }
}
std::map<std::string, int64_t> census(std::vector<bitworld> &planes, std::string (*adv)(pattern), bool recurse) {
std::map<std::string, int64_t> tally;
census(tally, planes, adv, recurse);
return tally;
}
std::map<std::string, int64_t> census(std::vector<bitworld> &planes, std::string (*adv)(pattern)) {
return census(planes, adv, true);
}
std::map<std::string, int64_t> census(bitworld &live, bitworld &env, std::string (*adv)(pattern)) {
std::vector<bitworld> bwv;
bwv.push_back(live); bwv.push_back(env);
return census(bwv, adv);
}
std::map<std::string, int64_t> census(bitworld &live, bitworld &env) {
return census(live, env, 0);
}
std::map<std::string, int64_t> census(pattern pat, int numgens, std::string (*adv)(pattern), bool recurse) {
pattern hist(&lh, "", rule + "History");
hist += pat;
hist = hist[numgens];
std::vector<bitworld> bwv;
for (uint64_t i = 0; i <= M; i++) { bwv.push_back(hist.flatlayer(i)); }
return census(bwv, adv, recurse);
}
std::map<std::string, int64_t> census(pattern pat, int numgens, std::string (*adv)(pattern)) {
return census(pat, numgens, adv, true);
}
std::map<std::string, int64_t> census(pattern pat, int numgens) {
return census(pat, numgens, 0);
}
base_classifier(lifetree_abstract<uint32_t>* lab, std::string rule) : lh(100)
{
this->lab = lab;
this->rule = rule;
gmax = 1048576;
/*
* We construct the transition table by bootstrapping: we run a
* pattern containing all 512 3-by-3 tiles and examine the
* centre cells of the tiles after one generation.
*/
std::string transrle = "3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob"
"5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob2ob2ob2ob2ob2o"
"b2ob2o2$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob"
"2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob"
"2ob2ob2ob2ob26o2$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob"
"5o3bo3bob2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bo"
"b2ob2ob2ob2ob2ob2ob2ob2o$o2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2b"
"o2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo$3bo3bob2o3b2obob5o3b"
"o3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2b"
"o2bo2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$o2bo2bo2bo2bo"
"2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo"
"2bo2bo2bo2bo$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo"
"3bob2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob"
"2ob2ob2ob2ob2ob2ob2o$bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2b"
"o2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo$3bo3bob2o3b2obob5o3bo3b"
"ob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2b"
"o2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$bo2bo2bo2bo2bo2b"
"o2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo"
"2bo2bo2bo$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bo"
"b2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob2ob"
"2ob2ob2ob2ob2ob2o$2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob"
"2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2o$3bo3bob2o3b2obob5o3bo3bob"
"2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo"
"2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$2ob2ob2ob2ob2ob2o"
"b2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob"
"2ob2ob2o$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob"
"2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob2ob2o"
"b2ob2ob2ob2ob2o$2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo"
"2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo$3bo3bob2o3b2obob5o3bo3bob2o"
"3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo2b"
"2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$2bo2bo2bo2bo2bo2bo"
"2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo2bo"
"2bo2bo2bo$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bo"
"b2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob2ob"
"2ob2ob2ob2ob2ob2o$ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2o"
"b2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2obo$3bo3bob2o3b2obob5o3bo3bob"
"2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo"
"2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$ob2ob2ob2ob2ob2ob"
"2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob"
"2ob2ob2obo$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3b"
"ob2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob2ob"
"2ob2ob2ob2ob2ob2o$b2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob"
"2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2o$3bo3bob2o3b2obob5o3bo3bob"
"2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo"
"2b2ob2ob2ob2ob2ob2ob2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$b2ob2ob2ob2ob2ob"
"2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob2ob"
"2ob2ob2ob2o$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo"
"3bob2o3b2obob5o$24bo2bo2bo2bo2bo2bo2bo2bo3bo2bo2bo2bo2bo2bo2bo2bob2ob"
"2ob2ob2ob2ob2ob2ob2o$96o$3bo3bob2o3b2obob5o3bo3bob2o3b2obob5o3bo3bob2o"
"3b2obob5o3bo3bob2o3b2obob5o$2bo2bo2bo2bo2bo2bo2bo2b2ob2ob2ob2ob2ob2ob"
"2ob2obob2ob2ob2ob2ob2ob2ob2ob26o$96o!";
b0 = false;
std::string subrule = rule;
// Strip Generations prefix:
if (subrule[0] == 'g') {
subrule = subrule.substr(1);
while ((subrule[0] >= '0') && (subrule[1] <= '9')) {
subrule = subrule.substr(1);
}
}
if (uli_get_family(rule2int(rule)) >= 6) {
zoi = get_zoi(rule);
} else if ((subrule[0] == 'b') && (subrule[1] == '0')) {
zoi = "99"; b0 = true;
} else if (subrule[0] == 'r') {
zoi = std::string(2 * (subrule[1] - '0'), '9');
} else if (subrule[0] == 'b') {
pattern transpat(lab, transrle, rule);
bitworld bw = transpat[1].flatlayer(0);
for (int i = 0; i < 512; i++) {
int x = 3 * (i & 31) + 1;
int y = 3 * (i >> 5) + 1;
transtable[i] = bw.getcell(x, y);
}
zoi = (diagbirth()) ? "99" : "95";
} else {
zoi = get_zoi(rule);
}
}
};
typedef base_classifier<1> classifier;
}