Golly scripts

For scripts to aid with computation or simulation in cellular automata.
drc
Posts: 1664
Joined: December 3rd, 2015, 4:11 pm

Re: Golly scripts

Post by drc » July 13th, 2017, 4:57 pm

P L E A S E keep it in one post, that was a literal quadruple post. I don't know what the last post's purpose was, either, because it's LifeHistory and not B3/S012345678 which is the incorrect rule to do that with anyway. It should be B/S

wwei23

Re: Golly scripts

Post by wwei23 » July 13th, 2017, 4:59 pm

I said 012345678//3, not 012345678/3.

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

Re: Golly scripts

Post by dvgrn » July 13th, 2017, 6:53 pm

wwei23 wrote:
gmc_nxtman wrote:Other scripts I would want are a polyomino calculator/displayer, and an inverse-rule generator.
That post from gmc_nxtman was from over two years ago, and most of the requests are out of date by this time. A script to generate polyominoes was posted almost two years ago, for example.

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » July 31st, 2017, 4:58 pm

Here is a script to generate a stamp collection of all objects found on Catagolue in a particular rule/symmetry, by frequency, excluding infinite-growth patterns. I first wrote it two years ago, but updated it to reflect more recent developments in the history of Catagolue (i.e., SS symmetry, non-totalistic rules, textcensuses not sorted by frequency).

Note: This script requires that the rulestring be entered the way it appears in Catagolue to work. However, it checks the rulestring against the one generated by g.getrule() for the same rule (after being converted to lowercase and having slashes removed), which doesn't always match the Catagolue one for some non-totalistic rules (e.g. Golly gives B37e/S23 while Catagolue gives b37-cs23. As a consequence, it does not work for all non-totalistic rules. I don't know how apgsearch v(0.5 + 0.2i) generates these rulestrings, so I can't really fix this problem.

WARNING: This script reads from the webpage catagolue.appspot.com/textcensus/<rule>/sorted, whose generation is known[1][2] to be computationally intensive. It gives a warning whenever there are more than 32768 objects in a rule/symmetry, but this warning happens after the page has already been accessed. Because of this, I recommend using the script rather infrequently, at least for commonly searched rules. I am not responsible for any trouble you might get into by running it.

Code: Select all

import golly as g
import urllib2

symmetries = ["SS",\
              "8x32",\
              "C1",\
              "C2_1", "C2_2", "C2_4",\
              "C4_1", "C4_4",\
              "D2_+1", "D2_+2", "D2_x",\
              "D4_+1", "D4_+2", "D4_+4", "D4_x1", "D4_x4",\
              "D8_1", "D8_4"]
census_prefix = 'http://catagolue.appspot.com/textcensus/'
rule_sym = g.getstring("Enter the desired rule and symmetry\n(eg. b3s23/C1):")
rule_sym_list = rule_sym.split('/')
if len(rule_sym_list) == 2:
    g.setrule(rule_sym_list[0])
    if g.getrule().replace('/', '').lower() != rule_sym_list[0]:
        g.note("Please enter your rule in bs format, in lower case letters, and without slashes.")
        g.exit()
    if rule_sym_list[1] == 'SS' and rule_sym_list[0] != 'b3s23':
        g.note("SS census data is only available for b3s23.")
        g.exit()
else:
    g.note("Invalid form submitted.")
    g.exit()
if (rule_sym.partition('/')[2] not in symmetries):
    g.note("Invalid symmetry.")
    g.exit()
# It was Supposed to be sorted frequency all along. I didn't just do that the first time because it was convenient.
textcensus = urllib2.urlopen(census_prefix + rule_sym + '/sorted')

def charnum(char):
    charstr = '0123456789abcdefghijklmnopqrstuv'
    if char in charstr:
        return charstr.index(char)
# Else it's undefined and it breaks, which is okay because it's never supposed to happen (It's up to Calcyman to keep this the case.)

def apgcodetopattern(apgcode, x, y):
    orig_x = x
    charlist = apgcode.split('_')[1]
    celllist = []
    lastchary = False
    for char in charlist:
        if char == 'w':
            x += 2
            continue
        if char == 'x':
            x += 3
            continue
        if char == 'y':
            x += 4
            lastchary = True
            continue
        if char == 'z':
            x = orig_x
            y += 5
            continue
        if lastchary:
            x += charnum(char)
            lastchary = False
            continue
        cnum = charnum(char)
        for pos in xrange(5):
            if (cnum >> pos) % 2:
                celllist.extend([x, y + pos])
        x += 1
    return celllist

g.new('')
x = 0
y = 0
occur = 0
obj_data = textcensus.readlines()
num_lines = len(obj_data)
if num_lines >= 32768:
    g.note("There are %d objects in the census!\nAre you sure you want to continue?" % num_lines)
for line in obj_data:
    obj_occur = line.split(',')
    apgcode = obj_occur[0].strip('"')
    if apgcode == 'apgcode':
        continue
    prevoccur = occur
    occur = int(obj_occur[1].strip('\n').strip('"'))
    if prevoccur != occur:
        x = 0
        y += 40
    if apgcode[0] != 'x':
        x += 40
        continue
    g.putcells(apgcodetopattern(apgcode, x, y))
    x += 40

drc
Posts: 1664
Joined: December 3rd, 2015, 4:11 pm

Re: Golly scripts

Post by drc » September 12th, 2017, 4:46 pm

flipper77 wrote:Here's a script that figures out if the specified object has been encountered by catagolue's records, and if so, finds the proper soups that generate the object (I borrowed some parts of apgsearch to assist):

Code: Select all

lol code
What you do is draw the object you want to find (hopefully, what you draw is stabilised), then using some apgsearch code, encodes the object, it even works for patterns that linearlyse() can identify. Also, the script assumes the current rule, if life-like, to be the rule to search under, symmetry part of the census you specify when you run it. There are some variables at the top to modify if you like to use different numbers(i.e. spacing is the amount of space between soups when placed down.)
I don't know if I requested this, but can someone make it so you can use an input textbox instead of drawing out the pattern? This would be useful for zz_'s, and PATHOLOGICALs.

User avatar
gameoflifemaniac
Posts: 1242
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: Golly scripts

Post by gameoflifemaniac » September 25th, 2017, 2:34 pm

Improved version of metafier.lua:

Code: Select all

