slmake

For scripts to aid with computation or simulation in cellular automata.
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

calcyman wrote: May 30th, 2023, 10:50 am The old slmake is a greedy algorithm, and the new version that I'm currently writing (pslmake) is a parallelised beam search with heuristic similar to that in the A* search algorithm, where the cost estimate is based on a linear model fitted on diagnostics produced by the old slmake.
Which is still a greedy algorithm, but not as greedy or slow as slmake. It’s still greedy though.
calcyman wrote: May 30th, 2023, 10:50 amI don't see how it's related to travelling salesman? The problem is to find a short path from a single block to the desired pattern in the (infinite) directed graph where each node is a constellation and each directed edge corresponds to a glider collision; this is a pathfinding problem. On the other hand, travelling salesman is about finding a minimum-length path/cycle that visits all nodes of a finite graph with weighted edges, which is a very different sort of problem (NP-complete as a function of the size of the graph, whereas pathfinding is polynomial-time in the size of the graph).
I was referring to a construction arm and finding a minimal path for the construction arm. Is there a route optimization for a 90 degree salvo? What about for a 0 degree single channel construction arm?
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

Does pslmake have a feature where you can specify to construct left to right or vise versa?
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by apg »

AlbertArmStain wrote: June 14th, 2023, 6:00 pm Does pslmake have a feature where you can specify to construct left to right or vise versa?
No, but the natural order is 'back to front' (and because pslmake works backwards, it handles the things at the front to begin with before gradually moving to the back). The left/right ordering is handled by the 'defragmentation' pass which is a postprocessing step, not part of the core algorithm.
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

calcyman wrote: June 14th, 2023, 6:25 pm
AlbertArmStain wrote: June 14th, 2023, 6:00 pm Does pslmake have a feature where you can specify to construct left to right or vise versa?
No, but the natural order is 'back to front' (and because pslmake works backwards, it handles the things at the front to begin with before gradually moving to the back). The left/right ordering is handled by the 'defragmentation' pass which is a postprocessing step, not part of the core algorithm.
So that’s why it’s so algorithmically greedy. It could possibly build a section back to front, then move to the next section, unless this is already implemented.
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

AlbertArmStain wrote: June 14th, 2023, 7:04 pm So that’s why it’s so algorithmically greedy. It could possibly build a section back to front, then move to the next section, unless this is already implemented.
The order of gliders in the salvo can be permuted in many cases (whole blocks). Minimizing the size of salvo would have exponential number of solutions even when considering just these permutations of one salvo. This is why prefering order of targets mostly for example with slowly decresing x+y reduces number of equivalent salvos.

Final defragmentation chooses among the same upto permutation recipe one which is with high probability among cheapest to invoke by an arm.

Next postprocessing is considering the configurtions of nongliders among collisions be the recipe and replacing each glider in salvo by several options of lines and (phases mod small integer) ... to allow the arm choose cheaper variant of recipe.

What I am planning to do is considering final functional pattern as the goal and creating other clusters not interferring with the pattern not to be a problem. So usually some gliders/levels (destroying these clusters) in the recipe could be skipped and still obtain the same functional pattern.
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

Does pslmake have a feature where it will pinpoint the location of the non-spartan objects?
User avatar
EvinZL
Posts: 1082
Joined: November 8th, 2018, 4:15 pm
Location: here

Re: slmake

Post by EvinZL »

Is pslmake released anywhere?
User avatar
EvinZL
Posts: 1082
Joined: November 8th, 2018, 4:15 pm
Location: here

Re: slmake

Post by EvinZL »

Doublepost, but here's what seems to be a bug in slmake.

Given this infile.mc:

Code: Select all

x = 114, y = 114, rule = LifeHistory
.A$A.A$.A.A$2.A.A$3.A.A$4.A.A$5.A.A$6.A.A$7.A.A$8.A.A$9.A.A$10.A.A$
11.A.A$12.A.A$13.A.A$14.A.A$15.A.A$16.A.A$17.A.A$18.A.A$19.A.A$20.A.A
$21.A.A$22.A.A$23.A.A$24.A.A$25.A.A$26.A.A$27.A.A$28.A.A$29.A.A$30.A.
A$31.A$32.3A.2A3.A$34.A.A3.A.A$34.A2.A3.A$33.2A3.A11.2A$35.3A12.A.A$
35.A15.2A3$52.2A$44.2A5.A.A$44.A.A5.A$45.A$51.2A$51.A.A$46.2A3.A$46.
2A61$112.A$111.A.A$107.2A.A2.A$107.2A.A.A$111.A!
slmake prints:

Code: Select all

Instruction set AVX2 detected
Loading elbow recipes...
Snarkmaker string = 10762 bytes.
...elbow recipes loaded.
Signature: [ 109 ]
Wrong orientation; flipping pattern.
Calling sparsebuild on initial pattern comprising 109 cells
 -- data/bespoke/blocker-orient1.rle has period 8.
 -- data/bespoke/blocker-orient2.rle has period 8.
 -- data/bespoke/blocker-orient3.rle has period 8.
 -- data/bespoke/eater2-orient1b.mc has period 1.
 -- data/bespoke/eater2-orient1.mc has period 1.
 -- data/bespoke/eater2-orient2.mc has period 1.
 -- data/bespoke/eater2-orient3.mc has period 1.
 -- data/bespoke/figure8-orient1.rle has period 8.
 -- data/bespoke/figure8-orient2.rle has period 8.
 -- data/bespoke/figure8-orient3.rle has period 8.
 -- data/bespoke/mw2g-orient1.mc has period 1.
 -- data/bespoke/mw2g-orient2.mc has period 1.
 -- data/bespoke/mw2g-orient3b.mc has period 1.
 -- data/bespoke/mw2g-orient3c.mc has period 1.
 -- data/bespoke/mw2g-orient3.mc has period 1.
 -- data/bespoke/mw2g-orient4.mc has period 1.
 -- data/bespoke/snarkheart-eater-orient3.rle has period 1.
 -- data/bespoke/snarkheart-orient1.rle has period 1.
 -- data/bespoke/snarkheart-orient2.rle has period 1.
 -- data/bespoke/snarkheart-orient4.rle has period 1.
 -- data/bespoke/xs17-easy.rle has period 1.
 -- data/bespoke/xs17-eater2.rle has period 1.
 -- data/bespoke/xs17-next.rle has period 1.
 -- data/bespoke/xs17-third.rle has period 1.
