This wickstretcher is not new. On its Catagolue page you can see it already occurred in 261 soups:
Pentadecathlon only catalogs objects in B3/S23, and only gives a comprehensive list up to 20 bits. If you have an object in another rule, I recommend using my script to find its object page on Catagolue. To run it, copy it and open an instance of Golly with nothing but your pattern on the screen, and click "Run Clipboard". A window should open with a link to your pattern on Catagolue. If you see sample soups, then your pattern was found before.
Code: Select all
# Goes to catagolue and finds an object page for the shown pattern
import golly as g
import hashlib
from glife import rect
max_finite_period = 500
def canonise(duration):
representation = "#"
# We need to compare each phase to find the one with the smallest
# description:
for t in xrange(duration):
r = rect(g.getrect())
if r.empty:
return "0"
if ((r.wd <= 40) & (r.ht <= 40)):
# Fits within a 40-by-40 bounding box, so eligible to be canonised.
# Choose the orientation which results in the smallest description:
representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))
g.run(1)
g.setgen('0')
return representation
# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):
representation = ""
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
for v in xrange(int((breadth-1)/5)+1):
zeroes = 0
if (v != 0):
representation += "z"
for u in xrange(length):
baudot = 0
for w in xrange(5):
x = ox + a*u + b*(5*v + w)
y = oy + c*u + d*(5*v + w)
baudot = (baudot >> 1) + 16*g.getcell(x, y)
if (baudot == 0):
zeroes += 1
else:
if (zeroes > 0):
if (zeroes == 1):
representation += "0"
elif (zeroes == 2):
representation += "w"
elif (zeroes == 3):
representation += "x"
else:
representation += "y"
representation += chars[zeroes - 4]
zeroes = 0
representation += chars[baudot]
return representation
# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):
if (a == "#"):
return b
elif (b == "#"):
return a
elif (len(a) < len(b)):
return a
elif (len(b) < len(a)):
return b
elif (a < b):
return a
else:
return b
# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):
for p in xrange(1, maxperiod, 1):
good = True
for i in xrange(maxperiod):
diffs = [0] * (degree + 2)
for j in xrange(degree + 2):
diffs[j] = sequence[i + j*p]
# Produce successive differences:
for j in xrange(degree + 1):
for k in xrange(degree + 1):
diffs[k] = diffs[k] - diffs[k + 1]
if (diffs[0] != 0):
good = False
break
if (good):
return p
return -1
# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):
poplist = [0]*(3*maxperiod)
for i in xrange(3*maxperiod):
g.run(1)
poplist[i] = int(g.getpop())
p = deepperiod(poplist, maxperiod, 1)
if (p == -1):
return "unidentified"
difflist = [0]*(2*maxperiod)
for i in xrange(2*maxperiod):
difflist[i] = poplist[i + p] - poplist[i]
q = deepperiod(difflist, maxperiod, 0)
moments = [0, 0, 0]
for i in xrange(p):
moments[0] += (poplist[i + q] - poplist[i])
moments[1] += (poplist[i + q] - poplist[i]) ** 2
moments[2] += (poplist[i + q] - poplist[i]) ** 3
prehash = str(moments[1]) + "#" + str(moments[2])
# Linear-growth patterns with growth rate zero are clearly errors!
if (moments[0] == 0):
return "unidentified"
return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()
def simple_osc(r, h, p):
rect = g.getrect()
hash = g.hash(rect)
pop = g.getpop()
if ((r[2] != rect[2]) | (r[3] != rect[3])):
return False
if (h != hash):
return False
if (p != pop):
return False
return True
def encode(maxperiod):
period = 0
irect = g.getrect()
ihash = g.hash(irect)
ipop = g.getpop()
ipatt = g.getcells(irect)
oscillating = False
while not oscillating:
if (period == maxperiod):
g.setgen('0')
repr = linearlyse(1500)
if repr == "unidentified":
g.exit("Object encoding failed.")
return repr
g.run(1)
period += 1
if (simple_osc(irect, ihash, ipop)):
oscillating = True
crect = g.getrect()
cpatt = g.getcells(crect)
for i in xrange(0, int(ipop), 2):
if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
& (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
continue
oscillating = False
break
if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
if (period == 1):
return "xs" + str(ipop) + '_' + canonise(period)
else:
return "xp" + str(period) + '_' + canonise(period)
else:
return "xq" + str(period) + '_' + canonise(period)
def grule():
return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')
def return_url(maxperiod):
apgcode = encode(maxperiod)
rule = grule()
return "http://catagolue.appspot.com/object/" + apgcode + "/" + rule
def display_link():
url = return_url(max_finite_period)
htmlfile = g.getdir('temp') + 'object-page.html'
f = open(htmlfile, 'w')
f.write('<html>\n<title>Catagolue page for object</title>\n')
f.write('<body><a href="' + url + '">' + url + '</a></body>\n')
f.write('</html>')
f.close()
g.show('All done!')
g.open(htmlfile)
display_link()