-- metafier.lua:
-- Uses the current selection to build a Brice Due metapixel pattern.
-- See http://otcametapixel.blogspot.com for more info.
-- Original author: Dave Greene, 12 June 2006.
--
-- Dave Greene, 23 June 2006:  added non-Conway's-Life rule support.
-- Also reduced the script size by a factor of six, at the expense of
-- adding a moderate-sized [constant] performance hit at the start
-- [due to composing the base tiles from smaller subpatterns.]
--
-- The delay is almost all due to running CellCenter[3680] at the end
-- to produce the large filled area for the ONcell.  Subpatterns are
-- generally (with some exceptions) defined to match the labeled slides at
-- http://otcametapixel.blogspot.com/2006/05/how-does-it-work.html .
--
-- It's still possible to simplify the script by another factor of
-- two or more, by replacing the four main types of P46 oscillators
-- [which are often quoted as flat patterns] with predefined library
-- versions in the correct phase; see samples in the slide-24 section.
--
-- 24 June 2006: the script now saves a reference copy of the OFFcell
-- and (particularly) the slow-to-construct ONcell, so these only have
-- to be constructed from scratch once, the first time the script runs.
--
-- If metapixel-ON.rle and metapixel-OFF.rle become corrupted, they
-- should be deleted so the script can re-generate them.
--
-- 3 July 2007:  added code to avoid errors in rules like Seeds,
--  where the Golly canonical form of the rule doesn't include a '/'
--
-- Modified by Andrew Trevorrow, 20 July 2006 (faster, simpler and
-- doesn't change the current clipboard).
--
-- Modified by Andrew Trevorrow, 5 October 2007 (metafied pattern is
-- created in a separate layer so we don't clobber the selection).
--
-- converted from Python to Lua by Dave Greene, 23 April 2016

local g = golly()
local gp = require "gplus"
local split = gp.split
local pattern = gp.pattern

local selrect = g.getselrect()
if #selrect == 0 then g.exit("There is no selection.") end

-- check that a layer is available for the metafied pattern;
-- if metafied layer already exists then we'll use that
local layername = "metafied"
local metalayer = -1
local i
for i= 0,g.numlayers()-1 do
    if g.getname(i) == layername then metalayer = i break end
end
if metalayer < 0 and g.numlayers() == g.maxlayers() then g.exit("Delete a layer.") end

-- note that getrule returns canonical rule string
rulestr, _ = split(g.getrule(),":")

if string.sub(rulestr,1,1)~="B" or string.find(rulestr,"/S") == nil then
    g.exit("This script only works with B*/S* rules.")
end

-- get the current selection
local slist = g.getcells(selrect)
local selwidth = selrect[3]
local selheight = selrect[4]

-- create a sparse table of 0s and 1s representing entire selection
livecell = {}
for i=1, #slist, 2 do
    livecell[(slist[i] - selrect[1])..","..(slist[i+1] - selrect[2])] = 1
end
local r1, r2 = split(rulestr,"/")
-- build a patch pattern based on the current rule
--  that fixes the appropriate broken eaters in the rules table

if string.sub(r1,1,1) == "B" then
    Bvalues = string.sub(r1,2)
    Svalues = string.sub(r2,2)
elseif string.sub(r1,1,1) == "S" then
    Svalues = string.sub(r1,2)
    Bvalues = string.sub(r2,2)
else
    Svalues, Bvalues = r1, r2
end

-- create metafied pattern in separate layer
if metalayer >= 0 then
    g.setlayer(metalayer)       -- switch to existing layer
else
    metalayer = g.addlayer()    -- create new layer
end

g.new(layername)                -- clear any existing pattern
g.setrule("Life")
g.setalgo("QuickLife")          -- qlife's setcell is faster

local Brle = "b10$b10$b26$b10$b10$b26$b10$b10$b!"
local Srle = Brle

for i = 1, #Bvalues do
   ind=225-Bvalues:byte(i)*4   -- because B and S values are highest at the top!
   Brle=string.sub(Brle,1,ind-1).."o"..string.sub(Brle,ind+1)
end
for i = 1, #Svalues do
   ind=225-Svalues:byte(i)*4   -- ASCII byte value of '0' is 48, '8' is 56
   Srle=string.sub(Srle,1,ind-1).."o"..string.sub(Srle,ind+1)
end
local RuleBits = pattern(Brle, 148, 1404) + pattern(Srle, 162, 1406)

-- load or generate the OFFcell tile:
local OFFcellFileName = g.getdir("data").."metapixel-OFF.rle"
local ONcellFileName = g.getdir("data").."metapixel-ON.rle"
local fOFF = io.open(OFFcellFileName, "r")
local fON = io.open(OFFcellFileName, "r")
local OFFcell, ONcell

if fON and fOFF then
    g.show("Opening metapixel-OFF and metapixel-ON from saved pattern file.")
    OFFcell = pattern(g.transform(g.load(OFFcellFileName),-5,-5,1,0,0,1))
    ONcell = pattern(g.transform(g.load(ONcellFileName),-5,-5,1,0,0,1))
    fON:close()
    fOFF:close()
else
    g.show("Building OFF metacell definition...")
    -- slide #21: programmables --------------------

    local LWSS = pattern("b4o$o3bo$4bo$o2bo!", 8, 0)

    local DiagProximityFuse = pattern([[
    bo$obo$bo2$4b2o$4b2o$59b2o$59b2o3$58b2o2b2o$9b2o3bo5bo5bo5bo5bo5bo5bo
    7b2o2b2o$9bo3bobo3bobo3bobo3bobo3bobo3bobo3bobo$10bo2bo2bo2bo2bo2bo2bo
    2bo2bo2bo2bo2bo2bo2bo2bo$11bobo3bobo3bobo3bobo3bobo3bobo3bobo3bo5b2o$
    12bo5bo5bo5bo5bo5bo5bo5bo3bobo$55bo2bo$56bobo$57bo!]], -5, -5)

    local OrthoProximityFuse = pattern("2o41b2o$2o41bo$41bobo$41b2o!", 1001, 0)

    local ProximityFuses = LWSS + DiagProximityFuse + OrthoProximityFuse

    local AllFuses = pattern()
    -- place four sets of fuses around cell perimeter
    for i=0,3 do AllFuses = AllFuses.t(2047, 0, gp.rcw) + ProximityFuses end

    local temp = pattern("o$3o$3bo$2bo!")  -- broken eater
    temp = temp + temp.t(0, 10) + temp.t(0, 20)
    temp = temp + temp.t(0, 46) + temp.t(0, 92)
    local TableEaters = temp.t(145, 1401) + temp.t(165, 1521, gp.flip)  -- 18 eaters in  all

    local HoneyBitsB = pattern([[
    bo$obo$obo$bo7$bo$obo$obo$bo7$bo$obo$obo$bo23$bo$obo$obo$bo7$bo$obo$ob
    o$bo7$bo$obo$obo$bo23$bo$obo$obo$bo7$bo$obo$obo$bo7$bo$obo$obo$bo!]], 123, 1384)
    local HoneyBitsS = HoneyBitsB.t(57, 34)

    -- silly composition of letter labels out of subpatterns
    temp = pattern("2bobo2$obobo2$obo2$obo2$2bobo4$obo2$obobo2$2bobo!")
    local S = temp + temp.t(10, 16, gp.flip)
    local B = S + pattern("o6$8bobo2$o2$obo6$o!")
    temp = pattern("2bobo2$obobo2$obo2$obo2$obo2$obo2$obo2$obobo2$2bobo!")
    local O = temp + temp.t(10, 16, gp.flip)
    temp = pattern("obo2$obo2$obobo2$obobo2$obo2bo2$obo2$obo2$obo2$obo!")
    local N = temp + temp.t(10, 16, gp.flip)
    temp = pattern("obobobobo2$obobobobo2$obo2$obo2$obo2$obo!")
    local F = temp + temp.t(0,6)  -- overlap, just to try it out...

    local Eatr = pattern("4bobo2$4bobo2$obo2$obo2$4bobobobobobo2$4bobobobobobo2$12bobo2$12bobo2$")
    local Eater = Eatr + pattern("obo2$obo2$")
    local LabelDigits = pattern([[
    obobo2$o3bo2$obobo2$o3bo2$obobo5$obobo2$4bo2$4bo2$4bo2$4bo5$obobo2$o2$
    obobo2$o3bo2$obobo12$obobo2$o2$obobo2$4bo2$obobo5$o3bo2$o3bo2$obobo2$
    4bo2$4bo5$obobo2$4bo2$obobo2$4bo2$obobo12$obobo2$4bo2$obobo2$o2$obobo
    5$2bo2$2bo2$2bo2$2bo2$2bo5$obobo2$o3bo2$o3bo2$o3bo2$obobo!]])
    local VLine = pattern(string.rep("o2$", 25))
    local VLine2 = pattern(string.rep("o2$", 71))
    local HLine = pattern(string.rep("ob", 38))

    local TableLabels = VLine.t(18, 1440) + VLine.t(96, 1440) + Eater.t(77, 1446) 
      + HLine.t(20, 1440) + HLine.t(20, 1488) + O.t(39, 1444) + N.t(55, 1444) 
      + O.t(23, 1468) + F.t(39, 1468) + F.t(55, 1468) + Eatr.t(77, 1470)
    TableLabels = TableLabels + pattern(string.rep("ob",29).."8bobobo", 120, 1533) 
      + HLine.t(118, 1391) + VLine2.t(118, 1393) + VLine2.t(192, 1393) 
      + B.t(123, 1410) + B.t(123, 1456) + B.t(123, 1502) 
      + S.t(177, 1398) + S.t(177, 1444) + S.t(177, 1490) 
      + LabelDigits.t(138, 1396) + LabelDigits.t(168, 1401)

    local all = AllFuses + TableEaters + TableLabels + HoneyBitsB + HoneyBitsS

    -- slide 22: linear clock --------------------

    LinearClockGun = pattern([[
    19bo$17b3o$16bo$16b2o$30bo$28b3o$27bo$27b2o$13b3o$12bo2b2o$11bo3b2o$
    12b3o$12b2o$15bo$13b2o$13b2o$2b2o5b2o2b2o$bobo9b2o8b2o$bo6b3o12b2o$2o
    8b4o$12b3o$12bob2o$11b2o2bo$11b2ob2o$12bobo$13bo2$6b2o$5bobo$5bo$4b2o$
    10b2o36bo6b2o$11bo37b2o4b2o$8b3o37b2o$8bo$57bo$15b2o39bobo$14bobo15b2o
    23bo$13b2obo15b2o24b3o63b2o$8bo5b2o44bo63b2o$6b3o6bo$5bo112bo5bo$6b3o
    6bo15b2o84b3o3b3o$8bo5b2o15b2o20b2o62bo2bobo2bo$13b2obo35b2ob2o62b2ob
    2o$14bobo18b3o15b4o$15b2o18bobo16b2o$35bobo$36bobo78bo7bo$38bo79bobobo
    bo$70bo47bobobobo$70b3o44bob2ob2obo$37bo35bo43b2o5b2o$37bo34b2o20b2o$
    59bo35bo$59b3o33bobo$62bo33b2o$61b2o4$28b2o7b2o11bo24bo$28bob2o3b2obo
    10b3o22b3o$28bo3bobo3bo9b2obo21b2o2bo41b2ob2o$28b2o2bobo2b2o9b3o21b2o
    3b2o11b2o28bobo$29b3o3b3o11b2o19bobo2bobo13bo10b3o8b2o5bobo$30bo5bo33b
    2o3b2o9b2o3bobo7bo2bo4bo3b2o6bo$65b2o19bobo3b2o6bo3bo3bobo$65b2o21bo
    11bo7bobo$25b2o13b2o46b2o13bo$25b2o2b2o5b2o2b2o58b2obo$29b2o5b2o$121b
    2o$103bob2o14bobo$103b3o17bo$40bo63bo18b2o$40bo2b2o72b2o$39bo5bo36b2o
    33bo$40bobo2b2o35bobo33b3o$27bo12bo3b2o38bo35bo$25b3o14b3o39b2o20b2o$
    24bo41b2o10b2o26bo$25b3o14b3o20bo2bo9bo28b3o$27bo12bo3b2o20b2o11b3o27b
    o$40bobo2b2o4b2o28bo$39bo5bo5b2o$40bo2b2o$40bo!]], 46, 1924)

    Gap = "3bo7bo7bo$b3o5b3o5b3o$o7bo7bo$2o6b2o6b2o21$"  -- string, not pattern!
    LinearClockEaters = pattern(string.rep("3bo$b3o$o$2o5$", 36)..Gap
      ..string.rep("3bo$b3o$o$2o5$",10).."24$"..string.rep("3bo$b3o$o$2o5$",17)
      ..Gap..string.rep("3bo$b3o$o$2o5$",42)..Gap.."3bo$b3o$o$2o5$3bo7bo$b3o5b3o$o7bo$2o6b2o13$"
      ..string.rep("3bo$b3o$o$2o5$",30).."8$"..string.rep("3bo$b3o$o$2o5$",2)..Gap.."16$"
      ..string.rep("3bo$b3o$o$2o5$",11).."8$"..string.rep("3bo$b3o$o$2o5$",19), 101, 422)
    LinearClockCap = pattern([[
    3b2o13bo7bo$2bo2bo10b3o5b3o$bobobo9bo7bo14b2o$3obo10b2o6b2o13bobo$3o
    29bo6b3o$30b2ob2ob3ob2o$30b2ob2ob5o$32bo5bo$2b2o29b2ob2o$2b2o3$32bo5bo
    $31b2o5b2o$30bob2o3b2obo$30bob2o3b2obo$31b3o3b3o$31b3o3b3o10$33b2ob2o$
    34bobo$34bobo$35bo!]], 83, 401)
    all = all + LinearClockGun[1840] + LinearClockEaters + LinearClockCap

    -- slide 23: MWSS track --------------------

    local MWSS = pattern("b5o$o4bo$5bo$o3bo$2bo!", 521, 1973)

    local P46Osc = pattern([[
    40bo$31b2o5b3o$10b3o14b2o3bo4bo$10bo3bo12b2o3bobo3bo$3bo6bo4bo17bobo3b
    o$b3o7bo3bo18bo3b2o$o$b3o7bo3bo7b3o$3bo6bo4bo5b2obob2o$10bo3bo5bo5b2o$
    10b3o8b2obob2o$23b3o!]])

    local P46Gun = pattern([[
    31b2o$30b2o$31b5o9bo$16b3o13b4o9b3o$14bo3bo29bo$3bo9bo4bo13b4o9b3o$b3o
    9bo3bo13b5o9bo$o29b2o$b3o9bo3bo13b2o$3bo9bo4bo$14bo3bo$16b3o!]])

   local GliderToMWSS = P46Osc.t(449, 1963) + P46Gun.t(474, 1981)
    -- exact match would be P46Gun[46](474, 1981), but that glider is absorbed

    local StateBit = pattern("b2o$obo$bo!", 569, 1970)
    local Snake = pattern("2o$o$bo$2o!", 570, 1965)

    local BitKeeper = pattern([[
    15bobo2b2o19b2o$14bo3bob3o19bo$15bo6b2o15b3o$3bo12b5obo16bo$b3o15b3o$o
    $b3o15b3o$3bo12b5obo$15bo6b2o3b2o$14bo3bob3o4b2o$15bobo2b2o!]], 524, 1982)

    local GliderFromTheBlue = pattern([[
    23b2o$23bo2bobo$25b2ob3o$31bo$25b2ob3o$12bo3bo8b2obo$11bo5bo$17bo$3bo
    8bo3b2o$b3o9b3o$o$b3o9b3o19bo$3bo8bo3b2o7bo7b3o$17bo4bo2bob2o3bo$11bo
    5bo10bo4bo$12bo3bo10bo4b2o2$23b5o$24b4o$26bo!]])

    local GliderToLWSS = pattern([[
    3bo$2bobo$bo3bo3b2o$b5o3b2o$obobobo$bo3bo2$bo3bo$obobobo$b5o$bo3bo$2bo
    bo$3bo6$b3o5b3o$2ob2o3b2ob2o$2obobobobob2o$bo3bobo3bo$bo2bo3bo2bo$2b3o
    3b3o4$4b2ob2o$5bobo$5bobo$6bo!]], 663, 1931)  -- P46Osc really

    local ReflectLWSS = pattern([[
    b2o$b2o8$b3o3b3o$o2bo3bo2bo$2obo3bob2o14$3b2ob2o$4bobo$4bobo$5bo!]], 589, 1940)

    local ReflectLWSS2 = pattern([[
    15b2o3bo$15b3obob2o4b2o$15b3o4bo4b2o$3bo14bo3bo$b3o15b3o$o$b3o15b3o$3b
    o14bo3bo$15b3o4bo$15b3obob2o$15b2o3bo!]], 573, 1884)  -- P46Osc really

    local LWSStoBoat = pattern([[
    2o14b2o9b2o$2o15b2o8b2o$13b5o$13b4o2$3b2o8b4o$2bobob2o5b5o$b2o3b2o9b2o
    8b2o$2bobob2o8b2o9b2o$3b2o!]], 821, 1883)  -- P46Osc really

    local ReflectMWSS = pattern([[
    22b2o5b2o$18b2o2b2o5b2o2b2o$18b2o2b2o5b2o2b2o$22b3o3b3o$21bo2bo3bo2bo$
    25bobo$21b2o7b2o$21b2o7b2o$24bo3bo$24b2ob2o$22b3o3b3o$22b2o5b2o$22bo7b
    o10$b2o27b2o$b2o25b2ob2o$26bo2bobo$6bo3b2o14bo$2o2b2obob3o14bo4bo$2o2b
    o4b3o15bo3bo$4bo3bo15b2o2b3o$5b3o16b2o2$5b3o$4bo3bo$2o2bo4b3o13b2o$2o
    2b2obob3o13b2o$6bo3b2o2$b2o$b2o!]])

    local HoneyBit = pattern("b2o$o2bo$b2o!")
    local CornerSignalSystem = pattern([[
    4b2o4b3o$4b2o3bo3bo$9b2ob2o$10bobo2$10bobo$9b2ob2o$9bo3bo$10b3o9$3b3o
    5b3o$3bo2b2ob2o2bo$4b3o3b3o$5bo5bo4$2o13b2o$2o2b2o5b2o2b2o$4b2o5b2o!]], 70, 9)  -- P46OscAlt1 really
    -- include stopper for pattern corners, destroyed in the interior of
    --  multi-cell metapatterns by the tub-LWSS-fuse combination
    CornerSignalSystem = CornerSignalSystem + pattern("5b2o$5b2o3$2o2b2o$2o2b2o!", 86, 1)
    + pattern([[
    11b2o$11b2o12$4b3o3b3o$4b3o3b3o$3bob2o3b2obo$3bob2o3b2obo$4b2o5b2o$5bo
    5bo6$2o13b2o$2o2b2o5b2o2b2o$4b2o5b2o!]], 159, 0)  -- P46OscAlt1 really

    local SignalSystem = pattern()
    for i=0,2 do  -- place three sets of mechanisms to signal neighbors
        SignalSystem = SignalSystem[32].t(2047, 0, gp.rcw)
        + HoneyBit.t(42, 111, gp.rcw) + ReflectMWSS.t(38, 46) + GliderFromTheBlue.t(73, 52)
        + CornerSignalSystem + HoneyBit.t(964, 40) + GliderFromTheBlue[12].t(953, 52)
    end
    
    -- need to add the fourth (west) side separately because signal stops there,
    --  so two-thirds of the pieces are customized or in slighly different locations
    --  (could have started in SW corner and used range(0,4) for some of it,
    --  but I happened to start in the NW corner and now I'm feeling lazy)

    -- west edge:
    SignalSystem = SignalSystem + HoneyBit.t(48, 1086, gp.rcw) + pattern([[
    11b2o$12bo$12bob2o$13bobo$3bo$2bobo$2bobo$b2ob2o$4bo$b2o2bo7bo$2bo2bo
    7bobo$o8b3o$2o9b3o2bo$9b2o4b2o$13b2o6$8bo3bo$8bo3bo3$5b2obo3bob2o$6b3o
    3b3o$7bo5bo6$8b2ob2o$9bobo$9bobo$10bo!]], 52, 1059)  -- GliderFromTheBlue really

    -- southwest corner:
    SignalSystem = SignalSystem + pattern([[
    11b2o$12bo$12bob2o$13bobo$3bo$2bobo$2bobo$b2ob2o2$b2ob2o7b3o$2bob2o6bo
    2bo$o13b2o$2o9bo3bo$11bo2b2o$13bob2o9$5b2o7b2o$5bob2o3b2obo$5bo3bobo3b
    o$5b2o2bobo2b2o$6b3o3b3o$7bo5bo4$8b2ob2o$9bobo6b2o$9bobo7bo$10bo5b3o$
    16bo!]], 52, 1885)  -- GliderFromTheBlue plus a terminating eater really
    SignalSystem = SignalSystem + pattern([[
    24b2o$24b2o3$2o14b3o6b2o$2o12bo3bo6b2o$13bo4bo$13bo3bo2$3b3o7bo3bo$b2o
    bob2o5bo4bo$b2o5bo5bo3bo6b2o$b2obob2o8b3o6b2o$3b3o2$24b2o$24b2o!
    ]], 0, 1818)  -- P46OscAlt1 really -- lengthened version of CornerSignalSystem
    SignalSystem = SignalSystem + pattern("2o$2o2b2o$4b2o3$4b2o$4b2o!", 1, 1955) + pattern([[
    24b2o$24b2o2$14b3o$13bo4bo6b2o$12bo5bo6b2o$13bo$14b2o2$14b2o$13bo$2o
    10bo5bo6b2o$2o11bo4bo6b2o$14b3o2$24b2o$24b2o!]], 9, 1961)  -- P46OscAlt1 really

    all = all + SignalSystem + GliderToMWSS + MWSS + Snake + BitKeeper
    all = all + GliderFromTheBlue.t(607, 1953) + GliderToLWSS + ReflectLWSS + ReflectLWSS2 + LWSStoBoat

    -- StateBit will be added later on, when defining OFFcell and ONcell

    -- slide 24: LWSS track --------------------
    -- [phase adjusters and a bunch of honeybits]

    local PhaseAdjuster = P46Gun.t(2, 11, gp.flip_y) + P46Gun[12].t(0, 32)
                    + pattern("o$3o$3bo$2b2o!", 0, 13)
                    + pattern("2b2o$bobo$bo$2o!", 2, 26)  -- eaters really

    all = all + PhaseAdjuster[43].t(1772,10) + PhaseAdjuster[26].t(2037, 1772, gp.rcw)
    all = all + PhaseAdjuster[43].t(269, 2037, gp.flip) + PhaseAdjuster[9].t(58, 1203, gp.swap_xy_flip)
    -- the third adjuster was a different shape in the original metacell,
    --  but one of the same shape could be substituted with no ill effects
    local LWSSPacketGun = pattern([[
    50b2o$52bo$37b2o10b2o2bo$37b2o11bo2bo$51bobo8bo$51b2o9b3o$65bo$40b2o9b
    2o9b3o$38bo3b2o7bobo8bo$38bo4bo6bo2bo$38bo3b2o5b2o2bo$40b2o10bo$50b2o
    5$52bo$52b3o$55bo$54b2o$41bo$41b3o$b2o5b2o34bo$b2o5b2o33b2o3$45b2o$43b
    o3bo5b2o9b2o$42bo5bo3bo2bo4b3o2bo$42bo4bo5bo4bo2b5o$42bo2bo7bo2b4o$43b
    obo8bob2o3bo$44bo8b2obob3o7b2o$55bo3bo8bobo$52b3o15bo$53bo16b2o$bo7bo$
    b2o5b2o$b3o3b3o$3b2ob2o45b2o$3bo3bo22b2o21b2o$2o7b2o20bo$2o7b2o17b3o$
    4bobo21bo35b2o$o2bo3bo2bo53bobo$b3o3b3o56bo$66b2o$60b2o$60bo$b2o25b2o
    31b3o$b2o25b2o33bo2$19b2o$17b2o2bo7b2o$17b6o6b2o$17b4o4$17b4o$17b6o6b
    2o$17b2o2bo7b2o$19b2o2$28b2o$12bobo13b2o$13b2o$13bo5$17b2o$17b2o19$49b
    o3b3o$48bobo2b5o3b2o$48bo3b2o3b2o2b2o$49bo2bo3b2o$50b3o3bo2$50b3o3bo$
    49bo2bo3b2o$34b2o12bo3b2o3b2o2b2o$34b2o12bobo2b5o3b2o$49bo3b3o5$39bo$
    17b3o19b3o$17bo2bo21bo$17bo23b2o$17bo10bo$18bobo7b3o$31bo$30b2o$43bo3b
    o$42b5obo$41b3o4b2o$41b2ob5o$43b3o2$45bo$41bo2b3o$40b2obo3bo7b2o$34b2o
    3b2ob3obo2b2o4bobo$34b2o3b3ob2o4b2o6bo$39b3o15b2o4$41bo$41bob3o$42bo3b
    o$13b2o3b2o8bo17bo$12bo2bobo2bo7bo2b2o10b2obo4b2o$11bo9bo5bo5bo11bo5bo
    bo$10b2o9b2o5bobo2b2o18bo$11bo9bo6bo3b2o3bo15b2o$12bo2bobo2bo9b3o4b3o
    7b2o$13b2o3b2o20bo6bo$30b3o4b3o8b3o$28bo3b2o3bo12bo$12b2o14bobo2b2o$
    12b2o13bo5bo$28bo2b2o$28bo!]], 108, 710)
    
    all = all + LWSSPacketGun + pattern([[
    b2o$b2o$7b3o$6bo3bo$6b2ob2o2$6b2ob2o$8bo4$bo7bo$o2bo3bo2bo$4bobo$4bobo
    $4bobo$o2bo3bo2bo$b3o3b3o8$3b2ob2o$4bobo$4bobo$5bo!]], 18, 725)  -- P46Osc really

    local P46OscAlt1 = pattern([[
    b2o$b2o2$6bo3b2o$2o2b2obob3o13b2o$2o2bo4b3o13b2o$4bo3bo$5b3o$19b2o3b2o
    $5b3o10b3o3b3o$4bo3bo7b3o7b3o$2o2bo4b3o4b3o7b3o$2o2b2obob3o4b3o7b3o$6b
    o3b2o6b3o3b3o$19b2o3b2o$b2o$b2o!]])

    all = all + P46OscAlt1.t(4, 24) + P46OscAlt1[12].t(224, 67, gp.swap_xy_flip)

    local P46OscAlt2 = pattern([[
    2o8b3o14b2o$2o8bo3bo12b2o$10bo4bo$11bo3bo2$11bo3bo7b3o$10bo4bo5b2obob
    2o$2o8bo3bo5bo5b2o$2o8b3o8b2obob2o$23b3o!]])

    all = all + P46OscAlt2.t(179, 19) + P46OscAlt1[29].t(2023, 4, gp.rcw)

    -- got impatient here, started throwing in placeholders instead of true definitions --
    -- NE corner along E edge:
    all = all + pattern([[
    b2o$b2o2$8b2o9b2o3b2o$2o5bobo8bob2ob2obo$2o4b2obo8bo7bo$7b2o9bob2ob2ob
    o$8bo10b2o3b2o2$8bo$7b2o$2o4b2obo15b2o$2o5bobo15b2o$8b2o2$b2o$b2o!]], 1980, 208) + pattern([[
    2b2o5b2o$2b2o5b2o15$b3o5b3o$2ob2o3b2ob2o$2obobobobob2o$bo3bobo3bo$bo2b
    o3bo2bo$2b3o3b3o6$9b2o$9b2o!]], 2018, 179)  -- both P46OscAlt really

    -- SE corner:
    all = all + pattern([[
    24b2o$24b2o$14bo$13bo2bo$12b5o8b2o$11b2ob3o8b2o$12bob2o$13b2o2$13b2o$
    12bob2o$2o9b2ob3o8b2o$2o10b5o8b2o$13bo2bo$14bo$24b2o$24b2o!]], 2017, 2007)  -- P46OscAlt really

    -- SE corner along S edge:
    all = all + pattern([[
    4b2o5b2o$2o2b2o5b2o2b2o$2o13b2o10$4bo7bo$3bobo5bobo$6bo3bo$3bo2bo3bo2b
    o$4bobo3bobo$6b2ob2o$3b2ob2ob2ob2o$3b2o2bobo2b2o$4b3o3b3o$5bo5bo4$4b2o
    $4b2o!]], 1823, 1980) + pattern([[
    20bo$15bo3bo$14bo8b2o2b2o$14bo2b2o5bo2b2o$15b2o5bobo$16b3o3b2o2$16b3o
    3b2o$15b2o5bobo$2o12bo2b2o5bo2b2o$2o12bo8b2o2b2o$15bo3bo$20bo!]], 1840, 2018)  -- both P46OscAlt really

    -- SW corner:
    all = all + pattern([[
    4b2o4b3o$4b2o3bo3bo$9b2ob2o$10bobo2$10bobo$9b2ob2o$9bo3bo$10b3o9$3b3o
    5b3o$3bo2b2ob2o2bo$4b3o3b3o$5bo5bo4$2o13b2o$2o2b2o5b2o2b2o$4b2o5b2o!]], 24, 2017)  -- P46OscAlt really

    -- SW corner along W edge:
    all = all + pattern([[
    24b2o$24b2o3$2o14b2o7b2o$2o15b2o6b2o$13b5o$13b4o2$3b2o8b4o$2bobob2o5b
    5o$b2o3b2o9b2o6b2o$2bobob2o8b2o7b2o$3b2o2$24b2o$24b2o!]], 41, 1769) + pattern([[
    b2o5bo$b2o4bobo$6bo3bo$6bo3bo$5b3ob3o$6bo3bo$6b2ob2o$7bobo$8bo5$3bo3bo
    $3bo3bo3$2obo3bob2o$b3o3b3o$2bo5bo8$b2o5b2o$b2o5b2o!]], 6, 1786)  -- both P46OscAlt really


    -- LWSS -> G -> LWSS timing adjustment, middle of W edge:
    all = all + pattern([[
    b2o5b2o$b2o5b2o16$3o5b3o$o2b2ob2o2bo$b3o3b3o$2bo5bo7$b2o$b2o!]], 10, 1217) + pattern([[
    4bo$2b5o10bo$bo2bob2o9b2o8b2o$o7bo9b2o7b2o$bo2bob2o5b2o2b2o$2b5o$4bo2$
    13b2o2b2o$2o16b2o7b2o$2o15b2o8b2o$17bo!]], 35, 1269)  -- both P46OscAlt really

    -- final LWSS reflector, middle of W edge:
    all = all + pattern([[
    15bo$14b2o$13b3obo9b2o$12b2o13b2o$3bo9b2o$b3o10bo$o$b3o10bo10bo$3bo9b
    2o8b2obo$12b2o8b2ob3o$13b3obo5bo2bo$14b2o8b2o$15bo!]], 8, 973)  -- P46Osc really

    -- slide 25: decode --------------------

    -- sync buffer:
    all = all + pattern([[
    b2o5b2o$b2o5b2o7$2bo5bo$b3o3b3o$o2b2ob2o2bo$3o5b3o9$b3o$o3bo$2ob2o$bob
    o2$bobo$2ob2o$o3bo3b2o$b3o4b2o!]], 150, 958)  -- P46OscAlt3 really

    all = all + pattern([[
    10b2o$2o6b2ob2o14b2o$2o6bo2bo15b2o$8bo2bo$9b2o2$9b2o$8bo2bo$8bo2bo15b
    2o$8b2ob2o14b2o$10b2o!]], 155, 998)  -- P46OscAlt3 really

    all = all + pattern([[
    15bobo2b2o$2o12bo3bob3o4b2o$2o13bo6b2o3b2o$16b5obo$19b3o2$19b3o$16b5ob
    o$2o13bo6b2o$2o12bo3bob3o$15bobo2b2o!]], 114, 1008)  -- P46OscAlt3 really

    all = all + pattern([[
    b2o$b2o11$2bo5bo$bobo3bobo$o3bobo3bo$o3bobo3bo$o9bo2$b2o5b2o3$3b2ob2o$
    4bobo$3b2ob2o$4bobo$3b2ob2o$4bobo$4bobo$5bo!]], 141, 1024)  -- P46OscAlt3 really

    -- P46 to P40 converter:
    all = all + pattern([[
    14bo$14b3o$17bo$16b2o50bo$10b2o54b3o$11bo53bo$11bobo51b2o$12b2o57b2o$
    48b2o21bo$48bo20bobo$33bo12bobo20b2o$33b3o10b2o$36bo$35b2o2$6b2o$7bo
    22bo$7bobo19b2o$8b2o10bo4bo49b2o$19b2o3b2o30bo18bo$18b2o3b2o29b4o15bob
    o$19b2o4bo27b2o3b2o13b2o$20b2o34bo3bo$56bo3bo$56bo2b2o$54bo3bo2$33b2o$
    33bo$34b3o$36bo11b2o$22b2o25bo$22bo23b3o$19bo3b3o20bo$17b3o5bo33b2o$
    16bo43bo$16b2o39b3o8bo$30bo26bo8b3o$28b3o34bo$27bo37b2o$27b2o42b2o$48b
    2o21bo$17b2ob2o26bo20bobo$17b2ob2o11bo12bobo20b2o$33b3o10b2o$24b2o10bo
    $24b2o9b2o21b2o$58b2o$16b2o$2b2o11b2obo15bo$bobo12bob4o13bo$bo15bo2b3o
    10b3o39b2o$2o16bo3bo52bo$18b4o51bobo$20bo8b3o34b2o5b2o$31bo21b3o7bo2b
    3o$17b2o11bo21b2ob2o2b4o2bo2bo$17b2o15b2o15bo4b2ob4ob2o2bo$34b2o16b6o
    6b2obo$53b2o2bo8bo$6b2o49b2obo$5bobo50bobo$5bo52bo$4b2o42b2o$10b2o37bo
    $11bo34b3o$8b3o35bo$8bo50b2o$60bo$57b3o$8bo48bo$6bobo$7bobo$7bo2$19b2o
    $19b2o4$2b2o13b2o$3bo11bo2bo$3bobo9bo2bo$4b2o8b2ob2o$13b2ob2o$12b2o3b
    2o$13b3o2bo$18bo$15bobo15b2o$16bo16bobo$35bo$35b2o$29b2o$29bo$30b3o$
    32bo$18b2o$18bo$14bo4b3o$14b3o4bo$17bo$16b2o50bo$10b2o54b3o$11bo53bo$
    11bobo51b2o$12b2o57b2o$48b2o21bo$48bo20bobo$23b2o8bo12bobo20b2o$23b2o
    8b3o10b2o$36bo$35b2o21b2o$58b2o$6b2o16bo$7bo15b3o$7bobo8bo3bo$8b2o7b3o
    bob2o8bo41b2o$16bo3b2obo8bobo19bo20bo$18b4o2bo7bo2bo17bobo3bobo4bo6bob
    o$12b5o2bo4bo5bo4bo16bo3b3obobo2bobo5b2o$12bo2b3o4bo2bo3bo5bo15b2o2bo
    4bobobo3b2o$12b2o9b2o5bo3bo15b2ob3o5bo3b2o$31b2o16bo4bo$50b4o6b2o4bo2b
    o$51b2o6bo6bobo$33b2o24b3o4b2o$33bo24b2o$34b3o21bo$36bo11b2o$14bo7b2o
    25bo$14b3o5bo23b3o$17bo5b3o20bo$16b2o7bo33b2o7bo$10b2o48bo5b3o$11bo45b
    3o5bo$11bobo43bo7b2o$12b2o57b2o$48b2o21bo$18b2o28bo20bobo$18b2o13bo12b
    obo20b2o$33b3o10b2o$20bo15bo26b2o$14b3o3b2ob2o10b2o26b2o$14b2o5bobobo
    32bo$6b2o8bo4bob2o31b2obo$7bo12b3o37bo5b2o$7bobo6b2o2bobo33b2o2bo5b2o$
    8b2o6b6o37bob2o12b2o$16b3o4b2o32bobob2o3b2o7bo$23b2o34bo2bo3b2o5bobo$
    57bob2o12b2o$59bob2o$60b2o4$33b2o$33bo$34b3o$36bo11b2o$14bo7b2o25bo$
    14b3o5bo23b3o$17bo5b3o20bo$16b2o7bo33b2o7bo$10b2o48bo5b3o$11bo45b3o5bo
    $11bobo43bo7b2o$12b2o57b2o$48b2o21bo$48bo20bobo$33bo12bobo20b2o$33b3o
    10b2o$36bo$35b2o2$6b2o$7bo10b3o8b2o$7bobo7bo2bo4bo3b2o$8b2o6bo3bo3bobo
    48b2o$16bo7bobo25bo22bo$19bo32b2o19bobo$16b2obo37bo4bo10b2o$57b2o3b2o$
    58b2o3b2o$19bob2o34bo4b2o$19b3o39b2o$20bo$33b2o$33bo$34b3o$36bo11b2o$
    22b2o25bo$22bo23b3o$19bo3b3o20bo$17b3o5bo33b2o$16bo43bo$16b2o39b3o8bo$
    30bo26bo8b3o$28b3o34bo$27bo37b2o$27b2o42b2o$48b2o21bo$17b2ob2o26bo20bo
    bo$17b2ob2o11bo12bobo20b2o$33b3o10b2o$16b2o6b2o10bo$14bo3bo5b2o9b2o21b
    2o$13bo4b2o38b2o$13bo5bo$2b2o$bobo10bo4bo$bo12b2obob2obo34b3o15b2o$2o
    30bo2b3o19b4o14bo$31bo3bobo14b2o3b2ob2o3b2o6bobo$32bob3o14bo7bo5b3o5b
    2o$49bo2bo5b3o3bob3o$49bo2bo5b2obo2b2obo$34b2o12bo3bo8b2ob2obo$34b2o
    13b3o7bobo3bobo$60bo4bo2bo$6b2o57b3o$5bobo50b2o6bo$5bo52b2o$4b2o42b2o$
    10b2o37bo$11bo34b3o$8b3o35bo$8bo50b2o$60bo$57b3o$8bo48bo$6bobo$7bobo$
    7bo2$19b2o$19b2o4$2b2o$3bo$3bobo$4b2o4b4o$9b2o2bo5b2o$8b2o2bo4bobob2o$
    9bo2bo2b2o5bo$10b2o6bo3bo$15bo5bo11b2o$16b4o13bobo$18bo16bo$35b2o$29b
    2o$29bo$30b3o$32bo$18b2o$18bo$19b3o$21bo!]], 116, 1059)  -- really 14 p184 guns

    -- logic core latches:
    all = all + pattern([[
    24bo2bo$14b3o7bo$13bo4bo9bo$12bo5bo8b2o$3bo9bo8bo$b3o10b2o8b2o$o$b3o
    10b2o8b2o$3bo9bo8bo3bo$12bo5bo4bo2bo$13bo4bo4bo2bo$14b3o7b2o!
    ]], 33, 1332)  -- P46Osc really

    all = all + pattern([[
    4b2o5b2o$2o2b2o5b2o2b2o$2o13b2o2$4b3o3b3o$4bo2bobo2bo$3bo3bobo3bo$4bo
    2bobo2bo$6bo3bo$4b2o5b2o$3b3o5b3o$3b3o5b3o5$10b3o$10b3o$9b5o$8b2o3b2o$
    8b2o3b2o4$8b2o3b2o$4b2o2b2o3b2o$4b2o3b5o$10b3o$10b3o!]], 35, 1348)  -- P46OscAlt1 really

    all = all + pattern([[
    b2o$b2o2$13bobo2b2o$2o10bo3bob3o$2o11bo6b2o$14b5obo$17b3o2$17b3o$14b5o
    bo$2o11bo6b2o3b2o$2o10bo3bob3o4b2o$13bobo2b2o2$b2o$b2o!
    ]], 24, 1661)  -- P46OscAlt1 really

    all = all + pattern([[
    b2o$b2o12$b3o3b3o$b3o3b3o$ob2o3b2obo$ob2o3b2obo$b2o5b2o$2bo5bo3$3b2ob
    2o$4bobo$4bobo$4bobo$3b2ob2o$4bobo$4bobo$5bo!]], 49, 1679)  -- P46Osc really

    all = all + pattern([[
    b2o$o2bo$o7bo$o2bo3bobo$2ob2ob2ob2o$4bobo$3b2ob2o5$b3o3b3o$o9bo$o3bobo
    3bo$b3o3b3o$2bo5bo10$3b2ob2o$4bobo$4bobo$5bo!]], 140, 1546)  -- P46Osc really

    all = all + pattern([[
    2b3o$2b3o4b2o$bo3bo3b2o$o5bo$b2ob2o2$b2ob2o$o5bo$bo3bo$2b3o$2b3o7$b2o
    7b2o$bob2o3b2obo$bo3bobo3bo$b2o2bobo2b2o$2b3o3b3o$3bo5bo6$2b2o5b2o$2b
    2o5b2o!]], 184, 1538)  -- P46OscAlt3 really

    all = all + pattern("2o$o$b3o$3bo!", 159, 1537)  -- eater really

    -- slide 26: read B/S table --------------------
    -- most of the B/S logic latches -- missing some reflectors off to the left (?)
    -- TODO: take this apart and integrate with missing pieces

    all = all + pattern([[
    34bo$33bobo$33bobo$32b2ob2o11$30bo7bo22b2o5b2o$29bobo5bobo21b2o5b2o$
    32bo3bo$29bo2bo3bo2bo$30bobo3bobo$32b2ob2o$29b2ob2ob2ob2o$29b2o2bobo2b
    2o$30b3o3b3o$31bo5bo23b3o3b3o$60bo2bo3bo2bo$60b2obo3bob2o2$37b2o$37b2o
    6$62bo$60b2ob2o$60b2ob2o15b2o5b2o$80b2o5b2o$59bobobobo2$60b2ob2o$61b3o
    4b2o$62bo5b2o4$81bo5bo$80b3o3b3o$80bob2ob2obo$82b2ob2o$82b2ob2o$82b2ob
    2o6$80b3o$80b3o2$79b2ob2o$79bo3bo$80b3o$81bo5b2o$87b2o199$51bo$50bobo$
    50bobo$49b2ob2o13$47bo7bo$46b4o3b4o$46bo3bobo3bo$47bo2bobo2bo$47b3o3b
    3o7$47b2o$47b2o9$62b3ob3o9b2o$61bob2ob2obo7b2ob2o6b2o$60b2o7b2o7bo2bo
    6b2o$61bob2ob2obo8bo2bo$62b3ob3o10b2o2$79b2o$78bo2bo$61b2o15bo2bo6b2o$
    61b2o14b2ob2o6b2o$78b2o5$80b2o5b2o$80b2o5b2o11$81bo5bo$80bobo3bobo$79b
    o3bobo3bo$79bo3bobo3bo$79bo9bo2$80b2o5b2o4$82bo3bo$80b2o$79bo3bobo3b2o
    $79bo3bobo$80b3o$87bo2bo$87b2o12$8b2o$8b2o13$b2o5b2o$o2bo3bo2bo$bo2bob
    o2bo$4bobo$2b3ob3o$3o5b3o$2o7b2o$2o7b2o$bob2ob2obo$bob2ob2obo2$3b2ob2o
    $4bobo$4bobo$5bo26$9bo$8bobo$8bobo$7b2ob2o11$5b2o5b2o$4bo2bo3bo2bo$7b
    2ob2o$6bobobobo$6bobobobo$4bo9bo$3bo11bo2$7b2ob2o$5bo2bobo2bo$5b3o3b3o
    3$5b2o$5b2o38$10bo$9bobo28bo$9bobo21b2o4bobo$8b2ob2o20bo5bobo$31bobo4b
    2ob2o$18b2o11b2o$19bo18b2ob2o$19bobo$20b2o16b2ob2o3$23b2o$22bobo11bo7b
    o$24bo$6b2o5b2o19b3o7b3o$5bo2bo3bo2bo19b2ob2ob2ob2o$6bo2bobo2bo15b2o4b
    3o3b3o$9bobo18bobo4bo5bo$7b3ob3o16bo$5b3o5b3o$5b2o7b2o$5b2o7b2o$6bob2o
    b2obo$6b3o3b3o$7bo5bo$9b3o3b3o$8bo2bo3bo2bo$12bobo$8b2o7b2o$8b2o7b2o$
    11bo3bo$11b2ob2o$9b3o3b3o$9b2o5b2o16bo5bo$9bo7bo15bobo3bobo$32bo3bobo
    3bo$32bo3bobo3bo$32bo9bo2$33b2o5b2o3$35b2ob2o$36bobo$35b2ob2o$11b2ob2o
    20bobo$12bobo20b2ob2o$12bobo21bobo$13bo22bobo$37bo3$52b2o$51b5o$35b2o
    14bo4bo5b2o$35b2o14b3o2bo5b2o$52bo2b2o$53b2o$37bo3bo$35b2obobob2o9b2o$
    34bob2o3b2obo7bo2b2o$33bo2bo5bo2bo5b3o2bo5b2o$34bob2o3b2obo6bo4bo5b2o$
    35b2obobob2o7b5o$37bo3bo10b2o8$19b2o$18bo2bo$18bobo2bo$19bo$20b2obo$
    22bo3$21b2o$21b2o4b3o$27b3o$26bo3bo$26b2ob2o$26bo3bo$27bobo$27bobo$28b
    o5$23b2ob2o$22bo5bo2$21bo7bo$21bo2bobo2bo$21b3o3b3o9$21b2o5b2o$21b2o5b
    2o23$9bo2bo$12bo7b3o$8bo9bo4bo$8b2o8bo5bo35bo$14bo8bo36b2o$11b2o8b2o
    24b2o9bob3o$47b2o13b2o$11b2o8b2o38b2o$14bo8bo37bo$8b2o8bo5bo10b2o$8bo
    9bo4bo11b2o24bo$12bo7b3o38b2o$9bo2bo34b2o13b2o10b2o$47b2o9bob3o11b2o$
    60b2o$60bo!]], 108, 1317)

    -- slide 27: boat logic --------------------

    all = all + pattern([[
    32b4o$31b2o2bo$30b2o2bo$26bo4bo2bo25b2o$24b3o5b2o25b2ob2o$23bo36bo2bo$
    24b3o5b2o26bo2bo4bo$26bo4bo2bo26b2o5b3o$30b2o2bo36bo$31b2o2bo25b2o5b3o
    $32b4o7bo16bo2bo4bo$41b2o17bo2bo$42b2o15b2ob2o$60b2o3$b2o$b2o2$14b4o$
    2o12bo2b2o$2o13bo2b2o$15bo2bo$16b2o2$16b2o15b2ob2o$15bo2bo12bobo3bo$2o
    13bo2b2o5b2o3bobo3bo$2o12bo2b2o6b2o3bo4bo$14b4o11b2o5b3o$38bo$b2o$b2o!
    ]], 209, 1852)  -- P46Osc plus P46Gun plus custom eater really

    -- boat-logic:  need to add outlying pieces to west, break up into 12
    all = all + pattern([[
    119bo$35b2o81b2ob2o$34b5o11b2o54b2o10b2o2bo3b2o217b2o$34bo4bo11bo53bo
    2bo11bob2o3bo217bo7b3o5b4o$34b3o2bo10bo49bo4bo2b2o9bo2bo3bo219bo6bo7b
    2obo$21bo13bo2b2o11b3o43bo3bo3bo2bo11b2o5b3o213b3o7b2obo7bo10bo$19b3o
    14b2o15bo41b7o5bo12bo8bo213bo11b2o5b2o11b3o$18bo75bo283bo$19b3o14b2o
    57b7o5bo247b2o5b2o11b3o$21bo13bo2b2o57bo3bo3bo2bo246b2o7bo10bo$34b3o2b
    o60bo4bo2b2o251b2obo$34bo4bo65bo2bo252b4o$34b5o4b2obo59b2o11b2obo227bo
    $35b2o6b2ob3o70b2ob3o223b6o$49bo75bo221bo4bo$43b2ob3o70b2ob3o223b3ob2o
    $41bo2bobo70bo2bobo227bobo2bo$41b2o74b2o235b2o11$53bo37b2o$48bo3bo28b
    2o7bobo187bo3bo$47bo7bo25b2o6b2obo186bo5bo$47bo2b2o5bo32b2o14bo107bobo
    2b2o58bo$36bo11b2o5b2obo32bo14b3o90b2o12bo3bob3o4b2o43bo7b2o3bo8bo$34b
    3o12b3o3b2ob3o48bo89b2o13bo6b2o3b2o41b3o9b3o9b3o$33bo27bo29bo14b3o106b
    5obo46bo27bo$34b3o12b3o3b2ob3o29b2o14bo111b3o48b3o9b3o9b3o$36bo11b2o5b
    2obo30b2obo17bo88bobo3bobo63bo7b2o3bo8bo$47bo2b2o5bo32bobo17b3o88bo3bo
    12b3o58bo$47bo7bo35b2o20bo83bo11bo5b5obo57bo5bo$48bo3bo59b2o82bo3bo5bo
    3bo3bo6b2o3b2o52bo3bo$53bo143bo11bo3bo3bob3o4b2o$201bo3bo8bobo2b2o$
    199bobo3bobo12$2b2o5b2o$2b2o5b2o$59bo229bob2o$58b2o228bo2b2o2b3o$57b2o
    229bo6b2o$51bo6b2o2b2o9bo101b2o109b4o3b3o12bo$49b3o21b3o99bo108b3o3bo
    3bo13b3o$48bo27bo99b3o70bo33bo27bo$49b3o21b3o102bo70b3o32b3o3bo3bo13b
    3o$51bo6b2o2b2o9bo178bo33b4o3b3o12bo$57b2o192b2o35bo6b2o$58b2o228bo2b
    2o2b3o$59bo229bob2o$2b3o3b3o$2b3o3b3o$bob2o3b2obo$bob2o3b2obo$2b2o5b2o
    $3bo5bo4$4b2ob2o$3bo5bo$b5ob2ob2o$2ob3ob2ob2o$3o6bo$bobo$2b2o263b4o5b
    6o$212b6o5b4o40bob2o7b4o$212b4o7b2obo40bo7bob2o$76b2o46b2o89b2obo7bo
    41b2o5b2o$75bo2bo44bo2bo90b2o5b2o$76b2o46b2o142b2o5b2o$217b2o5b2o41bo
    7bob2o$215b2obo7bo26b2o12bob2o7b4o$212b4o7b2obo12b2o12b2o12b4o5b6o$
    212b6o5b4o12b2o!]], 679, 1875)  -- P46Osc1-4 boat logic

    -- mystery stuff along bottom edge that needs a home in a slide:

    all = all + pattern([[
    b2o5b2o$b2o5b2o14$3o5b3o$3o5b3o$b2o5b2o$3bo3bo$bo2bobo2bo$o3bobo3bo$bo
    2bobo2bo$b3o3b3o5$8b2o$8b2o!]], 514, 1887)  -- P46OscAlt3 really

    all = all + pattern([[
    4bo7b2o$3b2o6bo2bo$2bo8bo2b2o$3b2obo4bo2bo10bo$4b3o6bo11b3o$28bo$4b3o
    6bo11b3o$bob2obo4bo2bo10bo$o10bo2b2o$o3bo6bo2bo$b4o7b2o!
    ]], 791, 1929)  -- P46Osc really

    all = all + pattern([[
    8bo$9bo3bo$2o2b2o8bo$2o2bo5b2o2bo$4bobo5b2o11bo$5b2o3b3o12b3o$28bo$5b
    2o3b3o12b3o$4bobo5b2o11bo$4bo5b2o2bo$4b2o8bo$9bo3bo$8bo!
    ]], 845, 1905)  -- P46Osc really

    all = all + pattern([[
    10b2o$2o6b2ob2o14b2o$2o6bo2bo15b2o$8bo2bo$9b2o2$9b2o10b3ob3o$8bo2bo8bo
    b2ob2obo$2o6bo2bo7b2o7b2o$2o6b2ob2o7bob2ob2obo$10b2o9b3ob3o!
    ]], 1050, 1903)  -- P46OscAlt2 really

    all = all + pattern([[
    9b2o$9b2o11$2b3o3b3o$bo3bobo3bo$o3b2ob2o3bo$ob2o5b2obo$2bo7bo11$2b2o5b
    2o$2b2o5b2o!]], 1088, 1920)  -- P46OscAlt2 really

    all = all + pattern([[
    11bo$10b4o9bo$2b2o4b2o3bo9bo2b2o$2bo5b2o12bo5bo$3bo4b3o12bobo2b2o$3o
    20bo3b2o3bo$o24b3o4b3o$35bo$25b3o4b3o$23bo3b2o3bo$23bobo2b2o$22bo5bo$
    7bob2o12bo2b2o$5b3ob2o12bo$4bo$5b3ob2o$7bobo2bo$11b2o!
    ]], 1106, 1875)  -- P46OscAlt4 (boat-bit catcher?) really [not defined yet]

    all = all + pattern([[
    25bobo$26bo$17b2o13b2o$16b2ob2o12bo$17bo2bo11bo$3bo13bo2bo4b2o6b3o$b3o
    14b2o15bo$o$b3o14b2o$3bo13bo2bo$17bo2bo$16b2ob2o$17b2o6b2obo$25b2ob3o$
    31bo$25b2ob3o$23bo2bobo$23b2o!]], 1227, 1875)  -- P46OscAlt4 really

    all = all + pattern("2o$obo$2bo$2b2o!", 1281, 1873)  -- eater

    all = all + pattern([[
    4b2o5b2o$2o2b2o5b2o2b2o$2o13b2o4$4b3o3b3o$4bo2bobo2bo$3bo3bobo3bo$3b4o
    3b4o$4bo7bo7$5bo$4b3o$3bo3bo$3b2ob2o$3b2ob2o2$3b2ob2o$3b2ob2o$3bo3bo3b
    2o$4b3o4b2o$5bo!]], 1375, 1980)  -- P46OscAlt1 really

    -- slide 28: clean up and start over --------------------
    LWSSToGlider = pattern([[
    4b2o5b2o$2obobo5bobob2o$2ob2o7b2ob2o$2b6ob6o$4bob2ob2obo2$2bo11bo$3bo
    9bo$5bobobobo$5bobobobo$6b2ob2o$3bo2bo3bo2bo$4b2o5b2o13$4b2o$4b2o!]], 443, 1980)

    -- slide 29: toggle dist [not sure what that means, actually] --------------------
    BoatLatchNE = pattern([[
    78b2o5b2o$78b2o5b2o15$77b2o7b2o$77bob2o3b2obo$77bo3bobo3bo$46b2o5b2o
    22b2o2bobo2b2o$46b2o5b2o23b3o3b3o$79bo5bo4$47bo5bo$46b3o3b3o$45bo2b2ob
    2o2bo22b2o$45bo3bobo3bo22b2o$47bobobobo2$44b2ob2o3b2ob2o$46bo7bo5$47bo
    $46b3o$45bo3bo$44bo5bo$44bo5bo$45bo3bo2$45bo3bo$44bo5bo$44bo5bo2b2o$
    45bo3bo3b2o$46b3o$47bo7$45b2o$45bo$46b3o$48bo20$13b2o$15bo$2o10b2o2bo
    10b2o$2o11bo2bo10b2o$14bobo$14b2o2$14b2o$14bobo$2o11bo2bo$2o10b2o2bo$
    15bo$13b2o12$47b4o$46b2o2bo14b2o$45b2o2bo15b2o$46bo2bo$47b2o2$47b2o$
    46bo2bo$38b2o5b2o2bo15b2o$38b2o6b2o2bo14b2o$47b4o!]], 120, 232)  -- four P46osc really

    local BoatLatchSW = pattern([[
    76bo$75bo2bo$74b5o10b2o$73b2ob3o10b2o$74bob2o$75b2o72b2o5b2o$145b2o3bo
    5bo3b2o$75b2o68bo15bo$74bob2o68bo13bo$62b2o9b2ob3o10b2o$62b2o10b5o10b
    2o$75bo2bo$76bo2$149b2o5b2o$149b2obobob2o$73b2o74bo2bobo2bo$73b2o74b3o
    3b3o2$82b2o$72b2o7bo2b2o11b2o$57b2o5b2o6b2o6b6o11b2o$53b2o2b2o5b2o2b2o
    12b4o77b2o89bo$53b2o13b2o93b2o84bo3bo$173bo60b2o12bo8b2o2b2o$173b2o59b
    2o12bo2b2o5bo2b2o$6bo75b4o76b2o7bob3o11b2o60b2o5bobo$5bobo64b2o6b6o76b
    2o11b2o10b2o61b3o3b2o$5bobo64b2o7bo2b2o88b2o$4b2ob2o73b2o90bo75b3o3b2o
    $156b2o64b2o25b2o5bobo$57b3o3b3o7b2o81b2o16bo48bo24bo2b2o5bo2b2o$56bo
    3bobo3bo6b2o99b2o47bobo22bo8b2o2b2o$55bo3b2ob2o3bo94b2o11b2o47b2o23bo
    3bo$55bob2o5b2obo94b2o7bob3o78bo$57bo7bo107b2o$173bo$163b2o$163b2o3$
    57b3o$b3o5b3o45bobo$2ob2o3b2ob2o43bo3bo$2obobobobob2o43bo3bo$bo3bobo3b
    o$bo2bo3bo2bo45b3o4b2o$2b3o3b3o53b2o6$2b2o$2b2o37$27b2o90b2o$27b2o81b
    2o7b2o79b2o$110b2o88b2o$47b3o53b3o25bobo2b2o$26b2o6b3o8b2obob2o50bo3bo
    11b2o10bo3bob3o$26b2o6bo3bo5bo5b2o50b2ob2o11b2o11bo6b2o54bo5bo$34bo4bo
    5b2obob2o80b5obo54b3o3b3o$35bo3bo7b3o52b2ob2o28b3o55bob2ob2obo$104bo
    87b2o7b2o$35bo3bo95b3o54b2o7b2o$34bo4bo92b5obo53b3o5b3o$26b2o6bo3bo12b
    2o65b2o11bo6b2o3b2o49b3ob3o$26b2o6b3o14b2o50bo7bo6b2o10bo3bob3o4b2o51b
    obo$102bo2bo3bo2bo18bobo2b2o55bo2bobo2bo$106bobo83bo2bo3bo2bo$27b2o77b
    obo10b2o72b2o5b2o$27b2o77bobo10b2o$102bo2bo3bo2bo$103b3o3b3o7$99b2o13b
    2o73b2o13b2o$99b2o2b2o5b2o2b2o73b2o2b2o5b2o2b2o$103b2o5b2o81b2o5b2o!
    ]], 1534, 1849)  -- really eleven P45OscAlt1 plus an eater

    all = all + BoatLatchNE + BoatLatchSW + pattern([[
    273b2o$272bo2bo$140b2o130bobobo10b2o2bobo$140b2o131bo2bo3b2o4b3obo3bo
    12b2o$277bo2b2o3b2o6bo13b2o$274bobo9bob5o$287b3o2$287b3o$286bob5o$285b
    2o6bo13b2o$286b3obo3bo12b2o$287b2o2bobo3$175b2o$140b3o3b3o22bobo2bo$
    140bo2bobo2bo20b3ob2o79b2o$140b2obobob2o19bo84bo2bo$140b2o5b2o20b4o80b
    o7bo$171bob2o13bo64bo2bo3bobo$188b2o63b2ob2ob2ob2o$189b2o66bobo$184b2o
    2b2o6bo42b2o15b2ob2o$142b2ob2o28bobo18b3o40b2o$140bo2bobo2bo26bobo21bo
    3bo$140bobo3bobo15bo10b2o19b3o4b3o5b2o11b4o$140b3o3b3o15b3o9bo7b2o2b2o
    6bo9bo4bo3b2o6b2o2bo12b2o$140b2o5b2o18bo5b2obobo10b2o14bo3bobo3b2o5b2o
    2bo13b2o12b3o3b3o$140b2o5b2o17bo11bo9b2o14bo3bobo12bo2bo26bo9bo$140b2o
    5b2o17b2o6bo2bo10bo15b2ob2o15b2o27bo3bobo3bo$176b2o76b3o3b3o$224b2o29b
    o5bo$223bo2bo$222b2o2bo13b2o$223b2o2bo12b2o$224b4o2$11b2o226b2o$10bobo
    226b2o$10bo30b2o$9b2o30b2o226b2o5b2o$33b2o4bo13bo3b3o55b2o63b2o74b2ob
    2o4b2obobo5bobob2o$33bo3b3o12bobo2b5o53bo7b3o5b4o35b2o6b2ob2o15b2o57bo
    bo5b2ob2o7b2ob2o$34bo3bobo11bo3b2o58bo6bo7b2obo35b2o6bo2bo17b2o34b2o5b
    2o13bobo7b6ob6o$31b3o5b2o12bo2bo3b2obo49b3o7b2obo7bo10bo32bo2bo16bo7b
    4o21b2o2b2o5b2o2b2o10bo10bob2ob2obo$31bo22b3o3b2ob3o47bo11b2o5b2o11b3o
    31b2o25bo2b2o20b2o13b2o$66bo81bo58bo2b2o55bo11bo$54b3o3b2ob3o59b2o5b2o
    11b3o31b2o26bo2bo4bo52bo9bo$53bo2bo3b2obo61b2o7bo10bo32bo2bo26b2o5b3o
    52bobobobo$52bo3b2o73b2obo35b2o6bo2bo36bo51bobobobo$52bobo2b5o69b4o35b
    2o6b2ob2o25b2o5b3o53b2ob2o$38bob2o11bo3b3o60bo59b2o25bo2bo4bo52bo2bo3b
    o2bo$36b3ob2o76b6o83bo2b2o23bo7bo25b2o5b2o$35bo81bo4bo83bo2b2o$36b3ob
    2o76b3ob2o82b4o23b3o7b3o$38bobo2bo76bobo2bo108b2ob2ob2ob2o$42b2o80b2o
    109b3o3b3o$236bo5bo2$114bo$10b2o9b3ob3o82b2o2bo$8b2ob2o7bob2ob2obo80bo
    5bo$8bo2bo7b2o7b2o78b2o2bobo$3bo4bo2bo8bob2ob2obo80b2o3bo12bo$b3o5b2o
    10b3ob3o82b3o14b3o$o129bo138b2o$b3o5b2o99b3o14b3o107b2ob2o27b2o$3bo4bo
    2bo97b2o3bo12bo110bobo$8bo2bo15b2o73b2o4b2o2bobo123bobo$8b2ob2o14b2o
    73b2o5bo5bo123bo$10b2o98b2o2bo$114bo13$170b2o2b2o4bo2b2o12b2o$170b2ob
    2o6b2obo12b2o$174bobo6bo$175b2o4b3o2$175b2o4b3o$174bobo6bo$170b2ob2o6b
    2obo$170b2o2b2o4bo2b2o!]], 178, 1939)  -- really P46Guns and Oscs, etc.

    all = all + pattern([[
    4b2o5b2o$2o2b2o5b2o2b2o$2o13b2o8$5bo5bo$4b3o3b3o$3b2ob2ob2ob2o$2b3o7b
    3o2$4bo7bo5$5bo$4b3o$3bo2bo$3bobobo$4b3o$5bo5b2o$11b2o!
    ]], 557, 1931)  -- P46OscAlt1 really
    -- this particular one reflects a B signal from the above sync-buffer circuitry;
    -- the resulting LWSS is converted to a glider which is stored as a boat-bit.

    -- a few more outlying LWSS reflectors:
    all = all + pattern([[
    12b3o$10bo4bo11b2o$10bo5bo10b2o$3bobobo7bo$b7o5b2o$o$b7o5b2o$3bobobo7b
    o$10bo5bo$10bo4bo$12b3o!]], 257,1840) + pattern([[
    15b2o$13b2o2bo$13b6o$13b4o4bo3bo$21b7o$28bo$21b7o$13b4o4bo3bo$2o11b6o$
    2o11b2o2bo$15b2o!]], 885, 2033)  -- both P46Osc really

    -- slide 30: HWSS control --------------------

    local HWSSGun = pattern([[
    21b2o23b2o$22bo22bobo$22bobo$23b2o$36b4o8bo$36bob2o7b2o$36bo$37b2o2$
    37b2o$36bo$7b2o2bobo22bob2o7b2o$2o4b3obo3bo21b4o8bo$2o3b2o6bo$6bob5o$
    7b3o35bobo$46b2o$7b3o$6bob5o14b2o$2o3b2o6bo8bo5bo$2o4b3obo3bo7bo4bo4b
    2o$7b2o2bobo12b3o3b2o$23bob2ob2o$25bo$22bo2b3o11b2o$2b2o18b2o2b2o9bo4b
    o$2b2o20bo2bo15bo$24b3o10bo5bo$16b2o20b6o$b2o13bobo$b2o13bob2o$17b2o$
    17bo13b2o$30bobo$17bo13bo67b2o$17b2o80b2o$b2o13bob2o29b2o2bo4b2o16b2o
    3b2o8bo$b2o13bobo30bob2o6b2o14bo2bobo2bo7bo2b2o$16b2o20bo11bo6bobo14bo
    9bo5bo5bo3b2o$36b3o11b3o4b2o14b2o9b2o5bobo2b2o2b2o$2b2o31bo38bo9bo6bo
    3b2o$2b2o32b3o11b3o4b2o16bo2bobo2bo9b3o$38bo11bo6bobo16b2o3b2o$24bo24b
    ob2o6b2o32b3o$23b3o23b2o2bo4b2o31bo3b2o$23bob2o35bo12b2o14bobo2b2o2b2o
    $24b3o11b2o22b2o11b2o13bo5bo3b2o$24b3o11bobo20bobo27bo2b2o$20b3ob3o13b
    o50bo$24b3o11b3o5bo52b2o$24b2o20b3o50b2o$2b2o45bo$2b2o19bo22b3o$11bo
    10bo15b3o5bo$7b2o2bo10bo17bo$b2o3bo5bo25bobo$b2o2b2o2bobo12bo13b2o$6b
    2o3bo38b2o$7b3o41b2o$50bo$7b3o$6b2o3bo$b2o2b2o2bobo$b2o3bo5bo48b2o$7b
    2o2bo13b2o34b2o$11bo13bobo$2b2o23bo16b3o3bo$2b2o23b2o13b5o2bobo10b2o$
    41b2o3b2o3bo10b2o$42b2o3bo2bo$43bo3b3o2$43bo3b3o$42b2o3bo2bo$41b2o3b2o
    3bo10b2o$42b5o2bobo10b2o$42bob3o3bo$40b2o$34b2o4b3o18b2o$34b2o3bo3bo
    17b2o$38bo5bo$39b2ob2o2$39b2ob2o$38bo5bo$39bo3bo$40b3o$40b3o7$33b2o7b
    2o$33bob2o3b2obo$33bo3bobo3bo$33b2o2bobo2b2o$34b3o3b3o$35bo5bo6$34b2o
    5b2o$34b2o5b2o3$40bo2bo$30b3o7bo$16b2o11bo4bo9bo$16b2o10bo5bo8b2o$29bo
    8bo$30b2o8b2o2$30b2o8b2o$29bo8bo3bo$16b2o10bo5bo4bo2bo$16b2o11bo4bo4bo
    2bo$30b3o7b2o4$79b2o$78bo2bo$73bo7bo$72bobo3bo2bo$22bo48b2ob2ob2ob2o$
    20b3o52bobo$19bo54b2ob2o$19b2o$33bo$31b3o$14b2o14bo$15bo14b2o40b3o3b3o
    $15b2obo17b2o33bo9bo$18bo17bo34bo3bobo3bo$19b2o13bobo35b3o3b3o$15b3o2b
    2o12b2o37bo5bo$17b2o$15bo2bo4bo$14bobobo3bo$13bo4bo$5b2o6bo2bob3o5bo$
    4bobo7bo3b3ob2o3bo$4bo10b2o5bo4bo$3b2o10b2o3b2o2bobo$15bo4bo$20b3o51b
    2ob2o$16b2o57bobo$75bobo$12b2o2b3o57bo$13bobobo$14b4o$9b2o4bo$8bobo$8b
    o$7b2o7bobo$17b2o$17bo3$7b2o$8bo$8bobo$9b2o2$13bob2o$13bob2o2$22bo$12b
    2o4bob2obo$12b2o2bo5bo$3b2o11bo5bo$4bo7b2o6b2o4b2o$4bobo5b2o4bobo5b2o$
    5b2o11b2o5$34b2o$34bobo$36bo$36b2o$30b2o$30bo$31b3o$33bo$19b2o$19bo$
    20b3o$22bo$34b2o7b2o$32b2o2bo6b2o$32b6o4bo2bo$22bo9b4o5bob2o$20b3o18bo
    b2o$19bo$20b3o18bob2o$22bo9b4o5bob2o14b2o$32b6o4b3ob2o11bo$32b2o2bo6bo
    bobo12b3o$34b2o7b4o15bo$44b2o!]])  -- TODO: take this apart further

    local LWSSFromTheBlue = pattern([[
    27b2o5b2o$23b2o2b2o5b2o2b2o$23b2o13b2o2$28bo5bo$27b3o3b3o$26bo2b2ob2o
    2bo$26bo3bobo3bo$28bobobobo2$25b2ob2o3b2ob2o$27bo7bo8$10b2o$8b2ob2o$8b
    o2bo$3bo4bo2bo$b3o5b2o15bo$o21b5o$b3o5b2o10bo2b3o7b2o$3bo4bo2bo10b2o
    10b2o$8bo2bo$8b2ob2o$10b2o$25bo$25bo!]])  -- P46Osc really

    local RowOfLWSSFTB = pattern("o$3o$3bo$2b2o!", 1737, 24)
    for i=0,42 do RowOfLWSSFTB =RowOfLWSSFTB + LWSSFromTheBlue[(i*12) % 46].t(40*i, 0) end

    local HWSSHalfControl = HWSSGun.t(106, 87) + RowOfLWSSFTB.t(188, 79)

    local CellCenter = HWSSHalfControl + HWSSHalfControl.t(2047, 2047, gp.swap_xy_flip)

    OFFcell = all + CellCenter + StateBit

    -- The metafier-OFF and -ON files technically should not exist (we just checked
    --  at the beginning of the script).  Shouldn't do any harm to check again, though:
    if not fOFF then OFFcell.save(OFFcellFileName) end
    
    -- slide 31: display on --------------------
    g.show("Building ON metacell definition...")

    -- switch on the HWSS guns:
    CellCenter = CellCenter + pattern("2o$2o!", 171, 124) + pattern("2o$2o!", 1922, 1875)

    -- now generate the ONcell HWSSs and LWSSs
    -- add rest of pattern after the center area is filled in
    -- and p184 HWSS guns are in the same phase (3680 = 184 * 40)
    ONcell = all + CellCenter[3680]

    if not fON then ONcell.save(ONcellFileName) end
    -- g.store(ONcell,ONcellFileName)  -- doesn't work; would need to be ONcell.array
end

OFFcell = OFFcell + RuleBits
ONcell = ONcell + RuleBits

for j=0,selheight-1 do
    for i=0,selwidth-1 do
        g.show("Placing ("..(i+1)..","..(j+1)..") tile"..
                   " in a "..selwidth.." by "..selheight.." rectangle.")
        if livecell[i..","..j]~=nil then
            ONcell.put(2048 * i - 5, 2048 * j - 5)
        else
            OFFcell.put(2048 * i - 5, 2048 * j - 5)
        end
        g.fit()
        g.setmag( g.getmag() - 3 )
        g.update()
    end
g.fit()
end

g.show("")
g.setalgo("HashLife")                -- no point running a metapattern without hashing
g.setoption("hyperspeed", 0)     -- avoid going too fast
g.setbase(35328)
g.setstep(1)
g.step()                             -- save start and populate hash tables

-- g.run(35328)  -- run one full cycle (can lock up Golly if construction has failed)
--
-- Note that the first cycle is abnormal, since it does not advance the metapattern by
-- one metageneration:  the first set of communication signals to adjacent cells is
-- generated during this first cycle.  Thus at the end of 35328 ticks, the pattern
-- is ready to start its first "normal" metageneration (where cell states may change).
--
-- It should be possible to define a version of ONcell that is not fully populated
-- with LWSSs and HWSSs until the end of the first full cycle.  This would be much
-- quicker to generate from a script definition, and so it wouldn't be necessary to
-- save it to a file.  The only disadvantage is that ONcells would be visually
-- indistinguishable from OFFcells until after the initial construction phase.



The difference is that it changes the base and step to 35328 and 1, and it shrinks the scale 8 times. I noticed that Golly is simulating those metapixels slower on a large scale. It was also slower while building a metapattern.
EDIT Fixed a bug that when reseting the pattern it changes the scale back to 8 times smaller than the scale it would have if it was fit.
I was so socially awkward in the past and it will haunt me for the rest of my life.

Code: Select all

b4o25bo$o29bo$b3o3b3o2bob2o2bob2o2bo3bobo$4bobo3bob2o2bob2o2bobo3bobo$
4bobo3bobo5bo5bo3bobo$o3bobo3bobo5bo6b4o$b3o3b3o2bo5bo9bobo$24b4o!

User avatar
gameoflifemaniac
Posts: 1242
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: Golly scripts

Post by gameoflifemaniac » October 11th, 2017, 9:39 am

This is a question about Golly itself.
What programming language (not counting the Lua and Python scripts) is Golly written in?
I was so socially awkward in the past and it will haunt me for the rest of my life.

Code: Select all

b4o25bo$o29bo$b3o3b3o2bob2o2bob2o2bo3bobo$4bobo3bob2o2bob2o2bobo3bobo$
4bobo3bobo5bo5bo3bobo$o3bobo3bobo5bo6b4o$b3o3b3o2bo5bo9bobo$24b4o!

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

Re: Golly scripts

Post by dvgrn » October 11th, 2017, 9:49 am

gameoflifemaniac wrote:This is a question about Golly itself.
What programming language (not counting the Lua and Python scripts) is Golly written in?
C++.

drc
Posts: 1664
Joined: December 3rd, 2015, 4:11 pm

Re: Golly scripts

Post by drc » October 23rd, 2017, 9:25 pm

Don't know where else to put these, but glider_rider shared these privately with me and allowed me to show them publicly. They find oscillators in certain symmetries:

Script #1:

Code: Select all

import golly as g
import math

rule=g.getstring("Rule:","B3/S23")
algo=g.getstring("Algorithm:","QuickLife")

max_period=20

exclude_periods=[1]
if rule=="GlideLife":
    exclude_periods=[1,2,4]
if rule=="olife":
    exclude_periods=[1,2,3,4,5,12,10,35,6]
if rule=="B3/S23":
    exclude_periods=[1,2,3,15,8,5,4,6,30]
if rule=="tlife":
    exclude_periods=[1,2,5,4,3,6,8,33,12]
if rule=="salad":
    exclude_periods=[1,2,4,5,3,10,29,6,7]
if rule=="B2i3-r4cS23":
    exclude_periods=[1,2,5,8,16,10,14,13,3,4]
if rule=="B2inkce_S12":
    exclude_periods=[1,6,4,3,2,14,5,10,12]
if rule=="B2inkce3_S1":
    exclude_periods=[1,8,4,6]
if rule=="B3678/S235678":
    exclude_periods=[1,2,3,4,5,6,14,12,8,10,17,24,7,34,9,15,11]
if rule=="B013578/S02":
    exclude_periods=[1,2,3,4,5,6,7,8,9,10,11,12,15,16,20,21,22,23,24,29,30,32,33,34,47,48]
if rule=="MoveIt":
    exclude_periods=[1,2,24,8,12,4,3,16,48,6,32,5,14,11,10]
if rule=="B34tw5y_S23":
    exclude_periods=[1,2,4,7,3,10]
if rule=="B35678/S357":
    exclude_periods=[1,2,6,3,4,8,5,35,14,12,13]
if rule=="B2-aS12":
    exclude_periods=[1,3,4,6,26,14,2,12,7,5,10,42,19,11,16,28,9,78,24,8]
if rule=="B1e4ay5ai6i7eS0123eijr4atwri5ayn6cai7":
    exclude_periods=[1]
if rule=="B2in3S2-in37":
    exclude_periods=[1,2,5,3,4,8,10,6,12]
if rule=="B2in3S02-in3-n":
    exclude_periods=[1,4,2,8,7,6,5,10,3,9,16,12]
if rule=="B37/S2-i34q":
    exclude_periods=[1,2,5,4,3]
if rule=="B3/S235e":
    exclude_periods=[1,2,5,3,15,8]
if rule=="PuffLife":
    exclude_periods=[1,4,2,3,6,8]
if rule=="B3-k/S2-i3-k4cen":
    exclude_periods=[1,2,5,4,3]
if rule=="B34ek5ak/S2-c34iz5y":
    exclude_periods=[1,2,4,6]
if rule=="b2ce3aiys12aei3r":
    exclude_periods=[1,2,6,8,4,14,10]
if rule=="B3-cnry4-acery5i/S23-a4-jknqr5y8":
    exclude_periods=[1,2,4]
if rule=="B3/S23-a4eiktz":
    exclude_periods=[1,2,4,5,3,6,9,7]
if rule=="B34e5e/S2-in3-y5jk":
    exclude_periods=[1,2,4,13,3,10,6]
if rule=="B2e3-r5i8/S23-a4kz7c":
    exclude_periods=[1,2,7,4,9,14,5,6,3]


def osc_test():
    if g.empty():
        return [False]
    g.setstep(0)
    cells=g.getcells(g.getrect())
    hash=g.hash(g.getrect())
    for i in range(1,max_period):
        g.step()
        if g.empty():
            return [False]
        if g.hash(g.getrect())==hash:
            if g.getcells(g.getrect())==cells:
                if i not in exclude_periods:
                    return [True,i]
                else:
                    return [False]
    return [False]


def put_symm(cell_list,x0=0,y0=0,axx=1,axy=0,ayx=0,ayy=1,mode="or"):

    g.putcells(cell_list,x0,y0,axx,axy,ayx,ayy,mode)
    if symm=="C2_1" or symm=="C4_1" or symm=="D4_+1" or symm=="D8_1" or symm=="D4_x1":
        g.putcells(cell_list,-x0,-y0,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="C4_1" or symm=="D8_1":
        g.putcells(cell_list,y0,-x0,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0,x0,-ayx,-ayy,axx,axy,mode)
    
    if symm=="C2_2" or symm=="D4_+2":
        g.putcells(cell_list,-x0-1,-y0,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="C2_4" or symm=="C4_4" or symm=="D4_+4" or symm=="D8_4" or symm=="D4_x4":
        g.putcells(cell_list,-x0-1,-y0-1,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="D2_+1" or symm=="D8_1" or symm=="D4_+1":
        g.putcells(cell_list,-x0,y0,-axx,-axy,ayx,ayy,mode)
    
    if symm=="D4_+1" or symm=="D8_1" or symm=="D4_+2":
        g.putcells(cell_list,x0,-y0,axx,axy,-ayx,-ayy,mode)
    
    if symm=="D2_+2" or symm=="D4_+2" or symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,-x0-1,y0,-axx,-axy,ayx,ayy,mode)
    
    if symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,x0,-y0-1,axx,axy,-ayx,-ayy,mode)
    
    if symm=="C4_4" or symm=="D8_4":
        g.putcells(cell_list,y0,-x0-1,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0-1,x0,-ayx,-ayy,axx,axy,mode)
    
    if symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)
    
    if symm=="D2_x" or symm=="D8_1" or symm=="D8_4" or symm=="D4_x1" or symm=="D4_x4":
        g.putcells(cell_list,y0,x0,ayx,ayy,axx,axy,mode)
    
    if symm=="D4_x1" or symm=="D8_1":
        g.putcells(cell_list,-y0,-x0,-ayx,-ayy,-axx,-axy,mode)
    
    if symm=="D4_x4" or symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)

x=int(g.getstring("Size:","4"))

start=int(g.getstring("Start:"))

def next(max_x):
    w=0
    h=0
    
    while g.getcell(w,h)==1:
        g.setcell(w,h,0)
        w+=1
        if w==max_x:
            w=0
            h+=1
            if h==max_x:
                return True
    g.setcell(w,h,1)
    return False


def main():
    g.new("BrutOsc")
    
    global symm
    
    count=start
    if start>2**(x**2):
        g.show("Start > "+str(2**(x**2)))
        return False
    for i in range(start):
        next(x)
    cell_list=g.getcells([0,0,x,x])
    
    g.setrule(rule)
    g.setalgo(algo)
    while True:
        for i in range(16):
            g.new("BrutOsc")
            g.setbase(2)
            g.setstep(6)
        
            symm=["C1", "C2_1", "C2_2", "C2_4", "D2_x", "D2_+1", "D2_+2", "C4_1", "C4_4", "D4_x1", "D4_x4", "D4_+1", "D4_+2", "D4_+4", "D8_1", "D8_4"][i]
            put_symm(cell_list)

            g.step()
            
            v=osc_test()
            if v[0]:
                g.show(str(count)+"*16,   Period "+str(v[1]))
                return True


        count+=1

        if count>2**(x**2):
            g.show(str(2**(x**2))+"*16 pats tested, no solutions.")
            return False

        if count%1==0:
            g.show(str(count)+"*16")

        g.new("BrutOsc")
        g.putcells(cell_list)
        next(x)

        cell_list=g.getcells([0,0,x,x])

main()
Script #2:

Code: Select all

import golly as g
import math
import random

rule=g.getstring("Rule:","B3/S23")
algo=g.getstring("Algorithm:","QuickLife")

s=g.getstring("Symmetry","All")


symm=0
if s!="All":
    symm=s



#symm can be either C1, C2_1, C2_2, C2_4, D2_x, D2_+1, D2_+2, C4_1, C4_4, D4_x1, D4_x4, D4_+1, D4_+2, D4_+4, D8_1, D8_4, All, Rot

max_period=300

exclude_periods=[1]
if rule=="GlideLife":
    exclude_periods=[1,2,4,6,12,16]
if rule=="olife":
    exclude_periods=[1,2,3,4,5,6,9,10,12,15,18,20,26,30,35]
if rule=="B3/S23":
    exclude_periods=[1,2,3,6,8,4,5,10,15,30,14]
if rule=="tlife":
    exclude_periods=[1,2]
if rule=="salad":
    exclude_periods=[1,2,4]
if rule=="B2inkce_S12":
    exclude_periods=[1,2,4]
if rule=="B3678/S235678":
    exclude_periods=[1,2,3,4,6,12,8]
if rule=="MoveIt":
    exclude_periods=[1,2,3,4,6,8,12,16,24,32,48]
if rule=="B35678/S357":
    exclude_periods=[1,2,6,4,3,8,12,35,5,14,24,13,10,15]
if rule=="B2-aS12":
    exclude_periods=[1,14,6,3,2,4,7,26,42,9,28,12,78,16,48,236,24,84,182,13,156,5,130,21,10,208,234,15,11,70,8,19]
if rule=="B2i35r_S023-a4i":
    exclude_periods=[1,2,4]
if rule=="B2in3S02-in3-n":
    exclude_periods=[1,4,5,8,7,6,20,12,28,2,9,16,36,10,42,72,14,56,3,40,63,30,140,45,35]
if rule=="B37/S2-i34q":
    exclude_periods=[1,2,5,4,3,20]
if rule=="B3/S235e":
    exclude_periods=[1,2,15,3,5,10,8,30,4,6,14,40]
if rule=="randomnn":
    exclude_periods=[20,4,14,7,28,140,84]
if rule=="Rotator":
    exclude_periods=[12,40,120,4,60,10,20,24,8]
if rule=="B2ein3/S13":
    exclude_periods=[1,2,4,6,5,10]
if rule=="PuffLife":
    exclude_periods=[1,2,4,8,3,6,16,12]
if rule=="B3-k/S2-i3-k4cen":
    exclude_periods=[1,2,5,4,3]
if rule=="B34ek5ak/S2-c34iz5y":
    exclude_periods=[1,2,4,6]
if rule=="B35/S2":
    exclude_periods=[2,4,1,3]
if rule=="B3568/S256":
    exclude_periods=[1,2,3,4,6,12,5,10,15,20,14,42]
if rule=="B356/S234i":
    exclude_periods=[1,2,4,6,12]
if rule=="b2ce3aiys12aei3r":
    exclude_periods=[1,2,4,6,7,8,10,12,14,15,24,26,28,29,30,58,42,62,94,126,138,170,186,202,234,266]
if rule=="B3-cnry4-acery5i/S23-a4-jknqr5y8":
    exclude_periods=[1,2,36,92,4,28,12,18,8]
if rule=="B3/S23-a4eiktz":
    exclude_periods=[1,2,4,5,10,78,7,14,9,36,3,6]
if rule=="B34e5e/S2-in3-y5jk":
    exclude_periods=[1,2,4,10,13,26,6,3]
if rule=="B2e3-r5i8/S23-a4kz7c":
    exclude_periods=[1,2,7,14,4]
if rule=="B3/S23-e4k":
    exclude_periods=[1,2,4,5,6,10,98,294,14,22,12]
if rule=="B34aq5c/S135":
    exclude_periods=[1,2,4,3,6,13,26,8,12,52,39]
if rule=="B2-a3/S1c23-ainr4cekn":
    exclude_periods=[1,2,4,12,31,6,62,8,5,10,124,28,20]
if rule=="B2-a3-in/S23":
    exclude_periods=[1,2,6,4,44,12,3,16,9,18,132,5,8,36,220,20,30,22,60]
if rule=="B2-a3-in/S235c":
    exclude_periods=[1,2,4,3,6,44,12,10,20,132,16,60,5,58,8]

def osc_test():
    if g.empty():
        return False
    g.setstep(0)
    cells=g.getcells(g.getrect())
    hash=g.hash(g.getrect())
    for i in range(1,max_period):
        g.step()
        if g.empty():
            return False
        if g.hash(g.getrect())==hash:
            if g.getcells(g.getrect())==cells:
                if i not in exclude_periods:
                    return True
                else:
                    return False
    return False


def put_symm(cell_list,x0=0,y0=0,axx=1,axy=0,ayx=0,ayy=1,mode="or"):
    global symm
    
    if s=="All":
        symm=["C1", "C2_1", "C2_2", "C2_4", "D2_x", "D2_+1", "D2_+2", "C4_1", "C4_4", "D4_x1", "D4_x4", "D4_+1", "D4_+2", "D4_+4", "D8_1", "D8_4"][random.randrange(16)]

    if s=="Rot":
        symm=["C1", "C2_1", "C2_2", "C2_4", "C4_1", "C4_4"][random.randrange(6)]
    
    g.putcells(cell_list,x0,y0,axx,axy,ayx,ayy,mode)
    if symm=="C2_1" or symm=="C4_1" or symm=="D4_+1" or symm=="D8_1" or symm=="D4_x1":
        g.putcells(cell_list,-x0,-y0,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="C4_1" or symm=="D8_1":
        g.putcells(cell_list,y0,-x0,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0,x0,-ayx,-ayy,axx,axy,mode)
    
    if symm=="C2_2" or symm=="D4_+2":
        g.putcells(cell_list,-x0-1,-y0,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="C2_4" or symm=="C4_4" or symm=="D4_+4" or symm=="D8_4" or symm=="D4_x4":
        g.putcells(cell_list,-x0-1,-y0-1,-axx,-axy,-ayx,-ayy,mode)
    
    if symm=="D2_+1" or symm=="D8_1" or symm=="D4_+1":
        g.putcells(cell_list,-x0,y0,-axx,-axy,ayx,ayy,mode)
    
    if symm=="D4_+1" or symm=="D8_1" or symm=="D4_+2":
        g.putcells(cell_list,x0,-y0,axx,axy,-ayx,-ayy,mode)
    
    if symm=="D2_+2" or symm=="D4_+2" or symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,-x0-1,y0,-axx,-axy,ayx,ayy,mode)
    
    if symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,x0,-y0-1,axx,axy,-ayx,-ayy,mode)
    
    if symm=="C4_4" or symm=="D8_4":
        g.putcells(cell_list,y0,-x0-1,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0-1,x0,-ayx,-ayy,axx,axy,mode)
    
    if symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)
    
    if symm=="D2_x" or symm=="D8_1" or symm=="D8_4" or symm=="D4_x1" or symm=="D4_x4":
        g.putcells(cell_list,y0,x0,ayx,ayy,axx,axy,mode)
    
    if symm=="D4_x1" or symm=="D8_1":
        g.putcells(cell_list,-y0,-x0,-ayx,-ayy,-axx,-axy,mode)
    
    if symm=="D4_x4" or symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)


