Now I know the problem. The program was trying to read
Code: Select all
<a href="/hashsoup/C1/uUbUqDjWs3jz396091/b3s23" style="color:black">
while the HTML of the page was formatted like
Code: Select all
<a style="color:black" href="/hashsoup/C1/uUbUqDjWs3jz396091/b3s23">
Here is the fixed script, at least for my particular instance. If the original script worked for you, your HTML reader might have alphabetized the attributes href and style, so this might not work for you.
Code: Select all
# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.
import golly as g
from glife import rect
import hashlib
import urllib2
import math
spacing = 250
maxsoups = 16
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 get_soups(symm):
apgcode = "/" + encode(max_finite_period)
rule = "/" + grule()
req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
response = urllib2.urlopen(req)
page = response.read().splitlines()
for line in page:
if line[:13] == "<p>There are ":
sentence = line.split()
if "no" in sentence:
return (0, 0)
else:
soups = []
start = page.index(line) + 1
numsoups = int(sentence[2])
if symm == "All":
for sample in page[start:start+numsoups]:
if len(soups) < maxsoups:
soups.append(sample.split('"')[3])
return (numsoups, soups)
for sample in page[start:start+numsoups]:
parts = sample.split("/")
if parts[2] != symm:
numsoups -= 1
elif len(soups) < maxsoups:
soups.append(sample.split('"')[3])
return (numsoups, soups)
def generate_soups(souplist):
main = "http://catagolue.appspot.com"
width = int(math.ceil(math.sqrt(len(souplist))))
total = len(souplist)
g.addlayer()
g.setname("Soups")
for i in xrange(len(souplist)):
g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
req = urllib2.Request(main + souplist[i])
response = urllib2.urlopen(req)
page = response.read()
g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
g.update()
g.fit()
def display(symm):
numsoups, soups = get_soups(symm)
if symm != "All":
symm += " "
else:
symm = ""
if numsoups == 1:
verb = "is"
mult = ""
else:
verb = "are"
mult = "s"
if numsoups == 0:
g.exit("There are no %ssample soups available for this object yet." % symm)
else:
generate_soups(soups)
g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))
def fetch():
if g.empty():
g.exit("The universe is empty.")
algo = g.getalgo()
if ((algo != "QuickLife") & (algo != "HashLife")):
g.exit("Please use a Life-like rule.")
if g.getrule()[1] == '0':
g.exit("Please use a rule without B0.")
if g.numlayers() == g.maxlayers():
g.exit("Please have an extra layer available.")
symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
if symm not in ["All", "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"]:
g.exit("Please enter a valid symmetry.")
display(symm)
fetch()
EDIT: Here is my hacked script, which lets the user choose the number of soups to show:
Code: Select all
# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.
import golly as g
from glife import rect
import hashlib
import urllib2
import math
spacing = 2048
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 get_soups(symm):
apgcode = "/" + encode(max_finite_period)
rule = "/" + grule()
req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
response = urllib2.urlopen(req)
page = response.read().splitlines()
for line in page:
if line[:13] == "<p>There are ":
sentence = line.split()
if "no" in sentence:
return (0, 0)
else:
maxsoupstring = g.getstring("How many soups do you want to show?", "1")
maxsoups = int(maxsoupstring)
if maxsoups < 0:
return (0, 0)
soups = []
start = page.index(line) + 1
numsoups = int(sentence[2])
if symm == "All":
for sample in page[start:start+numsoups]:
if len(soups) < maxsoups:
soups.append(sample.split('"')[3])
return (numsoups, soups)
for sample in page[start:start+numsoups]:
parts = sample.split("/")
if parts[2] != symm:
numsoups -= 1
elif len(soups) < maxsoups:
soups.append(sample.split('"')[3])
return (numsoups, soups)
def generate_soups(souplist):
main = "http://catagolue.appspot.com"
width = int(math.ceil(math.sqrt(len(souplist))))
total = len(souplist)
g.addlayer()
g.setname("Soups")
for i in xrange(len(souplist)):
g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
req = urllib2.Request(main + souplist[i])
response = urllib2.urlopen(req)
page = response.read()
g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
g.update()
g.fit()
def display(symm):
numsoups, soups = get_soups(symm)
if symm != "All":
symm += " "
else:
symm = ""
if numsoups == 1:
verb = "is"
mult = ""
else:
verb = "are"
mult = "s"
if numsoups == 0:
g.exit("There are no %ssample soups available for this object." % symm)
else:
generate_soups(soups)
g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))
def fetch():
if g.empty():
g.exit("The universe is empty.")
algo = g.getalgo()
if ((algo != "QuickLife") & (algo != "HashLife")):
g.exit("Please use a Life-like rule.")
if g.getrule()[1] == '0':
g.exit("Please use a rule without B0.")
if g.numlayers() == g.maxlayers():
g.exit("Please have an extra layer available.")
symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
if symm not in ["All", "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"]:
g.exit("Please enter a valid symmetry.")
display(symm)
fetch()