can be a pain. But not with the Offset Standardi(s|z)er™!
Watch what happens when we take this synthesis and run it through the Offset Standardi(s|z)er™ first before using the incremental-to-continuous script:
For just three easy payments of $19.99, you too can get your hands on an Offset Standardi(s|z)er™. Don't wait, call now!
Code: Select all
"""
This script is meant to be used in conjunction with the incremental-to-continuous script made by Chris Cain and Dave Greene:
https://github.com/dvgrn/b3s23life/blob/master/synthesis-tools/incremental-to-continuous-synthesis.py
This script converts the syntheses displayed on Catagolue object pages into a format understood by the script above.
To use it, paste a synthesis from Catagolue into Golly, run this script first, then run the incremental-to-continuous script
Created by Ian07, with the exception of the find_best_selection() function
Caveats:
-Vertical offset between steps still isn't allowed, but this shouldn't be a problem if you're copying the synthesis straight from Catagolue
-The script assumes that any 30-or-more-wide gap with no cells is a boundary between synthesis steps; if the synthesis in question involves an xWSS that has been converted into gliders
-If your synthesis contains especially large individual steps or especially large spaces, you may need to increase the maximum offset check
-Sometimes an "openClipboard failed" error appears. I don't know what causes this, but it doesn't appear to actually affect anything; just a minor annoyance.
-The actual incremental-to-continuous script isn't perfect, and tends to fail in situations involving intermediate oscillators and relatively large individual steps
"""
import golly as g
# Reset the universe so script works correctly
g.select(g.getrect())
g.copy()
g.new("output")
g.paste(0, 0, "or")
max_offset_check = int(g.getstring("Enter maximum offset to check for:","128"))
# Copied this function from the original script, with slight modifications:
def find_best_selection():
r = g.getrect()
all = g.getcells(r)
sep = 33 # minimum possible offset is 30 empty columns + width of a single glider, no need to check for lower values
# - run the pattern for 4096 ticks, get the new settled pattern
# - try XORing new pattern with original pattern for every possible offset up to 512
# - one of the offsets should give the lowest total population
# (will probably decrease the population instead of increasing it,
# unless what is being built is a prolific puffer or gun or some such)
bestscore, bestsep = len(all),-1 # = population * 2
allplus = g.evolve(all, 4096)
g.addlayer()
while sep<=max_offset_check:
g.show("Finding stage spacing -- testing " + str(sep))
g.new("sep=" + str(sep))
g.putcells(all)
g.putcells(allplus,sep,0,1,0,0,1,"xor")
score = int(g.getpop())
if bestscore>score: bestscore, bestsep = score, sep
sep += 1
g.dellayer()
sep = bestsep
g.show("found separation: " + str(sep))
bestblockscore, bestoffset = -999999, -1
for offset in range(sep):
g.select([r[0]-offset, r[1], sep, r[3]])
g.update()
blockscore = 0
for blockx in range(r[0]-offset,r[0]+r[2],sep):
g.select([blockx, r[1], sep, r[3]])
blockrect = g.getselrect()
block = g.getcells(blockrect)
if len(block)==0: # ran into empty block, this must not be the right separation
g.exit("Invalid pattern format found at separation = " + str(sep) + ": selected block is empty.")
g.shrink(1)
shrunkblockrect = g.getselrect()
leftdiff = shrunkblockrect[0]-blockrect[0]
rightdiff = (blockrect[0]+blockrect[2])-(shrunkblockrect[0]+shrunkblockrect[2])
blockscore += leftdiff + rightdiff
if leftdiff<10: blockscore -= (10-leftdiff)**2
if rightdiff<10: blockscore -= (10-rightdiff)**2
if blockscore>bestblockscore: bestblockscore, bestoffset = blockscore, offset
g.select([r[0]-bestoffset, r[1], r[2]+offset, r[3]])
return sep
# Current "streak" of empty columns; if this reaches >=30 it deems that a new step because that's how Catagolue displays syntheses
empty_columns = 0
# Point at which the most recent step began
last_x = 0
width = g.getrect()[2]
height = g.getrect()[3]
# Each synthesis step, formatted as a list of cell lists
step_list = []
for column in range(width+1):
g.select([column, 0, 1, height])
if len(g.getcells(g.getselrect())) == 0:
empty_columns += 1
else:
if empty_columns >= 30:
g.select([last_x, 0, column - last_x - empty_columns, height])
step_list.append(g.getcells(g.getselrect()))
# Mark this as the start of the next synthesis step
last_x = column
empty_columns = 0
# Once we've reached the end, append the final step
g.select([last_x, 0, column - last_x, height])
step_list.append(g.getcells(g.getselrect()))
g.addlayer()
max_offset = 0
# Record the offset value between each pair of consecutive steps
offset_list = []
for i in range(len(step_list) - 1):
g.putcells(step_list[i])
g.putcells(step_list[i+1])
current_offset = find_best_selection()
offset_list.append(current_offset)
max_offset = max(max_offset, current_offset)
g.select(g.getrect())
g.clear(0)
# Now let's put them all back together, this time with equal offset rather than equal separation
total_offset_so_far = 0
# The first fencepost
g.putcells(step_list[0])
for i in range(len(offset_list)):
total_offset_so_far += max_offset - offset_list[i]
g.putcells(step_list[i+1], total_offset_so_far)
#Reset universe again so next script works correctly
g.select(g.getrect())
g.copy()
g.new("output")
g.paste(0, 0, "or")
g.show("Offset standardization complete. Maximum offset: " + str(max_offset))