x=10

main_fill=50

def main():
    count=0
    
    g.setrule(rule)
    g.setalgo(algo)
    while True:
        g.new("RandOsc")
        g.setstep(2)
        
        g.select([0,0,x,x])
        g.randfill(main_fill)
        cell_list=g.getcells([0,0,x,x])
        g.clear(0)
        put_symm(cell_list)
        
        g.step()
        
        if osc_test():
            return True
        
        count+=1
        if count%100==0:
            g.show(str(count))

main()

User avatar
Saka
Posts: 3627
Joined: June 19th, 2015, 8:50 pm
Location: Indonesia
Contact:

Re: Golly scripts

Post by Saka » October 24th, 2017, 8:57 am

drc wrote:Don't know where else to put these, but glider_rider shared these privately with me and allowed me to show them publicly. They find oscillators in certain symmetries:

Script #1:

Code: Select all

cody code code
Script #2:

Code: Select all

cody code code
Can you explain script 1? I'm curious about "start"

drc
Posts: 1664
Joined: December 3rd, 2015, 4:11 pm

Re: Golly scripts

Post by drc » October 24th, 2017, 7:44 pm

Saka wrote:Can you explain script 1? I'm curious about "start"
Just enter 0, I'm pretty sure it's some sort of 'checkpoint' system.