Reading file data/edgy/xs4_33/xs4_33__xs5_253...
...data/edgy/xs4_33/xs4_33__xs5_253 successfully read.
Reading file data/edgy/xs4_33/xs5_253...
...data/edgy/xs4_33/xs5_253 successfully read.
Reading file data/edgy/xs6_696/xs5_253...
...data/edgy/xs6_696/xs5_253 successfully read.
Reading file data/edgy/xs4_252/xs5_253...
...data/edgy/xs4_252/xs5_253 successfully read.
Reading file data/edgy/xs4_33/xs4_252...
...data/edgy/xs4_33/xs4_252 successfully read.
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Reading file data/longmove/xs4_33...
...data/longmove/xs4_33 successfully read.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 3
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 9
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 27
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Attempting to construct xs4_33 with 7 gliders (2238 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 81
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Attempting to construct xs4_33 with 7 gliders (2238 recipes)
Attempting to construct xs4_33 with 8 gliders (2856 recipes)
Attempting to construct xs4_33 with 9 gliders (3648 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 243
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Attempting to construct xs4_33 with 7 gliders (2238 recipes)
Attempting to construct xs4_33 with 8 gliders (2856 recipes)
Attempting to construct xs4_33 with 9 gliders (3648 recipes)
Attempting to construct xs4_33 with 10 gliders (4645 recipes)
Attempting to construct xs4_33 with 11 gliders (4056 recipes)
Attempting to construct xs4_33 with 12 gliders (5092 recipes)
Attempting to construct xs4_33 with 13 gliders (6445 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 729
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 7 gliders (2238 recipes)
Attempting to construct xs4_33 with 8 gliders (2856 recipes)
Attempting to construct xs4_33 with 9 gliders (3648 recipes)
Attempting to construct xs4_33 with 10 gliders (4645 recipes)
Attempting to construct xs4_33 with 11 gliders (4056 recipes)
Attempting to construct xs4_33 with 12 gliders (5092 recipes)
Attempting to construct xs4_33 with 13 gliders (6445 recipes)
Attempting to construct xs4_33 with 14 gliders (6549 recipes)
Attempting to construct xs4_33 with 15 gliders (7692 recipes)
Attempting to construct xs4_33 with 16 gliders (8655 recipes)
Attempting to construct xs4_33 with 17 gliders (8928 recipes)
Attempting to construct xs4_33 with 18 gliders (9604 recipes)
Attempting to construct xs4_33 with 19 gliders (10513 recipes)
Reached bailout for strategy 'tree'
Increasing bailout to 2187
Obtained 10 dimers/monomers.
4 objects of < 32 cells.
Attempting to construct xs4_33 with 1 gliders (2 recipes)
Attempting to construct xs4_33 with 2 gliders (3 recipes)
Attempting to construct xs4_33 with 3 gliders (18 recipes)
Attempting to construct xs4_33 with 4 gliders (72 recipes)
Attempting to construct xs4_33 with 5 gliders (382 recipes)
Attempting to construct xs4_33 with 6 gliders (1185 recipes)
Attempting to construct xs4_33 with 7 gliders (2238 recipes)
Reached bailout for strategy 'deep'
Attempting to construct xs4_33 with 8 gliders (2856 recipes)
Attempting to construct xs4_33 with 9 gliders (3648 recipes)
Attempting to construct xs4_33 with 10 gliders (4645 recipes)
Attempting to construct xs4_33 with 11 gliders (4056 recipes)
Attempting to construct xs4_33 with 12 gliders (5092 recipes)
Attempting to construct xs4_33 with 13 gliders (6445 recipes)
Attempting to construct xs4_33 with 14 gliders (6549 recipes)
  • At the start, it says "4 objects of < 32 cells" when there are 5.
  • It can't construct a block, when it should be very easy to do so.
  • Why is it constructing a block first?
I tested it out with only the tub+big sl and it seems to work fine.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by apg »

EvinZL wrote: July 2nd, 2023, 11:26 am Doublepost, but here's what seems to be a bug in slmake.

Given this infile.mc:

Code: Select all

x = 114, y = 114, rule = LifeHistory
.A$A.A$.A.A$2.A.A$3.A.A$4.A.A$5.A.A$6.A.A$7.A.A$8.A.A$9.A.A$10.A.A$
11.A.A$12.A.A$13.A.A$14.A.A$15.A.A$16.A.A$17.A.A$18.A.A$19.A.A$20.A.A
$21.A.A$22.A.A$23.A.A$24.A.A$25.A.A$26.A.A$27.A.A$28.A.A$29.A.A$30.A.
A$31.A$32.3A.2A3.A$34.A.A3.A.A$34.A2.A3.A$33.2A3.A11.2A$35.3A12.A.A$
35.A15.2A3$52.2A$44.2A5.A.A$44.A.A5.A$45.A$51.2A$51.A.A$46.2A3.A$46.
2A61$112.A$111.A.A$107.2A.A2.A$107.2A.A.A$111.A!
Can you try that but with the input glider shifted SE by, let's say, (5, 5)? It separates gliders from everything else by computing (x & x[8]), so if gliders collide within the first 8 generations, it won't be parsed correctly.
EvinZL wrote: June 30th, 2023, 1:10 pm Is pslmake released anywhere?
Not yet: I was abroad this last week and didn't have access to my laptop until today. I need to make some user-friendliness adjustments (such as making everything command-line arguments instead of hardcoded in the source, and including a Python script for downloading pslmake databases).
AlbertArmStain wrote: June 27th, 2023, 10:27 am Does pslmake have a feature where it will pinpoint the location of the non-spartan objects?
I don't understand the question; please could you elaborate?
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
dvgrn
Moderator
Posts: 12040
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: slmake

Post by dvgrn »

calcyman wrote: July 3rd, 2023, 6:38 am
AlbertArmStain wrote: June 27th, 2023, 10:27 am Does pslmake have a feature where it will pinpoint the location of the non-spartan objects?
I don't understand the question; please could you elaborate?
I read that more or less as "some easy way to see where pslmake is getting stuck, if it gets stuck".

E.g., have a progress.rle file updated after every reduction, showing the remaining constellation. Could run a script in Golly to monitor that file and update a display when it changes... that would probably not _always_ highlight a problem area clearly, though -- maybe there's a better way to point out the region where pslmake might be working that it's having trouble with.
User avatar
Tawal
Posts: 865
Joined: October 8th, 2023, 7:20 am
Location: Mælar

Re: slmake

Post by Tawal »

Hi,

I'm on Linux and tried to compile slmake.
I get this error :

Code: Select all

$ ./slsparse.cpp 
Downloading lifelib...
Sous-module 'lifelib' (https://gitlab.com/apgoucher/lifelib.git) enregistré pour le chemin 'lifelib'
Clonage dans '/home/tawal/Term/Test/slmake/lifelib'...
Chemin de sous-module 'lifelib' : 'af0c7910e6a6c8fe276222ef5bcecec3e1495f2f' extrait
Sous-module 'cpads' (https://gitlab.com/hatsya/open-source/cpads.git) enregistré pour le chemin 'lifelib/cpads'
Clonage dans '/home/tawal/Term/Test/slmake/lifelib/cpads'...
Chemin de sous-module 'lifelib/cpads' : '07fea42b10e3a54f4b40f8e7fcc388d5726a446a' extrait
Compiling...
...compiled.
ven. 13 oct. 2023 10:19:11 CEST
Instruction set SSE3 detected
./slsparse.cpp : ligne 10 : 242731 Instruction non permise "$exename" "$@"
ven. 13 oct. 2023 10:19:11 CEST
$
$ ./slsparse
Instruction set SSE3 detected
Instruction non permise
$
Sorry for the French's output.
Instruction non permise = Instruction not allowed

Where's the matter ?
CPU capabilities ? (AMD Athlon X2 QL-62 (2) @ 2.000GHz)
Missing packages ?
Other ?

Thanks to help me.

Edit:
The error seems to be in the execution of slparse, not in the compilation.
My Linux : Debian GNU/Linux 12 (bookworm) x86_64
Alone we go faster … Together we go further …
Avatar's pattern
My Sandbox
Bom-Bom
ℝ - ℕ = ℝ or ℕ ⊄ ℝ
User avatar
confocaloid
Posts: 6697
Joined: February 8th, 2022, 3:15 pm
Location: learn to protect yourself against stray gliders and sparks and self-destruct mechanisms

Re: slmake

Post by confocaloid »

Tawal wrote: October 13th, 2023, 4:26 am

Code: Select all

Instruction set SSE3 detected
./slsparse.cpp : ligne 10 : 242731 Instruction non permise "$exename" "$@"
Sounds like the program requires some CPU capabilities. On my system it outputs the following:

Code: Select all

Instruction set SSE4.2 detected
Loading elbow recipes...
Snarkmaker string = 10762 bytes.
...elbow recipes loaded.
Signature: [ 252 ] 
Wrong orientation; flipping pattern.
Calling sparsebuild on initial pattern comprising 252 cells
(output trimmed)
127:1 B3/S234c User:Confocal/R (isotropic CA, incomplete)
Unlikely events happen.
My silence does not imply agreement, nor indifference. If I disagreed with something in the past, then please do not construe my silence as something that could change that.
User avatar
Tawal
Posts: 865
Joined: October 8th, 2023, 7:20 am
Location: Mælar

Re: slmake

Post by Tawal »

Thanks for your response.

But the error you quoted is not very important i think.
It comes from the bash hack at the beginning of the slsparse.ccp file.
I've tried to remove this trick and launch the compilation command directly.
The compilation is right, no errors. It output well a slsparse executable file.
But the execution of the slsparse get this error :

Code: Select all

$ ./slsparse
Instruction set SSE3 detected
Instruction non permise
$
How to verify which CPU capabilities is necessary ? (to be sure ...)

Thanks.

Edit:
Dave's LifeWiki Tutorial wrote:Linux folks will probably be clever enough to adjust the instructions to leave out the Cygwin-related details that they don't need.
I've not the level of cleverness :roll:
Alone we go faster … Together we go further …
Avatar's pattern
My Sandbox
Bom-Bom
ℝ - ℕ = ℝ or ℕ ⊄ ℝ
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

Suggested features for an improved version of Pslmake:
The ability to find spartan components.
Integrating APG’s glider seed script into Pslmake.
Being able to select how optimal the slow salvo will be.
Being able to adjust to multiple channels.

EDIT:
I also want it to have an optional import feature for certain Octohash databases.
Last edited by Anivec on January 2nd, 2024, 3:29 pm, edited 1 time in total.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by apg »

AlbertArmStain wrote: January 2nd, 2024, 11:49 am Suggested features for an improved version of Pslmake:
The ability to find spartan components.
Being able to adjust to multiple channels.
What do these mean, exactly? I don't know what 'spartan components' are; I only know of 'spartan' being used to describe conduits and circuitry, and the methods of finding those are completely different from what pslmake does.

Already, pslmake doesn't make a single-channel assumption: it just generates a slow salvo and it's the responsibility of downstream code to compile that slow salvo for a given construction arm.
Being able to select how optimal the slow salvo will be.
You can control the width of the beam search with the -w argument to pslmake.py:

Code: Select all

    # Optional beam width
    parser.add_argument('-w', '--beamwidth', type=int,
        help='Width of the beam search')
If unspecified, it defaults to 5 times the number of CPU threads.
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Anivec
Posts: 2011
Joined: January 28th, 2022, 7:18 pm
Location: In 4.3 miles, take a right onto Exit 54

Re: slmake

Post by Anivec »

calcyman wrote: January 2nd, 2024, 12:17 pm
AlbertArmStain wrote: January 2nd, 2024, 11:49 am Suggested features for an improved version of Pslmake:
The ability to find spartan components.
What do these mean, exactly? I don't know what 'spartan components' are; I only know of 'spartan' being used to describe conduits and circuitry, and the methods of finding those are completely different from what pslmake does.
I mean the feature to be able to add components to still lifes using common objects for a glider activated seed, no reflectors needed.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by apg »

AlbertArmStain wrote: January 2nd, 2024, 3:32 pm
calcyman wrote: January 2nd, 2024, 12:17 pm
AlbertArmStain wrote: January 2nd, 2024, 11:49 am Suggested features for an improved version of Pslmake:
The ability to find spartan components.
What do these mean, exactly? I don't know what 'spartan components' are; I only know of 'spartan' being used to describe conduits and circuitry, and the methods of finding those are completely different from what pslmake does.
I mean the feature to be able to add components to still lifes using common objects for a glider activated seed, no reflectors needed.
It's possible to do this by populating data/bespoke with those conversions. For example, it already contains this conversion which converts an eater into an eater-tie-eater:

Code: Select all

x = 58, y = 60, rule = B3/S23
8bo$7bobo$7bobo$8bo3$10b2o$9bo2bo$10b2o4$3bo5bo$b3o4bobo$o7bobo$2o7bo
14$35b3o$35bo$36bo26$55b3o$55bo$56bo!
You just need to drop in an RLE into data/bespoke for every step of whatever object you want to build, and then run the script refresh.sh to build the database of bespoke syntheses from those RLEs.
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

calcyman wrote: January 2nd, 2024, 8:18 pm
I am thinking about reduction of the implicit "progress graph: size.
When the frontline is huge, there is a lot of permutations in which the clusters could be processed.
The "transposition tables" ... filtering of the beam prevents the search from the exponential growth ... and defragment at the end choses nice order of the gliders.

It seems to me that adding following assymetry to the search would speedup the process for this case:
Split the "rounds to categories (probably 2 would suffice)", one category (not to be used too often) would use the unresticted search as it is now, but the other would restrict the considered clusters to be in lines in "local neighbourhood" of the last glider line.

So the search would tend to look at partially defragmented solutions, but this is not the goal, it is the property. Main achievement is that the restricted phases would run much faster then the unrestricted ones. I am not sure with the phase switching strategy, but I expect there will be no two consecutive unrestricted phases. I am not sure with the strategy of interrupting the restricted phases, but it would be bad policy to run different restrictions in the same round depending on the position ... Especially when the beam is not much wider then the number of cores it would lead to wait for the unresticted core ... . It would probably require to add "no move" to the beam in restricted phases.
Hmm ... maybe if a no-move appears in the unified beam ... it's time to run unrestricted version in the next round.

(I would still resrict the restricted phases to clusters near the frontline ...)

Hmm, probably not the best idea ... max_branching_factor prevents clusters far from frontline to be considered and narrowing the search to last glider line neigbourhood would probably waste a lot of time with clusters near the glider line, but far from the frontline (unless somehow filtered).
Oh no, it could work well ... there is minidx ... filtering to clusters in the neighbourhood of the frontline.

According the first experiments, it performs rather well..., the main advantage is in the positions where the entire frontline is close to local optimum (no incentives to continue ... pessimism not big enough) and when on some lane an improvement is found, the wide search still strugles elsewhere and the progress is near the lane ... so concentration to the lane neighbourhood skips the regions wrere is a small probability for success.

Oh seems have debugged the parallel loading of the beam ... the code contains the restricted search I was talking about. There are debug prints included to help thinking about optmizing the decission making ... probbaly should include the clocks as well.

pslmake:

Code: Select all

#include <unistd.h>
#include <map>
#include <thread>
#include <atomic>
#include "recipe_store.hpp"
#include "object_cache.hpp"
#include "lifelib/cpads/include/cpads/random/prng.hpp"

#define BIG_SAVE_FREQ 10

namespace psl {

template<int N>
std::vector<apg::bitworld> load_file(std::string filename) {
    std::vector<apg::bitworld> vbw;
    apg::lifetree<uint32_t, N> lt(100);
    apg::pattern pat = apg::pattern(&lt, filename);
    for (int i = 0; i < N; i++) { vbw.push_back(pat.flatlayer(i)); }
    return vbw;
}

/**
 * This extracts a 16-bit bitmask of valid signatures from either
 * a string representation of an integer or from a file containing
 * gliders with those signatures.
 */
int extract_signatures(std::string signatures, int lnum) {

    bool is_integer = true;

    for (size_t i = 0; i < signatures.length(); i++) {
        char c = signatures[i];
        if ((c < '0') || (c > '9')) {
            is_integer = false;
        }
    }

    if (is_integer) {
        return std::stoll(signatures);
    }

    apg::bitworld bw = load_file<4>(signatures)[lnum];
    apg::lifetree<uint32_t, 1> lt(100);
    std::vector<apg::bitworld> vbw(1, bw);
    apg::pattern x(&lt, vbw, "b3s23");

    // extract gliders:
    x -= x[8];

    sgsalvo y = dematerialise_gliders(x);

    int res = 0;
    for (auto& g : y) {
        res |= (1 << g.signature());
    }

    return res;
}

int run_tests() {

    apg::lifetree<uint32_t, 1> lt(1000);

    apg::pattern block(&lt, "oo$oo!", "b3s23");

    hh::PRNG prng(1, 2, 3);
    sgsalvo x;
    for (int i = 0; i < 50; i++) {
        x.push_back(SlowGlider<int32_t, 3>::construct(prng.generate(), prng.generate()));
    }

    auto y = dematerialise_gliders(materialise_gliders(block, x));

    int errors = 0;

    for (int i = 0; i < 50; i++) {
        if (x[i] != y[i]) {
            std::cerr << "Error: " << x[i].contents << " != " << y[i].contents << std::endl;
            errors += 1;
        }
    }

    apg::classifier cfier(&lt, "b3s23");
    ObjectCache oc(cfier);

    apg::pattern blinker(&lt, "ooo!", "b3s23");
    auto so = oc.pattern_to_so(blinker, false);

    if (so.bbox[2] != 3) { std::cerr << "Error: bbox[2]=" << so.bbox[2] << " is wrong." << std::endl; errors += 1; }
    if (so.bbox[3] != 3) { std::cerr << "Error: bbox[3]=" << so.bbox[3] << " is wrong." << std::endl; errors += 1; }
    if (so.rows[0] != 0) { std::cerr << "Error: rows[0]=" << so.rows[0] << " is wrong." << std::endl; errors += 1; }
    if (so.rows[1] != 7) { std::cerr << "Error: rows[1]=" << so.rows[1] << " is wrong." << std::endl; errors += 1; }

    auto rv = so.rowvecs();

    auto so2 = oc.pattern_to_so(blinker[1], false);
    auto rv2 = so2.rowvecs();

    if (rv.second  != 9) { std::cerr << "Error: rv.second=" << rv.second << " is wrong." << std::endl; errors += 1; }
    if (rv2.second != 6) { std::cerr << "Error: rv2.second=" << rv2.second << " is wrong." << std::endl; errors += 1; }
    if (rv.first != rv2.first) { std::cerr << "Error: key mismatch." << std::endl; errors += 1; }

    return (errors > 0);
}

struct PSLThread {

    apg::lifetree<uint32_t, 1> lt;
    apg::classifier cfier;
    ObjectCache oc;

    apg::pattern barrier;
    apg::pattern clearway;
    apg::pattern origin;

    std::vector<apg::pattern> cached_gliders;

    int valid_signatures;
    std::set<int> forbidden_lanes;
    int max_dimer_explore;
    int max_dimer_explore_after_progress;
    int max_branching_without_progress;
    int max_branching_per_dimer;
    int max_branching_per_block;
    double pessimism;
    double min_improve;

    std::vector<apg::pattern> bespokes;

    PSLThread() : lt(100),
        cfier(&lt, "b3s23"),
        oc(cfier),
        barrier(&lt, "", "b3s23"),
        clearway(&lt, "", "b3s23"),
        origin(&lt, "", "b3s23"),
        valid_signatures(65535),
        max_dimer_explore(1200),
        max_dimer_explore_after_progress(100),
        max_branching_without_progress(250),
        max_branching_per_dimer(100),
        max_branching_per_block(60),
        pessimism(1.1),
        min_improve(0.0) {

        apg::pattern eglider(&lt, "3o$o$bo!", "b3s23");

        for (int i = 0; i < 8; i++) {
            cached_gliders.push_back(eglider[i]);
        }
    }

    void populate_bespokes(const BespokeRecipeStore &brs) {

        apg::pattern res(&lt, "", "b3s23");
        int i = 0;

        std::map<uint32_t, std::vector<apg::pattern>> ordered_bespokes;
        for (auto it = brs.recipes.begin(); it != brs.recipes.end(); ++it) {
            auto x = oc.rows_to_pattern(it->first, 0, 0, 32, 32);
            ordered_bespokes[x.totalPopulation()].push_back(x);
        }
        for (auto it = ordered_bespokes.rbegin(); it != ordered_bespokes.rend(); ++it) {
            for (auto x : it->second) {
                bespokes.push_back(x);
                res += x.shift(64 * (i & 15), 64 * (i >> 4));
                i += 1;
            }
        }

        std::cerr << "#C " << bespokes.size() << " bespoke targets loaded:" << std::endl;
        res.write_rle(std::cerr);
    }


    apg::pattern make_glider(const SlowGlider<int32_t, 3> &g, int64_t y) {

        return cached_gliders[g.getPhase()].shift(g.getLane() + y, y);

    }

    apg::pattern b2p(const apg::bitworld &bw) {
        std::vector<apg::bitworld> vbw(1, bw);
        apg::pattern pat(&lt, vbw, "b3s23");
        return pat;
    }

    ProblemState parse_pattern(const std::vector<apg::bitworld> &vbw) {

        apg::pattern layer0 = b2p(vbw[0]);
        apg::pattern inf = layer0 & layer0[8];
        apg::pattern glider_pattern = layer0 - inf;
        auto gvec = dematerialise_gliders(glider_pattern);
        std::reverse(gvec.begin(), gvec.end());
        apg::pattern env = to_env(inf);

        if (vbw.size() >= 2) {
            clearway = b2p(vbw[1]) - env;
            for (size_t i = 2; i < vbw.size(); i++) {
                clearway -= b2p(vbw[i]);
            }
        }

        ProblemState ps;
        ps.slow_gliders = gvec;
        ps.objects = oc.objclusters(inf, bespokes);
        barrier = inf;
        ps.compute_hash();
        ps.estimate_cost(pessimism);
        return ps;
    }


    ProblemState parse_progress(const std::vector<apg::bitworld> &vbw) {

        //std::cerr << "A";
        apg::pattern layer0 = b2p(vbw[0]);
        //std::cerr << "B";
        apg::pattern inf = layer0 & layer0[8];
        //std::cerr << "C";
        apg::pattern glider_pattern = layer0 - inf;
        //std::cerr << "D";
        auto gvec = dematerialise_gliders(glider_pattern);
        //std::cerr << "E";
        std::reverse(gvec.begin(), gvec.end());
        //std::cerr << "F";
        ProblemState ps; // = ps;
        //std::cerr << "G";
        ps.slow_gliders = gvec;
        //std::cerr << "H";
        ps.objects = oc.objclusters(inf, bespokes);
        //std::cerr << "I";
        ps.compute_hash();
        //std::cerr << "J";
        ps.estimate_cost(pessimism);
        //std::cerr << "K";
        return ps;
    }

    void set_barrier(const apg::bitworld &bw) { barrier = b2p(bw); }
    void set_clearway(const apg::bitworld &bw) { clearway = b2p(bw); }
    void set_origin(const apg::bitworld &bw) {
        origin = b2p(bw);
        origin &= origin[8];
    }

    apg::pattern best_candidate(const ProblemState &ps) {

        apg::pattern dst = barrier;
        for (uint64_t i = 0; i < ps.objects.size(); i++) {
            dst += oc.so_to_pattern(ps.objects[i]);
        }

        sgsalvo gliders;
        for (auto it = ps.slow_gliders.rbegin(); it != ps.slow_gliders.rend(); ++it) {
            gliders.push_back(*it);
        }

        dst += materialise_gliders(dst, gliders);
        return dst;

    }

    void save_solution(UniqueEnsurer &ue, const ProblemState &ps) {
        apg::pattern res = best_candidate(ps);
        ue.save_solution(res, ps.slow_gliders.size());
    }

    void generate_successors(const ProblemState &ps, BeamSearchContainer &bsc, UniqueEnsurer &ue, RecipeStore &rs, bool wide_search) {

        bool has_origin = false;
        int64_t origin_bbox[4] = {0};
        if (origin.nonempty()) {
            has_origin = true;
            origin.getrect(origin_bbox);
        }

        int lastLane=0; // required only for !wide_search and initialised there
        if (!wide_search) {
            ProblemState ps_nochange = ps;
            ps_nochange.lastdimmer_bb_to_prevlane_dist=-1; //denoting no change
            bsc.try_insert(ps_nochange);
            lastLane = ps.slow_gliders[ps.slow_gliders.size()-1].getLane();
        }

        uint64_t minidx = ps.get_minidx(480);
        apg::pattern dst = barrier;
        for (uint64_t i = minidx; i < ps.objects.size(); i++) {
            dst += oc.so_to_pattern(ps.objects[i]);
        }

        if (dst != dst[8]) {
            std::cerr << "Critical error: dst unstable!!!" << std::endl;
            barrier.write_rle(std::cerr);
            dst.write_rle(std::cerr);
            for (auto& x : ps.objects) {
                std::cerr << x.repr() << std::endl;
            }
        }

        // create halo for convolution purposes:
        apg::pattern halo(&lt, "ooo$ooo$ooo!", "b3s23"); halo = halo.shift(-1, -1);
        apg::pattern halo2(&lt, "b3o$5o$5o$5o$b3o!", "b3s23"); halo2 = halo2.shift(-2, -2);
        halo2 = halo2.convolve(halo2).convolve(halo2); // radius 6
        halo2 = halo2.convolve(halo2); // radius 12

        apg::pattern och2 = origin.convolve(halo2);

        // decrementing counters for state-level and dimer-level branching:

        int branching_remaining = max_branching_without_progress;
        if (ps.objects.size() <= 4) { branching_remaining = 1200 / ps.objects.size(); }

        bool made_progress = false;
        bool built_bespoke = false;

        for (const auto &edge : ps.sorted_dimers) {

            int lastdimmer_bb_to_prevlane_dist;
            SmallObject dimer = ps.objects[edge.first] + ps.objects[edge.second];
            lastdimmer_bb_to_prevlane_dist = dimer.bbox[0]-dimer.bbox[1]-dimer.bbox[3]-lastLane;
            if (lastdimmer_bb_to_prevlane_dist < 0) {
                lastdimmer_bb_to_prevlane_dist = lastLane-dimer.bbox[0]+dimer.bbox[1]-dimer.bbox[2];
            }
            if (lastdimmer_bb_to_prevlane_dist < 0) {
                lastdimmer_bb_to_prevlane_dist = 0;
            }
            if (!wide_search && (lastdimmer_bb_to_prevlane_dist>20)) {// the constant should be optitmized according the statistics
                continue;
            }

            int dimer_explore_remaining = max_dimer_explore;
            if (made_progress) {
                dimer_explore_remaining = (ps.objects.size() <= 4) ? (1200 / ps.objects.size()) : max_dimer_explore_after_progress;
            }

            ProblemState ps_minus_dimer = ps;
            ps_minus_dimer.lastdimmer_bb_to_prevlane_dist = lastdimmer_bb_to_prevlane_dist;
            ps_minus_dimer.erase({edge.first, edge.second});

            // The thing that we're trying to construct:
            SmallObject dso = ps.objects[edge.first];
            if (edge.first != edge.second) {
                dso = dso + ps.objects[edge.second];
                if (oc.so_to_pattern(dso) != oc.so_to_pattern(ps.objects[edge.first]) + oc.so_to_pattern(ps.objects[edge.second])) {
                    std::cerr << "Non-additivity error!" << std::endl;
                }
            }
            apg::pattern dp = oc.so_to_pattern(dso);

            // Nearby objects, etc:
            apg::pattern background = dst - dp;
            apg::pattern nogo = background.convolve(halo) + clearway;

            bool is_isolated = ((background + och2) & dp.convolve(halo2)).empty();
            bool is_isolated_block = (dso.flags & FLAG_BLOCK) && is_isolated;
            int dimer_branching_remaining = 0;
            if (ps.objects.size() <= 4) {
                dimer_branching_remaining = 1200 / ps.objects.size();
            } else if (is_isolated_block) {
                dimer_branching_remaining = max_branching_per_block;
            } else {
                dimer_branching_remaining = max_branching_per_dimer;
            }

            auto f = [&](const std::vector<SmallObject> &objs, const sgsalvo &gliders) {

                if ((dimer_explore_remaining <= 0) || (dimer_branching_remaining <= 0) || (branching_remaining <= 0)) {
                    // We've already reached the maximum branching factor:
                    return;
                }

                for (const auto& g : gliders) {
                    if (((valid_signatures >> g.signature()) & 1) == 0) {
                        // ruled out due to monochromatic (or similar) constraint:
                        return;
                    }
                }

                uint64_t phash = ps_minus_dimer.objects_hash;
                for (const auto &x : objs) { phash += x.positionalHash; }
                uint32_t pcost = ps_minus_dimer.slow_gliders.size() + gliders.size();
                if (ue.leq_cost(phash, pcost)) {
                    // already accomplished with lower cost:
                    return;
                }

                apg::pattern newobjs(&lt, "", "b3s23");
                for (const auto &x : objs) { newobjs += oc.so_to_pattern(x); }
                if ((newobjs & nogo).nonempty()) {
                    // invalid location:
                    return;
                }

                dimer_explore_remaining -= 1;
                apg::pattern src = background + newobjs;

                if (src != src[8]) {
                    // unstable:
                    return;
                }

                int64_t bbox[4] = {0}; src.getrect(bbox);
                int64_t y = bbox[1] + bbox[3] + 128; y -= (y & 63);
                int epochs = (bbox[3] * 4 + 2048) >> 8;

                for (const auto &g : gliders) {

                    src += make_glider(g, y);

                    for (int i = 0; i < epochs; i++) {
                        apg::pattern src2 = src[256];
                        if (src2 == src) { goto stable; }
                        if ((src2 & background) != background) { return; }
                        src = src2;
                    }
                    return;
                    stable:
                    if ((src & clearway).nonempty()) { return; }
                }

                if (src != dst) {
                    // incorrect reaction:
                    return;
                }

                // compute new problem state:
                ProblemState ps_succ = ps_minus_dimer;
                for (const auto &x : objs) { ps_succ.append(x); }
                for (auto it = gliders.rbegin(); it != gliders.rend(); ++it) {
                    ps_succ.slow_gliders.push_back(*it);
                }

                // ps_succ.compute_hash();

                if (phash != ps_succ.objects_hash) {
                    std::cerr << "Hash inconsistency error!" << std::endl;
                }

                bool is_solution = (ps_succ.objects.size() == 1) && (ps_succ.objects[0].flags & FLAG_BLOCK);
                if (has_origin) {
                    is_solution = is_solution && (ps_succ.objects[0].bbox[0] == origin_bbox[0]) && (ps_succ.objects[0].bbox[1] == origin_bbox[1]);
                }

                if (ue.leq_cost_update(phash, pcost, is_solution)) {
                    // was overtaken by a different thread:
                    return;
                }

                if (is_solution) {
                    save_solution(ue, ps_succ);
                }

                // now do the expensive spanning tree computation:
                double newcost = ps_succ.estimate_cost(pessimism, has_origin, origin_bbox[0], origin_bbox[1]);
                if (is_isolated && (ps_succ.remaining_cost() >= ps.remaining_cost())) { return; }

                if (!made_progress) { dimer_branching_remaining -= 1; }
                branching_remaining -= 1;

                if (dso.flags & FLAG_BESPOKE) { made_progress = true; built_bespoke = true; }
                if (newcost < ps.total_cost - min_improve) { made_progress = true; }

                bsc.try_insert(ps_succ);

            }; // lambda function f the recipe application

            // search over all recipes:
            rs.search(dso, f, is_isolated, valid_signatures);

            if (branching_remaining <= 0) { break; }
        }

        std::cerr << (built_bespoke ? "B" : made_progress ? "P" : (branching_remaining <= 0) ? "Q" : "X") << std::flush;

    }

};

void run_worker(std::atomic<uint64_t> *ctr, uint64_t n_tasks, UniqueEnsurer *ue, RecipeStore *rs, PSLThread *thread, BeamSearchContainer *bsc, const ProblemState *problems, bool wide_search) {

    for (;;) {
        // dequeue subtask:
        uint64_t idx = (uint64_t) ((*ctr)++);
        if (idx >= n_tasks) { break; }
        thread->generate_successors(problems[idx], *bsc, *ue, *rs, wide_search);
    }
}

bool run1iter(int beamwidth, UniqueEnsurer &ue, RecipeStore &rs, std::vector<PSLThread> &pslv, std::vector<ProblemState> &problems, std::string progressbase, int generation, bool wide_search) {

    uint64_t n_tasks = problems.size();
    std::atomic<uint64_t> ctr{0ull};
    int parallelism = pslv.size();

    std::cerr << "Processing " << n_tasks << " tasks on " << parallelism << " threads..." << std::endl;

    std::vector<BeamSearchContainer> bscv(parallelism);
    std::vector<std::thread> workers;

    for (int i = 0; i < parallelism; i++) {
        bscv[i].maxsize = beamwidth; bscv[i].max_gliders = ue.max_gliders;
        workers.emplace_back(run_worker, &ctr, n_tasks, &ue, &rs, &(pslv[i]), &(bscv[i]), &(problems[0]), wide_search);
    }

    for (auto&& w : workers) { w.join(); }

    BeamSearchContainer bsc; bsc.maxsize = beamwidth; bsc.max_gliders = ue.max_gliders;
    for (int i = 0; i < parallelism; i++) { bsc += bscv[i]; }

    std::cerr << "\n" << ue.size() << " nodes visited; ";

    double start_best_eval=problems[0].total_cost;
    problems.clear();
    int beamindex=0;bool first=true; bool saveit=(generation % BIG_SAVE_FREQ)==0;
    for (auto it = bsc.pmpq.begin(); it != bsc.pmpq.end(); ++it) {
        apg::pattern res = pslv[0].best_candidate(bsc.contents[it->second]);
        if (saveit || first) {
            first = false;
            std::ofstream out(progressbase+std::to_string(generation)+"_"+std::to_string(++beamindex)+".mc");
            res.write_macrocell(out);
            out.close();
        }
        problems.push_back(bsc.contents[it->second]);
    }

    double end_worst_beam_eval=problems[problems.size()-1].total_cost;

    if (bsc.pmpq.empty()) {
        std::cerr << "search complete." << std::endl;
    } else {
        auto it = bsc.pmpq.begin();
        std::cerr << "progress = " << (100.0 * bsc.contents[it->second].slow_gliders.size() / it->first) << "%; ";
        std::cerr << "start best eval = " << start_best_eval << "; end worst beam eval = " << end_worst_beam_eval << "; to wide search diff = " << (end_worst_beam_eval - start_best_eval) ;
        std::cerr << "; lastdimmer_bb_to_prevlane_dist = " << bsc.contents[it->second].lastdimmer_bb_to_prevlane_dist;
        std::cerr << "; best new cost estimate = " << it->first << std::endl;
    }

    if (ue.max_gliders==999999999) {//solution not found yet
        beamindex=beamwidth;
        if (saveit) {
            for(int g=2;g<=generation;g++) {
                if (((generation+1-g) % BIG_SAVE_FREQ)==0) {
                    int treshold=1+(beamwidth/g);
                    //std::cerr << "removal beamindex " << beamindex << " treshold " << treshold << std::endl;
                    while (beamindex>treshold) {
                        std::string delfilename = (progressbase+std::to_string(generation+1-g)+"_"+std::to_string(beamindex--)+".mc");
                        //std::cerr << delfilename << std::endl;
                        std::remove(delfilename.c_str());
                    };
                    if (beamindex==1) {
                        break;
                    }
                }
            }
        }
    }
    return end_worst_beam_eval >= start_best_eval;
    // for (auto& ps : problems) { std::cerr << "size = " << ps.objects.size() << std::endl; }

    // problems.swap(bsc.contents);
}

void fed_worker(std::atomic<uint64_t> *ctr, uint64_t n_tasks, PSLThread *thread, BeamSearchContainer *bsc, std::string *file_names) {

    for (;;) {
        // dequeue subtask:
        uint64_t idx = (uint64_t) ((*ctr)++);
        if (idx >= n_tasks) { break; }
        //std::cerr << file_names[idx] << std::endl;
        //std::vector<apg::bitworld> vbw = load_file<1>(file_names[idx]);
        std::vector<apg::bitworld> vbw;
        apg::lifetree<uint32_t, 1> lt(100);
        apg::pattern pat = apg::pattern(&lt, file_names[idx]);
        //int64_t bbox[4] = {0, 0, 0, 0}; pat.getrect(bbox);
        //std::cerr << "(" << bbox[0] << "," << bbox[1] << "," << bbox[2] << "," << bbox[3] << ")";
        vbw.push_back(pat.flatlayer(0));
        ProblemState ps = thread -> parse_progress(vbw);
        bsc->try_insert(ps);
        //std::cerr << idx;
    }
}

bool test_generationfile(std::string progressbase, int generation) {
     std::string filename=progressbase+std::to_string(generation)+"_1.mc";
     std::ifstream f(filename.c_str());
     return f.good();
}

int problems_fed(std::string progressbase, std::vector<ProblemState> &problems, std::vector<PSLThread> &pslv, int beamwidth, int maxprogress) {
    // hope the pessimism prevents small generations to survive (unless near the solution)
    // rather slow especially in massively parallel environment ... should be parallelised
    std::cerr << "Loading progress..." << std::flush;
    int generation=1;
    const ProblemState ps = problems[0];

    while (test_generationfile(progressbase,generation)) {
        std::cerr << generation << " "  << std::flush;
        generation=2*generation;
    }
    int gen_anchor_t=generation,gen_anchor_b=generation/2;
    while (gen_anchor_t-gen_anchor_b>1) {
        generation = (gen_anchor_t+gen_anchor_b)/2;
        std::cerr << generation << " "  << std::flush;
        if (test_generationfile(progressbase,generation)) {
            gen_anchor_b=generation;
        } else {
            gen_anchor_t=generation;
        }
    }
    generation = gen_anchor_b;
    if ((maxprogress>0) && (generation > maxprogress)) {
        generation = maxprogress;
        std::cerr << generation << " "  << std::flush;
    }
    gen_anchor_b=generation-BIG_SAVE_FREQ;
    if (gen_anchor_b<0) {
        gen_anchor_b=0;
    }

    std::vector<std::string> file_names(0);
    while (gen_anchor_b<generation) {
        std::cerr << ++gen_anchor_b << " "  << std::flush;
        int beamindex=0;
        while (true) {
            std::string filename=progressbase+std::to_string(gen_anchor_b)+"_"+std::to_string(++beamindex)+".mc";
            std::ifstream f(filename.c_str());
            if (!f.good()) {
                    break;
            }
            file_names.push_back(filename);
        }
    }

    uint64_t n_tasks = file_names.size();
    std::atomic<uint64_t> ctr{0ull};
    int parallelism = pslv.size();

    std::cerr << "Loading " << n_tasks << " states on " << parallelism << " threads..." << std::endl;

    std::vector<BeamSearchContainer> bscv(parallelism);
    std::vector<std::thread> workers;

    for (int i = 0; i < parallelism; i++) {
        bscv[i].maxsize = beamwidth;
        workers.emplace_back(fed_worker, &ctr, n_tasks, &(pslv[i]), &(bscv[i]), &(file_names[0]));
    }

    std::cerr << "Waiting for workers" << std::endl;

    BeamSearchContainer bsc; bsc.maxsize = beamwidth;
    for (auto&& w : workers) { w.join(); }

    std::cerr << "Workers finished" << std::endl;

    for (int i = 0; i < parallelism; i++) { bsc += bscv[i]; }
    if (generation > 0) {
        problems.clear();
        for (auto it = bsc.pmpq.begin(); it != bsc.pmpq.end(); ++it) {
            problems.push_back(bsc.contents[it->second]);
        }
    }
    return generation;
}

void run_main(  int beamwidth,
                int parallelism,
                std::string dbfile,
                std::string infile,
                std::string outfile,
                std::string progress,
                int valid_signatures) {

    bool is_mono = ((valid_signatures & 0x00ff) == 0) || ((valid_signatures & 0xff00) == 0);

    int max_gliders=999999999;
    int maxprogress=-1;
    if (progress=="keep") {
        max_gliders--;
    } else {
        std::string inidig="123456789";
        if (inidig.find(progress.substr(0,1))!=std::string::npos){
            maxprogress = std::stoi(progress);
            max_gliders--;
        }
    }

    UniqueEnsurer ue(max_gliders, outfile);
    RecipeStore rs(dbfile);
    rs.read_table();

    std::vector<PSLThread> pslv(parallelism);
    std::vector<ProblemState> problems(1);

    std::cerr << "Loading bespoke database..." << std::flush;
    rs.brs.load("data/bespoke/bespokes.dat");
    std::cerr << " done!" << std::endl;

    // Obtain bespoke objects:
    pslv[0].populate_bespokes(rs.brs);
    int generation;
    std::string progress_base;
    int pos=outfile.rfind(".");
    progress_base=outfile.substr(0,pos)+"_";

    std::cerr << "Loading problem..." << std::flush;
    {
        std::vector<apg::bitworld> vbw = load_file<4>(infile);

        problems[0] = pslv[0].parse_pattern(vbw);
        auto clearway = pslv[0].clearway.flatlayer(0);
        auto barrier = pslv[0].barrier.flatlayer(0);

        for (int i = 0; i < parallelism; i++) {
            pslv[i].set_clearway(clearway);
            pslv[i].set_barrier(barrier);
            pslv[i].set_origin(vbw[3]);
            pslv[i].valid_signatures = valid_signatures;
            if (is_mono) { pslv[i].pessimism += 0.2; }
        }

        auto origin = pslv[0].origin;

        bool has_origin = false;
        int64_t origin_bbox[4] = {0};
        if (origin.nonempty()) {
            has_origin = true;
            origin.getrect(origin_bbox);
            std::cerr << "Origin: " << origin_bbox[0] << "," << origin_bbox[1] << std::endl;
        } else {
            std::cerr << "No origin! " << std::endl;
        }

        generation = problems_fed(progress_base,problems,pslv,beamwidth,maxprogress);
        if (generation==0) {
            problems[0].estimate_cost(pslv[0].pessimism, has_origin, origin_bbox[0], origin_bbox[1]);
        }
    }
    std::cerr << " done!" << std::endl;
    bool wide_search=true;
    while (!(problems.empty())) {
        std::cerr << " (" << generation << ")" ;
        wide_search = run1iter(beamwidth, ue, rs, pslv, problems, progress_base, ++generation, wide_search);
    }
}

} // namespace psl


int main(int argc, char* argv[]) {

    bool incorrectly_called = (argc != 8);

    for (int i = 1; i < argc; i++) {
        if (argv[i][0] == '-') { incorrectly_called = true; }
    }

    if (incorrectly_called) {
        std::string program_name = argv[0];
        program_name += ".py";
        std::cerr << "Warning: you called " << argv[0] << " when you probably meant " << program_name << std::endl;
        execvp(program_name.c_str(), argv);
        std::cerr << "Error: cannot run Python script." << std::endl;
        return 1;
    }

    int x = psl::run_tests();
    if (x) { return x; }

    int signatures = psl::extract_signatures(argv[4], 3);
    if (signatures == 0) { signatures = psl::extract_signatures(argv[7], 0); }
    std::string dbfile = argv[3];
    if (((signatures & 0xff)==0) || ((signatures & 0xff00)==0)) {
         if (dbfile.substr(0,8) == "data/std") {
            dbfile = "data/mono"+ dbfile.substr(8,dbfile.length()-3);
            std::cerr << "Dbfile changed from " << argv[3] << " to " << dbfile << " to better correspond to the signature" << std::endl;
         }
    }

    std::cerr << "\033[33;1mRunning pslmake with valid signatures " << signatures << "\033[0m" << std::endl;

    psl::run_main(
        std::stoll(argv[1]),
        std::stoll(argv[2]),
        dbfile, argv[4], argv[5], argv[6],
        signatures
    );

    return 0;

}
structures:

Code: Select all

#pragma once
#include <stdint.h>
#include <type_traits>
#include <vector>
#include <set>
#include <unordered_map>
#include <map>
#include <sstream>
#include <string>

#include "lifelib/cpads/include/cpads/mxor.hpp"
#include "unique_ensurer.hpp"
#include "lifelib/spantree.h"
#include "lifelib/avxlife/uli.h"


namespace psl {

constexpr static uint64_t FLAG_BESPOKE = 1;
constexpr static uint64_t FLAG_BLOCK = 2;
constexpr static uint64_t FLAG_DIMER = 4;

/**
 * A structure representing a single glider in a slow salvo.
 */
template<typename T, int PhaseBits>
struct SlowGlider {

    T contents;

    static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
        "T must be a signed integral type.");

    constexpr static uint64_t PhaseMask = (1ull << PhaseBits) - 1;

    static SlowGlider<T, PhaseBits> construct(int64_t lane, uint64_t phase) {
        T res = ((T) ((lane << PhaseBits) + (phase & PhaseMask)));
        return SlowGlider<T, PhaseBits>{res};
    }

    SlowGlider<T, PhaseBits> shift(int64_t dx) const {
        T res = contents + ((T) (dx << PhaseBits));
        return SlowGlider<T, PhaseBits>{res};
    }

    int64_t getLane() const {
        return ((int64_t) (contents >> PhaseBits));
    }

    uint64_t getPhase() const {
        return ((uint64_t) contents) & PhaseMask;
    }

    using ThisType = SlowGlider<T, PhaseBits>;
    INHERIT_COMPARATORS_FROM(ThisType, contents, _HI_)

    int signature() const {
        return (contents & 15);
    }

};

typedef std::vector<SlowGlider<int32_t, 3>> sgsalvo;

template<typename T>
sgsalvo translate_salvo(const T& gliders, int64_t dx, int64_t dy, int64_t dt) {

    sgsalvo res;

    for (const auto& g : gliders) {

        int64_t lane = g.getLane();
        uint64_t phase = g.getPhase();

        lane += (dx - dy);
        phase += (dt - 4*dy);

        res.push_back(SlowGlider<int32_t, 3>::construct(lane, phase));
    }

    return res;
}

std::vector<sgsalvo> translate_salvo2(const std::vector<sgsalvo>& gliders, int64_t dx, int64_t dy, int64_t dt) {
    std::vector<sgsalvo> res;
    for (const auto& g : gliders) {
        res.push_back(translate_salvo(g, dx, dy, dt));
    }
    return res;
}


_HI_ uint64_t hash_rows(const uint32_t *rows) {
    uint64_t state = 0;
    for (int i = 0; i < 32; i++) {
        state = state * 6364136223846793005ull + hh::mix_circulant(rows[i]);
    }
    // powerful mixing function (PractRand cannot distinguish the output
    // of two rounds of hh::fibmix() applied to a simple counter):
    return hh::fibmix(hh::fibmix(state));
}



struct SmallObject {

    // Bounding box of the envelope of the pattern:
    int64_t bbox[4]; // 32 bytes

    uint64_t positionalHash;
    uint64_t flags;
    double estimatedCost;
    int32_t period;
    int32_t popx8;

    uint32_t rows[32]; // 128 bytes

    std::string repr() const {
        std::ostringstream ss;
        ss << "# bbox = [" << bbox[0] << ", " << bbox[1] << ", " << bbox[2] << ", " << bbox[3] << "];" << std::endl;
        ss << "# flags = " << flags << "; cost = " << estimatedCost << "; period = " << period << "; pop = " << (0.125 * popx8) << ";" << std::endl;
        for (int i = 0; i < bbox[3]; i++) {
            for (int j = 0; j < bbox[2]; j++) {
                ss << ".*"[(rows[i] >> j) & 1];
            }
            ss << std::endl;
        }
        return ss.str();
    }

    std::vector<uint32_t> rowvec(int j) const {

        uint32_t d[32] = {0};

        // translate:
        for (size_t i = 2; i < 30; i++) { d[i] = rows[i-2] << 2; }

        if (j & 1) { b3s23::iterate_var_32_28_onegen(d); }

        std::vector<uint32_t> key(32);

        for (int y = 0; y < 28; y++) {
            uint32_t row = (d[y + 2] >> 2) & 0xfffffff;
            while (row) {
                int x = hh::ctz32(row);
                row ^= (1 << x);

                if (j & 2) {
                    key[x] |= (1 << y);
                } else {
                    key[y] |= (1 << x);
                }
            }
        }

        return key;
    }

    std::pair<std::vector<uint32_t>, uint32_t> rowvecs() const {

        std::vector<uint32_t> res = rowvec(0);
        uint32_t mask = 1;

        for (int j = 1; j < 4; j++) {
            if (j & 1 & period) { continue; }
            auto v = rowvec(j);
            if (v < res) {
                mask = (1 << j);
                res = v;
            } else if (v == res) {
                mask |= (1 << j);
            }
        }

        return std::pair<std::vector<uint32_t>, uint32_t>(res, mask);
    }

    /**
     * Get the diagonal position of the centre, measured in quarter-diagonals.
     */
    int64_t qd() const {
        return (bbox[0] + bbox[1]) * 2 + (bbox[2] + bbox[3]);
    }

    uint64_t hash() const {
        uint64_t state = hash_rows(rows);
        for (int i = 0; i < 4; i++) {
            state = hh::fibmix(state + ((uint64_t) bbox[i]));
        }
        return state;
    }

    /**
     * Comparison operator for the purpose of sorting by diagonal position.
     */
    bool operator<(const SmallObject &other) const {
        auto qd1 = qd();
        auto qd2 = other.qd();

        if (qd1 < qd2) { return true; }
        if (qd2 < qd1) { return false; }

        return bbox[0] < other.bbox[0];
    }

    void shift_append(int dx, int dy, const SmallObject &other) {

        if ((dx >= 32) || (dy >= 32)) { return; }

        for (int i = dy; i < 32; i++) {
            rows[i] |= (other.rows[i - dy] << dx);
        }
    }

    SmallObject operator+(const SmallObject &other) const {

        SmallObject res;
        memset(&res, 0, sizeof(res));

        res.bbox[0] = hh::min(bbox[0], other.bbox[0]);
        res.bbox[1] = hh::min(bbox[1], other.bbox[1]);
        res.bbox[2] = hh::max(bbox[0] + bbox[2], other.bbox[0] + other.bbox[2]) - res.bbox[0];
        res.bbox[3] = hh::max(bbox[1] + bbox[3], other.bbox[1] + other.bbox[3]) - res.bbox[1];

        res.shift_append(bbox[0] - res.bbox[0], bbox[1] - res.bbox[1], *this);
        res.shift_append(other.bbox[0] - res.bbox[0], other.bbox[1] - res.bbox[1], other);

        res.popx8 = popx8 + other.popx8;
        res.positionalHash = res.hash();
        res.flags = FLAG_DIMER;

        return res;
    }
};

static_assert(sizeof(SmallObject) == 192, "SmallObject should occupy 192 bytes");


struct ProblemState {

    // irredundant fields:
    std::vector<SmallObject> objects;
    sgsalvo slow_gliders;

    // computed fields:
    uint64_t objects_hash;
    std::vector<apg::edge64> sorted_dimers;
    double total_cost; // from objects + spanning tree length + slow_gliders.size()
    int lastdimmer_bb_to_prevlane_dist; //statstic to take into account for "concentration optimization" ... not needed for search

    bool operator<(const ProblemState &other) const {
        return total_cost < other.total_cost;
    }

    double remaining_cost() const {
        return total_cost - slow_gliders.size();
    }

    uint64_t compute_hash() {
        objects_hash = 0;
        for (const auto &x : objects) {
            objects_hash += x.positionalHash;
        }
        return objects_hash;
    }

    void append(const SmallObject &so) {
        objects.push_back(so);
        objects_hash += so.positionalHash;
    }

    void erase(const std::vector<uint64_t> &idxs) {
        std::vector<uint64_t> sorted_idxs = idxs;
        std::sort(sorted_idxs.begin(), sorted_idxs.end());
        sorted_idxs.erase(std::unique(sorted_idxs.begin(), sorted_idxs.end()), sorted_idxs.end());
        while (sorted_idxs.size()) {
            uint64_t idx = sorted_idxs.back();
            sorted_idxs.pop_back();
            objects_hash -= objects[idx].positionalHash;
            objects.erase(objects.begin() + idx);
        }
    }

    uint64_t get_minidx(int bandwidth) const {

        uint64_t minidx = objects.size() - 1;
        int64_t maxqd = objects[minidx].qd();

        while ((minidx > 0) && (objects[minidx-1].qd() >= maxqd - bandwidth)) {
            minidx -= 1;
        }

        return minidx;
    }

    // prepares sorted_dimmers for following search as well the graph creation for the spanning tree size calculation
    // is the bottleneck then we just filter edges which were already considered
    double estimate_cost(double pessimism, bool has_origin = false, int64_t ox = 0, int64_t oy = 0) {

        // contribution from existing gliders:
        total_cost = slow_gliders.size();
        sorted_dimers.clear();

        if (objects.empty()) { return total_cost; }

        std::sort(objects.begin(), objects.end());

        int64_t sqdist = 0;

        std::vector<apg::coords64> ccentres;
        for (const auto &x : objects) {

            // compute centre of bounding box in half-coordinates:
            int64_t mx2 = x.bbox[0] * 2 + x.bbox[2];
            int64_t my2 = x.bbox[1] * 2 + x.bbox[3];

            if (x.flags & FLAG_BESPOKE) {
                // weird trick to encourage blocks near bespoke objects to
                // be moved northwest so that they don't get in the way:
                mx2 -= 50; my2 -= 50; // 25fd behind where it actually is
            }

            int64_t dx = ox - x.bbox[0];
            int64_t dy = oy - x.bbox[1];
            sqdist = hh::max(sqdist, dx * dx + dy * dy);

            ccentres.emplace_back(mx2, my2);

            // contribution from objects:
            total_cost += x.estimatedCost * pessimism;
        }

        if (has_origin) { total_cost += 0.2 * std::sqrt(sqdist); }
        auto all_edges = apg::spanning_graph(ccentres);

        // correct for using half-coordinates:
        double stlength = 0.5 * apg::spanning_graph_to_tree(ccentres, 0, &std::sqrt, all_edges);

        // cost contribution from spanning tree:
        total_cost += 0.15 * stlength * pessimism;

        uint64_t minidx = get_minidx(200);

        std::vector<std::pair<int64_t, apg::edge64>> dimers;

        for (uint64_t i = minidx; i < objects.size(); i++) {
            int64_t pen = -2 * objects[i].qd();
            if (objects[i].flags & FLAG_BESPOKE) {
                // bespoke
                pen -= 480;
            } else if (objects[i].flags & FLAG_BLOCK) {
                // block
                pen += 480;
            } else {
                // monomer
                pen += 80;
            }
            dimers.emplace_back(pen, apg::edge64(i, i));
        }

        for (const apg::edge64 &edge : all_edges) {
            if ((edge.first < minidx) || (edge.second < minidx)) { continue; }
            int64_t x1 = ccentres[edge.first].first;
            int64_t y1 = ccentres[edge.first].second;
            int64_t x2 = ccentres[edge.second].first;
            int64_t y2 = ccentres[edge.second].second;

            if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > 10000) { continue; }
            if ((objects[edge.first].flags & FLAG_BESPOKE) || (objects[edge.second].flags & FLAG_BESPOKE)) { continue; }
            SmallObject dimer = objects[edge.first] + objects[edge.second];
            if ((dimer.bbox[2] > 28) || (dimer.bbox[3] > 28)) { continue; }
            dimers.emplace_back(0 - (x1 + y1 + x2 + y2), edge);
        }

        std::sort(dimers.begin(), dimers.end());
        for (const auto &x : dimers) {
            sorted_dimers.push_back(x.second);
        }

        return total_cost;
    }
};



struct BeamSearchContainer {

    uint64_t maxsize;
    std::vector<ProblemState> contents;
    std::set<std::pair<double, uint64_t>> pmpq; // poor man's priority queue
    std::unordered_map<uint64_t, uint64_t> hash_to_idx;
    int max_gliders=999999999;

    void try_insert(const ProblemState& ps) {

        double optimism = 0.66;
        if (ps.remaining_cost() * optimism + ps.slow_gliders.size() > max_gliders) { return; } // very small chance to beat the best solution

        auto it = hash_to_idx.find(ps.objects_hash);
        if (it != hash_to_idx.end()) {
            uint64_t idx = it->second;
            if (ps.slow_gliders.size() < contents[idx].slow_gliders.size()) {
                pmpq.erase(std::pair<double, uint64_t>{contents[idx].total_cost, idx});
                contents[idx] = ps;
                pmpq.insert(std::pair<double, uint64_t>{contents[idx].total_cost, idx});
            }
            return;
        }

        uint64_t idx = contents.size();

        if (idx >= maxsize) {
            auto opair = *(pmpq.rbegin());
            if (ps.total_cost >= opair.first) { return; }
            idx = opair.second;
            pmpq.erase(opair);
            hash_to_idx.erase(contents[idx].objects_hash);
            contents[idx] = ps;
        } else {
            contents.push_back(ps);
        }

        pmpq.insert(std::pair<double, uint64_t>{contents[idx].total_cost, idx});
        hash_to_idx[ps.objects_hash] = idx;
    }

    void operator+=(const BeamSearchContainer &other) {
        for (const auto &x : other.contents) {
            try_insert(x);
        }
    }
};


struct BasicSalvo {

    uint16_t src; // encodes a 4x4 pattern
    int8_t dx;
    int8_t dy;

    SlowGlider<int8_t, 1> gliders[12];

    uint64_t size() const {
        for (uint64_t i = 0; i < 12; i++) {
            if (gliders[i].contents == ((int8_t) 0x80)) {
                return i;
            }
        }
        return 12;
    }

    uint32_t qcost() const {
        uint32_t res = 12;
        switch (src) {
            case 0x0033: res = 0; break;
            case 0x0696: case 0x2552: res = 5; break;
            case 0x6996: res = 8; break;
            case 0x6952: case 0x69a4: case 0x4a96: case 0x2596: case 0x0252: res = 9; break;
            case 0x0352: case 0x0652: case 0x0253: case 0x0256: res = 10; break;
            case 0x0653: case 0x0356: res = 11; break;
        }
        return res;
    }

    bool operator<(const BasicSalvo &other) const {

        auto this_size = size();
        auto other_size = other.size();
        auto this_cost = qcost() + 4 * this_size;
        auto other_cost = other.qcost() + 4 * other_size;

        if (this_cost < other_cost) { return true; }
        if (other_cost < this_cost) { return false; }
        if (this_size < other_size) { return true; }
        if (other_size < this_size) { return false; }
        if (src < other.src) { return true; }
        if (other.src < src) { return false; }

        for (uint64_t i = 0; i < this_size; i++) {
            if (gliders[i] < other.gliders[i]) { return true; }
            if (other.gliders[i] < gliders[i]) { return false; }
        }

        return false;
    }

    bool operator==(const BasicSalvo &other) const {
        uint64_t res[4] = {0};
        memcpy(res, this, 16);
        memcpy(res + 2, &(other), 16);
        return ((res[0] == res[2]) && (res[1] == res[3]));
    }

    BasicSalvo transform(int j) const {

        BasicSalvo bs = (*this);
        uint8_t xordiffs[4] = {0x00, 0x01, 0xfe, 0xff};

        for (int i = 0; i < 12; i++) {
            if (gliders[i].contents == ((int8_t) 0x80)) { break; }
            bs.gliders[i].contents ^= xordiffs[j];
        }

        if (j & 2) {
            // swap x and y displacements:
            int8_t dz = bs.dx; bs.dx = bs.dy; bs.dy = dz;
            // transpose the source object bitarray:
            bs.src = hh::transpose4(bs.src);
        }

        return bs;
    }

    sgsalvo to_salvo(int64_t dx, int64_t dy) const {
        uint64_t n = size();
        std::vector<SlowGlider<int8_t, 1>> g(n);
        for (uint64_t i = 0; i < n; i++) { g[i] = gliders[i]; }
        return translate_salvo(g, dx, dy, 0);
    }
};

static_assert(sizeof(BasicSalvo) == 16, "BasicSalvo should be 16 bytes");
} // namespace psl
Part of output:

Code: Select all

Loading problem...No origin! 
Loading progress...1 2 4 8 16 32 64 128 256 512 1024 1536 1792 1664 1728 1696 1712 1704 1708 1710 1711 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 Loading 73 states on 64 threads...
Waiting for workers
Workers finished
 done!
 (1710)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ
15933 nodes visited; progress = 3.27912%; start best eval = 310971; end worst beam eval = 310970; to wide search diff = -0.914124; lastdimmer_bb_to_prevlane_dist = 1575; best new cost estimate = 310968
 (1711)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
28030 nodes visited; progress = 3.2814%; start best eval = 310968; end worst beam eval = 310966; to wide search diff = -1.85885; lastdimmer_bb_to_prevlane_dist = 2; best new cost estimate = 310965
 (1712)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
28030 nodes visited; progress = 3.2814%; start best eval = 310965; end worst beam eval = 310966; to wide search diff = 1.37722; lastdimmer_bb_to_prevlane_dist = -1; best new cost estimate = 310965
 (1713)Processing 64 tasks on 64 threads...
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
44606 nodes visited; progress = 3.28301%; start best eval = 310965; end worst beam eval = 310966; to wide search diff = 1.04134; lastdimmer_bb_to_prevlane_dist = 2625; best new cost estimate = 310965
 (1714)Processing 64 tasks on 64 threads...
QQQQQPPPPQQPQPPPQQQPQQQPPPQPQQQPPPPPQQPQPQPQQQQQQPQQQQPPQPQQQQQQ
60831 nodes visited; progress = 3.28527%; start best eval = 310965; end worst beam eval = 310965; to wide search diff = -0.454461; lastdimmer_bb_to_prevlane_dist = 3179; best new cost estimate = 310964
 (1715)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
61447 nodes visited; progress = 3.28527%; start best eval = 310964; end worst beam eval = 310965; to wide search diff = 0.924389; lastdimmer_bb_to_prevlane_dist = -1; best new cost estimate = 310964
 (1716)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
78023 nodes visited; progress = 3.28625%; start best eval = 310964; end worst beam eval = 310963; to wide search diff = -0.62461; lastdimmer_bb_to_prevlane_dist = 3618; best new cost estimate = 310962
 (1717)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
91399 nodes visited; progress = 3.28819%; start best eval = 310962; end worst beam eval = 310962; to wide search diff = -0.362752; lastdimmer_bb_to_prevlane_dist = 2; best new cost estimate = 310961
 (1718)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
91399 nodes visited; progress = 3.28819%; start best eval = 310961; end worst beam eval = 310962; to wide search diff = 0.681868; lastdimmer_bb_to_prevlane_dist = -1; best new cost estimate = 310961
 (1719)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
108817 nodes visited; progress = 3.29079%; start best eval = 310961; end worst beam eval = 310960; to wide search diff = -1.27493; lastdimmer_bb_to_prevlane_dist = 3386; best new cost estimate = 310959
 (1720)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
108817 nodes visited; progress = 3.29079%; start best eval = 310959; end worst beam eval = 310960; to wide search diff = 0.58979; lastdimmer_bb_to_prevlane_dist = -1; best new cost estimate = 310959
 (1721)Processing 64 tasks on 64 threads...
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
125393 nodes visited; progress = 3.29239%; start best eval = 310959; end worst beam eval = 310960; to wide search diff = 0.733259; lastdimmer_bb_to_prevlane_dist = 2625; best new cost estimate = 310960
 (1722)Processing 64 tasks on 64 threads...
lastdimmer_bb_to_prevlane_dist = -1 means the best in the beem remaned from the prev generation. This does not suggest the modifcation is worth it.

After addng timestempts ... it could actually be helpfull:

Code: Select all

 (1750)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
16016 nodes visited; Wed Jul  3 19:59:52 2024
progress = 3.35421%; start best eval = 310896; end worst beam eval = 310895; to wide search diff = -0.556073; lastdimmer_bb_to_prevlane_dist = 3092; best new cost estimate = 310893
 (1751)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXPPPPPXPPXXXXXXXXPXXXXXPXPPXXXXXXXXXPXPXXXPPXP
28770 nodes visited; Wed Jul  3 20:03:03 2024
progress = 3.35487%; start best eval = 310893; end worst beam eval = 310893; to wide search diff = -0.363397; lastdimmer_bb_to_prevlane_dist = 0; best new cost estimate = 310891
 (1752)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXPPPPPPP
36013 nodes visited; Wed Jul  3 20:06:34 2024
progress = 3.35876%; start best eval = 310891; end worst beam eval = 310891; to wide search diff = 0.289207; lastdimmer_bb_to_prevlane_dist = 10; best new cost estimate = 310889
 (1753)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
52812 nodes visited; Wed Jul  3 20:11:36 2024
progress = 3.35944%; start best eval = 310889; end worst beam eval = 310887; to wide search diff = -1.68072; lastdimmer_bb_to_prevlane_dist = 3617; best new cost estimate = 310885
 (1754)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXPXXXPXXXXXPXPPPXXXXXXXXXXXXXX
61493 nodes visited; Wed Jul  3 20:14:20 2024
progress = 3.35944%; start best eval = 310885; end worst beam eval = 310887; to wide search diff = 1.89222; lastdimmer_bb_to_prevlane_dist = -1; best new cost estimate = 310885
 (1755)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
79164 nodes visited; Wed Jul  3 20:19:25 2024
progress = 3.36269%; start best eval = 310885; end worst beam eval = 310884; to wide search diff = -0.947207; lastdimmer_bb_to_prevlane_dist = 3092; best new cost estimate = 310883
 (1756)Processing 64 tasks on 64 threads...
XXXXXXXXXXXXXXXXXXXXXXXPPPPPPPPPPPPPPPPPPXXXXXXXXXXXXXPPPPPPPPPP
87476 nodes visited; Wed Jul  3 20:22:09 2024
progress = 3.36527%; start best eval = 310883; end worst beam eval = 310883; to wide search diff = 0.237742; lastdimmer_bb_to_prevlane_dist = 2; best new cost estimate = 310881
 (1757)Processing 64 tasks on 64 threads...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
105226 nodes visited; Wed Jul  3 20:27:05 2024
progress = 3.36724%; start best eval = 310881; end worst beam eval = 310879; to wide search diff = -1.8366; lastdimmer_bb_to_prevlane_dist = 3021; best new cost estimate = 310878
 (1758)Processing 64 tasks on 64 threads...
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

Seems previous week I have finally debugged my changes in bespoke evaluations.
Problem was I have initialised cache with bespoke estimated costs only in first thread what lead to evaluation inconsistencies ...
Now pslmake works "slowly but steadily" with about 1-5 minutes per generation (on digital clock pattern).

This allows much more bespoke variants then the original pslmake was able to support ... we can make objects requiring several steps to construct.
(Actually the snarkheart required 2 steps already, but there was not too many objects required to add between the phases).

(I work with the following base

Code: Select all

#C 166 bespoke targets loaded:
#CLL state-numbering golly
x = 977, y = 648, rule = B3/S23
o79bo47b2o15b2o49bo15b2o45bo62b2o61b2o63b2o65b2o66b2obo55b2o62b2o
65b2o62b2o57b2o64bo$3o75b3o48bo15bo3bo46b3o13bobo42b3o61bobo61bobo
62bobo6bo56bobo57b2o3b2o2b2ob3o52bo2bo60bo2bo63bo2bo4b2o54bo2bo4b
2o50bobo62bobob2o$3bo73bo51bobo11bobo2bobo48bo14bo2b2o37bo64bo65bo
2b2o59bo6bobo51b2o2bo59bo2bo2bo8bo52bobo61bobo63bobo4bobo54bobo5b
2o52bo4b2o56bobobobo$2b2o73b2o51b2o11b2o3bobo47b2o13b2obobo37b2o
62b2o64b2obobo66bobo51bobob2o59b5o3b2ob3o54bo63bo59bo5bo4b3o50bo5b
o56b4ob2o2bo2bo51b2obobobobo2bo$15b2o47b2o71bo9b2ob3o61bo2bo169bo
2bo58b5o3b2ob3o50bo2bo69bobo179bobo9b2o50bobo61bo2bobobobob2o51bo
2bo2b2ob4o$15bo49bo70bobo14bo58bobo119bo50bobo60bo2bo2bo8bo52bobo
60bo6bobo179bo2bo11b2o47bo2bo9b3o51bobobobo56b2o4bo$13bobo49bobo
68bobo8b2ob3o59b2o50b2o64b2obobo49b2o61b2o3b2o2b2ob3o54b2o59bobo6b
o53b2o62b2o62b2o11b3o48b2o10b2o53b2obobo62bobo$13b2o51b2o69bo9b2ob
o114bo63bobobobo121b2obo117b2o60bo2bo60bo2bo140b2o54bo64b2o$5b2o
67b2o189bob2o57bo2bobobobob2o301bobo61bobo140b3o$4bo2bo65bo2bo124b
o9b2obo48b3o2bo57b4ob2o2bo2bo45b2obo128bob2o121bo63bo76b2o62bobo$
5b2o67b2o72b2o50bobo8b2ob3o45bo3b2o62bo4b2o47b2ob3o124b3ob2o262b2o
62b2o$148bobo49bobo14bo44b4o62bobo59bo122bo$150bo2bo47bo9b2ob3o48b
o62b2o54b2ob3o124b3ob2o187b2o$134b2o13b2obobo39b2o11b2o3bobo47b3o
120bobo128bobo126b2o59b3o$135bo14bo2b2o38bobo11bobo2bobo46bo123bob
o128bobo122b2o2b2o2b2o54bobo2bo2b2o$2b2o73b2o53b3o13bobo42bo15bo3b
o48b5o119bo130bo123b2o2bo2bobo54b2o2b2o2b2o$3bo73bo54bo15b2o42b2o
15b2o55bo380b3o59b2o$3o75b3o183bo382b2o$o79bo183b2o$4bob2o65b2obo$
2b3ob2o2b2o3b2o47b2o3b2o2b2ob3o$bo8bo2bo2bo47bo2bo2bo8bo$2b3ob2o3b
5o49b5o3b2ob3o$4bobo67bobo$4bobo6bo53bo6bobo$5bo6bobo51bobo6bo$13b
2o51b2o38$3b2o62b2o62b2o68b2o57b2o62b2o65b2o62b2o59b2o62b2o62b2o
62b2o65b2o62b2o62b2o62b2o$3bo63bo64bo67bobo56bo2bo60bo2bo63bo2bo4b
2o54bo2bo4b2o51bo2bo60bo2bo60bo2bo60bo2bo63bo2bo4b2o54bo2bo4b2o54b
o2bo4b2o54bo2bo4b2o$2obo65bo60bo63b2o4bo59bobo61bobo63bobo5b2o54bo
bo5b2o52bobo61bobo61bobo61bobo63bobo3bo2bo54bobo5b2o54bobo5b2o54bo
bo4bo2bo$o2b3o59b5o60b5o57bo2bo2b2ob4o56bo63bo59bo5bo57bo5bo6b2o
53bo63bo63bo63bo59bo5bo57bo5bo57bo5bo57bo5bo5bo$b2o3bo57bo70bo56b
2obobobobo2bo179bobo61bobo11bo305bobo9bo2bo48bobo61bobo11bo49bobo
10bo$3b4o58b3o64b3o60bobobobo182bo2bo9b2obo47bo2bo9bobo304bo2bo9bo
2bo47bo2bo9b2o49bo2bo9bobo48bo2bo9bob2o$3bo64bo62bo63bobob2o56b2o
62b2o62b2o11bobo48b2o10bob2o48b2o62b2o62b2o62b2o62b2o11bobo48b2o
10b2obo48b2o10bo2bo48b2o12b2o$4b3o58b4o62b4o61bo59bo2bo60bo2bo75bo
112bo2bo60bo2bo60bo2bo60bo2bo75bo64bo61bo2bo$7bo57bo3b2o58b2o3bo
121bobo61bobo75b2o112bobo61bobo61bobo61bobo141bo$2b5o59b3o2bo56bo
2b3o123bo63bo76b2o62b2o49bo63bo63bo63bo76b2o61bo2bo61bo2bo60b2o$2b
o65bob2o56b2obo266b2o62b2o318b2o62b2o62b2o62b2o$4bo63bo62bo$3b2o
62b2o62b2o573bobo$261bo63b2o187b4o63b2o62b2o2bo59bo$257b2o2b2ob3o
54b4o4b2o182bo7b2o54b2obo2bob2o54b2o2b2o3bo54bo4bo2b2o$257b2o4b4o
54b3ob2o2b2o182bo3b2o2b2o54b2o2bo4bo54b2o7bo54b2obo2bob2o$261b2o
63bo187bo2b2o63bo63b4o59b2o$583bobo47$2b2o63b2o62bo66bo58b2o62b2o
62b2o62b2o62b2o62b2o62b2ob2o59b2o3bo62b2o62b2o58bo3b2o59b2ob2o$2bo
bo62bo62b2o66b2o56bo2bo60bo2bo60bo2bo4b2o54bo2bo4b2o55bo4b2o56bo2b
o2b2o57b2obo2bo58bo2bobo56b2o4bo57b2o2bo2bo56bobo2bo58bo2bob2o$4bo
59b2obo61bob2o64b2obo55bobo61bobo61bobo4bobo54bobo5b2o56bo3b2o57b
2o2bobo60bob2o56bo4bobo56b2o3bo58bobo2b2o57bobo4bo56b2obo$4ob2o3b
2o52bo2b3o58b3o2bo62bo2b3o55bo63bo63bo4b3o56bo65bo63b2o63bo59b5obo
61bo62b2o60bob5o59bo$o2bobobobobo53b2o3bo59bobobo60bobobo190b2o
121bob5o59bo63b2o63bo59b5obo61bo62bo63b2o$3bobobobo57b4o60bobobo
58bobobo194b2o60b3o54bobo4bo56b2obo60bobo2b2o57b2o3bo58bo4bobo60bo
b2o58bo3b2o57b2o2bobo$4b2obobo57bo64bo2b3o54b3o2bo125b2o67b3o60b2o
55bobo2bo58bo2bob2o57b2o2bo2bo56b2o4bo59bo2bobo57b2obo2bo57bo4b2o
56bo2bo2b2o$8bo59b3o62b2obo56bob2o64b2o59b3o133b2o53bo3b2o59b2ob2o
62b2o62b2o58b2o3bo58b2ob2o59b2o62b2o$71bo62b2o58b2o61b2o2b2o2b2o
54bobo2bo2b2o126b3o$68b3o63bo60bo61b2o2bo2bobo54b2o2b2o2b2o61b2o
62bobo$67bo195b3o59b2o65b2o62b2o$67b2o194b2o53$b2o62b2o62b2o62b2o
62b2o62b2o62b2o62b2o62b2o62b2o62b2o62b2o$o2bo60bo2bo60bo2bo4b2o54b
o2bo4b2o54bo2bo60bo2bo60bo2bo60bo2bo60bo2bo4b2o54bo2bo4b2o54bo2bo
4b2o54bo2bo4b2o57bo60b3obo59b2o3bo64bo$obo61bobo61bobo5b2o54bobo5b
2o54bobo61bobo61bobo61bobo61bobo3bo2bo54bobo5b2o54bobo5b2o54bobo4b
o2bo55b3o59b5o59b2o3b2o62b3o$bo63bo63bo63bo6b2o55bo63bo63bo63bo63b
o63bo63bo63bo5bo57b3obo61b2o63bo2bo60bob3o$200bo317bo2bo126bo62bo
58bo3bo61bo2bo63b2o58bo3bo$135b2obo60bobo317bo2bo60b2o62bobo61bob
2o56bo3bo61b2o3b2o59b5o54bo3bo$136bobo60bob2o247bobo67bobo60b2obo
60bo2bo62b2o57bob3o61bo3b2o59bob3o53b3obo$5bo63b2o66bo120b4o63b2o
62b2o2bo59bo67bo64bo61bo2bo121b3o186b3o$b2o2b2ob3o54b4o4b2o61b2o
119bo7b2o54b2obo2bob2o54b2o2b2o3bo54bo4bo2b2o127bo187bo188bo$b2o4b
4o54b3ob2o2b2o61b2o62b2o55bo3b2o2b2o54b2o2bo4bo54b2o7bo54b2obo2bob
2o61b2o61bo2bo61bo2bo60b2o$5b2o63bo65b2o62b2o56bo2b2o63bo63b4o59b
2o65b2o62b2o62b2o62b2o$327bobo53$129b2o62b2o66b2o62b2o186bo63bo62b
2o64bo62b2o62b2o61b2obo64bo$6bo3b2o59bob3o53b2o62b2o66b2o62b2o57b
2o3b2o62b2o3b2o52bobo61bo3bo59b2o63bobo61b2o61bo2bo60b2ob3o61bobo$
5b2o3b2o59b5o117b2o130b2o57b2o2b3o62b3o2b2o53b2o61bo4bo62bo60bobo
194bo60bobo$4bo2bo63b2o121b2o128b2o63bob2o58b2obo60b2o60bo3bo58b5o
59b2ob3o126bo59b2ob3o59b3ob2o$3b2o63bo2bo59bo61b4o63bo62b4o63bo3bo
54bo3bo61bo63b2obo57bo4b2o63bo59bo62bo2bo60bobo60bo$5o59b2o3b2o58b
2o66bo63b2o59bo67bo4bo52bo4bo58b2obo65b3o2b2o54bo2bobo56b2ob3o59b
3o62b2ob2o58bobo61b3ob2o$3obo59b2o3bo60bo66b2o62bo59b2o68bo3bo52bo
3bo59bo2bob2o62b2o3b2o53b2o3bo57b2obo61b2ob2o62b3o59bo64bob2o$131b
4o61bo60b4o62bo70bo54bo64b2ob2o252bo2bo62bo$132b2o124b2o511bo$133b
2o122b2o$133b2o62b2o58b2o62b2o449bo2bo61b2o$133b2o62b2o58b2o62b2o
450b2o62b2o53$2ob2o64bo59b2o3bo62b2o62b2o58bo3b2o60bob2o59b2ob2o
63b2o66bo$2obo2bo61bobo59bo2bobo61b2o61bo2bo56bobo2bo59b3ob2o57bo
2bob2o63b2o63bo3bo182b3o63b2o62b2o63b3o$3bob2o61b2o58bo4b2o186b2o
4bo56bo63b2obo63bo66bo4bo54b3o64b3o57bo62b2o2b2o2b2o54b2o2b2o2b2o
62bo$3bo62b2o60b5o126bo63b5o57b3ob2o60bo63b5o62bo3bo55b3o64b3o57bo
3bo58b2o2bo2bobo54bobo2bo2b2o58bo3bo$2b2o63bo64bo64bo61bo2bo60bo
63bobo61b2o60b2o4bo61bob2o57b3o64b3o57bo2bobo63b3o56b3o63bobo2bo$
4b2o61bob2o57b2o66b3o58b2ob2o64b2o59bobo59b2o61bobo2bo58b2o2b3o62b
3o58b3o62bobo2bo61b2o58b2o61bo2bobo$4bobo57b2obo2bo57b2o64b2ob2o
58b3o66b2o60bo59bobo62bo3b2o57b2o3b2o62b3o58b3o63bo3bo184bo3bo$5bo
58b2ob2o124bo2bo61bo190bo195b3o58b3o67bo184bo$196bo576b3o186b3o2$
192bo2bo61b2o$193b2o62b2o53$2b2o62b2o62b2o62b2o62b2o62b2o66b2o62b
2o$65bobo62b2o62b2o61b3o62bobo129b3o55b2o62b2o62b2o3bo58b2o2bobo
67b2o62b2o58bo3b2o57bobo2b2o$o3bo59b3o189bobo2bo2b2o57b3o63bo3bo
54b2o2bo2bobo54b2o2b2o58b2o2b3o57b2ob2o59b2o2bo65b2o2b2o57b3o2b2o
59b2ob2o59bo2b2o$o4bo58b2o190b2o2b2o2b2o58b2o62bo4bo54b2o2b2o2b2o
59b2o63bo61bobo61bo2bo61b2o62bo63bobo60bo2bo$2bobobo60b2o60b3o62b
3o63b2o59b2o64bobobo60b2o62bo2bo61bobo61bo63b2o61bo2bo60bobo63bo
62b2o$3bobobo58b3o60b2o64b2o124b3o62bobobo128bo2b2o59b2ob2o57b3o2b
2o58b2o2b2o52b2o2bo59b2ob2o59b2o2b3o57b2o2b2o$4bo4bo122b2o58b2o
190bo4bo127bobo2b2o58bo3b2o62b2o62b2o52b2o2bobo57b2o3bo58b2o62b2o$
5bo3bo121b3o58b3o189bo3bo$66b2o62bobo60bobo126b2o$6b2o58b2o62b2o
62b2o126b2o62b2o55$2o63b2o62b2o62b2o62b2o64b2o64b2o62b2o62b2o62b2o
62b2o57b2o3b2o$obo62b2o62b2o62b2o62b2o63bobo63bobo62b2o62b2o62b2o
62b2o57bo2bo2bo60bo64bo64bo62b2o$2bo2bo317bo61bo2bo316b5o122b2o2b
2ob3o54b3ob2o2b2o54b4o4b2o$b2obobo251bo125bobob2o255bo123bob3o58b
2o4b4o54b4o4b2o54b3ob2o2b2o$2bo2b2o59bo62b3o62bobo61b2o61b5o58b2o
2bo64bo61bobo62b3o61b2o61bo63b2obo61b2o62b2o63bo$obo63bobo63bo61b
2o2bo58bo2b2o58bo2bo2bo61bobo60bobo59bo2b2o61bo62b2o2bo59bobo62bob
2o$2o64b2o2bo58bo2b2o61bo63bobo58b2o3b2o62b2o58bo2b2o62bo61b2o2bo
59bobo62b2o63b3obo$68b2o61bobo62b3o62bo188b2o61b3o62bobo61bo$69bo
380bo323bo2$69b2o62b2o62b2o62b2o186b2o62b2o62b2o62b2o$69b2o62b2o
62b2o62b2o186b2o62b2o62b2o62b2o53$130b2o62b2o62b2o62b2o445bobo62b
2o62b2o62b2o$4b2o64bo59b2o62b2o62b2o62b2o61b4o63b2o62b2o59bo2b2o
62b2o2bo60b4o59bo59bo2bo62b2o62b2o$2o4b4o248b2o62b2o60bo7b2o54b2ob
o2bob2o54b2obo2bob2o54bo3b2o2b2o54b2o2b2o3bo54b2o7bo54bo4bo2b2o$2o
2b2ob3o58b3obo185bo64bo60bo3b2o2b2o54b2o2bo4bo54bo4bo2b2o54bo7b2o
54b2o7bo54b2o2b2o3bo54b2obo2bob2o54bo2bo$4bo62bob2o58b2obo60bob2o
60bobo62bobo60bo2b2o63bo62bo60b4o64b4o59b2o2bo59b2o59bo2bo60b2o64b
2o$67b2obo59bobo60bobo61bob2o60b2obo129bobo56bobo318bobo60b2obo60b
ob2o$65bob3o61bo62bo640bo64bo60bo$130b2o62b2o704bo60bo$67bo62b2o
62b2o62b2o62b2o510b2o61bo2bo60bo2bo$130b2o62b2o62b2o62b2o510b2o62b
2o62b2o55$2b2o62b2o62b2o62b2o62b2o66bobo183b2o62b2o63b2o63b2o62b2o
62b2o63b2o62bobo$2b2o62b2o61bo2bo60bo2bo61bo2bo63bo61bo66bo57bobo
61bo5b2o57bo65bo61bobo62bobo61bobo60b3ob3o$129bo66bo123b2o2bo4bo
56bobo64bobo58bo62b3obobo58b3o59b3o62bo62bobob3o59bo61bo7bo$2bo64b
o61bo66bo61bo2bo58b2obo2bob2o55bo3bo62bo3bo57b2o63bobo62bo59bo63b
2o62b2o5bo57b2o61b2o5b2o$bobo62bobo60bob2o60b2obo60bo2bo63b2o60bo
3bo60bo3bo60bo63b2o60b2o61b2o131b2o56bo$bo2bo60bo2bo62b2o60b2o62bo
bo127bo3bo58bo3bo59b3o125bo63bo61b2o126b3o$2bo2bo58bo2bo190bo129bo
3bo56bo3bo59bo126bobo63bobo60bo129bo$389bobo58bobo60b2o125b2o65b2o
60bobo126b2o$2bo2bo58bo2bo62b2o62b2o62b2o130bo60bo318b2o$2b2o62b2o
62b2o62b2o62b2o55$4b2o64b2o184b2o62b2o$3bobo58b2o5bo184bobo61bobo$
b3obobo56bobob3o59b2o66b2o57bo63bo2b2o$o5b2o58bobo61b2obo62bob2o
60b2o63bo$2o64b2o66bo60bo62bo2bo61bo$131bo66bo59b2o63b2o$132bob2o
58b2obo$134b2o58b2o!
, the start of the program is slow as all the bespokes should be evaluated first. I have progress save and automatic restart ... required when the computation takes several months. It would save some time after each restart if I would use bespoke base including only bespokes which appear in the constructed pattern.
(Any change in bespoke base requires "refresh" of the "database" ... )

The changes were ... computing bespoke estimated costs as a sum of its small objects (including other bespokes) instead of spanning tree cost (with variable multiplication constant) there is 1000 added what should be enough. The estimated cost of the entire pattern is therefore roughly increased by 1000 times number of bespoke steps which should be used. This gives big incentive to expand bespokes as early as possible.

Another change for pattern with very long frontline where there are too many build paths differing just by comuting the order of otherwise independent updates ... the computation works in two modes ... either the dimer to be constructed is in line close to last issued glider ... or the dimer to be constructed is close to the cluster with maximal x+y center. The close line method works till there is noticeable decrease in evaluation.

(The close to cluster with maximal x+y center ... was introduced to compensate for the hill in evaluation function ... what would otherwise move cheaply clusters without a progress ... it was not sufficient to compensate the evaluation bug, but it seems to me, it makes beam more efficient even after the bug correction.)

-----
I have still not persuaded myself to implement all the persistent delaunay triangulation stuff to support fast spanning tree cost updates and probably even the cluster to build "filtering". The digital clock seed is definitely good testing pattern for efficiency comparison.
Last edited by Hippo.69 on June 5th, 2025, 2:53 am, edited 1 time in total.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by apg »

Hippo.69 wrote: June 2nd, 2025, 9:26 am This allows much more bespoke variants then the original pslmake was able to support
Excellent! Could you please submit the changes to https://gitlab.com/apgoucher/pslmake as a merge request?
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

calcyman wrote: June 4th, 2025, 11:42 am
Hippo.69 wrote: June 2nd, 2025, 9:26 am This allows much more bespoke variants then the original pslmake was able to support
Excellent! Could you please submit the changes to https://gitlab.com/apgoucher/pslmake as a merge request?
I will try (in near future) ... now I am debuging bespoke recipes preprocessing to compute reaction envelope intersection (of alternatives),
and applying it in generate_successors for filtering not to call rs.search when there is no chance to succeed.

It would be some work for you process the merge ... as I have used a lot of debug prints ...

Actually the reaction envelope computation I made could be definitely done easier using LifeHistory then by adding Life subpatterns as I did.
But I am not familiar with lifelib to work with LifeHistory and translate LifeHistory from/to Life. I do not consider it to be a problem, as it is done just once in startup ... but it could be significant for pslmaking small patterns.
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

Finally I have pushed all the work to the repository.
I hope I have not rewritten something what happened while my branches diverged.

(The last proposal with the reaction envelopes was not debugged and is not included).

I have left a lot of debug prints commented out in the code, feel free to remove them.

Oops, I should recheck how I merged it.

Hmm I have a problem as I have added

Code: Select all

+    double spanning_graph_to_tree(const std::vector<coords64> &coords, std::vector<edge64> *streeptr, double (*scorer)(double), const std::vector<edge64> &sgraph, const std::vector<std::uint32_t> &target_distances) {
+
+        double score = 0.0;
+
+        if (coords.size() != target_distances.size()) { // wrong call at all
+            std::cerr << "Error spanning_graph_to_tree called with target_distances size different from coords size " << std::endl;
+            return 0;
+        }
+
+        // Initially colour every vertex differently:
+        dsds colours(coords.size());
+
+        // Compute squared length of edges:
+        std::vector<std::pair<int64_t, edge64> > sedges;
+        for (auto it = sgraph.begin(); it != sgraph.end(); ++it) {
+            coords64 a = coords[it->first];
+            coords64 b = coords[it->second];
+            int64_t xdiff = a.first - b.first;
+            int64_t ydiff = a.second - b.second;
+            int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
+            sedges.push_back(std::make_pair(sqlength, *it));
+        }
+
+        // Sort edges into ascending order:
+        std::sort(sedges.begin(), sedges.end());
+
+        // Apply Kruskal's algorithm:
+        for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
+            uint64_t a = it->second.first;
+            uint64_t b = it->second.second;
+            if (!colours.connected(a, b)) {
+                colours.merge(a, b);
+                if (streeptr != 0) { streeptr->push_back(it->second); }
+                if (scorer != 0) {
+                    double dist = (*scorer)(it->first);
+                    double tgt_dist = (double) (target_distances[a] > target_distances[b] ? target_distances[a] : target_distances[b]);
+                    if (dist>tgt_dist) {
+                        score+=dist;
+                    } else {
+                        dist -= tgt_dist;
+                        score += tgt_dist + dist;// even bigger repulsion
+                    }
+                }
+            }
+        }
+
+        return score;
+    }
to the lifelib spantree.h, what I rely on. ... and I have no permission to push to lifelib.
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

Hippo.69 wrote: July 12th, 2025, 1:12 pm Hmm I have a problem as I have added

Code: Select all

+    double spanning_graph_to_tree(const std::vector<coords64> &coords, std::vector<edge64> *streeptr, double (*scorer)(double), const std::vector<edge64> &sgraph, const std::vector<std::uint32_t> &target_distances) {
+
+        double score = 0.0;
+
+        if (coords.size() != target_distances.size()) { // wrong call at all
+            std::cerr << "Error spanning_graph_to_tree called with target_distances size different from coords size " << std::endl;
+            return 0;
+        }
+
+        // Initially colour every vertex differently:
+        dsds colours(coords.size());
+
+        // Compute squared length of edges:
+        std::vector<std::pair<int64_t, edge64> > sedges;
+        for (auto it = sgraph.begin(); it != sgraph.end(); ++it) {
+            coords64 a = coords[it->first];
+            coords64 b = coords[it->second];
+            int64_t xdiff = a.first - b.first;
+            int64_t ydiff = a.second - b.second;
+            int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
+            sedges.push_back(std::make_pair(sqlength, *it));
+        }
+
+        // Sort edges into ascending order:
+        std::sort(sedges.begin(), sedges.end());
+
+        // Apply Kruskal's algorithm:
+        for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
+            uint64_t a = it->second.first;
+            uint64_t b = it->second.second;
+            if (!colours.connected(a, b)) {
+                colours.merge(a, b);
+                if (streeptr != 0) { streeptr->push_back(it->second); }
+                if (scorer != 0) {
+                    double dist = (*scorer)(it->first);
+                    double tgt_dist = (double) (target_distances[a] > target_distances[b] ? target_distances[a] : target_distances[b]);
+                    if (dist>tgt_dist) {
+                        score+=dist;
+                    } else {
+                        dist -= tgt_dist;
+                        score += tgt_dist - dist;// even bigger repulsion
+                    }
+                }
+            }
+        }
+
+        return score;
+    }
to the lifelib spantree.h, what I rely on. ... and I have no permission to push to lifelib.
Hmm, I made some more local changes to be put to repository, and change in the lifelib function as well (the sign flip in targetting ... making the targetting to do something).

The local changes mainly concern issue with restart, when "problems fed" evaluation didnot consider the origin position, what required sufficient improvement in the evaluation of the first new generation position to be considered in the search. Surprisingly it took a while before such restart caused a problem.
I have also changed targeting to be more similar to slparse one ... targeting is for the first sorted dimmer, not the closest cluster.

I would wait a while (with the code running) to see if the update is worth pushing to the repository.

Yes, slmake like targeting helps. The main problem of closest targeting was the cluster target could be at the end of frontline (considering penalties) and its neighbour clusters could not fit to the frontline yet. If the neighbour out of frontline is actually a spanning tree leaf, it would be desirable to move it towards the target, but this was not considered.

Now with slmake like targeting if the cluster is targeted, it is at the frontline start (considering penalties), so its relatively close neighbours are in the frontline as well, so such spanning tree leaves are considered.

Pushed to the repository ... just the lifelib/spantree.h has to be manually changed as well.
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

Anivec wrote: June 14th, 2023, 6:00 pm Does pslmake have a feature where you can specify to construct left to right or vise versa?
pslmake "works backwards" ... it gradually reduces pattern to be constructed ... extending salvo doing the job.
This is why it concentrates on maximal x+y living cells at first (as they can be easily accessed by gliders from SE).
pslmake is done when no more reduction is possible as a single block remains.
There is an option to specify where the single block should be positioned.
It is ignored till the reduction is done and then the moving block process starts.
It is parallel beam search evaluation guided.
The beam could help to avoid stucking in local minima, but its efficiency in doing so was not big as due to high comutativity in the reduction process there is a lot of simillar states in the beam
(this is why I added slmake like targetting to the pslmake to reduce the transposition richness).

You can watch pslmake output by playing it a bit forward and then back to see the process of reduction.
Current version of pslmake saves progress to numbered files, so you can check them as well.

(There are 3 tasks to be guided by the evaluation ... by the decreasing priority
  • bespoke replacements ... the "nonspartan" configurations we need a special salvo/hard step to construct. ... we need a space near the object so the salvo would not colide with the rest of the pattern ... this is why we want to reduce/shift other objects. But we want to make bespoke steps as early as possible ... as it introduces other objects to be "reduced ...
  • joining more objects (clusters) to one ... dimers ... two clusters mut fit to 28x28, and their reduction must be in database ... typically we have more solutions in the database and they are tried in the order by the glider count.
    (the reaction is tested to see there is no collision with other objects).
    There is no support to join more then two objects in one step, so the recipe constructing traffic lights or honey bee would look horibly inefficient. Detecting trimers or quadmers ... limited to parts of trafic lights/honey bees would complicate pslmake and it is questionable if it would be worth it. (Fortunatelly you rarely need them in the final pattern. Salvo inefficiency could be caused when a bespoke introduces such constalations.)
  • reducing distances among clusters to get to the state two of them could be joined (but not to block bespokes).
    This converts clusters to other shapes in the process and beam selection choses cheaper. There are no recipes starting from (=reducing to) blinker in current recipe set.
There is no time to test all possible reductions from a given position so we have to select the possible reductions considering the priorities and "carefully cut the searched options". The "cut strategy" is what determines the pslmake efficiency)

pslmake has other features
... considering big clusters ... it has not to try to reduce them,
... zones where temporal clusters are prohibited (they would for example block the construction arm functionality)
... signatures ... you can specify which color, phases mod 8 of gliders are available

There is defrag in the tool set, which takes a salvo and considers its reordering having the same final effect.
The main goal of defrag is to reduce sum of diferences of consecutive glider lanes.
Independently on the arm this is usually what would be close to cheapest order to fire the gliders.

slmake did the same in the first phase, but not by parallel beam search, but rather by an evaluation guided greedy way. slmake had defrag incorporated. slmake had translation to single lane (time distance determined) translation incorporated as well.

It was good decision that pslmake does not include defrag and single lane transformation.

The defrag in slsparse was rather slow, it had no precomputed dependency graph (if I remember well).
defrag in pslmake collection used the dependency graph, but was single threaded.
current version locates the possible salvo flip positions in parallel and saves progress files ... the progress files are probably unnecessary for small patterns, but they help a lot for huge ones.

----
So your question rather is ... does defrag have an option prefering starting lane to have x-y as small as possible?
And the answer is ... no, current version does not have this option.
I did workarounds for such purposes ... modifing the salvo pattern by adding leftmost glider in front of it forcing the pattern to work only when the glider remains first. But I agree it is a bad solution to rely on such workarounds.

Maybe you also want to include option prefering final lane to have x-y as big as possible.
---
This would be rather easy to modify defrag to support this features ... probably adding --extrastart<lane> --extraend<lane> options, allowing "+" resp "-" to present infinities (max of the lanes/min of the lanes).
With such options you could specify prefered "arm position" at the start and end of the salvo.

I am plannig to incorporate this parametrisation to defrag in near future.
Anivec wrote: June 14th, 2023, 7:04 pm So that’s why it’s so algorithmically greedy. It could possibly build a section back to front, then move to the next section, unless this is already implemented.
slmake used to construct pattern in simillar sections. Or rather does not considered other sections in the evaluation.
There is no section planning in pslmake and recomputing spanning tree of clusters during each evaluation is affected by it (... and it is requird to navigate the third priority). (we are still far from implementing dynamic spannig tree maintainance).
If you want to run pslmake more parallel, you could split the pattern to such sections (expecting they are well separated) and compute their salvos independently (adding extra SE accessible block to each section except the SE most). The extra block would be origin for the previous section salvo.
This splitting would probably reduce the total computational time, but for the cost of biger salvo.
There is no guarantee the closer section construction would not colide with further constructions.

Saving results in .mc format helps with synchronization of these partial results.

Current version of defrag accepts chain of patterns defining salvo so this simplifies this section based approach.
---
I am computing the salvo using whole remaining pattern, but when I have to restart the computation, I often save the current salvo "chunk", create new starting progress file by removing the salvo from it (to reduce unnecessary maintinance of the "salvo tail"). The salvo tail would be put back in defrag. (I lose a bit of salvo efficiency in each restart by not considering whole beam).

I am planning to make some experiments with "defrag comutativity" ... could I defrag the tails while computing the salvo ... how would it affect the final defrag result and effectivity?
AlbertArmStain wrote: January 2nd, 2024, 3:32 pm I mean the feature to be able to add components to still lifes using common objects for a glider activated seed, no reflectors needed.
calcyman wrote: January 2nd, 2024, 8:18 pm
AlbertArmStain wrote: January 2nd, 2024, 3:32 pm I mean the feature to be able to add components to still lifes using common objects for a glider activated seed, no reflectors needed.
It's possible to do this by populating data/bespoke with those conversions. For example, it already contains this conversion which converts an eater into an eater-tie-eater:

Code: Select all

x = 58, y = 60, rule = B3/S23
8bo$7bobo$7bobo$8bo3$10b2o$9bo2bo$10b2o4$3bo5bo$b3o4bobo$o7bobo$2o7bo
14$35b3o$35bo$36bo26$55b3o$55bo$56bo!
You just need to drop in an RLE into data/bespoke for every step of whatever object you want to build, and then run the script refresh.sh to build the database of bespoke syntheses from those RLEs.
This is the first priority task of pslmake ... what had sometimes problem when you used construction requiring sequence of nontrivial objects. There was often negative incentive in evaluation to replace bespoke object by another bespoke object. It improved in current version.

One have to know the bespokes containing only one glider are processed differently. They are intended version to constuct p8 objects. For them versions of recipes containing case 0 degree turners are created as well allowing color and phase mod 8(resp. object period) shifting (to allow signatures).

The (re)strart of pslmake is slowed down by computing bespoke reduction costs so if this would be a problem, one can use refresh with filelist.txt containing only bespokes which will be required for the pattern processing.

---
That would be terible idea for example to try to optimize by adding (subset of) traffic light/honey bee to bespokes. There are few reasons why it is not ideal (that would consider them as hardly to construct, so giving them high priority and repelling other clusters just to make space to process them early, the evaluation "would skyrocket" ...)
but the main reason is there would be different decompositions of some patterns to the bespokes so there would be undetected *mers and the recipe salvo would not be tried on them. Additionally the decomposition is not "recomputed" during the search, so adding fourth beehive to finish honeybee would not result in creating detected honeybee bespoke so the recipe salvo would not apply them as well. (I can imagine repelling the extra beehive to make space then transforming partial honeybee by "hiting" the missing bee (backwards) and finally reducing the honeybee to block (hitting accordingly block positioned in one of 2 possible positions).)

So if someone wants to optimize these easily constructible trimers or quadmers, it should be extension of "dimers" detection. (Pattern is maintained decomposed on bespokes and clusters, bespokes are addressed individually and clusters are adressed as monomers or in dynamically detected pairs as dimers. Cluster could be considered in more dimers.)

--------
defrag with parametrization allowing extra_start_lane and extra_end_lane pushed to git.
User avatar
Hippo.69
Posts: 698
Joined: July 14th, 2020, 7:35 pm
Contact:

Re: slmake

Post by Hippo.69 »

Anivec wrote: June 14th, 2023, 6:00 pm Does pslmake have a feature where you can specify to construct left to right or vise versa?
Here are different outputs of defrag
... calls

Code: Select all

../pslmake/defrag.exe FNWCorderAbsorbersSalvo.mc FNWCorderAbsorbers_1.rle qd=160 > FNWCorderAbsorbers_1.txt
../pslmake/defrag.exe FNWCorderAbsorbersSalvo.mc FNWCorderAbsorbers_em1.rle eel=- qd=160 > FNWCorderAbsorbers_em1.txt
../pslmake/defrag.exe FNWCorderAbsorbersSalvo.mc FNWCorderAbsorbers_ep1.rle eel=+ qd=160 > FNWCorderAbsorbers_ep1.txt

Code: Select all

x = 34775, y = 34886, rule = B3/S23
2o$2o31$27b3o$27bo$28bo80$107b3o$107bo$108bo76$187b3o$187bo$188bo81$
266bo$265b2o$265bobo78$344bo$343b2o$343bobo82$418b3o$418bo$419bo80$
493b3o$493bo$494bo74$576b3o$576bo$577bo70$664b3o$664bo$665bo76$748b3o$
748bo$749bo85$822bo$821b2o$821bobo76$904bo$903b2o$903bobo89$972b3o$
972bo$973bo80$1049b3o$1049bo$1050bo82$1124b3o$1124bo$1125bo78$1205b3o$
1205bo$1206bo80$1282b3o$1282bo$1283bo81$1362bo$1361b2o$1361bobo74$
1443bo$1442b2o$1442bobo78$1524bo$1523b2o$1523bobo75$1608b3o$1608bo$
1609bo77$1687bo$1686b2o$1686bobo72$1774bo$1773b2o$1773bobo79$1853b3o$
1853bo$1854bo82$1928b3o$1928bo$1929bo76$2011b3o$2011bo$2012bo78$2093b
3o$2093bo$2094bo78$2172b3o$2172bo$2173bo78$2250b3o$2250bo$2251bo79$
2330bo$2329b2o$2329bobo68$2421bo$2420b2o$2420bobo84$2494bo$2493b2o$
2493bobo76$2577bo$2576b2o$2576bobo94$2644b3o$2644bo$2645bo84$2715b3o$
2715bo$2716bo80$2794b3o$2794bo$2795bo80$2874b3o$2874bo$2875bo79$2948b
3o$2948bo$2949bo82$3025b3o$3025bo$3026bo76$3106b3o$3106bo$3107bo82$
3185b3o$3185bo$3186bo82$3261b3o$3261bo$3262bo75$3342bo$3341b2o$3341bob
o90$3409bo$3408b2o$3408bobo78$3491bo$3490b2o$3490bobo84$3566bo$3565b2o
$3565bobo80$3643bo$3642b2o$3642bobo90$3713b3o$3713bo$3714bo78$3791b3o$
3791bo$3792bo88$3864b3o$3864bo$3865bo75$3944bo$3943b2o$3943bobo86$
4019bo$4018b2o$4018bobo78$4099bo$4098b2o$4098bobo77$4180b3o$4180bo$
4181bo73$4262bo$4261b2o$4261bobo78$4342b3o$4342bo$4343bo74$4425b3o$
4425bo$4426bo86$4498b3o$4498bo$4499bo74$4583b3o$4583bo$4584bo78$4662b
3o$4662bo$4663bo79$4741b3o$4741bo$4742bo78$4824b3o$4824bo$4825bo72$
4907b3o$4907bo$4908bo78$4990b3o$4990bo$4991bo81$5065bo$5064b2o$5064bob
o76$5144b3o$5144bo$5145bo78$5227b3o$5227bo$5228bo73$5310bo$5309b2o$
5309bobo84$5386bo$5385b2o$5385bobo74$5470bo$5469b2o$5469bobo79$5547b3o
$5547bo$5548bo76$5628b3o$5628bo$5629bo77$5711bo$5710b2o$5710bobo79$
5789b3o$5789bo$5790bo78$5869b3o$5869bo$5870bo90$5936b3o$5936bo$5937bo
76$6019b3o$6019bo$6020bo80$6096b3o$6096bo$6097bo82$6175b3o$6175bo$
6176bo77$6256b3o$6256bo$6257bo76$6338b3o$6338bo$6339bo77$6421bo$6420b
2o$6420bobo79$6499b3o$6499bo$6500bo78$6579b3o$6579bo$6580bo98$6640b3o$
6640bo$6641bo72$6725b3o$6725bo$6726bo90$6792b3o$6792bo$6793bo76$6874b
3o$6874bo$6875bo70$6962b3o$6962bo$6963bo91$7030bo$7029b2o$7029bobo88$
7101bo$7100b2o$7100bobo85$7172b3o$7172bo$7173bo77$7252b3o$7252bo$7253b
o78$7331b3o$7331bo$7332bo78$7413b3o$7413bo$7414bo80$7491b3o$7491bo$
7492bo78$7569b3o$7569bo$7570bo83$7646bo$7645b2o$7645bobo74$7728bo$
7727b2o$7727bobo82$7805bo$7804b2o$7804bobo87$7877b3o$7877bo$7878bo82$
7953b3o$7953bo$7954bo84$8027b3o$8027bo$8028bo78$8106b3o$8106bo$8107bo
71$8193bo$8192b2o$8192bobo86$8264bo$8263b2o$8263bobo84$8341b3o$8341bo$
8342bo82$8417b3o$8417bo$8418bo78$8498b3o$8498bo$8499bo78$8578b3o$8578b
o$8579bo82$8653b3o$8653bo$8654bo80$8732b3o$8732bo$8733bo75$8813bo$
8812b2o$8812bobo76$8895bo$8894b2o$8894bobo83$8969b3o$8969bo$8970bo101$
9027b3o$9027bo$9028bo76$9109b3o$9109bo$9110bo83$9181bo$9180b2o$9180bob
o84$9258bo$9257b2o$9257bobo74$9341bo$9340b2o$9340bobo82$9417bo$9416b2o
$9416bobo76$9497bo$9496b2o$9496bobo74$9581bo$9580b2o$9580bobo91$9651b
3o$9651bo$9652bo46$9760b3o$9760bo$9761bo74$9846b3o$9846bo$9847bo81$
9926b3o$9926bo$9927bo78$10006b3o$10006bo$10007bo76$10086b3o$10086bo$
10087bo73$10170bo$10169b2o$10169bobo88$10241bo$10240b2o$10240bobo89$
10309b3o$10309bo$10310bo80$10388b3o$10388bo$10389bo79$10469bo$10468b2o
$10468bobo80$10544bo$10543b2o$10543bobo80$10624bo$10623b2o$10623bobo
81$10702b3o$10702bo$10703bo78$10779b3o$10779bo$10780bo81$10857bo$
10856b2o$10856bobo82$10933bo$10932b2o$10932bobo89$11003b3o$11003bo$
11004bo76$11085b3o$11085bo$11086bo75$11166bo$11165b2o$11165bobo76$
11251bo$11250b2o$11250bobo83$11324b3o$11324bo$11325bo94$11388b3o$
11388bo$11389bo74$11472b3o$11472bo$11473bo82$11548b3o$11548bo$11549bo
84$11622b3o$11622bo$11623bo83$11696bo$11695b2o$11695bobo78$11777bo$
11776b2o$11776bobo75$11860b3o$11860bo$11861bo76$11944b3o$11944bo$
11945bo76$12023b3o$12023bo$12024bo78$12104b3o$12104bo$12105bo72$12189b
3o$12189bo$12190bo79$12267b3o$12267bo$12268bo78$12347b3o$12347bo$
12348bo78$12429b3o$12429bo$12430bo73$12513bo$12512b2o$12512bobo88$
12584bo$12583b2o$12583bobo98$12645b3o$12645bo$12646bo76$12726b3o$
12726bo$12727bo80$12803b3o$12803bo$12804bo81$12883bo$12882b2o$12882bob
o73$12967b3o$12967bo$12968bo76$13049b3o$13049bo$13050bo88$13120b3o$
13120bo$13121bo78$13199b3o$13199bo$13200bo92$13263b3o$13263bo$13264bo
85$13335b3o$13335bo$13336bo72$13420b3o$13420bo$13421bo82$13499b3o$
13499bo$13500bo92$13564b3o$13564bo$13565bo72$13649b3o$13649bo$13650bo
84$13725b3o$13725bo$13726bo90$13793b3o$13793bo$13794bo78$13870b3o$
13870bo$13871bo80$13948b3o$13948bo$13949bo74$14035b3o$14035bo$14036bo
74$14119b3o$14119bo$14120bo82$14192b3o$14192bo$14193bo86$14264b3o$
14264bo$14265bo76$14346b3o$14346bo$14347bo84$14422b3o$14422bo$14423bo
91$14491b3o$14491bo$14492bo78$14569b3o$14569bo$14570bo76$14652b3o$
14652bo$14653bo71$14738bo$14737b2o$14737bobo58$14837b3o$14837bo$14838b
o74$14920b3o$14920bo$14921bo78$15003b3o$15003bo$15004bo88$15073b3o$
15073bo$15074bo75$15155bo$15154b2o$15154bobo73$15239b3o$15239bo$15240b
o89$15312b3o$15312bo$15313bo75$15394bo$15393b2o$15393bobo82$15468bo$
15467b2o$15467bobo105$15523b3o$15523bo$15524bo78$15604b3o$15604bo$
15605bo78$15683b3o$15683bo$15684bo74$15766b3o$15766bo$15767bo82$15844b
3o$15844bo$15845bo79$15923bo$15922b2o$15922bobo78$16001b3o$16001bo$
16002bo82$16077b3o$16077bo$16078bo68$16164b3o$16164bo$16165bo88$16235b
3o$16235bo$16236bo85$16310b3o$16310bo$16311bo73$16393b3o$16393bo$
16394bo64$16489b3o$16489bo$16490bo80$16564b3o$16564bo$16565bo74$16650b
3o$16650bo$16651bo78$16730b3o$16730bo$16731bo80$16809b3o$16809bo$
16810bo82$16882b3o$16882bo$16883bo70$16970b3o$16970bo$16971bo86$17042b
3o$17042bo$17043bo67$17136b3o$17136bo$17137bo76$17218b3o$17218bo$
17219bo77$17301bo$17300b2o$17300bobo79$17379b3o$17379bo$17380bo78$
17459b3o$17459bo$17460bo71$17546bo$17545b2o$17545bobo67$17638b3o$
17638bo$17639bo80$17716b3o$17716bo$17717bo68$17805b3o$17805bo$17806bo
80$17882b3o$17882bo$17883bo68$17972b3o$17972bo$17973bo76$18056b3o$
18056bo$18057bo76$18137b3o$18137bo$18138bo67$18227b3o$18227bo$18228bo
78$18307b3o$18307bo$18308bo76$18389b3o$18389bo$18390bo86$18459b3o$
18459bo$18460bo74$18544b3o$18544bo$18545bo82$18618b3o$18618bo$18619bo
76$18700b3o$18700bo$18701bo75$18785bo$18784b2o$18784bobo83$18858b3o$
18858bo$18859bo76$18940b3o$18940bo$18941bo105$18996b3o$18996bo$18997bo
77$19076b3o$19076bo$19077bo76$19158b3o$19158bo$19159bo75$19239bo$
19238b2o$19238bobo81$19319b3o$19319bo$19320bo78$19399b3o$19399bo$
19400bo71$19486bo$19485b2o$19485bobo69$19573b3o$19573bo$19574bo76$
19655b3o$19655bo$19656bo77$19738bo$19737b2o$19737bobo79$19816b3o$
19816bo$19817bo78$19896b3o$19896bo$19897bo71$19983bo$19982b2o$19982bob
o65$20076b3o$20076bo$20077bo76$20158b3o$20158bo$20159bo78$20238b3o$
20238bo$20239bo81$20315bo$20314b2o$20314bobo68$20403bo$20402b2o$20402b
obo80$20483bo$20482b2o$20482bobo91$20550b3o$20550bo$20551bo82$20624b3o
$20624bo$20625bo80$20705b3o$20705bo$20706bo68$20793b3o$20793bo$20794bo
79$20873b3o$20873bo$20874bo80$20952b3o$20952bo$20953bo72$21038b3o$
21038bo$21039bo78$21118b3o$21118bo$21119bo80$21197b3o$21197bo$21198bo
74$21281b3o$21281bo$21282bo73$21364b3o$21364bo$21365bo71$21453b3o$
21453bo$21454bo80$21532b3o$21532bo$21533bo75$21614bo$21613b2o$21613bob
o84$21685b3o$21685bo$21686bo77$21768bo$21767b2o$21767bobo79$21846b3o$
21846bo$21847bo78$21926b3o$21926bo$21927bo71$22013bo$22012b2o$22012bob
o70$22100bo$22099b2o$22099bobo71$22189b3o$22189bo$22190bo65$22282b3o$
22282bo$22283bo76$22364b3o$22364bo$22365bo77$22447bo$22446b2o$22446bob
o79$22525b3o$22525bo$22526bo78$22605b3o$22605bo$22606bo71$22692bo$
22691b2o$22691bobo73$22777b3o$22777bo$22778bo68$22865b3o$22865bo$
22866bo83$22940b3o$22940bo$22941bo76$23023b3o$23023bo$23024bo80$23100b
3o$23100bo$23101bo80$23177b3o$23177bo$23178bo79$23260b3o$23260bo$
23261bo76$23342b3o$23342bo$23343bo75$23423bo$23422b2o$23422bobo79$
23501b3o$23501bo$23502bo78$23581b3o$23581bo$23582bo71$23668bo$23667b2o
$23667bobo70$23759bo$23758b2o$23758bobo69$23846b3o$23846bo$23847bo47$
23956b3o$23956bo$23957bo82$24032b3o$24032bo$24033bo74$24114b3o$24114bo
$24115bo78$24196b3o$24196bo$24197bo81$24273b3o$24273bo$24274bo76$
24356b3o$24356bo$24357bo80$24433b3o$24433bo$24434bo82$24512b3o$24512bo
$24513bo87$24578b3o$24578bo$24579bo82$24657b3o$24657bo$24658bo76$
24739b3o$24739bo$24740bo75$24820bo$24819b2o$24819bobo74$24903bo$24902b
2o$24902bobo85$24978b3o$24978bo$24979bo80$25056b3o$25056bo$25057bo76$
25138b3o$25138bo$25139bo75$25219bo$25218b2o$25218bobo81$25299b3o$
25299bo$25300bo78$25379b3o$25379bo$25380bo71$25466bo$25465b2o$25465bob
o71$25551b3o$25551bo$25552bo76$25633b3o$25633bo$25634bo77$25716bo$
25715b2o$25715bobo79$25794b3o$25794bo$25795bo78$25874b3o$25874bo$
25875bo71$25961bo$25960b2o$25960bobo58$26061b3o$26061bo$26062bo78$
26142b3o$26142bo$26143bo82$26217b3o$26217bo$26218bo78$26300b3o$26300bo
$26301bo80$26377b3o$26377bo$26378bo72$26464b3o$26464bo$26465bo80$
26540b3o$26540bo$26541bo82$26618b3o$26618bo$26619bo66$26707b3o$26707bo
$26708bo78$26790b3o$26790bo$26791bo71$26875bo$26874b2o$26874bobo81$
26953b3o$26953bo$26954bo84$27027b3o$27027bo$27028bo80$27106b3o$27106bo
$27107bo76$27186b3o$27186bo$27187bo83$27261bo$27260b2o$27260bobo78$
27343bo$27342b2o$27342bobo84$27413b3o$27413bo$27414bo76$27495b3o$
27495bo$27496bo77$27578bo$27577b2o$27577bobo79$27656b3o$27656bo$27657b
o78$27736b3o$27736bo$27737bo59$27838b3o$27838bo$27839bo80$27916b3o$
27916bo$27917bo76$27996b3o$27996bo$27997bo76$28080b3o$28080bo$28081bo
88$28149b3o$28149bo$28150bo78$28230b3o$28230bo$28231bo82$28303bo$
28302b2o$28302bobo75$28384b3o$28384bo$28385bo76$28466b3o$28466bo$
28467bo77$28549bo$28548b2o$28548bobo79$28627b3o$28627bo$28628bo78$
28707b3o$28707bo$28708bo71$28794bo$28793b2o$28793bobo69$28884bo$28883b
2o$28883bobo71$28972b3o$28972bo$28973bo80$29049b3o$29049bo$29050bo86$
29123b3o$29123bo$29124bo76$29205b3o$29205bo$29206bo75$29286bo$29285b2o
$29285bobo81$29366b3o$29366bo$29367bo78$29446b3o$29446bo$29447bo71$
29533bo$29532b2o$29532bobo64$29626bo$29625b2o$29625bobo65$29716bo$
29715b2o$29715bobo79$29796b3o$29796bo$29797bo78$29876b3o$29876bo$
29877bo76$29959bo$29958b2o$29958bobo73$30044b3o$30044bo$30045bo84$
30119b3o$30119bo$30120bo72$30205b3o$30205bo$30206bo76$30286b3o$30286bo
$30287bo77$30369bo$30368b2o$30368bobo74$30452bo$30451b2o$30451bobo83$
30525b3o$30525bo$30526bo68$30615b3o$30615bo$30616bo76$30700b3o$30700bo
$30701bo78$30777b3o$30777bo$30778bo76$30859b3o$30859bo$30860bo74$
30944b3o$30944bo$30945bo77$31025bo$31024b2o$31024bobo73$31111b3o$
31111bo$31112bo76$31192b3o$31192bo$31193bo73$31279bo$31278b2o$31278bob
o83$31351b3o$31351bo$31352bo77$31435bo$31434b2o$31434bobo76$31513b3o$
31513bo$31514bo82$31589b3o$31589bo$31590bo74$31673b3o$31673bo$31674bo
67$31763bo$31762b2o$31762bobo78$31844bo$31843b2o$31843bobo78$31923bo$
31922b2o$31922bobo79$32005b3o$32005bo$32006bo78$32083b3o$32083bo$
32084bo80$32160b3o$32160bo$32161bo80$32239b3o$32239bo$32240bo72$32325b
3o$32325bo$32326bo75$32410bo$32409b2o$32409bobo86$32481b3o$32481bo$
32482bo76$32563b3o$32563bo$32564bo77$32646bo$32645b2o$32645bobo79$
32724b3o$32724bo$32725bo78$32804b3o$32804bo$32805bo71$32891bo$32890b2o
$32890bobo74$32972b3o$32972bo$32973bo74$33058b3o$33058bo$33059bo73$
33141bo$33140b2o$33140bobo84$33217bo$33216b2o$33216bobo74$33301bo$
33300b2o$33300bobo79$33378b3o$33378bo$33379bo78$33461b3o$33461bo$
33462bo75$33542bo$33541b2o$33541bobo79$33620b3o$33620bo$33621bo78$
33700b3o$33700bo$33701bo71$33787bo$33786b2o$33786bobo124$33825b3o$
33825bo$33826bo76$33907b3o$33907bo$33908bo75$33988bo$33987b2o$33987bob
o81$34068b3o$34068bo$34069bo78$34148b3o$34148bo$34149bo71$34235bo$
34234b2o$34234bobo101$34290b3o$34290bo$34291bo84$34363b3o$34363bo$
34364bo76$34445b3o$34445bo$34446bo77$34528bo$34527b2o$34527bobo79$
34606b3o$34606bo$34607bo78$34686b3o$34686bo$34687bo71$34773bo$34772b2o
$34772bobo!

Code: Select all

x = 34477, y = 35193, rule = B3/S23
2o$2o33$29b3o$29bo$30bo78$107b3o$107bo$108bo78$189b3o$189bo$190bo79$
266bo$265b2o$265bobo80$346bo$345b2o$345bobo82$420b3o$420bo$421bo82$
497b3o$497bo$498bo74$580b3o$580bo$581bo70$668b3o$668bo$669bo74$750b3o$
750bo$751bo85$824bo$823b2o$823bobo76$906bo$905b2o$905bobo89$974b3o$
974bo$975bo82$1053b3o$1053bo$1054bo82$1128b3o$1128bo$1129bo78$1209b3o$
1209bo$1210bo80$1286b3o$1286bo$1287bo79$1364bo$1363b2o$1363bobo76$
1447bo$1446b2o$1446bobo78$1528bo$1527b2o$1527bobo73$1610b3o$1610bo$
1611bo79$1691bo$1690b2o$1690bobo72$1778bo$1777b2o$1777bobo79$1857b3o$
1857bo$1858bo82$1932b3o$1932bo$1933bo76$2015b3o$2015bo$2016bo76$2095b
3o$2095bo$2096bo78$2174b3o$2174bo$2175bo80$2254b3o$2254bo$2255bo79$
2334bo$2333b2o$2333bobo66$2423bo$2422b2o$2422bobo86$2498bo$2497b2o$
2497bobo74$2579bo$2578b2o$2578bobo94$2646b3o$2646bo$2647bo86$2719b3o$
2719bo$2720bo80$2798b3o$2798bo$2799bo78$2876b3o$2876bo$2877bo81$2952b
3o$2952bo$2953bo82$3029b3o$3029bo$3030bo76$3110b3o$3110bo$3111bo80$
3187b3o$3187bo$3188bo82$3263b3o$3263bo$3264bo77$3346bo$3345b2o$3345bob
o90$3413bo$3412b2o$3412bobo76$3493bo$3492b2o$3492bobo84$3568bo$3567b2o
$3567bobo80$3645bo$3644b2o$3644bobo90$3715b3o$3715bo$3716bo80$3795b3o$
3795bo$3796bo86$3866b3o$3866bo$3867bo77$3948bo$3947b2o$3947bobo84$
4021bo$4020b2o$4020bobo78$4101bo$4100b2o$4100bobo77$4182b3o$4182bo$
4183bo75$4266bo$4265b2o$4265bobo76$4344b3o$4344bo$4345bo76$4429b3o$
4429bo$4430bo84$4500b3o$4500bo$4501bo74$4585b3o$4585bo$4586bo78$4664b
3o$4664bo$4665bo81$4745b3o$4745bo$4746bo76$4826b3o$4826bo$4827bo74$
4911b3o$4911bo$4912bo76$4992b3o$4992bo$4993bo83$5069bo$5068b2o$5068bob
o76$5148b3o$5148bo$5149bo76$5229b3o$5229bo$5230bo75$5314bo$5313b2o$
5313bobo82$5388bo$5387b2o$5387bobo74$5472bo$5471b2o$5471bobo81$5551b3o
$5551bo$5552bo76$5632b3o$5632bo$5633bo75$5713bo$5712b2o$5712bobo81$
5793b3o$5793bo$5794bo78$5873b3o$5873bo$5874bo71$5960bo$5959b2o$5959bob
o77$6038b3o$6038bo$6039bo78$6118b3o$6118bo$6119bo78$6203bo$6202b2o$
6202bobo73$6288b3o$6288bo$6289bo82$6361b3o$6361bo$6362bo72$6447b3o$
6447bo$6448bo78$6530b3o$6530bo$6531bo75$6611bo$6610b2o$6610bobo74$
6694bo$6693b2o$6693bobo85$6769b3o$6769bo$6770bo68$6859b3o$6859bo$6860b
o74$6942b3o$6942bo$6943bo80$7021b3o$7021bo$7022bo76$7103b3o$7103bo$
7104bo74$7188b3o$7188bo$7189bo77$7269bo$7268b2o$7268bobo71$7353b3o$
7353bo$7354bo78$7436b3o$7436bo$7437bo71$7521bo$7520b2o$7520bobo85$
7595b3o$7595bo$7596bo75$7677bo$7676b2o$7676bobo78$7757b3o$7757bo$7758b
o82$7833b3o$7833bo$7834bo74$7917b3o$7917bo$7918bo67$8007bo$8006b2o$
8006bobo78$8088bo$8087b2o$8087bobo78$8167bo$8166b2o$8166bobo77$8247b3o
$8247bo$8248bo80$8327b3o$8327bo$8328bo80$8404b3o$8404bo$8405bo80$8483b
3o$8483bo$8484bo72$8569b3o$8569bo$8570bo73$8652bo$8651b2o$8651bobo88$
8725b3o$8725bo$8726bo76$8807b3o$8807bo$8808bo75$8888bo$8887b2o$8887bob
o81$8968b3o$8968bo$8969bo78$9048b3o$9048bo$9049bo71$9135bo$9134b2o$
9134bobo74$9216b3o$9216bo$9217bo72$9300b3o$9300bo$9301bo75$9385bo$
9384b2o$9384bobo82$9459bo$9458b2o$9458bobo74$9543bo$9542b2o$9542bobo
81$9622b3o$9622bo$9623bo76$9703b3o$9703bo$9704bo77$9786bo$9785b2o$
9785bobo79$9864b3o$9864bo$9865bo78$9944b3o$9944bo$9945bo71$10031bo$
10030b2o$10030bobo122$10067b3o$10067bo$10068bo76$10149b3o$10149bo$
10150bo77$10232bo$10231b2o$10231bobo79$10310b3o$10310bo$10311bo78$
10390b3o$10390bo$10391bo71$10477bo$10476b2o$10476bobo103$10534b3o$
10534bo$10535bo84$10607b3o$10607bo$10608bo76$10689b3o$10689bo$10690bo
75$10770bo$10769b2o$10769bobo81$10850b3o$10850bo$10851bo78$10930b3o$
10930bo$10931bo71$11017bo$11016b2o$11016bobo112$11060b3o$11060bo$
11061bo76$11143b3o$11143bo$11144bo80$11220b3o$11220bo$11221bo81$11300b
3o$11300bo$11301bo76$11382b3o$11382bo$11383bo75$11463bo$11462b2o$
11462bobo79$11541b3o$11541bo$11542bo78$11621b3o$11621bo$11622bo71$
11708bo$11707b2o$11707bobo88$11777b3o$11777bo$11778bo95$11842b3o$
11842bo$11843bo72$11927b3o$11927bo$11928bo92$11996b3o$11996bo$11997bo
76$12078b3o$12078bo$12079bo70$12166b3o$12166bo$12167bo89$12232bo$
12231b2o$12231bobo88$12303bo$12302b2o$12302bobo87$12376b3o$12376bo$
12377bo75$12454b3o$12454bo$12455bo80$12535b3o$12535bo$12536bo76$12615b
3o$12615bo$12616bo80$12693b3o$12693bo$12694bo80$12773b3o$12773bo$
12774bo67$12865b3o$12865bo$12866bo78$12946b3o$12946bo$12947bo82$13021b
3o$13021bo$13022bo76$13102b3o$13102bo$13103bo80$13179b3o$13179bo$
13180bo72$13266b3o$13266bo$13267bo82$13344b3o$13344bo$13345bo80$13420b
3o$13420bo$13421bo68$13511b3o$13511bo$13512bo76$13592b3o$13592bo$
13593bo73$13679bo$13678b2o$13678bobo79$13755b3o$13755bo$13756bo84$
13829b3o$13829bo$13830bo80$13908b3o$13908bo$13909bo78$13990b3o$13990bo
$13991bo83$14065bo$14064b2o$14064bobo76$14145bo$14144b2o$14144bobo63$
14240b3o$14240bo$14241bo80$14318b3o$14318bo$14319bo78$14400b3o$14400bo
$14401bo74$14482b3o$14482bo$14483bo73$14568bo$14567b2o$14567bobo71$
14656b3o$14656bo$14657bo80$14733b3o$14733bo$14734bo84$14805b3o$14805bo
$14806bo76$14887b3o$14887bo$14888bo77$14970bo$14969b2o$14969bobo79$
15048b3o$15048bo$15049bo78$15128b3o$15128bo$15129bo71$15215bo$15214b2o
$15214bobo101$15271b3o$15271bo$15272bo78$15352b3o$15352bo$15353bo81$
15428b3o$15428bo$15429bo76$15510b3o$15510bo$15511bo75$15591bo$15590b2o
$15590bobo81$15671b3o$15671bo$15672bo78$15751b3o$15751bo$15752bo71$
15838bo$15837b2o$15837bobo99$15897b3o$15897bo$15898bo76$15979b3o$
15979bo$15980bo75$16060bo$16059b2o$16059bobo79$16138b3o$16138bo$16139b
o78$16218b3o$16218bo$16219bo71$16305bo$16304b2o$16304bobo96$16368bo$
16367b2o$16367bobo76$16452bo$16451b2o$16451bobo80$16527bo$16526b2o$
16526bobo87$16599b3o$16599bo$16600bo82$16675b3o$16675bo$16676bo84$
16749b3o$16749bo$16750bo78$16828b3o$16828bo$16829bo71$16915bo$16914b2o
$16914bobo88$16988bo$16987b2o$16987bobo82$17063b3o$17063bo$17064bo82$
17139b3o$17139bo$17140bo78$17220b3o$17220bo$17221bo78$17300b3o$17300bo
$17301bo82$17375b3o$17375bo$17376bo80$17454b3o$17454bo$17455bo77$
17537bo$17536b2o$17536bobo76$17619bo$17618b2o$17618bobo83$17693b3o$
17693bo$17694bo99$17749b3o$17749bo$17750bo76$17831b3o$17831bo$17832bo
85$17905bo$17904b2o$17904bobo82$17980bo$17979b2o$17979bobo74$18063bo$
18062b2o$18062bobo82$18139bo$18138b2o$18138bobo78$18221bo$18220b2o$
18220bobo74$18305bo$18304b2o$18304bobo89$18373b3o$18373bo$18374bo48$
18484b3o$18484bo$18485bo72$18568b3o$18568bo$18569bo81$18648b3o$18648bo
$18649bo78$18728b3o$18728bo$18729bo78$18810b3o$18810bo$18811bo73$
18894bo$18893b2o$18893bobo88$18965bo$18964b2o$18964bobo80$19041bo$
19040b2o$19040bobo71$19130b3o$19130bo$19131bo45$19238b3o$19238bo$
19239bo82$19314b3o$19314bo$19315bo76$19398b3o$19398bo$19399bo76$19478b
3o$19478bo$19479bo83$19557b3o$19557bo$19558bo76$19640b3o$19640bo$
19641bo80$19717b3o$19717bo$19718bo80$19794b3o$19794bo$19795bo89$19862b
3o$19862bo$19863bo80$19939b3o$19939bo$19940bo76$20021b3o$20021bo$
20022bo77$20104bo$20103b2o$20103bobo74$20187bo$20186b2o$20186bobo83$
20260b3o$20260bo$20261bo80$20338b3o$20338bo$20339bo76$20420b3o$20420bo
$20421bo77$20503bo$20502b2o$20502bobo79$20581b3o$20581bo$20582bo78$
20661b3o$20661bo$20662bo71$20748bo$20747b2o$20747bobo73$20835b3o$
20835bo$20836bo76$20917b3o$20917bo$20918bo75$20998bo$20997b2o$20997bob
o79$21076b3o$21076bo$21077bo78$21156b3o$21156bo$21157bo71$21243bo$
21242b2o$21242bobo132$21273b3o$21273bo$21274bo80$21352b3o$21352bo$
21353bo77$21431bo$21430b2o$21430bobo82$21508bo$21507b2o$21507bobo78$
21586bo$21585b2o$21585bobo81$21664b3o$21664bo$21665bo80$21743b3o$
21743bo$21744bo81$21821bo$21820b2o$21820bobo82$21897bo$21896b2o$21896b
obo73$21979b3o$21979bo$21980bo70$22069b3o$22069bo$22070bo81$22142b3o$
22142bo$22143bo76$22225b3o$22225bo$22226bo80$22302b3o$22302bo$22303bo
82$22381b3o$22381bo$22382bo77$22462b3o$22462bo$22463bo76$22544b3o$
22544bo$22545bo77$22627bo$22626b2o$22626bobo79$22705b3o$22705bo$22706b
o78$22785b3o$22785bo$22786bo71$22872bo$22871b2o$22871bobo103$22925b3o$
22925bo$22926bo76$23007b3o$23007bo$23008bo77$23090bo$23089b2o$23089bob
o74$23173bo$23172b2o$23172bobo85$23248b3o$23248bo$23249bo80$23326b3o$
23326bo$23327bo76$23408b3o$23408bo$23409bo75$23489bo$23488b2o$23488bob
o79$23567b3o$23567bo$23568bo78$23647b3o$23647bo$23648bo71$23734bo$
23733b2o$23733bobo103$23792b3o$23792bo$23793bo74$23876b3o$23876bo$
23877bo82$23952b3o$23952bo$23953bo84$24026b3o$24026bo$24027bo83$24100b
o$24099b2o$24099bobo78$24181bo$24180b2o$24180bobo75$24264b3o$24264bo$
24265bo74$24346b3o$24346bo$24347bo78$24427b3o$24427bo$24428bo78$24508b
3o$24508bo$24509bo72$24593b3o$24593bo$24594bo79$24671b3o$24671bo$
24672bo78$24751b3o$24751bo$24752bo76$24831b3o$24831bo$24832bo73$24915b
o$24914b2o$24914bobo88$24986bo$24985b2o$24985bobo82$25064bo$25063b2o$
25063bobo69$25151b3o$25151bo$25152bo103$25207b3o$25207bo$25208bo78$
25290b3o$25290bo$25291bo80$25367b3o$25367bo$25368bo79$25445bo$25444b2o
$25444bobo73$25529b3o$25529bo$25530bo76$25611b3o$25611bo$25612bo88$
25682b3o$25682bo$25683bo78$25761b3o$25761bo$25762bo94$25827b3o$25827bo
$25828bo85$25899b3o$25899bo$25900bo72$25984b3o$25984bo$25985bo80$
26061b3o$26061bo$26062bo92$26126b3o$26126bo$26127bo74$26213b3o$26213bo
$26214bo82$26287b3o$26287bo$26288bo90$26355b3o$26355bo$26356bo80$
26434b3o$26434bo$26435bo80$26512b3o$26512bo$26513bo72$26597b3o$26597bo
$26598bo74$26681b3o$26681bo$26682bo84$26756b3o$26756bo$26757bo86$
26828b3o$26828bo$26829bo76$26910b3o$26910bo$26911bo82$26984b3o$26984bo
$26985bo91$27053b3o$27053bo$27054bo80$27133b3o$27133bo$27134bo76$
27216b3o$27216bo$27217bo71$27302bo$27301b2o$27301bobo58$27401b3o$
27401bo$27402bo74$27484b3o$27484bo$27485bo76$27565b3o$27565bo$27566bo
88$27635b3o$27635bo$27636bo75$27717bo$27716b2o$27716bobo75$27803b3o$
27803bo$27804bo87$27874b3o$27874bo$27875bo75$27956bo$27955b2o$27955bob
o84$28032bo$28031b2o$28031bobo69$28120b3o$28120bo$28121bo80$28198b3o$
28198bo$28199bo68$28287b3o$28287bo$28288bo82$28366b3o$28366bo$28367bo
68$28456b3o$28456bo$28457bo74$28538b3o$28538bo$28539bo76$28619b3o$
28619bo$28620bo67$28709b3o$28709bo$28710bo78$28789b3o$28789bo$28790bo
76$28871b3o$28871bo$28872bo88$28943b3o$28943bo$28944bo72$29026b3o$
29026bo$29027bo84$29102b3o$29102bo$29103bo76$29184b3o$29184bo$29185bo
73$29267bo$29266b2o$29266bobo85$29342b3o$29342bo$29343bo76$29424b3o$
29424bo$29425bo86$29497b3o$29497bo$29498bo76$29579b3o$29579bo$29580bo
75$29660bo$29659b2o$29659bobo79$29738b3o$29738bo$29739bo78$29818b3o$
29818bo$29819bo71$29905bo$29904b2o$29904bobo65$29998b3o$29998bo$29999b
o76$30080b3o$30080bo$30081bo78$30160b3o$30160bo$30161bo81$30237bo$
30236b2o$30236bobo70$30327bo$30326b2o$30326bobo78$30405bo$30404b2o$
30404bobo91$30472b3o$30472bo$30473bo84$30548b3o$30548bo$30549bo78$
30627b3o$30627bo$30628bo70$30717b3o$30717bo$30718bo79$30797b3o$30797bo
$30798bo80$30876b3o$30876bo$30877bo72$30962b3o$30962bo$30963bo78$
31042b3o$31042bo$31043bo78$31119b3o$31119bo$31120bo74$31203b3o$31203bo
$31204bo73$31286b3o$31286bo$31287bo71$31375b3o$31375bo$31376bo80$
31454b3o$31454bo$31455bo75$31536bo$31535b2o$31535bobo86$31609b3o$
31609bo$31610bo75$31690bo$31689b2o$31689bobo79$31768b3o$31768bo$31769b
o78$31848b3o$31848bo$31849bo71$31935bo$31934b2o$31934bobo138$31960b3o$
31960bo$31961bo75$32038b3o$32038bo$32039bo76$32120b3o$32120bo$32121bo
77$32203bo$32202b2o$32202bobo79$32281b3o$32281bo$32282bo78$32361b3o$
32361bo$32362bo71$32448bo$32447b2o$32447bobo110$32500b3o$32500bo$
32501bo76$32582b3o$32582bo$32583bo75$32663bo$32662b2o$32662bobo79$
32741b3o$32741bo$32742bo78$32821b3o$32821bo$32822bo71$32908bo$32907b2o
$32907bobo101$32965b3o$32965bo$32966bo78$33046b3o$33046bo$33047bo78$
33125b3o$33125bo$33126bo76$33210b3o$33210bo$33211bo80$33286b3o$33286bo
$33287bo79$33365bo$33364b2o$33364bobo78$33443b3o$33443bo$33444bo82$
33519b3o$33519bo$33520bo70$33608b3o$33608bo$33609bo88$33679b3o$33679bo
$33680bo80$33757b3o$33757bo$33758bo62$33851b3o$33851bo$33852bo82$
33928b3o$33928bo$33929bo72$34012b3o$34012bo$34013bo78$34092b3o$34092bo
$34093bo80$34171b3o$34171bo$34172bo84$34246b3o$34246bo$34247bo70$
34334b3o$34334bo$34335bo86$34406b3o$34406bo$34407bo93$34474b3o$34474bo
$34475bo!

Code: Select all

x = 34833, y = 34833, rule = B3/S23
2o$2o33$29b3o$29bo$30bo78$107b3o$107bo$108bo78$189b3o$189bo$190bo79$
266bo$265b2o$265bobo80$346bo$345b2o$345bobo82$420b3o$420bo$421bo82$
497b3o$497bo$498bo74$580b3o$580bo$581bo70$668b3o$668bo$669bo74$750b3o$
750bo$751bo85$824bo$823b2o$823bobo76$906bo$905b2o$905bobo89$974b3o$
974bo$975bo82$1053b3o$1053bo$1054bo82$1128b3o$1128bo$1129bo78$1209b3o$
1209bo$1210bo80$1286b3o$1286bo$1287bo79$1364bo$1363b2o$1363bobo76$
1447bo$1446b2o$1446bobo78$1528bo$1527b2o$1527bobo73$1610b3o$1610bo$
1611bo79$1691bo$1690b2o$1690bobo72$1778bo$1777b2o$1777bobo79$1857b3o$
1857bo$1858bo82$1932b3o$1932bo$1933bo76$2015b3o$2015bo$2016bo76$2095b
3o$2095bo$2096bo78$2174b3o$2174bo$2175bo80$2254b3o$2254bo$2255bo79$
2334bo$2333b2o$2333bobo66$2423bo$2422b2o$2422bobo86$2498bo$2497b2o$
2497bobo74$2579bo$2578b2o$2578bobo94$2646b3o$2646bo$2647bo86$2719b3o$
2719bo$2720bo80$2798b3o$2798bo$2799bo78$2876b3o$2876bo$2877bo81$2952b
3o$2952bo$2953bo82$3029b3o$3029bo$3030bo76$3110b3o$3110bo$3111bo80$
3187b3o$3187bo$3188bo82$3263b3o$3263bo$3264bo77$3346bo$3345b2o$3345bob
o90$3413bo$3412b2o$3412bobo76$3493bo$3492b2o$3492bobo84$3568bo$3567b2o
$3567bobo80$3645bo$3644b2o$3644bobo90$3715b3o$3715bo$3716bo80$3795b3o$
3795bo$3796bo86$3866b3o$3866bo$3867bo77$3948bo$3947b2o$3947bobo84$
4021bo$4020b2o$4020bobo78$4101bo$4100b2o$4100bobo77$4182b3o$4182bo$
4183bo75$4266bo$4265b2o$4265bobo76$4344b3o$4344bo$4345bo76$4429b3o$
4429bo$4430bo84$4500b3o$4500bo$4501bo74$4585b3o$4585bo$4586bo78$4664b
3o$4664bo$4665bo81$4745b3o$4745bo$4746bo76$4826b3o$4826bo$4827bo74$
4911b3o$4911bo$4912bo76$4992b3o$4992bo$4993bo83$5069bo$5068b2o$5068bob
o76$5148b3o$5148bo$5149bo76$5229b3o$5229bo$5230bo75$5314bo$5313b2o$
5313bobo82$5388bo$5387b2o$5387bobo74$5472bo$5471b2o$5471bobo81$5551b3o
$5551bo$5552bo76$5632b3o$5632bo$5633bo75$5713bo$5712b2o$5712bobo81$
5793b3o$5793bo$5794bo78$5873b3o$5873bo$5874bo90$5940b3o$5940bo$5941bo
76$6023b3o$6023bo$6024bo80$6100b3o$6100bo$6101bo80$6177b3o$6177bo$
6178bo95$6242b3o$6242bo$6243bo72$6327b3o$6327bo$6328bo92$6396b3o$6396b
o$6397bo76$6478b3o$6478bo$6479bo70$6566b3o$6566bo$6567bo89$6632bo$
6631b2o$6631bobo88$6703bo$6702b2o$6702bobo87$6776b3o$6776bo$6777bo75$
6854b3o$6854bo$6855bo80$6935b3o$6935bo$6936bo76$7015b3o$7015bo$7016bo
80$7093b3o$7093bo$7094bo80$7173b3o$7173bo$7174bo81$7248bo$7247b2o$
7247bobo76$7332bo$7331b2o$7331bobo80$7407bo$7406b2o$7406bobo87$7479b3o
$7479bo$7480bo82$7555b3o$7555bo$7556bo84$7629b3o$7629bo$7630bo78$7708b
3o$7708bo$7709bo71$7795bo$7794b2o$7794bobo88$7868bo$7867b2o$7867bobo
82$7943b3o$7943bo$7944bo82$8019b3o$8019bo$8020bo78$8100b3o$8100bo$
8101bo78$8180b3o$8180bo$8181bo82$8255b3o$8255bo$8256bo80$8334b3o$8334b
o$8335bo77$8417bo$8416b2o$8416bobo76$8499bo$8498b2o$8498bobo83$8573b3o
$8573bo$8574bo99$8629b3o$8629bo$8630bo76$8711b3o$8711bo$8712bo85$8785b
o$8784b2o$8784bobo82$8860bo$8859b2o$8859bobo74$8943bo$8942b2o$8942bobo
82$9019bo$9018b2o$9018bobo78$9101bo$9100b2o$9100bobo74$9185bo$9184b2o$
9184bobo89$9253b3o$9253bo$9254bo48$9364b3o$9364bo$9365bo72$9448b3o$
9448bo$9449bo81$9528b3o$9528bo$9529bo78$9608b3o$9608bo$9609bo78$9690b
3o$9690bo$9691bo73$9774bo$9773b2o$9773bobo88$9845bo$9844b2o$9844bobo
89$9913b3o$9913bo$9914bo80$9992b3o$9992bo$9993bo77$10071bo$10070b2o$
10070bobo82$10148bo$10147b2o$10147bobo78$10226bo$10225b2o$10225bobo81$
10304b3o$10304bo$10305bo80$10383b3o$10383bo$10384bo81$10461bo$10460b2o
$10460bobo82$10537bo$10536b2o$10536bobo87$10605b3o$10605bo$10606bo76$
10687b3o$10687bo$10688bo77$10770bo$10769b2o$10769bobo74$10853bo$10852b
2o$10852bobo85$10928b3o$10928bo$10929bo94$10992b3o$10992bo$10993bo74$
11076b3o$11076bo$11077bo82$11152b3o$11152bo$11153bo84$11226b3o$11226bo
$11227bo83$11300bo$11299b2o$11299bobo78$11381bo$11380b2o$11380bobo75$
11464b3o$11464bo$11465bo74$11546b3o$11546bo$11547bo78$11627b3o$11627bo
$11628bo78$11708b3o$11708bo$11709bo72$11793b3o$11793bo$11794bo79$
11871b3o$11871bo$11872bo78$11951b3o$11951bo$11952bo76$12031b3o$12031bo
$12032bo73$12115bo$12114b2o$12114bobo88$12186bo$12185b2o$12185bobo98$
12247b3o$12247bo$12248bo78$12330b3o$12330bo$12331bo80$12407b3o$12407bo
$12408bo79$12485bo$12484b2o$12484bobo73$12569b3o$12569bo$12570bo76$
12651b3o$12651bo$12652bo88$12722b3o$12722bo$12723bo78$12801b3o$12801bo
$12802bo94$12867b3o$12867bo$12868bo85$12939b3o$12939bo$12940bo72$
13024b3o$13024bo$13025bo80$13101b3o$13101bo$13102bo92$13166b3o$13166bo
$13167bo74$13253b3o$13253bo$13254bo82$13327b3o$13327bo$13328bo90$
13395b3o$13395bo$13396bo80$13474b3o$13474bo$13475bo80$13552b3o$13552bo
$13553bo72$13637b3o$13637bo$13638bo74$13721b3o$13721bo$13722bo84$
13796b3o$13796bo$13797bo86$13868b3o$13868bo$13869bo76$13950b3o$13950bo
$13951bo82$14024b3o$14024bo$14025bo91$14093b3o$14093bo$14094bo80$
14173b3o$14173bo$14174bo76$14256b3o$14256bo$14257bo71$14342bo$14341b2o
$14341bobo93$14405b3o$14405bo$14406bo78$14486b3o$14486bo$14487bo78$
14565b3o$14565bo$14566bo76$14650b3o$14650bo$14651bo80$14726b3o$14726bo
$14727bo79$14805bo$14804b2o$14804bobo78$14883b3o$14883bo$14884bo82$
14959b3o$14959bo$14960bo70$15048b3o$15048bo$15049bo88$15119b3o$15119bo
$15120bo85$15194b3o$15194bo$15195bo73$15277b3o$15277bo$15278bo62$
15371b3o$15371bo$15372bo82$15448b3o$15448bo$15449bo72$15532b3o$15532bo
$15533bo78$15612b3o$15612bo$15613bo80$15691b3o$15691bo$15692bo84$
15766b3o$15766bo$15767bo70$15854b3o$15854bo$15855bo86$15926b3o$15926bo
$15927bo44$16041b3o$16041bo$16042bo74$16124b3o$16124bo$16125bo76$
16205b3o$16205bo$16206bo88$16275b3o$16275bo$16276bo75$16357bo$16356b2o
$16356bobo75$16443b3o$16443bo$16444bo87$16514b3o$16514bo$16515bo75$
16596bo$16595b2o$16595bobo84$16672bo$16671b2o$16671bobo91$16740b3o$
16740bo$16741bo76$16822b3o$16822bo$16823bo75$16903bo$16902b2o$16902bob
o79$16981b3o$16981bo$16982bo78$17061b3o$17061bo$17062bo71$17148bo$
17147b2o$17147bobo67$17240b3o$17240bo$17241bo80$17318b3o$17318bo$
17319bo68$17407b3o$17407bo$17408bo82$17486b3o$17486bo$17487bo68$17576b
3o$17576bo$17577bo74$17658b3o$17658bo$17659bo76$17739b3o$17739bo$
17740bo67$17829b3o$17829bo$17830bo78$17909b3o$17909bo$17910bo76$17991b
3o$17991bo$17992bo88$18063b3o$18063bo$18064bo72$18146b3o$18146bo$
18147bo84$18222b3o$18222bo$18223bo76$18304b3o$18304bo$18305bo73$18387b
o$18386b2o$18386bobo85$18462b3o$18462bo$18463bo76$18544b3o$18544bo$
18545bo105$18600b3o$18600bo$18601bo75$18678b3o$18678bo$18679bo76$
18760b3o$18760bo$18761bo77$18843bo$18842b2o$18842bobo79$18921b3o$
18921bo$18922bo78$19001b3o$19001bo$19002bo71$19088bo$19087b2o$19087bob
o71$19177b3o$19177bo$19178bo76$19259b3o$19259bo$19260bo75$19340bo$
19339b2o$19339bobo79$19418b3o$19418bo$19419bo78$19498b3o$19498bo$
19499bo71$19585bo$19584b2o$19584bobo65$19678b3o$19678bo$19679bo76$
19760b3o$19760bo$19761bo78$19840b3o$19840bo$19841bo81$19917bo$19916b2o
$19916bobo70$20007bo$20006b2o$20006bobo78$20085bo$20084b2o$20084bobo
91$20152b3o$20152bo$20153bo84$20228b3o$20228bo$20229bo78$20307b3o$
20307bo$20308bo70$20397b3o$20397bo$20398bo79$20477b3o$20477bo$20478bo
80$20556b3o$20556bo$20557bo72$20642b3o$20642bo$20643bo78$20722b3o$
20722bo$20723bo78$20799b3o$20799bo$20800bo74$20883b3o$20883bo$20884bo
73$20966b3o$20966bo$20967bo71$21055b3o$21055bo$21056bo80$21134b3o$
21134bo$21135bo75$21216bo$21215b2o$21215bobo86$21289b3o$21289bo$21290b
o75$21370bo$21369b2o$21369bobo79$21448b3o$21448bo$21449bo78$21528b3o$
21528bo$21529bo71$21615bo$21614b2o$21614bobo72$21704bo$21703b2o$21703b
obo69$21791b3o$21791bo$21792bo67$21886b3o$21886bo$21887bo76$21968b3o$
21968bo$21969bo75$22049bo$22048b2o$22048bobo79$22127b3o$22127bo$22128b
o78$22207b3o$22207bo$22208bo71$22294bo$22293b2o$22293bobo73$22379b3o$
22379bo$22380bo70$22469b3o$22469bo$22470bo81$22542b3o$22542bo$22543bo
76$22625b3o$22625bo$22626bo80$22702b3o$22702bo$22703bo82$22781b3o$
22781bo$22782bo77$22862b3o$22862bo$22863bo76$22944b3o$22944bo$22945bo
77$23027bo$23026b2o$23026bobo79$23105b3o$23105bo$23106bo78$23185b3o$
23185bo$23186bo71$23272bo$23271b2o$23271bobo68$23361bo$23360b2o$23360b
obo71$23450b3o$23450bo$23451bo45$23558b3o$23558bo$23559bo82$23634b3o$
23634bo$23635bo76$23718b3o$23718bo$23719bo76$23798b3o$23798bo$23799bo
83$23877b3o$23877bo$23878bo76$23960b3o$23960bo$23961bo80$24037b3o$
24037bo$24038bo80$24114b3o$24114bo$24115bo89$24182b3o$24182bo$24183bo
80$24259b3o$24259bo$24260bo76$24341b3o$24341bo$24342bo77$24424bo$
24423b2o$24423bobo74$24507bo$24506b2o$24506bobo83$24580b3o$24580bo$
24581bo80$24658b3o$24658bo$24659bo76$24740b3o$24740bo$24741bo77$24823b
o$24822b2o$24822bobo79$24901b3o$24901bo$24902bo78$24981b3o$24981bo$
24982bo71$25068bo$25067b2o$25067bobo73$25155b3o$25155bo$25156bo76$
25237b3o$25237bo$25238bo75$25318bo$25317b2o$25317bobo79$25396b3o$
25396bo$25397bo78$25476b3o$25476bo$25477bo71$25563bo$25562b2o$25562bob
o60$25665b3o$25665bo$25666bo78$25746b3o$25746bo$25747bo82$25821b3o$
25821bo$25822bo76$25902b3o$25902bo$25903bo80$25979b3o$25979bo$25980bo
72$26066b3o$26066bo$26067bo82$26144b3o$26144bo$26145bo80$26220b3o$
26220bo$26221bo68$26311b3o$26311bo$26312bo76$26392b3o$26392bo$26393bo
73$26479bo$26478b2o$26478bobo79$26555b3o$26555bo$26556bo84$26629b3o$
26629bo$26630bo80$26708b3o$26708bo$26709bo78$26790b3o$26790bo$26791bo
83$26865bo$26864b2o$26864bobo76$26945bo$26944b2o$26944bobo86$27017b3o$
27017bo$27018bo76$27099b3o$27099bo$27100bo75$27180bo$27179b2o$27179bob
o79$27258b3o$27258bo$27259bo78$27338b3o$27338bo$27339bo71$27425bo$
27424b2o$27424bobo66$27520b3o$27520bo$27521bo80$27598b3o$27598bo$
27599bo78$27680b3o$27680bo$27681bo84$27751b3o$27751bo$27752bo78$27832b
3o$27832bo$27833bo81$27908b3o$27908bo$27909bo76$27990b3o$27990bo$
27991bo75$28071bo$28070b2o$28070bobo81$28151b3o$28151bo$28152bo78$
28231b3o$28231bo$28232bo71$28318bo$28317b2o$28317bobo74$28402b3o$
28402bo$28403bo73$28488bo$28487b2o$28487bobo71$28576b3o$28576bo$28577b
o80$28653b3o$28653bo$28654bo84$28725b3o$28725bo$28726bo76$28807b3o$
28807bo$28808bo77$28890bo$28889b2o$28889bobo79$28968b3o$28968bo$28969b
o78$29048b3o$29048bo$29049bo71$29135bo$29134b2o$29134bobo75$29220b3o$
29220bo$29221bo76$29302b3o$29302bo$29303bo75$29383bo$29382b2o$29382bob
o79$29461b3o$29461bo$29462bo78$29541b3o$29541bo$29542bo71$29628bo$
29627b2o$29627bobo67$29720bo$29719b2o$29719bobo77$29798b3o$29798bo$
29799bo78$29878b3o$29878bo$29879bo78$29963bo$29962b2o$29962bobo73$
30048b3o$30048bo$30049bo82$30121b3o$30121bo$30122bo72$30207b3o$30207bo
$30208bo78$30290b3o$30290bo$30291bo75$30371bo$30370b2o$30370bobo74$
30454bo$30453b2o$30453bobo85$30529b3o$30529bo$30530bo68$30619b3o$
30619bo$30620bo74$30702b3o$30702bo$30703bo80$30781b3o$30781bo$30782bo
76$30863b3o$30863bo$30864bo74$30948b3o$30948bo$30949bo77$31029bo$
31028b2o$31028bobo71$31113b3o$31113bo$31114bo78$31196b3o$31196bo$
31197bo71$31281bo$31280b2o$31280bobo85$31355b3o$31355bo$31356bo75$
31437bo$31436b2o$31436bobo78$31517b3o$31517bo$31518bo82$31593b3o$
31593bo$31594bo74$31677b3o$31677bo$31678bo87$31747b3o$31747bo$31748bo
76$31829b3o$31829bo$31830bo77$31912bo$31911b2o$31911bobo79$31990b3o$
31990bo$31991bo78$32070b3o$32070bo$32071bo71$32157bo$32156b2o$32156bob
o103$32214b3o$32214bo$32215bo84$32287b3o$32287bo$32288bo76$32369b3o$
32369bo$32370bo75$32450bo$32449b2o$32449bobo81$32530b3o$32530bo$32531b
o78$32610b3o$32610bo$32611bo71$32697bo$32696b2o$32696bobo45$32807bo$
32806b2o$32806bobo78$32888bo$32887b2o$32887bobo78$32967bo$32966b2o$
32966bobo77$33047b3o$33047bo$33048bo80$33127b3o$33127bo$33128bo80$
33204b3o$33204bo$33205bo80$33283b3o$33283bo$33284bo72$33369b3o$33369bo
$33370bo73$33452bo$33451b2o$33451bobo88$33525b3o$33525bo$33526bo76$
33607b3o$33607bo$33608bo75$33688bo$33687b2o$33687bobo81$33768b3o$
33768bo$33769bo78$33848b3o$33848bo$33849bo71$33935bo$33934b2o$33934bob
o74$34016b3o$34016bo$34017bo72$34100b3o$34100bo$34101bo75$34185bo$
34184b2o$34184bobo82$34259bo$34258b2o$34258bobo74$34343bo$34342b2o$
34342bobo81$34422b3o$34422bo$34423bo76$34503b3o$34503bo$34504bo77$
34586bo$34585b2o$34585bobo79$34664b3o$34664bo$34665bo78$34744b3o$
34744bo$34745bo71$34831bo$34830b2o$34830bobo!
Hmm, I have to choose a smaller example. ... Replaced and still too big so paste to golly ...
Last edited by Hippo.69 on August 18th, 2025, 2:31 am, edited 1 time in total.
Post Reply