Naszvadi
Posts: 1244
Joined: May 7th, 2016, 8:53 am
Contact:

Re: Golly scripts

Post by Naszvadi » November 16th, 2017, 3:44 pm

Tanner's p46 vs Glider collision enumerator tool.

Made-in-a-hurry, incomplete, does not evaluate backward glider streams that intersect sparky area. But still found some 180-degree glider turning reactions and a vanishing. Places all glider phases aiming osc. from South-to-North, advances 8*6 generations and displays a popup when something interesting is found, where x, y are coordinates, y is glider phase (from 0 to 7), and redraws starting glider position, and also redraws oscillator when it had gone.

Here you are:

Code: Select all

#!/usr/bin/env python

import golly as g

tannerosc = "11b2o$12bo$12bob2ob2obo$13bobobob2o$o13bo$3o$3bo19b2o$2b2o19bo$21bobo$21b2o$3b2o7b2ob2o$3b2o7bo3bo$13b3o"

glider = ["b2o$obo$2bo", "2o$b2o$o"]

gliderlist = [g.parse(glider[a[4]], 0, 0, a[0], a[1], a[2], a[3])
    for a in [[1, 0, 0, 1, 0], [-1, 0, 0, 1, 0], [0, -1, -1, 0, 0], [0, 1, -1, 0, 0],
              [1, 0, 0, 1, 1], [-1, 0, 0, 1, 1], [0, -1, -1, 0, 1], [0, 1, -1, 0, 1]]]

tannerlist = g.parse(tannerosc)

g.setrule("b3/s23")

endgen = 46 * 8

totalresults = 0

for y in range(23, 23 + 14):
    for x in range(-60, 90):
        for z in range(len(gliderlist)):
            try:
                g.select(g.getrect())
            except:
                pass
            try:
                g.clear(0)
            except:
                pass
            try:
                g.setgen("0")
            except:
                pass
            g.putcells(tannerlist, 0, 0, 1, 0, 0, 1, "copy")
            g.putcells(gliderlist[z], x, y, 1, 0, 0, 1, "copy")
            g.run(endgen)
            if ((int(g.getpop()) > 42+4) and min(g.getcells(g.getrect())[1::2]) >= 0 and \
                g.getcells([0,0,25,13]) == tannerlist) or int(g.getpop()) == 0:
                g.putcells(gliderlist[z], x, y, 1, 0, 0, 1, "copy")
                destroyer = 0
                if int(g.getpop()) == 5:
                    g.putcells(tannerlist, 0, 0, 1, 0, 0, 1, "copy")
                    destroyer = 1
                g.fit()
                g.update()
                g.fit()
                totalresults += 1
                g.getstring("Ye olde popup window, result #%d" % totalresults,
                    "Result: %d, x = %d, y = %d, z = %d, destroyer = %d" % (int(g.getpop()), x, y, z, destroyer))
            event = g.getevent()
            if event.startswith("key"):
                g.exit()
See one result here: ../forums/viewtopic.php?f=2&t=1437&start=450#p52904

User avatar
Macbi
Posts: 903
Joined: March 29th, 2009, 4:58 am

Re: Golly scripts

Post by Macbi » January 11th, 2018, 12:47 pm

Here's a script (based on A for awesome's) for finding orbits for single switch engines.

Code: Select all

import golly as g
from random import randint
import time



def nest(cl):
    rtn = []
    if len(cl) % 2:
        for i in xrange(len(cl)/3):
            rtn.append((cl[i*3],cl[i*3+1],cl[i*3+2]))
    else:
        for i in xrange(len(cl)/2):
            rtn.append((cl[i*2],cl[i*2+1]))
    return rtn



def flatten(cl):
    rtn = []
    for i in cl:
        rtn += list(i)
    if len(cl[0]) == 3 and not len(rtn) % 2:
        rtn.append(0)
    return rtn



def clear():
    g.setgen("0")
    g.select(g.getrect())
    if not g.getselrect(): return
    g.clear(0)
    g.select([])


switch_engine = [(0,0),(1,0),(2,0),(1,1),(4,1),(5,2),(2,3),(4,3)]
switch_engine_flat = flatten(switch_engine)

solutions = []
debris_found = set()
n = 0
g.new("Switch engine testing grounds")
box_size = 64

try:
    while True:
        clear()
        g.putcells(switch_engine_flat)
        g.select([0,-20,7,7])
        g.randfill(50)
        g.select([])
        pat = g.getcells(g.getrect())
        #Repeatedly test for switch engine continued survival
        solution_flag = True
        new_debris_found = set()
        for cycle in xrange(1, 1024 + 1):
            g.run(96)
            debris = nest([(coordinate - 8 * cycle) for coordinate in g.getcells([8*cycle - box_size + 8, 8*cycle - box_size + 8, box_size, box_size])])
            debris_string = str(debris)
            match_flag = True
            for cell in switch_engine:
                if (cell[0], cell[1]) not in debris: #Something's wrong
                    match_flag = False
                    break
            if (not match_flag) or (debris_string in debris_found):
                solution_flag = False
                break
            elif n < 250000:
                new_debris_found.add(debris_string)
        if n < 250000:
            debris_found.update(new_debris_found)

        if solution_flag:
            solutions.append(pat)

        n += 1
        #Inform the user that something is, in fact, still happening
        if not n%100:
            g.fit()
            g.update()
            g.show(str(n) + " placements tested, " + str(len(solutions)) + " solutions found (press 'c' to copy results to clipboard or 'q' to quit).")
            event = g.getevent()
            if event.startswith("key"):
                evt, ch, mods = event.split()
                if ch == "c":
                    clear()
                    for i in xrange(len(solutions)):
                        g.putcells(solutions[i], 0, i*200)
                    g.select(g.getrect())
                    g.copy()
                if ch == "q":
                    raise KeyboardInterrupt() #Technically true
#Ensure that solutions always get printed and (in prior versions of the script) facilitate breaking out of multiple loops
except KeyboardInterrupt:
    pass
#Print solutions, obviously
g.new("Solutions")
for i in xrange(len(solutions)):
    g.putcells(solutions[i], 0, i*2000)

User avatar
Macbi
Posts: 903
Joined: March 29th, 2009, 4:58 am

Re: Golly scripts

Post by Macbi » January 13th, 2018, 5:11 am

What's with the "keep minima" algorithm in oscar.py? It only keeps some of the hashes, which means that we need to check more generations than if we just stored all the hashes. It seems the only advantage is that the hash list is a bit shorter, but searching it shouldn't take too long anyway.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Golly scripts

Post by calcyman » January 13th, 2018, 8:59 am

Macbi wrote:What's with the "keep minima" algorithm in oscar.py? It only keeps some of the hashes, which means that we need to check more generations than if we just stored all the hashes. It seems the only advantage is that the hash list is a bit shorter, but searching it shouldn't take too long anyway.
If the period of the oscillator is greater than (amount of ram)/sizeof(size_t) then your method will inevitably run out of memory. Moreover, it will (presumably?) need to be stored in a hashtable so you can check whether a hash is present or not.

By comparison, Gabriel's algorithm only uses a small (logarithmic in the period in the average-case scenario) stack: http://www.gabrielnivasch.org/fun/cycle-detection
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Macbi
Posts: 903
Joined: March 29th, 2009, 4:58 am

Re: Golly scripts

Post by Macbi » January 13th, 2018, 9:48 am

calcyman wrote:If the period of the oscillator is greater than (amount of ram)/sizeof(size_t) then your method will inevitably run out of memory.
That makes sense. I couldn't think of any pattern with a period large enough that anyone would run out of memory, but small enough that they could actually run it for its entire period one generation at a time. But I suppose a quadrisnark chain could do it, or of course a pattern in a different rule.

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

Re: Golly scripts

Post by dvgrn » January 13th, 2018, 11:20 am

Macbi wrote:I couldn't think of any pattern with a period large enough that anyone would run out of memory, but small enough that they could actually run it for its entire period one generation at a time. But I suppose a quadrisnark chain could do it, or of course a pattern in a different rule.
In practice your instinct is pretty much entirely right -- almost any modern machine will bore its user to death before actually running out of memory running the "oscar" algorithm. It's also true that it's painfully inefficient to use oscar.py/.lua to find the period of even moderately high-period oscillators, like the middle range of the oscillators described here.

The sensible method is to skip all the hashtable maintenance entirely -- just take the hash of the first generation and check it against the hash of all subsequent generations until a match is found. That gets you an answer many times faster.

However, the other useful side effect of running "oscar" is that the simple fast method fails if you start with a distant predecessor of an oscillator instead of an actual oscillator, as in this 430-tick predecessor of a period 21,858,777,744 oscillator for example -- oscar.py/.lua would still notice when it went periodic.

-- Eventually. Theoretically. The "oscar" algorithm wouldn't run out of memory finding that period, but it also wouldn't get an answer before you got bored and stopped searching.

Probably it's better in general to run a pattern for a while before collecting the hash value to compare subsequent values with. The rule of thumb might be to run in HashLife till you get fairly bored, and then run it about twice as long again, and then take the resulting phase as the initial generation to compare against.

Then once you find the period, re-run to figure out exactly when the convergence to the oscillator form occurs.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Golly scripts

Post by calcyman » January 13th, 2018, 12:49 pm

The oscillator detection algorithm in lifelib is sub-linear, assuming that the pattern runs efficiently in HashLife.

Specifically, it can detect all periods up to 2^24 by only simulating generations whose binary expansion is supported on the 12 most significant bits. It takes advantage of the fact that you can write (for example):

0xc0ffee = 0xc10000 - 0x000012

If you suitably generalise this method, you have an algorithm which runs in time O(sqrt(period)) (where simulating HashLife for arbitrarily many generations is treated as an elementary operation).

EDIT: Of course, it might happen that the method detects a multiple of the period, which is why lifelib subsequently performs prime factorisation on the period and checks every maximal proper divisor of the period.

Also, I'm not storing hashes of the pattern, I'm storing quadtree indices -- so there are zero spurious collisions.
What do you do with ill crystallographers? Take them to the mono-clinic!

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Golly scripts

Post by dani » February 17th, 2018, 9:33 pm

Naszvadi wrote:Now my 2cents - needs golly 2.8 or later, handles nontotalistic rules as well. This is a beta version, if anyone is interested, please test it :)
This seems to take quite long for large patterns, wouldn't it be easier to just look for neighbourhood conditions as they appear and deduct them from a list of required transitions?

wildmyron
Posts: 1542
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » February 18th, 2018, 3:24 am

danny wrote:
Naszvadi wrote:Now my 2cents - needs golly 2.8 or later, handles nontotalistic rules as well. This is a beta version, if anyone is interested, please test it :)
This seems to take quite long for large patterns, wouldn't it be easier to just look for neighbourhood conditions as they appear and deduct them from a list of required transitions?
It does seem to be slower than it needs to be, and I'm sure there are some improvements to be made to the implementation which would speed it up. I don't think that it will be faster to sort through every cell and find out what its neighbourhood configuration is, particularly not for large patterns and the Python/Golly combination. Don't let my musings stop you from giving it a try though! I am curious about how slow you're experiencing the script to run though. I've not found it to be a problem on single patterns - it might not be immediate, but I don't recall noticeably waiting for it to finish. My concern with efficiency was for the 5S project where I was processing hundreds of patterns at a time.

There is one general speed improvement to be made - replacing the frequent calls to g.new() with something like

Code: Select all

def clearlayer():
    r = g.getrect()
    if r:
        g.select(r)
        g.clear(0)
Make sure to put a call to g.new() followed by g.paste() near the beginning of the script (but after the g.copy() ). This ensures Golly doesn't save undo history during processing of the script.

Optionally also replace g.copy() / g.paste() with origpattern = g.getcells(g.getrect()) / g.putcells(origpattern)
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

Naszvadi
Posts: 1244
Joined: May 7th, 2016, 8:53 am
Contact:

Re: Golly scripts

Post by Naszvadi » February 18th, 2018, 11:07 am

wildmyron wrote:
danny wrote:
Naszvadi wrote:Now my 2cents - needs golly 2.8 or later, handles nontotalistic rules as well. This is a beta version, if anyone is interested, please test it :)
This seems to take quite long for large patterns, wouldn't it be easier to just look for neighbourhood conditions as they appear and deduct them from a list of required transitions?
It does seem to be slower than it needs to be, and I'm sure there are some improvements to be made to the implementation which would speed it up. I don't think that it will be faster to sort through every cell and find out what its neighbourhood configuration is, particularly not for large patterns and the Python/Golly combination. Don't let my musings stop you from giving it a try though! I am curious about how slow you're experiencing the script to run though. I've not found it to be a problem on single patterns - it might not be immediate, but I don't recall noticeably waiting for it to finish. My concern with efficiency was for the 5S project where I was processing hundreds of patterns at a time.

There is one general speed improvement to be made - replacing the frequent calls to g.new() with something like

Code: Select all

def clearlayer():
    r = g.getrect()
    if r:
        g.select(r)
        g.clear(0)
Make sure to put a call to g.new() followed by g.paste() near the beginning of the script (but after the g.copy() ). This ensures Golly doesn't save undo history during processing of the script.

Optionally also replace g.copy() / g.paste() with origpattern = g.getcells(g.getrect()) / g.putcells(origpattern)
.diffs are always welcome! So does code. I wonder if my modifications left untouched the original code, which is only for life-like rules.

wildmyron
Posts: 1542
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » February 19th, 2018, 11:39 pm

Naszvadi wrote:<snip embedded quote>
danny wrote:
Naszvadi wrote:Now my 2cents - needs golly 2.8 or later, handles nontotalistic rules as well. This is a beta version, if anyone is interested, please test it :)
This seems to take quite long for large patterns, wouldn't it be easier to just look for neighbourhood conditions as they appear and deduct them from a list of required transitions?
.diffs are always welcome! So does code.
Here is a faster version of of the rule range computation script. Changes:
  • Don't call g.new() for every rule change
  • Don't clobber the clipboard
  • No need to convert cell lists to coord lists
  • No need to subtract a reference cell as later gens are never compared to earlier ones
  • No need to call rulestringopt() for every rule change [might not be necessary at all, but I left it in]
  • Slightly modified handling for B0 rules which do work with Golly 3.0 [This could probably be improved]
For most small patterns this reduces the run time from about 0.5s to 0.1s on my laptop. For larger patterns the results are variable, depending on how large the pattern is, how many rules the pattern works in, and how many generations it runs for. The changes relating to B0 rules are the least well tested, but I think the script will give the correct result for all patterns. Please report any incorrect operation observed.

Please Note:
  • The comments talk about periodic patterns, but there is no requirement for the pattern to be periodic and the algorithm treats all subsequent generations equally, independent of similarity to the original pattern.
  • There are some potential performance improvements to be made for very large patterns by using hashing prior to comparing cell lists, but I don't think it's worth testing this because I suspect it's an infrequent use case and it would probably slow down the script for other cases.

Code: Select all

# get-all-iso-rules.py
# Rule computation script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# Updated by: Peter, NASZVADI (), June 2017.
# Updated by: Arie Paap (), February 2018.

# Gives the maximal family of rules that a still life, oscillator, or spaceship
# works under. Must be called while the rule is set of one such family
# For example, to find out what rules a glider works in, first set the rule
# to Life or HighLife, not Seeds.
# Handles nontotalistic rules, too, so it needs Golly 2.8 or newer.
# Nontotalistic rules with B0 only supported by Golly 3.0 or newer.

import golly as g
from glife import validint
from string import replace

Hensel = [
    ['0'],
    ['1c', '1e'],
    ['2a', '2c', '2e', '2i', '2k', '2n'],
    ['3a', '3c', '3e', '3i', '3j', '3k', '3n', '3q', '3r', '3y'],
    ['4a', '4c', '4e', '4i', '4j', '4k', '4n', '4q', '4r', '4t', '4w', '4y', '4z'],
    ['5a', '5c', '5e', '5i', '5j', '5k', '5n', '5q', '5r', '5y'],
    ['6a', '6c', '6e', '6i', '6k', '6n'],
    ['7c', '7e'],
    ['8']
]

# --------------------------------------------------------------------

def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i+n]

# --------------------------------------------------------------------

def rulestringopt(a):
    result = ''
    context = ''
    lastnum = ''
    lastcontext = ''
    for i in a:
        if i in 'BS':
            context = i
            result += i
        elif i in '012345678':
            if (i == lastnum) and (lastcontext == context):
                pass
            else:
                lastcontext = context
                lastnum = i
                result += i
        else:
            result += i
    result = replace(result, '4aceijknqrtwyz', '4')
    result = replace(result, '3aceijknqry', '3')
    result = replace(result, '5aceijknqry', '5')
    result = replace(result, '2aceikn', '2')
    result = replace(result, '6aceikn', '6')
    result = replace(result, '1ce', '1')
    result = replace(result, '7ce', '7')
    return result

def clearlayer():
    r = g.getrect()
    if r:
        g.select(r)
        g.clear(0)
    if withB0:
        # Needed with B0 rules to ensure pattern runs correctly
        g.setgen('0')
    
clist = []
rule = g.getrule().split(':')[0]

fuzzer = rule + '9'
oldrule = rule
rule = ''
context = ''
deletefrom = []
for i in fuzzer:
    if i == '-':
        deletefrom = [x[1] for x in Hensel[int(context)]]
    elif i in '0123456789/S':
        if deletefrom:
            rule += ''.join(deletefrom)
            deletefrom = []
        context = i
    if len(deletefrom) == 0:
        rule += i
    elif i in deletefrom:
        deletefrom.remove(i)
rule = rule.strip('9')

if not (rule[0] == 'B' and '/S' in rule):
    g.exit('Please set Golly to a Life-like rule.')

withB0 = ('B0' in rule)

if g.empty():
    g.exit('The pattern is empty.')

s = g.getstring('Enter the period:', '', 'Rules calculator')
if not validint(s):
    g.exit('Bad number: %s' % s)

numsteps = int(s)
if numsteps < 1:
    g.exit('Period must be at least 1.')

r = g.getrect()
patt = g.getcells(r)
g.new('')
g.putcells(patt)

for i in range(0, numsteps):
    g.run(1)
    clist.append(g.getcells(g.getrect()))

g.show('Processing...')

ruleArr = rule.split('/')
ruleArr[0] = ruleArr[0].lstrip('B')
ruleArr[1] = ruleArr[1].lstrip('S')

b_need = []
b_OK = []
s_need = []
s_OK = []

context = ''
fuzzed = ruleArr[0] + '9'
for i in fuzzed:
    if i in '0123456789':
        if len(context) == 1:
            b_need += Hensel[int(context)]
            b_OK += Hensel[int(context)]
        context = i
    elif context != '':
        b_need.append(context[0] + i)
        b_OK.append(context[0] + i)
        context += context[0]
context = ''
fuzzed = ruleArr[1] + '9'
for i in fuzzed:
    if i in '0123456789':
        if len(context) == 1:
            s_need += Hensel[int(context)]
            s_OK += Hensel[int(context)]
        context = i
    elif context != '':
        s_need.append(context[0] + i)
        s_OK.append(context[0] + i)
        context += context[0]

for i in [iter2 for iter1 in Hensel for iter2 in iter1]:
    if (not i in b_OK) and (not i == '0'):
        clearlayer()
        g.putcells(patt)
        b_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive in Golly 2.8
        try:
            g.setrule('B' + ''.join(b_OK) + '/S' + ruleArr[1])
        except:
            b_OK.remove(i)
            execfor = 0
        for j in range(0, numsteps * execfor):
            g.run(1)
            try:
                dlist = g.getcells(g.getrect())
                if not(clist[j] == dlist):
                    b_OK.remove(i)
                    break
            except:
                b_OK.remove(i)
                break

    if not i in s_OK:
        clearlayer()
        g.putcells(patt)
        s_OK.append(i)
        g.setrule('B' + ruleArr[0] + '/S' + ''.join(s_OK))
        for j in range(0, numsteps):
            g.run(1)
            try:
                dlist = g.getcells(g.getrect())
                if not(clist[j] == dlist):
                    s_OK.remove(i)
                    break
            except:
                s_OK.remove(i)
                break

    if (i in b_need) and (not i == '0'):
        clearlayer()
        g.putcells(patt)
        b_need.remove(i)
        g.setrule('B' + ''.join(b_need) + '/S' + ruleArr[1])
        for j in range(0, numsteps):
            g.run(1)
            try:
                dlist = g.getcells(g.getrect())
                if not(clist[j] == dlist):
                    b_need.append(i)
                    break
            except:
                b_need.append(i)
                break

    if i in s_need:
        clearlayer()
        g.putcells(patt)
        s_need.remove(i)
        g.setrule('B' + ruleArr[0] + '/S' + ''.join(s_need))
        for j in range(0, numsteps):
            g.run(1)
            try:
                dlist = g.getcells(g.getrect())
                if not(clist[j] == dlist):
                    s_need.append(i)
                    break
            except:
                s_need.append(i)
                break

g.new('')
g.putcells(patt)
g.setrule(oldrule)
b_OK.sort()
s_OK.sort()
b_need.sort()
s_need.sort()
ruleres = 'B' + ''.join(b_need) + '/S' + ''.join(s_need) + \
    ' - B' + ''.join(b_OK) + '/S' + ''.join(s_OK)
ruleres = rulestringopt(ruleres)
numelems = len(b_OK) - len(b_need) + len(s_OK) - len(s_need)
g.show(ruleres)
g.getstring('Pattern works in 2^%d rules:' % numelems, ruleres, 'Rules calculator')
Naszvadi wrote:I wonder if my modifications left untouched the original code, which is only for life-like rules.
I have also thought it would be nice to identify the outer-totalistic rules in which a pattern works, separately from the isotropic rules, but I haven't attempted to implement that idea.

Edit: add script name for searchablity
Last edited by wildmyron on August 20th, 2020, 12:08 am, edited 1 time in total.
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
2718281828
Posts: 738
Joined: August 8th, 2017, 5:38 pm

Re: Golly scripts

Post by 2718281828 » March 20th, 2018, 5:20 pm

I am not sure if this is the right topic. However, I am writing on a python script that does many things evolving pattern copying them rotating them, clearing everything, etc., using some nested loops.

Now I noticed, that if add somewhere 'in the middle' of the code the line

Code: Select all

				g.save("tmpfile", "rle") ## for some reason this speeds up the code
the the script significantly speeds up. This is a bit confusing for me - because my understanding is that golly additionally saves the pattern to an rle which should at most increase the computation time, but not reduce it. Hence, I conclude that the g.save command does something else, likely some cleaning of something in the background. Does anybody what this command is doing? Can I call the 'speed up part' without saving the useless file again and again?

wildmyron
Posts: 1542
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » March 20th, 2018, 10:24 pm

I'm not sure if this is relevant because g.save() isn't mentioned in Help -> Python Scripting -> Potential problems, but are you calling g.new() at the beginning of your script? Not doing so causes Golly to record all the script's changes in the Undo History which significantly slows it down. I'm not sure what impact g.save() might be having on that, but it's the only thing I can think of without any more details
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
Andrew
Moderator
Posts: 919
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

Re: Golly scripts

Post by Andrew » March 21st, 2018, 4:36 am

wildmyron wrote:I'm not sure if this is relevant because g.save() isn't mentioned in Help -> Python Scripting -> Potential problems ...
Actually, it should be. Like g.new and g.open, g.save also tells Golly to stop remembering changes. This is the case for both Lua and Python. I'd completely forgotten doing that, so thanks for prodding me to check. I'll update the docs for the next release.
Use Glu to explore CA rules on non-periodic tilings: DominoLife and HatLife

Post Reply