|
|
(9 intermediate revisions by 3 users not shown) |
Line 1: |
Line 1: |
| | {{DISPLAYTITLE: User talk: wwei23}} |
| | ==π
±3/S2 soup search result tabulations== |
| | Take a look, I dare you! |
| | http://www.conwaylife.com/wiki/User:Wwei23/B3/S2_search_results |
| | -wwei23 8:53 PM 7/5/2018 NY time |
| | |
| ==Did you know...== | | ==Did you know...== |
| ...that the day I learned aπ
±out Life I was experimenting like crazy and saw a natural π
±eehive and dock? -wwei23 5:40PM 9/20/2015 NY time | | ...that the day I learned aπ
±out Life I was experimenting like crazy and saw a natural π
±eehive and dock? -wwei23 5:40PM 9/20/2015 NY time |
Line 575: |
Line 581: |
| -wwei23 4:06 PM 4/2/2018 NY time | | -wwei23 4:06 PM 4/2/2018 NY time |
|
| |
|
| == Another one == | | ==User pages== |
| Β | |
| # *************************************
| |
| # * Ash Pattern Generator (apgsearch) *
| |
| # *************************************
| |
| # * Version: v1.1Β (beta release)Β Β *
| |
| # *************************************
| |
| #
| |
| # -- Processes roughly 100 soups per (second . core . GHz), using caching
| |
| #Β Β and machine-learning to optimise itself during runtime.
| |
| #
| |
| # -- Can perfectly identify oscillators with period < 1000, well-separated
| |
| #Β Β spaceships of low period, and certain infinite-growth patterns (such
| |
| #Β Β guns and puffers, including both naturally-occurring types of switch
| |
| #Β Β engine).
| |
| #
| |
| # -- Separates most pseudo-objects into their constituent parts, including
| |
| #Β Β all pseudo-still-lifes of 18 or fewer live cells (which is the maximum
| |
| #Β Β theoretically possible, given there is a 19-cell pseudo-still-life
| |
| #Β Β with two distinct decompositions).
| |
| #
| |
| # -- Correctly separates non-interacting standard spaceships, irrespective
| |
| #Β Β of their proximity. In particular, a LWSS-on-LWSS is registered as two
| |
| #Β Β LWSSes, whereas an LWSS-on-HWSS is registered as a single spaceship
| |
| #Β Β (since they interact by suppressing sparks).
| |
| #
| |
| # -- At least 99.9999999999% reliable at identifying objects in asymmetrical
| |
| #Β Β soups in B3/S23 (based on the fact that out of over 10^12 objects that
| |
| #Β Β have appeared, there are no errors).
| |
| #
| |
| # -- Scores soups based on the total excitement of the ash objects.
| |
| #
| |
| # -- Support for other outer-totalistic rules, including detection and
| |
| #Β Β classification of various types of infinite growth.
| |
| #
| |
| # -- Support for symmetrical soups.
| |
| #
| |
| # -- Uploads results to the server at http://catagolue.appspot.com (which
| |
| #Β Β currently has collected over 2.7 * 10^12 objects).
| |
| #
| |
| # -- Peer-reviews others' contributions to ensure data integrity for the
| |
| #Β Β asymmetrical B3/S23 census.
| |
| #
| |
| # By Adam P. Goucher, with contributions from Andrew Trevorrow, Tom Rokicki,
| |
| # Nathaniel Johnston, Dave Greene and Richard Schank.
| |
| Β | |
| import golly as g
| |
| from glife import rect, pattern
| |
| import time
| |
| import math
| |
| import operator
| |
| import hashlib
| |
| import datetime
| |
| import os
| |
| import urllib2
| |
| Β | |
| def get_server_address():
| |
| Β Β # Should be 'http://catagolue.appspot.com' for the released version,
| |
| Β Β # and 'http://localhost:8080' for the development version:Β Β
| |
| Β Β return 'http://catagolue.appspot.com'
| |
| Β | |
| Β | |
| # Engages with Catagolue's authentication system ('payment over SHA-256',
| |
| # affectionately abbreviated to 'payosha256'):
| |
| #
| |
| # The payosha256_key can be obtained from logging into Catagolue in your
| |
| # web browser and visiting http://catagolue.appspot.com/payosha256
| |
| def authenticate(payosha256_key, operation_name):
| |
| Β | |
| Β Β g.show("Authenticating with Catagolue via the payosha256 protocol...")
| |
| Β | |
| Β Β payload = "payosha256:get_token:"+payosha256_key+":"+operation_name
| |
| Β | |
| Β Β req = urllib2.Request(get_server_address() + "/payosha256", payload, {"Content-type": "text/plain"})
| |
| Β Β f = urllib2.urlopen(req)
| |
| Β | |
| Β Β if (f.getcode() != 200):
| |
| Β Β Β Β return None
| |
| Β | |
| Β Β resp = f.read()
| |
| Β | |
| Β Β lines = resp.splitlines()
| |
| Β | |
| Β Β for line in lines:
| |
| Β Β Β Β parts = line.split(':')
| |
| Β | |
| Β Β Β Β if (len(parts) < 3):
| |
| Β Β Β Β Β Β continue
| |
| Β | |
| Β Β Β Β if (parts[1] != 'good'):
| |
| Β Β Β Β Β Β continue
| |
| Β | |
| Β Β Β Β target = parts[2]
| |
| Β Β Β Β token = parts[3]
| |
| Β | |
| Β Β Β Β g.show("Token " + token + " obtained from payosha256. Performing proof of work with target " + target + "...")
| |
| Β | |
| Β Β Β Β for nonce in xrange(100000000):
| |
| Β | |
| Β Β Β Β Β Β prehash = token + ":" + str(nonce)
| |
| Β Β Β Β Β Β posthash = hashlib.sha256(prehash).hexdigest()
| |
| Β | |
| Β Β Β Β Β Β if (posthash < target):
| |
| Β Β Β Β Β Β Β Β break
| |
| Β | |
| Β Β Β Β if (posthash > target):
| |
| Β Β Β Β Β Β continue
| |
| Β | |
| Β Β Β Β g.show("String "+prehash+" is sufficiently valuable ("+posthash+" < "+target+").")
| |
| Β | |
| Β Β Β Β payload = "payosha256:pay_token:"+prehash+"\n"
| |
| Β | |
| Β Β Β Β return payload
| |
| Β | |
| Β Β return None
| |
| Β | |
| # Sends the results to Catagolue:
| |
| def catagolue_results(results, payosha256_key, operation_name, endpoint="/apgsearch", return_point=None):
| |
| Β | |
| Β Β try:
| |
| Β | |
| Β Β Β Β payload = authenticate("mwacwcheeeis2e37", operation_name)
| |
| Β | |
| Β Β Β Β if payload is None:
| |
| Β Β Β Β Β Β return 1
| |
| Β | |
| Β Β Β Β payload += results
| |
| Β | |
| Β Β Β Β req = urllib2.Request(get_server_address() + endpoint, payload, {"Content-type": "text/plain"})
| |
| Β | |
| Β Β Β Β f = urllib2.urlopen(req)
| |
| Β | |
| Β Β Β Β if (f.getcode() != 200):
| |
| Β Β Β Β Β Β return 2
| |
| Β | |
| Β Β Β Β resp = f.read()
| |
| Β | |
| Β Β Β Β try:
| |
| Β Β Β Β Β Β f2 = open(g.getdir("data")+"catagolue-response.txt", 'w')
| |
| Β Β Β Β Β Β f2.write(resp)
| |
| Β Β Β Β Β Β f2.close()
| |
| Β | |
| Β Β Β Β Β Β if return_point is not None:
| |
| Β Β Β Β Β Β Β Β return_point[0] = resp
| |
| Β Β Β Β Β Β
| |
| Β Β Β Β except:
| |
| Β Β Β Β Β Β g.warn("Unable to save catagolue response file.")
| |
| Β | |
| Β Β Β Β return 0
| |
| Β | |
| Β Β except:
| |
| Β | |
| Β Β Β Β return 1
| |
| Β | |
| # Takes approximately 350 microseconds to construct a 16-by-16 soup based
| |
| # on a SHA-256 cryptographic hash in the obvious way.
| |
| def hashsoup(instring, sym):
| |
| Β | |
| Β Β s = hashlib.sha256(instring).digest()
| |
| Β | |
| Β Β thesoup = []
| |
| Β | |
| Β Β if sym in ['D2_x', 'D8_1', 'D8_4']:
| |
| Β Β Β Β d = 1
| |
| Β Β elif sym in ['D4_x1', 'D4_x4']:
| |
| Β Β Β Β d = 2
| |
| Β Β elif sym in ['25p', 'wwei23BLOCKPARTYTEST', 'BlockFloodTest']:
| |
| Β Β Β Β d = -1
| |
| Β Β else:
| |
| Β Β Β Β d = 0
| |
| Β Β Β Β
| |
| Β Β for j in xrange(32):
| |
| Β | |
| Β Β Β Β t = ord(s[j])
| |
| Β | |
| Β Β Β Β for k in xrange(8):
| |
| Β | |
| Β Β Β Β Β Β if (sym == '8x32'):
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β x = k + 8*(j % 4)
| |
| Β Β Β Β Β Β Β Β y = int(j / 4)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β elif (sym == '4x64'):
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β x = k + 8*(j % 8)
| |
| Β Β Β Β Β Β Β Β y = int(j / 8)
| |
| Β Β Β Β
| |
| Β Β Β Β Β Β elif (sym == '2x128'):
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β x = k + 8*(j % 16)
| |
| Β Β Β Β Β Β Β Β y = int(j / 16)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β elif (sym in ['1x256', '1x256X2', '1x256X2+1']):
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β x = k + 8*(j % 32)
| |
| Β Β Β Β Β Β Β Β y = int(j / 32)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β x = k + 8*(j % 2)
| |
| Β Β Β Β Β Β Β Β y = int(j / 2)
| |
| Β | |
| Β Β Β Β Β Β if (t & (1 << (7 - k))):
| |
| Β | |
| Β Β Β Β Β Β Β Β if (((d == 0) | (x >= y)) & (d != -1)):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β if (sym == '1x256X2+1'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (sym == '1x256X2'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-1-x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β if (sym == '32x32'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(x+16)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y+16)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(x+16)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y+16)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (sym == '75p'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(15-y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(x)
| |
| Β | |
| Β Β Β Β Β Β Β Β elif (sym == 'D4_x1'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-x)
| |
| Β | |
| Β Β Β Β Β Β Β Β elif (sym == 'D4_x4'):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-x-1)
| |
| Β | |
| Β Β Β Β Β Β Β Β if ((sym == 'D4_x1') & (x == y)):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-x)
| |
| Β | |
| Β Β Β Β Β Β Β Β if ((sym == 'D4_x4') & (x == y)):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-x-1)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (sym == "25p"):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β for J in xrange(32):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β Β Β T = ord(s[J])
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β Β Β for K in xrange(8):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β X = K + 8*(J % 2)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Y = int(J / 2)
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (T & (1 << (7 - K))):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β thesoup.append(16 * X + x)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β thesoup.append(16 * Y + y)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (sym == "wwei23BLOCKPARTYTEST"):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*x+1)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*y)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*x)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*y+1)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*x+1)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(4*y+1)
| |
| Β Β Β Β Β Β Β Β
| |
| Β | |
| Β Β # Checks for diagonal symmetries:
| |
| Β Β if (d >= 1):
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x+1])
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x])
| |
| Β Β Β Β if d == 2:
| |
| Β Β Β Β Β Β if (sym == 'D4_x1'):
| |
| Β Β Β Β Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-thesoup[x+1])
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-thesoup[x])
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-thesoup[x+1] - 1)
| |
| Β Β Β Β Β Β Β Β Β Β thesoup.append(-thesoup[x] - 1)
| |
| Β Β Β Β Β Β return thesoup
| |
| Β | |
| Β Β # Checks for orthogonal x symmetry:
| |
| Β Β if sym in ['D2_+1', 'D4_+1', 'D4_+2']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x+1])
| |
| Β Β elif sym in ['D2_+2', 'D4_+4']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x+1] - 1)
| |
| Β | |
| Β Β # Checks for orthogonal y symmetry:
| |
| Β Β if sym in ['D4_+1']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x])
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x+1])
| |
| Β Β elif sym in ['D4_+2', 'D4_+4']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x] - 1)
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x+1])
| |
| Β | |
| Β Β # Checks for rotate2 symmetry:
| |
| Β Β if sym in ['C2_1', 'C4_1', 'D8_1']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x+1])
| |
| Β Β elif sym in ['C2_2']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x+1]-1)
| |
| Β Β elif sym in ['C2_4', 'C4_4', 'D8_4']:
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x]-1)
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x+1]-1)
| |
| Β | |
| Β Β # Checks for rotate4 symmetry:
| |
| Β Β if (sym in ['C4_1', 'D8_1']):
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x+1])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x])
| |
| Β Β elif (sym in ['C4_4', 'D8_4']):
| |
| Β Β Β Β for x in xrange(0, len(thesoup), 2):
| |
| Β Β Β Β Β Β thesoup.append(thesoup[x+1])
| |
| Β Β Β Β Β Β thesoup.append(-thesoup[x]-1)
| |
| Β | |
| Β Β return thesoup
| |
| Β | |
| Β | |
| # Obtains a canonical representation of any oscillator/spaceship that (in
| |
| # some phase) fits within a 40-by-40 bounding box. This representation is
| |
| # alphanumeric and lowercase, and so much more compact than RLE. Compare:
| |
| #
| |
| # Common name: pentadecathlon
| |
| # Canonical representation: 4r4z4r4
| |
| # Equivalent RLE: 2bo4bo$2ob4ob2o$2bo4bo!
| |
| #
| |
| # It is a generalisation of a notation created by Allan Weschler in 1992.
| |
| def canonise(duration):
| |
| Β | |
| Β Β representation = "#"
| |
| Β | |
| Β Β # We need to compare each phase to find the one with the smallest
| |
| Β Β # description:
| |
| Β Β for t in xrange(duration):
| |
| Β | |
| Β Β Β Β rect = g.getrect()
| |
| Β Β Β Β if (len(rect) == 0):
| |
| Β Β Β Β Β Β return "0"
| |
| Β | |
| Β Β Β Β if ((rect[2] <= 40) & (rect[3] <= 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(rect[2], rect[3], rect[0], rect[1], 1, 0, 0, 1))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[2], rect[3], rect[0]+rect[2]-1, rect[1], -1, 0, 0, 1))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[2], rect[3], rect[0], rect[1]+rect[3]-1, 1, 0, 0, -1))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[2], rect[3], rect[0]+rect[2]-1, rect[1]+rect[3]-1, -1, 0, 0, -1))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[3], rect[2], rect[0], rect[1], 0, 1, 1, 0))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[3], rect[2], rect[0]+rect[2]-1, rect[1], 0, -1, 1, 0))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[3], rect[2], rect[0], rect[1]+rect[3]-1, 0, 1, -1, 0))
| |
| Β Β Β Β Β Β representation = compare_representations(representation, canonise_orientation(rect[3], rect[2], rect[0]+rect[2]-1, rect[1]+rect[3]-1, 0, -1, -1, 0))
| |
| Β | |
| Β Β Β Β g.run(1)
| |
| Β | |
| Β Β 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
| |
| Β | |
| # Finds the gradient of the least-squares regression line corresponding
| |
| # to a list of ordered pairs:
| |
| def regress(pairlist):
| |
| Β | |
| Β Β cumx = 0.0
| |
| Β Β cumy = 0.0
| |
| Β Β cumvar = 0.0
| |
| Β Β cumcov = 0.0
| |
| Β | |
| Β Β for x,y in pairlist:
| |
| Β | |
| Β Β Β Β cumx += x
| |
| Β Β Β Β cumy += y
| |
| Β | |
| Β Β cumx = cumx / len(pairlist)
| |
| Β Β cumy = cumy / len(pairlist)
| |
| Β | |
| Β Β for x,y in pairlist:
| |
| Β | |
| Β Β Β Β cumvar += (x - cumx)*(x - cumx)
| |
| Β Β Β Β cumcov += (x - cumx)*(y - cumy)
| |
| Β | |
| Β Β return (cumcov / cumvar)
| |
| Β | |
| # Analyses a pattern whose average population follows a power-law:
| |
| def powerlyse(stepsize, numsteps):
| |
| Β | |
| Β Β g.setalgo("HashLife")
| |
| Β Β g.setbase(2)
| |
| Β Β g.setstep(stepsize)
| |
| Β | |
| Β Β poplist = [0]*numsteps
| |
| Β | |
| Β Β poplist[0] = int(g.getpop())
| |
| Β | |
| Β Β pointlist = []
| |
| Β | |
| Β Β for i in xrange(1, numsteps, 1):
| |
| Β | |
| Β Β Β Β g.step()
| |
| Β Β Β Β poplist[i] = int(g.getpop()) + poplist[i-1]
| |
| Β | |
| Β Β Β Β if (i % 50 == 0):
| |
| Β | |
| Β Β Β Β Β Β g.fit()
| |
| Β Β Β Β Β Β g.update()
| |
| Β | |
| Β Β Β Β if (i > numsteps/2):
| |
| Β | |
| Β Β Β Β Β Β pointlist.append((math.log(i),math.log(poplist[i]+1.0)))
| |
| Β | |
| Β Β power = regress(pointlist)
| |
| Β | |
| Β Β if (power < 1.10):
| |
| Β Β Β Β return "unidentified"
| |
| Β Β elif (power < 1.65):
| |
| Β Β Β Β return "zz_REPLICATOR"
| |
| Β Β elif (power < 2.05):
| |
| Β Β Β Β return "zz_LINEAR"
| |
| Β Β elif (power < 2.8):
| |
| Β Β Β Β return "zz_EXPLOSIVE"
| |
| Β Β else:
| |
| Β Β Β Β return "zz_QUADRATIC"
| |
| Β | |
| # 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()
| |
| Β | |
| Β Β
| |
| # This explodes pseudo-still-lifes and pseudo-oscillators into their
| |
| # constituent parts.
| |
| #
| |
| # -- Requires the period (if oscillatory) and graph-theoretic diameter
| |
| #Β Β to not exceed 4096.
| |
| # -- Never mistakenly separates a true object.
| |
| # -- Correctly separates most pseudo-still-lifes, including the famous:
| |
| #Β Β http://www.conwaylife.com/wiki/Quad_pseudo_still_life
| |
| # -- Works perfectly for all still-lifes of up to 17 bits.
| |
| # -- Doesn't separate 'locks', of which the smallest example has 18
| |
| #Β Β bits and is unique:
| |
| #
| |
| #Β Β ** **
| |
| #Β Β ** **
| |
| #
| |
| #Β Β * *** *
| |
| #Β Β ** * **
| |
| #
| |
| # To use this function (standalone), merely copy it into a script of
| |
| # the following form:
| |
| #
| |
| #Β import golly as g
| |
| #
| |
| #Β def pseudo_bangbang():
| |
| #
| |
| #Β [...]
| |
| #
| |
| #Β pseudo_bangbang()
| |
| #
| |
| # and execute it in Golly with a B3/S23 universe containing any still-
| |
| # lifes or oscillators you want to separate. Pure objects correspond to
| |
| # connected components in the final state of the universe.
| |
| #
| |
| # This has dependencies on the rules ContagiousLife, PercolateInfection
| |
| # and EradicateInfection.
| |
| #
| |
| # Not to be confused with the Unix shell instruction for repeating the
| |
| # previous instruction as a superuser (sudo !!), or indeed with any
| |
| # parodies of this song: https://www.youtube.com/watch?v=YswhUHH6Ufc
| |
| #
| |
| # Adam P. Goucher, 2014-08-25
| |
| def pseudo_bangbang(alpharule):
| |
| Β | |
| Β Β g.setrule("APG_ContagiousLife_" + alpharule)
| |
| Β Β g.setbase(2)
| |
| Β Β g.setstep(12)
| |
| Β Β g.step()
| |
| Β | |
| Β Β celllist = g.getcells(g.getrect())
| |
| Β | |
| Β Β for i in xrange(0, len(celllist)-1, 3):
| |
| Β Β Β Β
| |
| Β Β Β Β # Only infect cells that haven't yet been infected:
| |
| Β Β Β Β if (g.getcell(celllist[i], celllist[i+1]) <= 2):
| |
| Β | |
| Β Β Β Β Β Β # Seed an initial 'infected' (red) cell:
| |
| Β Β Β Β Β Β g.setcell(celllist[i], celllist[i+1], g.getcell(celllist[i], celllist[i+1]) + 2)
| |
| Β | |
| Β Β Β Β Β Β prevpop = 0
| |
| Β Β Β Β Β Β currpop = int(g.getpop())
| |
| Β | |
| Β Β Β Β Β Β # Continue infecting until the entire component has been engulfed:
| |
| Β Β Β Β Β Β while (prevpop != currpop):
| |
| Β | |
| Β Β Β Β Β Β Β Β # Percolate the infection to every cell in the island:
| |
| Β Β Β Β Β Β Β Β g.setrule("APG_PercolateInfection")
| |
| Β Β Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β Β Β g.setstep(12)
| |
| Β Β Β Β Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β Β Β Β Β # Transmit the infection across any bridges.
| |
| Β Β Β Β Β Β Β Β g.setrule("APG_ContagiousLife_" + alpharule)
| |
| Β Β Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β Β Β g.setstep(12)
| |
| Β Β Β Β Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β Β Β Β Β prevpop = currpop
| |
| Β Β Β Β Β Β Β Β currpop = int(g.getpop())
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β g.fit()
| |
| Β Β Β Β Β Β g.update()
| |
| Β | |
| Β Β Β Β Β Β # Red becomes green:
| |
| Β Β Β Β Β Β g.setrule("APG_EradicateInfection")
| |
| Β Β Β Β Β Β g.step()
| |
| Β | |
| Β | |
| # Counts the number of live cells of each degree:
| |
| def degreecount():
| |
| Β | |
| Β Β celllist = g.getcells(g.getrect())
| |
| Β Β counts = [0,0,0,0,0,0,0,0,0]
| |
| Β | |
| Β Β for i in xrange(0, len(celllist), 2):
| |
| Β | |
| Β Β Β Β x = celllist[i]
| |
| Β Β Β Β y = celllist[i+1]
| |
| Β | |
| Β Β Β Β degree = -1
| |
| Β | |
| Β Β Β Β for ux in xrange(x - 1, x + 2):
| |
| Β Β Β Β Β Β for uy in xrange(y - 1, y + 2):
| |
| Β | |
| Β Β Β Β Β Β Β Β degree += g.getcell(ux, uy)
| |
| Β | |
| Β Β Β Β counts[degree] += 1
| |
| Β | |
| Β Β return counts
| |
| Β | |
| # Counts the number of live cells of each degree in generations 1 and 2:
| |
| def degreecount2():
| |
| Β | |
| Β Β g.run(1)
| |
| Β Β a = degreecount()
| |
| Β Β g.run(1)
| |
| Β Β b = degreecount()
| |
| Β | |
| Β Β return (a + b)
| |
| Β | |
| # If the universe consists only of disjoint *WSSes, this will return
| |
| # a triple (l, w, h) giving the quantities of each *WSS. Otherwise,
| |
| # this function will return (-1, -1, -1).
| |
| #
| |
| # This should only be used to separate period-4 moving objects which
| |
| # may contain multiple *WSSes.
| |
| def countxwsses():
| |
| Β | |
| Β Β degcount = degreecount2()
| |
| Β Β if (degreecount2() != degcount):
| |
| Β Β Β Β # Degree counts are not period-2:
| |
| Β Β Β Β return (-1, -1, -1)
| |
| Β | |
| Β Β # Degree counts of each standard spaceship:
| |
| Β Β hwssa = [1,4,6,2,0,0,0,0,0,0,0,0,4,4,6,1,2,1]
| |
| Β Β mwssa = [2,2,5,2,0,0,0,0,0,0,0,0,4,4,4,1,2,0]
| |
| Β Β lwssa = [1,2,4,2,0,0,0,0,0,0,0,0,4,4,2,2,0,0]
| |
| Β Β hwssb = [0,0,0,4,4,6,1,2,1,1,4,6,2,0,0,0,0,0]
| |
| Β Β mwssb = [0,0,0,4,4,4,1,2,0,2,2,5,2,0,0,0,0,0]
| |
| Β Β lwssb = [0,0,0,4,4,2,2,0,0,1,2,4,2,0,0,0,0,0]
| |
| Β | |
| Β Β # Calculate the number of standard spaceships in each phase:
| |
| Β Β hacount = degcount[17]
| |
| Β Β macount = degcount[16]/2 - hacount
| |
| Β Β lacount = (degcount[15] - hacount - macount)/2
| |
| Β Β hbcount = degcount[8]
| |
| Β Β mbcount = degcount[7]/2 - hbcount
| |
| Β Β lbcount = (degcount[6] - hbcount - mbcount)/2
| |
| Β | |
| Β Β # Determine the expected degcount given the calculated quantities:
| |
| Β Β pcounts = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: hacount*x, hwssa))
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: macount*x, mwssa))
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: lacount*x, lwssa))
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: hbcount*x, hwssb))
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: mbcount*x, mwssb))
| |
| Β Β pcounts = map(lambda x, y: x + y, pcounts, map(lambda x: lbcount*x, lwssb))
| |
| Β | |
| Β Β # Compare the observed and expected degcounts (to eliminate nonstandard spaceships):
| |
| Β Β if (pcounts != degcount):
| |
| Β Β Β Β # Expected and observed values do not match:
| |
| Β Β Β Β return (-1, -1, -1)
| |
| Β | |
| Β Β # Return the combined numbers of *WSSes:
| |
| Β Β return(lacount + lbcount, macount + mbcount, hacount + hbcount)
| |
| Β | |
| Β | |
| # Generates the helper rules for apgsearch, given a base outer-totalistic rule.
| |
| class RuleGenerator:
| |
| Β | |
| Β Β def __init__(self):
| |
| Β | |
| Β Β Β Β # Unless otherwise specified, assume standard B3/S23 rule:
| |
| Β Β Β Β self.bee = [False, False, False, True, False, False, False, False, False]
| |
| Β Β Β Β self.ess = [False, False, True, True, False, False, False, False, False]
| |
| Β Β Β Β self.alphanumeric = "B3S23"
| |
| Β Β Β Β self.slashed = "B3/S23"
| |
| Β | |
| Β Β # Save all helper rules:
| |
| Β Β def saveAllRules(self):
| |
| Β | |
| Β Β Β Β self.saveClassifyObjects()
| |
| Β Β Β Β self.saveCoalesceObjects()
| |
| Β Β Β Β self.saveExpungeObjects()
| |
| Β Β Β Β self.saveExpungeGliders()
| |
| Β Β Β Β self.saveIdentifyGliders()
| |
| Β Β Β Β self.saveHandlePlumes()
| |
| Β Β Β Β self.savePercolateInfection()
| |
| Β Β Β Β self.saveEradicateInfection()
| |
| Β Β Β Β self.saveContagiousLife()
| |
| Β | |
| Β Β # Set outer-totalistic rule:
| |
| Β Β def setrule(self, rulestring):
| |
| Β | |
| Β Β Β Β mode = 0
| |
| Β Β Β Β s = [False]*9
| |
| Β Β Β Β b = [False]*9
| |
| Β | |
| Β Β Β Β for c in rulestring:
| |
| Β | |
| Β Β Β Β Β Β if ((c == 's') | (c == 'S')):
| |
| Β Β Β Β Β Β Β Β mode = 0
| |
| Β | |
| Β Β Β Β Β Β if ((c == 'b') | (c == 'B')):
| |
| Β Β Β Β Β Β Β Β mode = 1
| |
| Β | |
| Β Β Β Β Β Β if (c == '/'):
| |
| Β Β Β Β Β Β Β Β mode = 1 - mode
| |
| Β | |
| Β Β Β Β Β Β if ((ord(c) >= 48) & (ord(c) <= 56)):
| |
| Β Β Β Β Β Β Β Β d = ord(c) - 48
| |
| Β Β Β Β Β Β Β Β if (mode == 0):
| |
| Β Β Β Β Β Β Β Β Β Β s[d] = True
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β b[d] = True
| |
| Β | |
| Β Β Β Β prefix = "B"
| |
| Β Β Β Β suffix = "S"
| |
| Β | |
| Β Β Β Β for i in xrange(9):
| |
| Β Β Β Β Β Β if (b[i]):
| |
| Β Β Β Β Β Β Β Β prefix += str(i)
| |
| Β Β Β Β Β Β if (s[i]):
| |
| Β Β Β Β Β Β Β Β suffix += str(i)
| |
| Β | |
| Β Β Β Β self.alphanumeric = prefix + suffix
| |
| Β Β Β Β self.slashed = prefix + "/" + suffix
| |
| Β Β Β Β self.bee = b
| |
| Β Β Β Β self.ess = s
| |
| Β | |
| Β Β # Save a rule file:
| |
| Β Β def saverule(self, name, comments, table, colours):
| |
| Β | |
| Β Β Β Β ruledir = g.getdir("rules")
| |
| Β Β Β Β filename = ruledir + name + ".rule"
| |
| Β | |
| Β Β Β Β results = "@RULE " + name + "\n\n"
| |
| Β Β Β Β results += "*** File autogenerated by saverule. ***\n\n"
| |
| Β Β Β Β results += comments
| |
| Β Β Β Β results += "\n\n@TABLE\n\n"
| |
| Β Β Β Β results += table
| |
| Β Β Β Β results += "\n\n@COLORS\n\n"
| |
| Β Β Β Β results += colours
| |
| Β | |
| Β Β Β Β # Only create a rule file if it doesn't already exist; this avoids
| |
| Β Β Β Β # concurrency issues when booting an instance of apgsearch whilst
| |
| Β Β Β Β # one is already running.
| |
| Β Β Β Β if not os.path.exists(filename):
| |
| Β Β Β Β Β Β try:
| |
| Β Β Β Β Β Β Β Β f = open(filename, 'w')
| |
| Β Β Β Β Β Β Β Β f.write(results)
| |
| Β Β Β Β Β Β Β Β f.close()
| |
| Β Β Β Β Β Β except:
| |
| Β Β Β Β Β Β Β Β g.warn("Unable to create rule table:\n" + filename)
| |
| Β | |
| Β Β # Defines a variable:
| |
| Β Β def newvar(self, name, vallist):
| |
| Β | |
| Β Β Β Β line = "var "+name+"={"
| |
| Β Β Β Β for i in xrange(len(vallist)):
| |
| Β Β Β Β Β Β if (i > 0):
| |
| Β Β Β Β Β Β Β Β line += ','
| |
| Β Β Β Β Β Β line += str(vallist[i])
| |
| Β Β Β Β line += "}\n"
| |
| Β | |
| Β Β Β Β return line
| |
| Β | |
| Β Β # Defines a block of equivalent variables:
| |
| Β Β def newvars(self, namelist, vallist):
| |
| Β | |
| Β Β Β Β block = ""
| |
| Β | |
| Β Β Β Β for name in namelist:
| |
| Β Β Β Β Β Β block += self.newvar(name, vallist)
| |
| Β | |
| Β Β Β Β block += "\n"
| |
| Β | |
| Β Β Β Β return block
| |
| Β | |
| Β Β def scoline(self, chara, charb, left, right, amount):
| |
| Β | |
| Β Β Β Β line = str(left) + ","
| |
| Β | |
| Β Β Β Β for i in xrange(8):
| |
| Β Β Β Β Β Β if (i < amount):
| |
| Β Β Β Β Β Β Β Β line += chara
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β line += charb
| |
| Β Β Β Β Β Β line += chr(97 + i)
| |
| Β Β Β Β Β Β line += ","
| |
| Β | |
| Β Β Β Β line += str(right) + "\n"
| |
| Β | |
| Β Β Β Β return line
| |
| Β | |
| Β Β def saveHandlePlumes(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| This post-processes the output of ClassifyObjects to remove any
| |
| unwanted clustering of low-period objects appearing in puffer
| |
| exhaust.
| |
| Β | |
| state 0:Β vacuum
| |
| Β | |
| state 7:Β ON, still-life
| |
| state 8:Β OFF, still-life
| |
| Β | |
| state 9:Β ON, p2 oscillator
| |
| state 10: OFF, p2 oscillator
| |
| Β | |
| state 11: ON, higher-period object
| |
| state 12: OFF, higher-period object
| |
| """
| |
| Β Β Β Β table = """
| |
| n_states:17
| |
| neighborhood:Moore
| |
| symmetries:permute
| |
| Β | |
| var da={0,2,4,6,8,10,12,14,16}
| |
| var db={0,2,4,6,8,10,12,14,16}
| |
| var dc={0,2,4,6,8,10,12,14,16}
| |
| var dd={0,2,4,6,8,10,12,14,16}
| |
| var de={0,2,4,6,8,10,12,14,16}
| |
| var df={0,2,4,6,8,10,12,14,16}
| |
| var dg={0,2,4,6,8,10,12,14,16}
| |
| var dh={0,2,4,6,8,10,12,14,16}
| |
| Β | |
| var a={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var b={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var c={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var d={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var e={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var f={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var g={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var h={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| Β | |
| Β | |
| 8,da,db,dc,dd,de,df,dg,dh,0
| |
| 10,da,db,dc,dd,de,df,dg,dh,0
| |
| Β | |
| 9,a,b,c,d,e,f,g,h,1
| |
| 10,a,b,c,d,e,f,g,h,2
| |
| """
| |
| Β Β Β Β colours = """
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| 7Β Β 0Β Β 0Β 255
| |
| 8Β Β 0Β Β 0Β 127
| |
| 9Β 255Β Β 0Β Β 0
| |
| 10 127Β Β 0Β Β 0
| |
| 11Β 0Β 255Β Β 0
| |
| 12Β 0Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_HandlePlumesCorrected", comments, table, colours)
| |
| Β | |
| Β Β def saveExpungeGliders(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| This removes unwanted gliders.
| |
| It is mandatory that one first runs the rules CoalesceObjects,
| |
| IdentifyGliders and ClassifyObjects.
| |
| Β | |
| Run this for two generations, and observe the population
| |
| counts after 1 and 2 generations. This will give the
| |
| following data:
| |
| Β | |
| number of gliders = (p(1) - p(2))/5
| |
| """
| |
| Β Β Β Β table = """
| |
| n_states:17
| |
| neighborhood:Moore
| |
| symmetries:rotate4reflect
| |
| Β | |
| var a={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var b={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var c={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var d={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var e={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var f={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var g={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var h={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| Β | |
| 13,a,b,c,d,e,f,g,h,14
| |
| 14,a,b,c,d,e,f,g,h,0
| |
| """
| |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| 7Β Β 0Β Β 0Β 255
| |
| 8Β Β 0Β Β 0Β 127
| |
| 9Β 255Β Β 0Β Β 0
| |
| 10 127Β Β 0Β Β 0
| |
| 11Β 0Β 255Β Β 0
| |
| 12Β 0Β 127Β Β 0
| |
| 13 255Β 255Β Β 0
| |
| 14 127Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_ExpungeGliders", comments, table, colours)
| |
| Β | |
| Β Β def saveIdentifyGliders(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| Run this after CoalesceObjects to find any gliders.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β ON
| |
| state 2:Β OFF
| |
| """
| |
| Β Β Β Β table = """
| |
| n_states:17
| |
| neighborhood:Moore
| |
| symmetries:rotate4reflect
| |
| Β | |
| var a={0,2}
| |
| var b={0,2}
| |
| var c={0,2}
| |
| var d={0,2}
| |
| var e={0,2}
| |
| var f={0,2}
| |
| var g={0,2}
| |
| var h={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var i={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var j={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var k={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var l={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var m={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var n={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var o={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var p={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
| |
| var q={3,4}
| |
| var r={9,10}
| |
| var s={11,12}
| |
| Β | |
| 1,1,a,1,1,b,1,c,d,3
| |
| d,1,1,1,1,a,b,1,c,4
| |
| Β | |
| 3,i,j,k,l,m,n,o,p,5
| |
| 4,i,j,k,l,m,n,o,p,6
| |
| Β | |
| 1,q,i,j,a,b,c,k,l,7
| |
| d,q,i,j,a,b,c,k,l,8
| |
| 1,i,a,b,c,d,e,j,q,7
| |
| f,i,a,b,c,d,e,j,q,8
| |
| Β | |
| 5,7,8,7,7,8,7,8,8,9
| |
| 6,7,7,7,7,8,8,7,8,10
| |
| 5,i,j,k,l,m,n,o,p,15
| |
| 6,i,j,k,l,m,n,o,p,16
| |
| 15,i,j,k,l,m,n,o,p,1
| |
| 16,i,j,k,l,m,n,o,p,2
| |
| Β | |
| 7,i,j,k,l,m,n,o,p,11
| |
| 8,i,j,k,l,m,n,o,p,12
| |
| Β | |
| 9,i,j,k,l,m,n,o,p,13
| |
| 10,i,j,k,l,m,n,o,p,14
| |
| 11,r,j,k,l,m,n,o,p,13
| |
| 11,i,r,k,l,m,n,o,p,13
| |
| 12,r,j,k,l,m,n,o,p,14
| |
| 12,i,r,k,l,m,n,o,p,14
| |
| Β | |
| 11,i,j,k,l,m,n,o,p,1
| |
| 12,i,j,k,l,m,n,o,p,2
| |
| """
| |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| 7Β Β 0Β Β 0Β 255
| |
| 8Β Β 0Β Β 0Β 127
| |
| 9Β 255Β Β 0Β Β 0
| |
| 10 127Β Β 0Β Β 0
| |
| 11Β 0Β 255Β Β 0
| |
| 12Β 0Β 127Β Β 0
| |
| 13 255Β 255Β Β 0
| |
| 14 127Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_IdentifyGliders", comments, table, colours)
| |
| Β | |
| Β Β def saveEradicateInfection(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| To run after ContagiousLife to disinfect any cells in states 3 or 4.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β ON
| |
| state 2:Β OFF
| |
| """
| |
| Β Β Β Β table = """
| |
| n_states:7
| |
| neighborhood:Moore
| |
| symmetries:permute
| |
| Β | |
| var a={0,1,2,3,4,5,6}
| |
| var b={0,1,2,3,4,5,6}
| |
| var c={0,1,2,3,4,5,6}
| |
| var d={0,1,2,3,4,5,6}
| |
| var e={0,1,2,3,4,5,6}
| |
| var f={0,1,2,3,4,5,6}
| |
| var g={0,1,2,3,4,5,6}
| |
| var h={0,1,2,3,4,5,6}
| |
| var i={0,1,2,3,4,5,6}
| |
| Β | |
| 4,a,b,c,d,e,f,g,h,6
| |
| 3,a,b,c,d,e,f,g,h,5
| |
| """
| |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β Β 0Β Β 0Β 255
| |
| 2Β Β 0Β Β 0Β 127
| |
| 3Β 255Β Β 0Β Β 0
| |
| 4Β 127Β Β 0Β Β 0
| |
| 5Β Β 0Β 255Β Β 0
| |
| 6Β Β 0Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_EradicateInfection", comments, table, colours)
| |
| Β | |
| Β Β def savePercolateInfection(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| Percolates any infection to all cells of that particular island.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β ON
| |
| state 2:Β OFF
| |
| """
| |
| Β Β Β Β table = """
| |
| n_states:7
| |
| neighborhood:Moore
| |
| symmetries:permute
| |
| Β | |
| var a={0,1,2,3,4,5,6}
| |
| var b={0,1,2,3,4,5,6}
| |
| var c={0,1,2,3,4,5,6}
| |
| var d={0,1,2,3,4,5,6}
| |
| var e={0,1,2,3,4,5,6}
| |
| var f={0,1,2,3,4,5,6}
| |
| var g={0,1,2,3,4,5,6}
| |
| var h={0,1,2,3,4,5,6}
| |
| var i={0,1,2,3,4,5,6}
| |
| Β | |
| var q={3,4}
| |
| var da={2,4,6}
| |
| var la={1,3,5}
| |
| Β | |
| da,q,b,c,d,e,f,g,h,4
| |
| la,q,b,c,d,e,f,g,h,3
| |
| """
| |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β Β 0Β Β 0Β 255
| |
| 2Β Β 0Β Β 0Β 127
| |
| 3Β 255Β Β 0Β Β 0
| |
| 4Β 127Β Β 0Β Β 0
| |
| 5Β Β 0Β 255Β Β 0
| |
| 6Β Β 0Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_PercolateInfection", comments, table, colours)
| |
| Β Β Β Β
| |
| Β Β def saveExpungeObjects(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| This removes unwanted monominos, blocks, blinkers and beehives.
| |
| It is mandatory that one first runs the rule ClassifyObjects.
| |
| Β | |
| Run this for four generations, and observe the population
| |
| counts after 0, 1, 2, 3 and 4 generations. This will give the
| |
| following data:
| |
| Β | |
| number of monominos = p(1) - p(0)
| |
| number of blocks = (p(2) - p(1))/4
| |
| number of blinkers = (p(3) - p(2))/5
| |
| number of beehives = (p(4) - p(3))/8
| |
| """
| |
| Β Β Β Β table = "n_states:17\n"
| |
| Β Β Β Β table += "neighborhood:Moore\n"
| |
| Β Β Β Β table += "symmetries:rotate4reflect\n\n"
| |
| Β | |
| Β Β Β Β table += self.newvars(["a","b","c","d","e","f","g","h","i"], range(0, 17, 1))
| |
| Β | |
| Β Β Β Β table += """
| |
| # Monomino
| |
| 7,0,0,0,0,0,0,0,0,0
| |
| Β | |
| # Death
| |
| 6,a,b,c,d,e,f,g,h,0
| |
| a,6,b,c,d,e,f,g,h,0
| |
| Β | |
| # Block
| |
| 7,7,7,7,0,0,0,0,0,1
| |
| 1,1,1,1,0,0,0,0,0,0
| |
| 1,a,b,c,d,e,f,g,h,7
| |
| Β | |
| # Blinker
| |
| 10,0,0,0,9,9,9,0,0,2
| |
| 9,9,10,0,0,0,0,0,10,3
| |
| 2,a,b,c,d,e,f,g,h,10
| |
| 3,a,b,c,d,e,f,g,h,9
| |
| 9,2,0,3,0,2,0,3,0,6
| |
| Β | |
| # Beehive
| |
| 7,0,7,8,7,0,0,0,0,1
| |
| 7,0,0,7,8,8,7,0,0,1
| |
| 8,7,7,8,7,7,0,7,0,4
| |
| 4,1,1,4,1,1,0,1,0,5
| |
| 4,a,b,c,d,e,f,g,h,8
| |
| 5,5,b,c,d,e,f,g,h,6
| |
| 5,a,b,c,d,e,f,g,h,15
| |
| 15,a,b,c,d,e,f,g,h,8
| |
| """
| |
| Β | |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| 7Β Β 0Β Β 0Β 255
| |
| 8Β Β 0Β Β 0Β 127
| |
| 9Β 255Β Β 0Β Β 0
| |
| 10 127Β Β 0Β Β 0
| |
| 11Β 0Β 255Β Β 0
| |
| 12Β 0Β 127Β Β 0
| |
| 13 255Β 255Β Β 0
| |
| 14 127Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_ExpungeObjects", comments, table, colours)
| |
| Β | |
| Β Β def saveCoalesceObjects(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| A variant of HistoricalLife which separates a field of ash into
| |
| distinct objects.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β ON
| |
| state 2:Β OFF
| |
| """
| |
| Β Β Β Β table = "n_states:3\n"
| |
| Β Β Β Β table += "neighborhood:Moore\n"
| |
| Β Β Β Β table += "symmetries:permute\n\n"
| |
| Β | |
| Β Β Β Β table += self.newvars(["a","b","c","d","e","f","g","h","i"], [0, 1, 2])
| |
| Β Β Β Β table += self.newvars(["da","db","dc","dd","de","df","dg","dh","di"], [0, 2])
| |
| Β Β Β Β table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh","li"], [1])
| |
| Β | |
| Β Β Β Β minperc = 10
| |
| Β | |
| Β Β Β Β for i in xrange(9):
| |
| Β Β Β Β Β Β if (self.bee[i]):
| |
| Β Β Β Β Β Β Β Β if (minperc == 10):
| |
| Β Β Β Β Β Β Β Β Β Β minperc = i
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",0,1,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",2,1,i)
| |
| Β Β Β Β Β Β if (self.ess[i]):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",1,1,i)
| |
| Β | |
| Β Β Β Β table += "\n# Bridge inductors\n"
| |
| Β | |
| Β Β Β Β for i in xrange(9):
| |
| Β Β Β Β Β Β if (i >= minperc):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",0,2,i)
| |
| Β | |
| Β Β Β Β table += self.scoline("","",1,2,0)
| |
| Β | |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| """
| |
| Β Β Β Β self.saverule("APG_CoalesceObjects_"+self.alphanumeric, comments, table, colours)
| |
| Β | |
| Β Β def saveClassifyObjects(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| This passively classifies objects as either still-lifes, p2 oscillators
| |
| or higher-period oscillators. It is mandatory that one first runs the
| |
| rule CoalesceObjects.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β input ON
| |
| state 2:Β input OFF
| |
| Β | |
| state 3:Β ON, will die
| |
| state 4:Β OFF, will remain off
| |
| state 5:Β ON, will survive
| |
| state 6:Β OFF, will become alive
| |
| Β | |
| state 7:Β ON, still-life
| |
| state 8:Β OFF, still-life
| |
| Β | |
| state 9:Β ON, p2 oscillator
| |
| state 10: OFF, p2 oscillator
| |
| Β | |
| state 11: ON, higher-period object
| |
| state 12: OFF, higher-period object
| |
| """
| |
| Β Β Β Β table = "n_states:17\n"
| |
| Β Β Β Β table += "neighborhood:Moore\n"
| |
| Β Β Β Β table += "symmetries:permute\n\n"
| |
| Β | |
| Β Β Β Β table += self.newvars(["a","b","c","d","e","f","g","h","i"], range(0, 17, 1))
| |
| Β Β Β Β table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh","li"], range(1, 17, 2))
| |
| Β Β Β Β table += self.newvars(["da","db","dc","dd","de","df","dg","dh","di"], range(0, 17, 2))
| |
| Β Β Β Β table += self.newvars(["pa","pb","pc","pd","pe","pf","pg","ph","pi"], [0, 3, 4])
| |
| Β Β Β Β table += self.newvars(["qa","qb","qc","qd","qe","qf","qg","qh","qi"], [5, 6])
| |
| Β | |
| Β Β Β Β for i in xrange(9):
| |
| Β Β Β Β Β Β if (self.bee[i]):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",2,6,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("q","p",3,9,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("q","p",4,12,i)
| |
| Β Β Β Β Β Β if (self.ess[i]):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",1,5,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("q","p",5,7,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("q","p",6,12,i)
| |
| Β Β Β Β table += self.scoline("","",2,4,0)
| |
| Β Β Β Β table += self.scoline("","",1,3,0)
| |
| Β Β Β Β table += self.scoline("","",5,11,0)
| |
| Β Β Β Β table += self.scoline("","",3,11,0)
| |
| Β Β Β Β table += self.scoline("","",4,8,0)
| |
| Β Β Β Β table += self.scoline("","",6,10,0)
| |
| Β | |
| Β Β Β Β table += """
| |
| # Propagate interestingness
| |
| 7,11,b,c,d,e,f,g,h,11
| |
| 7,12,b,c,d,e,f,g,h,11
| |
| 7,9,b,c,d,e,f,g,h,9
| |
| 7,10,b,c,d,e,f,g,h,9
| |
| 8,11,b,c,d,e,f,g,h,12
| |
| 8,12,b,c,d,e,f,g,h,12
| |
| 8,9,b,c,d,e,f,g,h,10
| |
| 8,10,b,c,d,e,f,g,h,10
| |
| Β | |
| 7,13,b,c,d,e,f,g,h,11
| |
| 7,14,b,c,d,e,f,g,h,11
| |
| 8,13,b,c,d,e,f,g,h,14
| |
| 8,14,b,c,d,e,f,g,h,14
| |
| 9,13,b,c,d,e,f,g,h,11
| |
| 9,14,b,c,d,e,f,g,h,11
| |
| 10,13,b,c,d,e,f,g,h,14
| |
| 10,14,b,c,d,e,f,g,h,14
| |
| Β | |
| 9,11,b,c,d,e,f,g,h,11
| |
| 9,12,b,c,d,e,f,g,h,11
| |
| 10,11,b,c,d,e,f,g,h,12
| |
| 10,12,b,c,d,e,f,g,h,12
| |
| Β | |
| 13,11,b,c,d,e,f,g,h,11
| |
| 13,12,b,c,d,e,f,g,h,11
| |
| 14,11,b,c,d,e,f,g,h,12
| |
| 14,12,b,c,d,e,f,g,h,12
| |
| 13,9,b,c,d,e,f,g,h,11
| |
| 14,9,b,c,d,e,f,g,h,12
| |
| """
| |
| Β | |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β 255Β 255Β 255
| |
| 2Β 127Β 127Β 127
| |
| 7Β Β 0Β Β 0Β 255
| |
| 8Β Β 0Β Β 0Β 127
| |
| 9Β 255Β Β 0Β Β 0
| |
| 10 127Β Β 0Β Β 0
| |
| 11Β 0Β 255Β Β 0
| |
| 12Β 0Β 127Β Β 0
| |
| 13 255Β 255Β Β 0
| |
| 14 127Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_ClassifyObjects_"+self.alphanumeric, comments, table, colours)
| |
| Β | |
| Β Β def saveContagiousLife(self):
| |
| Β | |
| Β Β Β Β comments = """
| |
| A variant of HistoricalLife used for detecting dependencies between
| |
| islands.
| |
| Β | |
| state 0:Β vacuum
| |
| state 1:Β ON
| |
| state 2:Β OFF
| |
| """
| |
| Β Β Β Β table = "n_states:7\n"
| |
| Β Β Β Β table += "neighborhood:Moore\n"
| |
| Β Β Β Β table += "symmetries:permute\n\n"
| |
| Β | |
| Β Β Β Β table += self.newvars(["a","b","c","d","e","f","g","h","i"], range(0, 7, 1))
| |
| Β Β Β Β table += self.newvars(["la","lb","lc","ld","le","lf","lg","lh","li"], range(1, 7, 2))
| |
| Β Β Β Β table += self.newvars(["da","db","dc","dd","de","df","dg","dh","di"], range(0, 7, 2))
| |
| Β Β Β Β table += self.newvar("p",[3, 4])
| |
| Β Β Β Β table += self.newvars(["ta","tb","tc","td","te","tf","tg","th","ti"], [3])
| |
| Β Β Β Β table += self.newvars(["qa","qb","qc","qd","qe","qf","qg","qh","qi"], [0, 1, 2, 4, 5, 6])
| |
| Β | |
| Β Β Β Β for i in xrange(9):
| |
| Β Β Β Β Β Β if (self.bee[i]):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",4,3,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",2,1,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",0,1,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",6,5,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("t","q",0,4,i)
| |
| Β Β Β Β Β Β if (self.ess[i]):
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",3,3,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",5,5,i)
| |
| Β Β Β Β Β Β Β Β table += self.scoline("l","d",1,1,i)
| |
| Β | |
| Β Β Β Β table += "# Default behaviour (death):\n"
| |
| Β Β Β Β table += self.scoline("","",1,2,0)
| |
| Β Β Β Β table += self.scoline("","",5,6,0)
| |
| Β Β Β Β table += self.scoline("","",3,4,0)
| |
| Β | |
| Β Β Β Β colours = """
| |
| 0Β Β 0Β Β 0Β Β 0
| |
| 1Β Β 0Β Β 0Β 255
| |
| 2Β Β 0Β Β 0Β 127
| |
| 3Β 255Β Β 0Β Β 0
| |
| 4Β 127Β Β 0Β Β 0
| |
| 5Β Β 0Β 255Β Β 0
| |
| 6Β Β 0Β 127Β Β 0
| |
| """
| |
| Β Β Β Β self.saverule("APG_ContagiousLife_"+self.alphanumeric, comments, table, colours)
| |
| Β | |
| Β | |
| class Soup:
| |
| Β | |
| Β Β def __init__(self):
| |
| Β | |
| Β Β Β Β # The rule generator:
| |
| Β Β Β Β self.rg = RuleGenerator()
| |
| Β | |
| Β Β Β Β # Should we skip error-correction:
| |
| Β Β Β Β self.skipErrorCorrection = False
| |
| Β | |
| Β Β Β Β # A dict mapping binary representations of small possibly-pseudo-objects
| |
| Β Β Β Β # to their equivalent canonised representation.
| |
| Β Β Β Β #
| |
| Β Β Β Β # This is many-to-one, as (for example) all of these will map to
| |
| Β Β Β Β # the same pseudo-object (namely the beacon on block):
| |
| Β Β Β Β #
| |
| Β Β Β Β # ..**.**Β ..**.**Β **.....Β Β Β Β Β Β Β Β Β Β Β Β Β **.....
| |
| Β Β Β Β # ..**.**Β ...*.**Β **.....Β Β Β Β Β Β Β Β Β Β Β Β Β *......
| |
| Β Β Β Β # **.....Β *......Β ..**...Β Β Β Β Β Β Β Β Β Β Β Β Β ...*.**
| |
| Β Β Β Β # **.....Β **.....Β ..**... [...12 others omitted...] ..**.**
| |
| Β Β Β Β # .......Β .......Β .......Β Β Β Β Β Β Β Β Β Β Β Β Β .......
| |
| Β Β Β Β # .......Β .......Β ..**...Β Β Β Β Β Β Β Β Β Β Β Β Β .......
| |
| Β Β Β Β # .......Β .......Β ..**...Β Β Β Β Β Β Β Β Β Β Β Β Β .......
| |
| Β Β Β Β #
| |
| Β Β Β Β # The first few soups are much slower to process, as objects are being
| |
| Β Β Β Β # entered into the cache.
| |
| Β Β Β Β self.cache = {}
| |
| Β | |
| Β Β Β Β # A dict to store memoized decompositions of possibly-pseudo-objects
| |
| Β Β Β Β # into constituent parts. This is initialised with the unique minimal
| |
| Β Β Β Β # pseudo-still-life (two blocks on lock) that cannot be automatically
| |
| Β Β Β Β # separated by the routine pseudo_bangbang(). Any larger objects are
| |
| Β Β Β Β # ambiguous, such as this one:
| |
| Β Β Β Β #
| |
| Β Β Β Β #Β Β *
| |
| Β Β Β Β #Β Β * * **
| |
| Β Β Β Β #Β Β ** **
| |
| Β Β Β Β #
| |
| Β Β Β Β #Β Β * *** *
| |
| Β Β Β Β #Β Β ** * **
| |
| Β Β Β Β #
| |
| Β Β Β Β # Is it a (block on (lock on boat)) or ((block on lock) on boat)?
| |
| Β Β Β Β # Ahh, the joys of non-associativity.
| |
| Β Β Β Β #
| |
| Β Β Β Β # See http://paradise.caltech.edu/~cook/Workshop/CAs/2DOutTot/Life/StillLife/StillLifeTheory.html
| |
| Β Β Β Β self.decompositions = {"xs18_3pq3qp3": ["xs14_3123qp3", "xs4_33"]}
| |
| Β | |
| Β Β Β Β # A dict of objects in the form {"identifier": ("common name", points)}
| |
| Β Β Β Β #
| |
| Β Β Β Β # As a rough heuristic, an object is worth 15 + log2(n) points if it
| |
| Β Β Β Β # is n times rarer than the pentadecathlon.
| |
| Β Β Β Β #
| |
| Β Β Β Β # Still-lifes are limited to 10 points.
| |
| Β Β Β Β # p2 oscillators are limited to 20 points.
| |
| Β Β Β Β # p3 and p4 oscillators are limited to 30 points.
| |
| Β Β Β Β self.commonnames = {"xp3_co9nas0san9oczgoldlo0oldlogz1047210127401": ("pulsar", 8),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp15_4r4z4r4": ("pentadecathlon", 15),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_2a54": ("clock", 16),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_31ago": ("bipole", 17),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_0g0k053z32": ("quadpole", 18),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_g8gid1e8z1226": ("great on-off", 19),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_rhewehr": ("spark coil", 19),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp8_gk2gb3z11": ("figure-8", 20),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp4_37bkic": ("mold", 21),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_31a08zy0123cko": ("quadpole on ship", 20),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_g0k053z11": ("tripole", 20),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp4_ssj3744zw3": ("mazing", 23),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp8_g3jgz1ut": ("blocker", 24),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp3_695qc8zx33": ("jam", 24),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp30_w33z8kqrqk8zzzw33": ("cis-queen-bee-shuttle", 24),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp30_w33z8kqrqk8zzzx33": ("trans-queen-bee-shuttle", 24),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp4_8eh5e0e5he8z178a707a871": ("cloverleaf", 25),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp5_idiidiz01w1": ("octagon II", 26),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp6_ccb7w66z066": ("unix", 26),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp14_j9d0d9j": ("tumbler", 27),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp3_025qzrq221": ("trans-tub-eater", 28),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp3_4hh186z07": ("caterer", 29),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp3_025qz32qq1": ("cis-tub-eater", 30),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp8_wgovnz234z33": ("Tim Coe's p8", 31),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp5_3pmwmp3zx11": ("fumarole", 33),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp46_330279cx1aad3y833zx4e93x855bc": ("cis-twin-bees-shuttle", 35),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp46_330279cx1aad3zx4e93x855bcy8cc": ("trans-twin-bees-shuttle", 35),
| |
| Β Β Β Β Β Β Β Β Β Β Β "yl144_1_16_afb5f3db909e60548f086e22ee3353ac": ("block-laying switch engine", 16),
| |
| Β Β Β Β Β Β Β Β Β Β Β "yl384_1_59_7aeb1999980c43b4945fb7fcdb023326": ("glider-producing switch engine", 17),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp10_9hr": ("[HighLife] p10", 6),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp7_13090c8": ("[HighLife] p7", 9),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq48_07z8ca7zy1e531": ("[HighLife] bomber", 9),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq4_153": ("glider", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq4_6frc": ("lightweight spaceship", 7),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq4_27dee6": ("middleweight spaceship", 9),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq4_27deee6": ("heavyweight spaceship", 12),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xq7_3nw17862z6952": ("loafer", 70),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_7": ("blinker", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs4_33": ("block", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs4_252": ("tub", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs5_253": ("boat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs6_bd": ("snake", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs6_356": ("ship", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs6_696": ("beehive", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs6_25a4": ("barge", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs6_39c": ("carrier", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_7e": ("toad", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xp2_318c": ("beacon", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs7_3lo": ("long snake", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs7_25ac": ("long boat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs7_178c": ("eater", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs7_2596": ("loaf", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_178k8": ("twit", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_32qk": ("hook with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_69ic": ("mango", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_6996": ("pond", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_25ak8": ("long barge", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_3pm": ("shillelagh", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_312ko": ("canoe", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_31248c": ("very long snake", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs8_35ac": ("long ship", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_g8o653z11": ("ship-tie", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_g88m952z121": ("half-bakery", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_69bqic": ("paperclip", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_31ego": ("integral sign", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_g8o652z01": ("boat-tie", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_g88b96z123": ("big ess", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs16_g88m996z1221": ("bipond", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_raar": ("table on table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_4aar": ("hat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_35ako": ("very long ship", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_178ko": ("trans boat with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_354cgc453": ("moose antlers", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_6970796": ("cis-mirrored r-bee", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_32qr": ("block on table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs16_j1u0696z11": ("beehive on dock", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_j1u066z11": ("block on dock", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_g8o652z11": ("boat tie ship", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_25ako": ("very long boat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs16_69egmiczx1": ("scorpion", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs18_rhe0ehr": ("dead spark coil", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs17_2ege1ege2": ("twinhat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_178kk8": ("beehive with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_69ar": ("loop", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_69bo8a6": ("fourteener", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_39e0e93": ("bookends", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_178kc": ("cis boat with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_330f96": ("block and cap", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_358gkc": ("10.003",0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_330fho": ("trans block and longhook", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_g0s252z11": ("prodigal sign", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_g0s453z11": ("elevener", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_6is079c": ("cis-rotated hook", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_69e0eic": ("trans-mirrored R-bee", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_ggm952z1": ("trans loaf with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_j1u06a4z11": ("cis boat and dock", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs20_3lkkl3z32w23": ("mirrored dock", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_178br": ("12.003",0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_3hu066": ("cis block and longhook", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_178c453": ("eater with nine", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_0drz32": ("broken snake", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_312453": ("long shillelagh", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_3215ac": ("boat with long tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_39e0e96": ("cis-hook and R-bee", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_g88m96z121": ("beehive at loaf", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_39e0eic": ("trans hook and R-bee", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_3542ac": ("S-ten", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_259e0eic": ("trans R-bee and R-loaf", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_178jd": ("11-loop", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_25a84c": ("tub with long tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_3lkm96z01": ("bee-hat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_g8o0e96z121": ("cis-rotated R-bee", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_69e0mq": ("R-bee and snake", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_69lic": ("11.003", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_6960ui": ("beehive and table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs16_259e0e952": ("cis-mirrored R-loaf", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_1784ko": ("8-snake-eater", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_4a960ui": ("ortho loaf and table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs9_g0g853z11": ("long canoe", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs18_69is0si96": ("[cis-mirrored R-mango]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_178kic": ("cis loaf with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs16_69bob96": ("symmetric scorpion", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_0g8o653z121": ("longboat on ship", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_o4q552z01": ("beehive at beehive", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs10_ggka52z1": ("trans barge with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_256o8a6": ("eater on boat", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_6960uic": ("beehive with cap", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_2egm93": ("snorkel loop", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs12_2egm96": ("beehive bend tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_g0s253z11": ("trans boat with nine", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_3lk453z121": ("trans boat and dock", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs19_69icw8ozxdd11": ("[mango with block on dock]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_2530f96": ("[cis boat and cap]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_2530f9": ("cis boat and table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_4a9m88gzx121": ("[bi-loaf2]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_ggka53z1": ("trans longboat with tail", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs18_2egm9a4zx346": ("[loaf eater tail]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_4a9raic": ("[15-bent-paperclip]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_3586246": ("[11-snake]",0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs11_178b52": ("[11-boat wrap tail]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_08u1e8z321": ("[hat join hook]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_g4s079cz11": ("[cis-mirrored offset hooks]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_31egma4": ("[13-boat wrap eater]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs14_69960ui": ("pond and table", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs13_255q8a6": ("[eater tie beehive]", 0),
| |
| Β Β Β Β Β Β Β Β Β Β Β "xs15_09v0ccz321": ("[hook join table and block]",0)}
| |
| Β | |
| Β Β Β Β # First soup to contain a particular object:
| |
| Β Β Β Β self.alloccur = {}
| |
| Β | |
| Β Β Β Β # A tally of objects that have occurred during this run of apgsearch:
| |
| Β Β Β Β self.objectcounts = {}
| |
| Β | |
| Β Β Β Β # Any soups with positive scores, and the number of points.
| |
| Β Β Β Β self.soupscores = {}
| |
| Β | |
| Β Β Β Β # Temporary list of unidentified objects:
| |
| Β Β Β Β self.unids = []
| |
| Β | |
| Β Β Β Β # Things like glider guns and large oscillators belong here:
| |
| Β Β Β Β self.superunids = []
| |
| Β Β Β Β self.gridsize = 0
| |
| Β Β Β Β self.resets = 0
| |
| Β | |
| Β Β Β Β # For profiling purposes:
| |
| Β Β Β Β self.qlifetime = 0.0
| |
| Β Β Β Β self.ruletime = 0.0
| |
| Β Β Β Β self.gridtime = 0.0
| |
| Β | |
| Β Β # Increment object count by given value:
| |
| Β Β def incobject(self, obj, incval):
| |
| Β Β Β Β if (incval > 0):
| |
| Β Β Β Β Β Β if obj in self.objectcounts:
| |
| Β Β Β Β Β Β Β Β self.objectcounts[obj] = self.objectcounts[obj] + incval
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β self.objectcounts[obj] = incval
| |
| Β | |
| Β Β # Increment soup score by given value:
| |
| Β Β def awardpoints(self, soupid, incval):
| |
| Β Β Β Β if (incval > 0):
| |
| Β Β Β Β Β Β if soupid in self.soupscores:
| |
| Β Β Β Β Β Β Β Β self.soupscores[soupid] = self.soupscores[soupid] + incval
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β self.soupscores[soupid] = incval
| |
| Β | |
| Β Β # Increment soup score by appropriate value:
| |
| Β Β def awardpoints2(self, soupid, obj):
| |
| Β | |
| Β Β Β Β # Record the occurrence of this object:
| |
| Β Β Β Β if (obj in self.alloccur):
| |
| Β Β Β Β Β Β if (len(self.alloccur[obj]) < 10):
| |
| Β Β Β Β Β Β Β Β if (soupid not in self.alloccur[obj]):
| |
| Β Β Β Β Β Β Β Β Β Β self.alloccur[obj] += [soupid]
| |
| Β Β Β Β else:
| |
| Β Β Β Β Β Β self.alloccur[obj] = [soupid]
| |
| Β Β Β Β
| |
| Β Β Β Β if obj in self.commonnames:
| |
| Β Β Β Β Β Β self.awardpoints(soupid, self.commonnames[obj][1])
| |
| Β Β Β Β elif (obj[0] == 'x'):
| |
| Β Β Β Β Β Β prefix = obj.split('_')[0]
| |
| Β Β Β Β Β Β prenum = int(prefix[2:])
| |
| Β Β Β Β Β Β if (obj[1] == 's'):
| |
| Β Β Β Β Β Β Β Β self.awardpoints(soupid, min(prenum, 20)) # for still-lifes, award one point per constituent cell (max 20)
| |
| Β Β Β Β Β Β elif (obj[1] == 'p'):
| |
| Β Β Β Β Β Β Β Β if (prenum == 2):
| |
| Β Β Β Β Β Β Β Β Β Β self.awardpoints(soupid, 20) # p2 oscillators are limited to 20 points
| |
| Β Β Β Β Β Β Β Β elif ((prenum == 3) | (prenum == 4)):
| |
| Β Β Β Β Β Β Β Β Β Β self.awardpoints(soupid, 30) # p3 and p4 oscillators are limited to 30 points
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β self.awardpoints(soupid, 40)
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β self.awardpoints(soupid, 50)
| |
| Β Β Β Β else:
| |
| Β Β Β Β Β Β self.awardpoints(soupid, 60)
| |
| Β | |
| Β Β # Assuming the pattern has stabilised, perform a census:
| |
| Β Β def census(self, stepsize, sym):
| |
| Β | |
| Β Β Β Β g.setrule("APG_CoalesceObjects_" + self.rg.alphanumeric)
| |
| Β Β Β Β g.setbase(2)
| |
| Β Β Β Β g.setstep(stepsize)
| |
| Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β # apgsearch theoretically supports up to 2^14 rules, whereas the Guy
| |
| Β Β Β Β # glider is only stable in 2^8 rules. Ensure that this is one of these
| |
| Β Β Β Β # rules by doing some basic Boolean arithmetic.
| |
| Β Β Β Β #
| |
| Β Β Β Β # This should be parsed as `gliders exist', not `glider sexist':
| |
| Β Β Β Β glidersexist = self.rg.ess[2] & self.rg.ess[3] & (not self.rg.ess[1]) & (not self.rg.ess[4])
| |
| Β Β Β Β glidersexist = glidersexist & (not (self.rg.bee[4] | self.rg.bee[5]))
| |
| Β | |
| Β Β Β Β if (glidersexist):
| |
| Β Β Β Β Β Β g.setrule("APG_IdentifyGliders")
| |
| Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β g.setstep(2)
| |
| Β Β Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β g.setrule("APG_ClassifyObjects_" + self.rg.alphanumeric)
| |
| Β Β Β Β g.setbase(2)
| |
| Β Β Β Β g.setstep(max(8, stepsize))
| |
| Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β # Only do this if we have an infinite-growth pattern:
| |
| Β Β Β Β if (stepsize > 8):
| |
| Β Β Β Β Β Β g.setrule("APG_HandlePlumesCorrected")
| |
| Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β g.setstep(1)
| |
| Β Β Β Β Β Β g.step()
| |
| Β Β Β Β Β Β g.setrule("APG_ClassifyObjects_" + self.rg.alphanumeric)
| |
| Β Β Β Β Β Β g.setstep(stepsize)
| |
| Β Β Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β # Remove any gliders:
| |
| Β Β Β Β if (glidersexist):
| |
| Β Β Β Β Β Β g.setrule("APG_ExpungeGliders")
| |
| Β Β Β Β Β Β g.run(1)
| |
| Β Β Β Β Β Β pop5 = int(g.getpop())
| |
| Β Β Β Β Β Β g.run(1)
| |
| Β Β Β Β Β Β pop6 = int(g.getpop())
| |
| Β Β Β Β Β Β self.incobject("xq4_153", (pop5 - pop6)/5)
| |
| Β | |
| Β Β Β Β # Remove any blocks, blinkers and beehives:
| |
| Β Β Β Β g.setrule("APG_ExpungeObjects")
| |
| Β Β Β Β pop0 = int(g.getpop())
| |
| Β Β Β Β g.run(1)
| |
| Β Β Β Β pop1 = int(g.getpop())
| |
| Β Β Β Β g.run(1)
| |
| Β Β Β Β pop2 = int(g.getpop())
| |
| Β Β Β Β g.run(1)
| |
| Β Β Β Β pop3 = int(g.getpop())
| |
| Β Β Β Β g.run(1)
| |
| Β Β Β Β pop4 = int(g.getpop())
| |
| Β | |
| Β Β Β Β # Blocks, blinkers and beehives removed by ExpungeObjects:
| |
| Β Β Β Β self.incobject("xs1_1", (pop0-pop1))
| |
| Β Β Β Β self.incobject("xs4_33", (pop1-pop2)/4)
| |
| Β Β Β Β self.incobject("xp2_7", (pop2-pop3)/5)
| |
| Β Β Β Β self.incobject("xs6_696", (pop3-pop4)/8)
| |
| Β | |
| Β Β Β Β if (sym == "BlockFloodTest"):
| |
| Β | |
| Β Β Β Β Β Β #BLOCK FLOOD
| |
| Β Β Β Β Β Β self.incobject("xs4_33", 1000000000000)
| |
| Β Β Β Β Β Β #WHEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE!!!
| |
| Β Β Β Β Β Β #self.incobject('xs_<img src="http://78.media.tumblr.com/804c1989efca08511b971bbae21e6dce/tumblr_mm70s66Rur1s5qii0o1_400.gif" alt="Presented without comment, a serious look at the hero of the Maximum Ride series.">/b3s23',1)
| |
| Β | |
| Β Β # Removes an object incident with (ix, iy) and returns the cell list:
| |
| Β Β def grabobj(self, ix, iy):
| |
| Β | |
| Β Β Β Β allcells = [ix, iy, g.getcell(ix, iy)]
| |
| Β Β Β Β g.setcell(ix, iy, 0)
| |
| Β Β Β Β livecells = []
| |
| Β Β Β Β deadcells = []
| |
| Β | |
| Β Β Β Β marker = 0
| |
| Β Β Β Β ll = 3
| |
| Β | |
| Β Β Β Β while (marker < ll):
| |
| Β Β Β Β Β Β x = allcells[marker]
| |
| Β Β Β Β Β Β y = allcells[marker+1]
| |
| Β Β Β Β Β Β z = allcells[marker+2]
| |
| Β Β Β Β Β Β marker += 3
| |
| Β | |
| Β Β Β Β Β Β if ((z % 2) == 1):
| |
| Β Β Β Β Β Β Β Β livecells.append(x)
| |
| Β Β Β Β Β Β Β Β livecells.append(y)
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β deadcells.append(x)
| |
| Β Β Β Β Β Β Β Β deadcells.append(y)
| |
| Β | |
| Β Β Β Β Β Β for nx in xrange(x - 1, x + 2):
| |
| Β Β Β Β Β Β Β Β for ny in xrange(y - 1, y + 2):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β nz = g.getcell(nx, ny)
| |
| Β Β Β Β Β Β Β Β Β Β if (nz > 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(nx)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(ny)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(nz)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β g.setcell(nx, ny, 0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β ll += 3
| |
| Β | |
| Β Β Β Β return livecells
| |
| Β | |
| Β Β # Command to Grab, Remove and IDentify an OBJect:
| |
| Β Β def gridobj(self, ix, iy, gsize, gspacing, pos):
| |
| Β | |
| Β Β Β Β allcells = [ix, iy, g.getcell(ix, iy)]
| |
| Β Β Β Β g.setcell(ix, iy, 0)
| |
| Β Β Β Β livecells = []
| |
| Β Β Β Β deadcells = []
| |
| Β | |
| Β Β Β Β # This tacitly assumes the object is smaller than 1000-by-1000.
| |
| Β Β Β Β # But this is okay, since it is only used by the routing logic.
| |
| Β Β Β Β dleft = ix + 1000
| |
| Β Β Β Β dright = ix - 1000
| |
| Β Β Β Β dtop = iy + 1000
| |
| Β Β Β Β dbottom = iy - 1000
| |
| Β | |
| Β Β Β Β lleft = ix + 1000
| |
| Β Β Β Β lright = ix - 1000
| |
| Β Β Β Β ltop = iy + 1000
| |
| Β Β Β Β lbottom = iy - 1000
| |
| Β | |
| Β Β Β Β lpop = 0
| |
| Β Β Β Β dpop = 0
| |
| Β | |
| Β Β Β Β marker = 0
| |
| Β Β Β Β ll = 3
| |
| Β | |
| Β Β Β Β while (marker < ll):
| |
| Β Β Β Β Β Β x = allcells[marker]
| |
| Β Β Β Β Β Β y = allcells[marker+1]
| |
| Β Β Β Β Β Β z = allcells[marker+2]
| |
| Β Β Β Β Β Β marker += 3
| |
| Β | |
| Β Β Β Β Β Β if ((z % 2) == 1):
| |
| Β Β Β Β Β Β Β Β livecells.append(x)
| |
| Β Β Β Β Β Β Β Β livecells.append(y)
| |
| Β Β Β Β Β Β Β Β lleft = min(lleft, x)
| |
| Β Β Β Β Β Β Β Β lright = max(lright, x)
| |
| Β Β Β Β Β Β Β Β ltop = min(ltop, y)
| |
| Β Β Β Β Β Β Β Β lbottom = max(lbottom, y)
| |
| Β Β Β Β Β Β Β Β lpop += 1
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β deadcells.append(x)
| |
| Β Β Β Β Β Β Β Β deadcells.append(y)
| |
| Β Β Β Β Β Β Β Β dleft = min(dleft, x)
| |
| Β Β Β Β Β Β Β Β dright = max(dright, x)
| |
| Β Β Β Β Β Β Β Β dtop = min(dtop, y)
| |
| Β Β Β Β Β Β Β Β dbottom = max(dbottom, y)
| |
| Β Β Β Β Β Β Β Β dpop += 1
| |
| Β | |
| Β Β Β Β Β Β for nx in xrange(x - 1, x + 2):
| |
| Β Β Β Β Β Β Β Β for ny in xrange(y - 1, y + 2):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β nz = g.getcell(nx, ny)
| |
| Β Β Β Β Β Β Β Β Β Β if (nz > 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(nx)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(ny)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β allcells.append(nz)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β g.setcell(nx, ny, 0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β ll += 3
| |
| Β | |
| Β Β Β Β lwidth = max(0, 1 + lright - lleft)
| |
| Β Β Β Β lheight = max(0, 1 + lbottom - ltop)
| |
| Β Β Β Β dwidth = max(0, 1 + dright - dleft)
| |
| Β Β Β Β dheight = max(0, 1 + dbottom - dtop)
| |
| Β | |
| Β Β Β Β llength = max(lwidth, lheight)
| |
| Β Β Β Β lbreadth = min(lwidth, lheight)
| |
| Β Β Β Β dlength = max(dwidth, dheight)
| |
| Β Β Β Β dbreadth = min(dwidth, dheight)
| |
| Β | |
| Β Β Β Β self.gridsize = max(self.gridsize, llength)
| |
| Β | |
| Β Β Β Β objid = "unidentified"
| |
| Β Β Β Β bitstring = 0
| |
| Β | |
| Β Β Β Β if (lpop == 0):
| |
| Β Β Β Β Β Β objid = "nothing"
| |
| Β Β Β Β else:
| |
| Β Β Β Β Β Β if ((lwidth <= 7) & (lheight <= 7)):
| |
| Β Β Β Β Β Β Β Β for i in xrange(0, lpop*2, 2):
| |
| Β Β Β Β Β Β Β Β Β Β bitstring += (1 << ((livecells[i] - lleft) + 7*(livecells[i + 1] - ltop)))
| |
| Β | |
| Β Β Β Β Β Β Β Β if bitstring in self.cache:
| |
| Β Β Β Β Β Β Β Β Β Β objid = self.cache[bitstring]
| |
| Β | |
| Β Β Β Β if (objid == "unidentified"):
| |
| Β Β Β Β Β Β # This has passed through the routing logic without being identified,
| |
| Β Β Β Β Β Β # so save it in a temporary list for later identification:
| |
| Β Β Β Β Β Β self.unids.append(bitstring)
| |
| Β Β Β Β Β Β self.unids.append(livecells)
| |
| Β Β Β Β Β Β self.unids.append(lleft)
| |
| Β Β Β Β Β Β self.unids.append(ltop)
| |
| Β Β Β Β elif (objid != "nothing"):
| |
| Β Β Β Β Β Β # The object is non-empty, so add it to the census:
| |
| Β Β Β Β Β Β ux = int(0.5 + float(lleft)/float(gspacing))
| |
| Β Β Β Β Β Β uy = int(0.5 + float(ltop)/float(gspacing))
| |
| Β Β Β Β Β Β soupid = ux + (uy * gsize) + pos
| |
| Β | |
| Β Β Β Β Β Β # Check whether the cached object is in the set of decompositions
| |
| Β Β Β Β Β Β # (this is usually the case, unless for example it is a high-period
| |
| Β Β Β Β Β Β # albeit small spaceship):
| |
| Β Β Β Β Β Β if objid in self.decompositions:Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β for comp in self.decompositions[objid]:
| |
| Β Β Β Β Β Β Β Β Β Β self.incobject(comp, 1)
| |
| Β Β Β Β Β Β Β Β Β Β self.awardpoints2(soupid, comp)
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β self.incobject(objid, 1)
| |
| Β Β Β Β Β Β Β Β self.awardpoints2(soupid, objid)
| |
| Β | |
| Β | |
| Β Β # Tests for population periodicity:
| |
| Β Β def naivestab(self, period, security, length):
| |
| Β | |
| Β Β Β Β depth = 0
| |
| Β Β Β Β prevpop = 0
| |
| Β Β Β Β for i in xrange(length):
| |
| Β Β Β Β Β Β g.run(period)
| |
| Β Β Β Β Β Β currpop = int(g.getpop())
| |
| Β Β Β Β Β Β if (currpop == prevpop):
| |
| Β Β Β Β Β Β Β Β depth += 1
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β depth = 0
| |
| Β Β Β Β Β Β prevpop = currpop
| |
| Β Β Β Β Β Β if (depth == security):
| |
| Β Β Β Β Β Β Β Β # Population is periodic.
| |
| Β Β Β Β Β Β Β Β return True
| |
| Β | |
| Β Β Β Β return False
| |
| Β | |
| Β Β # This should catch most short-lived soups with few gliders produced:
| |
| Β Β def naivestab2(self, period, length):
| |
| Β | |
| Β Β Β Β for i in xrange(length):
| |
| Β Β Β Β Β Β r = g.getrect()
| |
| Β Β Β Β Β Β if (len(r) == 0):
| |
| Β Β Β Β Β Β Β Β return True
| |
| Β Β Β Β Β Β pop0 = int(g.getpop())
| |
| Β Β Β Β Β Β g.run(period)
| |
| Β Β Β Β Β Β hash1 = g.hash(r)
| |
| Β Β Β Β Β Β pop1 = int(g.getpop())
| |
| Β Β Β Β Β Β g.run(period)
| |
| Β Β Β Β Β Β hash2 = g.hash(r)
| |
| Β Β Β Β Β Β pop2 = int(g.getpop())
| |
| Β | |
| Β Β Β Β Β Β if ((hash1 == hash2) & (pop0 == pop1) & (pop1 == pop2)):
| |
| Β | |
| Β Β Β Β Β Β Β Β if (g.getrect() == r):
| |
| Β Β Β Β Β Β Β Β Β Β return True
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β g.run((2*int(max(r[2], r[3])/period)+1)*period)
| |
| Β Β Β Β Β Β Β Β hash3 = g.hash(r)
| |
| Β Β Β Β Β Β Β Β pop3 = int(g.getpop())
| |
| Β Β Β Β Β Β Β Β if ((hash2 == hash3) & (pop2 == pop3)):
| |
| Β Β Β Β Β Β Β Β Β Β return True
| |
| Β | |
| Β Β Β Β return False
| |
| Β Β Β Β Β Β
| |
| Β Β # Runs a pattern until stabilisation with a 99.99996% success rate.
| |
| Β Β # False positives are handled by a later error-correction stage.
| |
| Β Β def stabilise3(self):
| |
| Β | |
| Β Β Β Β # Phase I of stabilisation detection, designed to weed out patterns
| |
| Β Β Β Β # that stabilise into a cluster of low-period oscillators within
| |
| Β Β Β Β # about 6000 generations.
| |
| Β | |
| Β Β Β Β if (self.naivestab2(12, 10)):
| |
| Β Β Β Β Β Β return 4;
| |
| Β | |
| Β Β Β Β if (self.naivestab(12, 30, 200)):
| |
| Β Β Β Β Β Β return 4;
| |
| Β | |
| Β Β Β Β if (self.naivestab(30, 30, 200)):
| |
| Β Β Β Β Β Β return 5;
| |
| Β | |
| Β Β Β Β # Phase II of stabilisation detection, which is much more rigorous
| |
| Β Β Β Β # and based on oscar.py.
| |
| Β | |
| Β Β Β Β # Should be sufficient:
| |
| Β Β Β Β prect = [-2000, -2000, 4000, 4000]
| |
| Β | |
| Β Β Β Β # initialize lists
| |
| Β Β Β Β hashlist = []Β Β Β Β # for pattern hash values
| |
| Β Β Β Β genlist = []Β Β Β Β # corresponding generation counts
| |
| Β | |
| Β Β Β Β for j in xrange(4000):
| |
| Β | |
| Β Β Β Β Β Β g.run(30)
| |
| Β | |
| Β Β Β Β Β Β h = g.hash(prect)
| |
| Β | |
| Β Β Β Β Β Β # determine where to insert h into hashlist
| |
| Β Β Β Β Β Β pos = 0
| |
| Β Β Β Β Β Β listlen = len(hashlist)
| |
| Β Β Β Β Β Β while pos < listlen:
| |
| Β Β Β Β Β Β Β Β if h > hashlist[pos]:
| |
| Β Β Β Β Β Β Β Β Β Β pos += 1
| |
| Β Β Β Β Β Β Β Β elif h < hashlist[pos]:
| |
| Β Β Β Β Β Β Β Β Β Β # shorten lists and append info below
| |
| Β Β Β Β Β Β Β Β Β Β del hashlist[pos : listlen]
| |
| Β Β Β Β Β Β Β Β Β Β del genlist[pos : listlen]
| |
| Β Β Β Β Β Β Β Β Β Β break
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β period = (int(g.getgen()) - genlist[pos])
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β prevpop = g.getpop()
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β for i in xrange(20):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β g.run(period)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β currpop = g.getpop()
| |
| Β Β Β Β Β Β Β Β Β Β Β Β if (currpop != prevpop):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β period = max(period, 4000)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β break
| |
| Β Β Β Β Β Β Β Β Β Β Β Β prevpop = currpop
| |
| Β Β Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β Β Β return max(1 + int(math.log(period, 2)),3)
| |
| Β | |
| Β Β Β Β Β Β hashlist.insert(pos, h)
| |
| Β Β Β Β Β Β genlist.insert(pos, int(g.getgen()))
| |
| Β | |
| Β Β Β Β g.setalgo("HashLife")
| |
| Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β Β Β Β g.setbase(2)
| |
| Β Β Β Β g.setstep(16)
| |
| Β Β Β Β g.step()
| |
| Β Β Β Β stepsize = 12
| |
| Β Β Β Β g.setalgo("QuickLife")
| |
| Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β | |
| Β Β Β Β return 12
| |
| Β | |
| Β Β # Differs from oscar.py in that it detects absolute cycles, not eventual cycles.
| |
| Β Β def bijoscar(self, maxsteps):
| |
| Β | |
| Β Β Β Β initpop = int(g.getpop())
| |
| Β Β Β Β initrect = g.getrect()
| |
| Β Β Β Β if (len(initrect) == 0):
| |
| Β Β Β Β Β Β return 0
| |
| Β Β Β Β inithash = g.hash(initrect)
| |
| Β | |
| Β Β Β Β for i in xrange(maxsteps):
| |
| Β | |
| Β Β Β Β Β Β g.run(1)
| |
| Β | |
| Β Β Β Β Β Β if (int(g.getpop()) == initpop):
| |
| Β | |
| Β Β Β Β Β Β Β Β prect = g.getrect()
| |
| Β Β Β Β Β Β Β Β phash = g.hash(prect)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (phash == inithash):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β period = i + 1
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β if (prect == initrect):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β return period
| |
| Β Β Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β return -period
| |
| Β Β Β Β return -1
| |
| Β | |
| Β Β # For a non-moving unidentified object, we check the dictionary of
| |
| Β Β # memoized decompositions of possibly-pseudo-objects. If the object is
| |
| Β Β # not already in the dictionary, it will be memoized.
| |
| Β Β #
| |
| Β Β # Low-period spaceships are also separated by this routine, although
| |
| Β Β # this is less important now that there is a more bespoke prodecure
| |
| Β Β # to handle disjoint unions of standard spaceships.
| |
| Β Β #
| |
| Β Β # @param movingΒ a bool which specifies whether the object is moving
| |
| Β Β def enter_unid(self, unidname, soupid, moving):
| |
| Β | |
| Β Β Β Β if not(unidname in self.decompositions):
| |
| Β | |
| Β Β Β Β Β Β # Separate into pure components:
| |
| Β Β Β Β Β Β if (moving):
| |
| Β Β Β Β Β Β Β Β g.setrule("APG_CoalesceObjects_" + self.rg.alphanumeric)
| |
| Β Β Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β Β Β g.setstep(3)
| |
| Β Β Β Β Β Β Β Β g.step()
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β pseudo_bangbang(self.rg.alphanumeric)
| |
| Β | |
| Β Β Β Β Β Β listoflists = [] # which incidentally don't contain themselves.
| |
| Β | |
| Β Β Β Β Β Β # Someone who plays the celllo:
| |
| Β Β Β Β Β Β celllist = g.join(g.getcells(g.getrect()), [0])
| |
| Β | |
| Β Β Β Β Β Β for i in xrange(0, len(celllist)-1, 3):
| |
| Β Β Β Β Β Β Β Β if (g.getcell(celllist[i], celllist[i+1]) != 0):
| |
| Β Β Β Β Β Β Β Β Β Β livecells = self.grabobj(celllist[i], celllist[i+1])
| |
| Β Β Β Β Β Β Β Β Β Β if (len(livecells) > 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β listoflists.append(livecells)
| |
| Β | |
| Β Β Β Β Β Β listofobjs = []
| |
| Β | |
| Β Β Β Β Β Β for livecells in listoflists:
| |
| Β | |
| Β Β Β Β Β Β Β Β g.new("Subcomponent")
| |
| Β Β Β Β Β Β Β Β #####
| |
| Β Β Β Β Β Β Β Β g.setalgo("HashLife")
| |
| Β Β Β Β Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β Β Β Β Β Β Β Β g.putcells(livecells)
| |
| Β Β Β Β Β Β Β Β period = self.bijoscar(1000)
| |
| Β Β Β Β Β Β Β Β canonised = canonise(abs(period))
| |
| Β Β Β Β Β Β Β Β if (period < 0):
| |
| Β Β Β Β Β Β Β Β Β Β listofobjs.append("xq"+str(0-period)+"_"+canonised)
| |
| Β Β Β Β Β Β Β Β elif (period == 1):
| |
| Β Β Β Β Β Β Β Β Β Β listofobjs.append("xs"+str(len(livecells)/2)+"_"+canonised)
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β listofobjs.append("xp"+str(period)+"_"+canonised)
| |
| Β | |
| Β Β Β Β Β Β self.decompositions[unidname] = listofobjs
| |
| Β | |
| Β Β Β Β # Actually add to the census:
| |
| Β Β Β Β for comp in self.decompositions[unidname]:
| |
| Β Β Β Β Β Β self.incobject(comp, 1)
| |
| Β Β Β Β Β Β self.awardpoints2(soupid, comp)
| |
| Β | |
| Β Β # This function has lots of arguments (hence the name):
| |
| Β Β #
| |
| Β Β # @param gsizeΒ Β the square-root of the number of soups per page
| |
| Β Β # @param gspacingΒ the minimum distance between centres of soups
| |
| Β Β # @param ashesΒ Β a list of cell lists
| |
| Β Β # @param stepsizeΒ binary logarithm of amount of time to coalesce objects
| |
| Β Β # @param intergenΒ binary logarithm of amount of time to run HashLife
| |
| Β Β # @param posΒ Β Β the index of the first soup on the page
| |
| Β Β # @param symΒ Β Β the symmetry of the soup
| |
| Β Β def teenager(self, gsize, gspacing, ashes, stepsize, intergen, pos, sym):
| |
| Β | |
| Β Β Β Β # For error-correction:
| |
| Β Β Β Β if (intergen > 0):
| |
| Β Β Β Β Β Β g.setalgo("HashLife")
| |
| Β Β Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β | |
| Β Β Β Β # If this gets incremented, we panic and perform error-correction:
| |
| Β Β Β Β pathological = 0
| |
| Β | |
| Β Β Β Β # Draw the soups:
| |
| Β Β Β Β for i in xrange(gsize * gsize):
| |
| Β | |
| Β Β Β Β Β Β x = int(i % gsize)
| |
| Β Β Β Β Β Β y = int(i / gsize)
| |
| Β | |
| Β Β Β Β Β Β g.putcells(ashes[3*i], gspacing * x, gspacing * y)
| |
| Β | |
| Β Β Β Β # Because why not?
| |
| Β Β Β Β g.fit()
| |
| Β Β Β Β g.update()
| |
| Β | |
| Β Β Β Β # For error-correction:
| |
| Β Β Β Β if (intergen > 0):
| |
| Β Β Β Β Β Β g.setbase(2)
| |
| Β Β Β Β Β Β g.setstep(intergen)
| |
| Β Β Β Β Β Β g.step()
| |
| Β | |
| Β Β Β Β # Apply rules to coalesce objects and expunge annoyances such as
| |
| Β Β Β Β # blocks, blinkers, beehives and gliders:
| |
| Β Β Β Β start_time = time.clock()
| |
| Β Β Β Β self.census(stepsize, sym)
| |
| Β Β Β Β end_time = time.clock()
| |
| Β Β Β Β self.ruletime += (end_time - start_time)
| |
| Β | |
| Β | |
| Β Β Β Β # Now begin identifying objects:
| |
| Β Β Β Β start_time = time.clock()
| |
| Β Β Β Β celllist = g.join(g.getcells(g.getrect()), [0])
| |
| Β | |
| Β Β Β Β if (len(celllist) > 2):
| |
| Β Β Β Β Β Β for i in xrange(0, len(celllist)-1, 3):
| |
| Β Β Β Β Β Β Β Β if (g.getcell(celllist[i], celllist[i+1]) != 0):
| |
| Β Β Β Β Β Β Β Β Β Β self.gridobj(celllist[i], celllist[i+1], gsize, gspacing, pos)
| |
| Β | |
| Β Β Β Β # If we have leftover unidentified objects, attempt to canonise them:
| |
| Β Β Β Β while (len(self.unids) > 0):
| |
| Β Β Β Β Β Β ux = int(0.5 + float(self.unids[-2])/float(gspacing))
| |
| Β Β Β Β Β Β uy = int(0.5 + float(self.unids[-1])/float(gspacing))
| |
| Β Β Β Β Β Β soupid = ux + (uy * gsize) + pos
| |
| Β Β Β Β Β Β unidname = self.process_unid()
| |
| Β Β Β Β Β Β if (unidname == "PATHOLOGICAL"):
| |
| Β Β Β Β Β Β Β Β pathological += 1
| |
| Β Β Β Β Β Β if (unidname != "nothing"):
| |
| Β | |
| Β Β Β Β Β Β Β Β if ((unidname[0] == 'U') & (unidname[1] == 'S') & (unidname[2] == 'S')):
| |
| Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β Β Β # Union of standard spaceships:
| |
| Β Β Β Β Β Β Β Β Β Β countlist = unidname.split('_')
| |
| Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β Β Β self.incobject("xq4_6frc", int(countlist[1]))
| |
| Β Β Β Β Β Β Β Β Β Β for i in xrange(int(countlist[1])):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.awardpoints2(soupid, "xq4_6frc")
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β self.incobject("xq4_27dee6", int(countlist[2]))
| |
| Β Β Β Β Β Β Β Β Β Β for i in xrange(int(countlist[2])):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.awardpoints2(soupid, "xq4_27dee6")
| |
| Β Β Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β Β Β self.incobject("xq4_27deee6", int(countlist[3]))
| |
| Β Β Β Β Β Β Β Β Β Β for i in xrange(int(countlist[3])):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.awardpoints2(soupid, "xq4_27deee6")
| |
| Β Β Β Β Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β elif ((unidname[0] == 'x') & ((unidname[1] == 's') | (unidname[1] == 'p'))):
| |
| Β Β Β Β Β Β Β Β Β Β self.enter_unid(unidname, soupid, False)
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β if ((unidname[0] == 'x') & (unidname[1] == 'q') & (unidname[3] == '_')):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β # Separates low-period (<= 9) non-standard spaceships in medium proximity:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.enter_unid(unidname, soupid, True)
| |
| Β Β Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.incobject(unidname, 1)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β self.awardpoints2(soupid, unidname)
| |
| Β | |
| Β Β Β Β end_time = time.clock()
| |
| Β Β Β Β self.gridtime += (end_time - start_time)
| |
| Β | |
| Β Β Β Β return pathological
| |
| Β | |
| Β Β def stabilise_soups_parallel(self, root, pos, gsize, sym):
| |
| Β | |
| Β Β Β Β souplist = [[sym, root + str(pos + i)] for i in xrange(gsize * gsize)]
| |
| Β | |
| Β Β Β Β return self.stabilise_soups_parallel_orig(gsize, souplist, pos)
| |
| Β | |
| Β Β def stabilise_soups_parallel_list(self, gsize, stringlist, pos):
| |
| Β | |
| Β Β Β Β souplist = [s.split('/') for s in stringlist]
| |
| Β | |
| Β Β Β Β return self.stabilise_soups_parallel_orig(gsize, souplist, pos)
| |
| Β | |
| Β Β # This basically orchestrates everything:
| |
| Β Β def stabilise_soups_parallel_orig(self, gsize, souplist, pos):
| |
| Β | |
| Β Β Β Β ashes = []
| |
| Β Β Β Β stepsize = 3
| |
| Β | |
| Β Β Β Β g.new("Random soups")
| |
| Β Β Β Β #####
| |
| Β Β Β Β g.setalgo("QuickLife")
| |
| Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β | |
| Β Β Β Β gspacing = 0
| |
| Β | |
| Β Β Β Β # Generate and run the soups until stabilisation:
| |
| Β Β Β Β for i in xrange(gsize * gsize):
| |
| Β | |
| Β Β Β Β Β Β if (i < len(souplist)):
| |
| Β | |
| Β Β Β Β Β Β Β Β sym = souplist[i][0]
| |
| Β Β Β Β Β Β Β Β prehash = souplist[i][1]
| |
| Β | |
| Β Β Β Β Β Β Β Β # Generate the soup from the SHA-256 of the concatenation of the
| |
| Β Β Β Β Β Β Β Β # seed with the index:
| |
| Β Β Β Β Β Β Β Β g.putcells(hashsoup(prehash, sym), 0, 0)
| |
| Β | |
| Β Β Β Β Β Β # Run the soup until stabilisation:
| |
| Β Β Β Β Β Β start_time = time.clock()
| |
| Β Β Β Β Β Β stepsize = max(stepsize, self.stabilise3())
| |
| Β Β Β Β Β Β end_time = time.clock()
| |
| Β Β Β Β Β Β self.qlifetime += (end_time - start_time)
| |
| Β | |
| Β Β Β Β Β Β # Ironically, the spelling of this variable is incurrrect:
| |
| Β Β Β Β Β Β currrect = g.getrect()
| |
| Β Β Β Β Β Β ashes.append(g.getcells(currrect))
| |
| Β | |
| Β Β Β Β Β Β if (len(currrect) == 4):
| |
| Β Β Β Β Β Β Β Β ashes.append(currrect[0])
| |
| Β Β Β Β Β Β Β Β ashes.append(currrect[1])
| |
| Β Β Β Β Β Β Β Β # Choose the grid spacing based on the size of the ash:
| |
| Β Β Β Β Β Β Β Β gspacing = max(gspacing, 2*currrect[2])
| |
| Β Β Β Β Β Β Β Β gspacing = max(gspacing, 2*currrect[3])
| |
| Β Β Β Β Β Β Β Β g.select(currrect)
| |
| Β Β Β Β Β Β Β Β g.clear(0)
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β ashes.append(0)
| |
| Β Β Β Β Β Β Β Β ashes.append(0)
| |
| Β Β Β Β Β Β g.select([])
| |
| Β | |
| Β Β Β Β # Account for any extra enlargement caused by running CoalesceObjects:
| |
| Β Β Β Β gspacing += 2 ** (stepsize + 1) + 1000
| |
| Β | |
| Β Β Β Β start_time = time.clock()
| |
| Β | |
| Β Β Β Β # Remember the dictionary, just in case we have a pathological object:
| |
| Β Β Β Β prevdict = self.objectcounts.copy()
| |
| Β Β Β Β prevscores = self.soupscores.copy()
| |
| Β Β Β Β prevunids = self.superunids[:]
| |
| Β | |
| Β Β Β Β # Process the soups:
| |
| Β Β Β Β returncode = self.teenager(gsize, gspacing, ashes, stepsize, 0, pos, sym)
| |
| Β | |
| Β Β Β Β end_time = time.clock()
| |
| Β | |
| Β Β Β Β # Calculate the mean delay incurred (excluding qlifetime or error-correction):
| |
| Β Β Β Β meandelay = (end_time - start_time) / (gsize * gsize)
| |
| Β | |
| Β Β Β Β if (returncode > 0):
| |
| Β Β Β Β Β Β if (self.skipErrorCorrection == False):
| |
| Β Β Β Β Β Β Β Β # Arrrggghhhh, there's a pathological object! Usually this means
| |
| Β Β Β Β Β Β Β Β # that naive stabilisation detection returned a false positive.
| |
| Β Β Β Β Β Β Β Β self.resets += 1
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β # Reset the object counts:
| |
| Β Β Β Β Β Β Β Β self.objectcounts = prevdict
| |
| Β Β Β Β Β Β Β Β self.soupscores = prevscores
| |
| Β Β Β Β Β Β Β Β self.superunids = prevunids
| |
| Β | |
| Β Β Β Β Β Β Β Β # 2^18 generations should suffice. This takes about 30 seconds in
| |
| Β Β Β Β Β Β Β Β # HashLife, but error-correction only occurs very infrequently, so
| |
| Β Β Β Β Β Β Β Β # this has a negligible impact on mean performance:
| |
| Β Β Β Β Β Β Β Β gspacing += 2 ** 19
| |
| Β Β Β Β Β Β Β Β stepsize = max(stepsize, 12)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β # Clear the universe:
| |
| Β Β Β Β Β Β Β Β g.new("Error-correcting phase")
| |
| Β Β Β Β Β Β Β Β self.teenager(gsize, gspacing, ashes, stepsize, 18, pos, sym)
| |
| Β | |
| Β Β Β Β # Erase any ashes. Not least because England usually loses...
| |
| Β Β Β Β ashes = []
| |
| Β | |
| Β Β Β Β # Return the mean delay so that we can use machine-learning to
| |
| Β Β Β Β # find the optimal value of sqrtspp:
| |
| Β Β Β Β return meandelay
| |
| Β | |
| Β Β def reset(self):
| |
| Β | |
| Β Β Β Β self.objectcounts = {}
| |
| Β Β Β Β self.soupscores = {}
| |
| Β Β Β Β self.alloccur = {}
| |
| Β Β Β Β self.superunids = []
| |
| Β Β Β Β self.unids = []
| |
| Β | |
| Β Β # Pop the last unidentified object from the stack, and attempt to
| |
| Β Β # ascertain its period and classify it.
| |
| Β Β def process_unid(self):
| |
| Β | |
| Β Β Β Β g.new("Unidentified object")
| |
| Β Β Β Β g.setalgo("QuickLife")
| |
| Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β Β Β Β y = self.unids.pop()
| |
| Β Β Β Β x = self.unids.pop()
| |
| Β Β Β Β livecells = self.unids.pop()
| |
| Β Β Β Β bitstring = self.unids.pop()
| |
| Β Β Β Β g.putcells(livecells, -x, -y, 1, 0, 0, 1, "or")
| |
| Β Β Β Β period = self.bijoscar(1000)
| |
| Β Β Β Β
| |
| Β Β Β Β if (period == -1):
| |
| Β Β Β Β Β Β # Infinite growth pattern, probably. Most infinite-growth
| |
| Β Β Β Β Β Β # patterns are linear-growth (such as puffers, wickstretchers,
| |
| Β Β Β Β Β Β # guns etc.) so we analyse to see whether we have a linear-
| |
| Β Β Β Β Β Β # growth pattern:
| |
| Β Β Β Β Β Β descriptor = linearlyse(1500)
| |
| Β Β Β Β Β Β if (descriptor[0] == "y"):
| |
| Β Β Β Β Β Β Β Β return descriptor
| |
| Β | |
| Β Β Β Β Β Β # Similarly check for irregular power-law growth. This will
| |
| Β Β Β Β Β Β # catch replicators, for instance. Spend around 375 000
| |
| Β Β Β Β Β Β # generations; this seems like a reasonable amount of time.
| |
| Β Β Β Β Β Β descriptor = powerlyse(8, 1500)
| |
| Β Β Β Β Β Β if (descriptor[0] == "z"):
| |
| Β Β Β Β Β Β Β Β return descriptor
| |
| Β | |
| Β Β Β Β Β Β # It may be an unstabilised ember that slipped through the net,
| |
| Β Β Β Β Β Β # but this will be handled by error-correction (unless it
| |
| Β Β Β Β Β Β # persists another 2^18 gens, which is so unbelievably improbable
| |
| Β Β Β Β Β Β # that you are more likely to be picked up by a passing ship in
| |
| Β Β Β Β Β Β # the vacuum of space).
| |
| Β Β Β Β Β Β self.superunids.append(livecells)
| |
| Β Β Β Β Β Β self.superunids.append(x)
| |
| Β Β Β Β Β Β self.superunids.append(y)
| |
| Β Β Β Β Β Β
| |
| Β Β Β Β Β Β return "PATHOLOGICAL"
| |
| Β Β Β Β elif (period == 0):
| |
| Β Β Β Β Β Β return "nothing"
| |
| Β Β Β Β else:
| |
| Β Β Β Β Β Β if (period == -4):
| |
| Β | |
| Β Β Β Β Β Β Β Β triple = countxwsses()
| |
| Β | |
| Β Β Β Β Β Β Β Β if (triple != (-1, -1, -1)):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β # Union of Standard Spaceships:
| |
| Β Β Β Β Β Β Β Β Β Β return ("USS_" + str(triple[0]) + "_" + str(triple[1]) + "_" + str(triple[2]))
| |
| Β | |
| Β Β Β Β Β Β
| |
| Β Β Β Β Β Β canonised = canonise(abs(period))
| |
| Β | |
| Β Β Β Β Β Β if (canonised == "#"):
| |
| Β | |
| Β Β Β Β Β Β Β Β # Okay, we know that it's an oscillator or spaceship with
| |
| Β Β Β Β Β Β Β Β # a non-astronomical period. But it's too large to canonise
| |
| Β Β Β Β Β Β Β Β # in any of its phases (i.e. transcends a 40-by-40 box).
| |
| Β Β Β Β Β Β Β Β self.superunids.append(livecells)
| |
| Β Β Β Β Β Β Β Β self.superunids.append(x)
| |
| Β Β Β Β Β Β Β Β self.superunids.append(y)
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β # Append a suffix according to whether it is a still-life,
| |
| Β Β Β Β Β Β Β Β # oscillator or moving object:
| |
| Β Β Β Β Β Β Β Β if (period == 1):
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("ov_s"+str(len(livecells)/2))
| |
| Β Β Β Β Β Β Β Β elif (period > 0):
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("ov_p"+str(period))
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("ov_q"+str(0-period))
| |
| Β | |
| Β Β Β Β Β Β Β Β return descriptor
| |
| Β Β Β Β Β Β
| |
| Β Β Β Β Β Β else:
| |
| Β | |
| Β Β Β Β Β Β Β Β # Prepend a prefix according to whether it is a still-life,
| |
| Β Β Β Β Β Β Β Β # oscillator or moving object:
| |
| Β Β Β Β Β Β Β Β if (period == 1):
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("xs"+str(len(livecells)/2)+"_"+canonised)
| |
| Β Β Β Β Β Β Β Β elif (period > 0):
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("xp"+str(period)+"_"+canonised)
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β descriptor = ("xq"+str(0-period)+"_"+canonised)
| |
| Β | |
| Β Β Β Β Β Β Β Β if (bitstring > 0):
| |
| Β Β Β Β Β Β Β Β Β Β self.cache[bitstring] = descriptor
| |
| Β | |
| Β Β Β Β Β Β Β Β return descriptor
| |
| Β | |
| Β Β # This doesn't really do much, since unids should be empty and
| |
| Β Β # actual pathological/oversized objects will rarely arise naturally.
| |
| Β Β def display_unids(self):
| |
| Β | |
| Β Β Β Β g.new("Unidentified objects")
| |
| Β Β Β Β g.setalgo("QuickLife")
| |
| Β Β Β Β g.setrule(self.rg.slashed)
| |
| Β | |
| Β Β Β Β rowlength = 1 + int(math.sqrt(len(self.superunids)/3))
| |
| Β | |
| Β Β Β Β for i in xrange(len(self.superunids)/3):
| |
| Β | |
| Β Β Β Β Β Β xpos = i % rowlength
| |
| Β Β Β Β Β Β ypos = int(i / rowlength)
| |
| Β | |
| Β Β Β Β Β Β g.putcells(self.superunids[3*i], xpos * (self.gridsize + 8) - self.superunids[3*i + 1], ypos * (self.gridsize + 8) - self.superunids[3*i + 2], 1, 0, 0, 1, "or")
| |
| Β | |
| Β Β Β Β g.fit()
| |
| Β Β Β Β g.update()
| |
| Β | |
| Β Β def compactify_scores(self):
| |
| Β | |
| Β Β Β Β # Number of soups to record:
| |
| Β Β Β Β highscores = 100
| |
| Β Β Β Β ilist = sorted(self.soupscores.iteritems(), key=operator.itemgetter(1), reverse=True)
| |
| Β | |
| Β Β Β Β # Empty the high score table:
| |
| Β Β Β Β self.soupscores = {}
| |
| Β Β Β Β
| |
| Β Β Β Β for soupnum, score in ilist[:highscores]:
| |
| Β Β Β Β Β Β self.soupscores[soupnum] = score
| |
| Β | |
| Β Β # Saves a machine-readable textual file containing the census:
| |
| Β Β def save_progress(self, numsoups, root, symmetry='C1', save_file=True, payosha256_key=None):
| |
| Β | |
| Β Β Β Β g.show("Saving progress...")
| |
| Β | |
| Β Β Β Β # Count the total number of objects:
| |
| Β Β Β Β totobjs = 0
| |
| Β Β Β Β censustable = "@CENSUS TABLE\n"
| |
| Β Β Β Β tlist = sorted(self.objectcounts.iteritems(), key=operator.itemgetter(1), reverse=True)
| |
| Β Β Β Β for objname, count in tlist:
| |
| Β Β Β Β Β Β totobjs += count
| |
| Β Β Β Β Β Β censustable += objname + " " + str(count) + "\n"
| |
| Β | |
| Β Β Β Β g.show("Writing header information...")
| |
| Β | |
| Β Β Β Β # The MD5 hash of the root string:
| |
| Β Β Β Β md5root = hashlib.md5(root).hexdigest()
| |
| Β | |
| Β Β Β Β # Header information:
| |
| Β Β Β Β results = "@VERSION v1.1\n"
| |
| Β Β Β Β results += "@MD5 "+md5root+"\n"
| |
| Β Β Β Β results += "@ROOT "+root+"\n"
| |
| Β Β Β Β results += "@RULE "+self.rg.alphanumeric+"\n"
| |
| Β Β Β Β results += "@SYMMETRY "+symmetry+"\n"
| |
| Β Β Β Β results += "@NUM_SOUPS "+str(numsoups)+"\n"
| |
| Β Β Β Β results += "@NUM_OBJECTS "+str(totobjs)+"\n"
| |
| Β | |
| Β Β Β Β results += "\n"
| |
| Β | |
| Β Β Β Β # Census table:
| |
| Β Β Β Β results += censustable
| |
| Β | |
| Β Β Β Β g.show("Compactifying score table...")
| |
| Β | |
| Β Β Β Β results += "\n"
| |
| Β | |
| Β Β Β Β # Number of soups to record:
| |
| Β Β Β Β highscores = 100
| |
| Β | |
| Β Β Β Β results += "@TOP "+str(highscores)+"\n"
| |
| Β | |
| Β Β Β Β ilist = sorted(self.soupscores.iteritems(), key=operator.itemgetter(1), reverse=True)
| |
| Β | |
| Β Β Β Β # Empty the high score table:
| |
| Β Β Β Β self.soupscores = {}
| |
| Β Β Β Β
| |
| Β Β Β Β for soupnum, score in ilist[:highscores]:
| |
| Β Β Β Β Β Β self.soupscores[soupnum] = score
| |
| Β Β Β Β Β Β results += str(soupnum) + " " + str(score) + "\n"
| |
| Β | |
| Β Β Β Β g.show("Saving soupids for rare objects...")
| |
| Β | |
| Β Β Β Β results += "\n@SAMPLE_SOUPIDS\n"
| |
| Β Β Β Β for objname, count in tlist:
| |
| Β Β Β Β Β Β # blinkers and gliders have no alloccur[] entry for some reason,
| |
| Β Β Β Β Β Β # so the line below avoids errors in B3/S23, maybe other rules too?
| |
| Β Β Β Β Β Β if objname in self.alloccur:
| |
| Β Β Β Β Β Β Β Β results += objname
| |
| Β Β Β Β Β Β Β Β for soup in self.alloccur[objname]:
| |
| Β Β Β Β Β Β Β Β Β Β results += " " + str(soup)
| |
| Β Β Β Β Β Β Β Β results += "\n"
| |
| Β | |
| Β Β Β Β g.show("Writing progress file...")
| |
| Β | |
| Β Β Β Β dirname = g.getdir("data")
| |
| Β Β Β Β separator = dirname[-1]
| |
| Β Β Β Β progresspath = dirname + "apgsearch" + separator + "progress" + separator
| |
| Β Β Β Β if not os.path.exists(progresspath):
| |
| Β Β Β Β Β Β os.makedirs(progresspath)
| |
| Β | |
| Β Β Β Β filename = progresspath + "search_" + md5root + ".txt"
| |
| Β Β Β Β
| |
| Β Β Β Β try:
| |
| Β Β Β Β Β Β f = open(filename, 'w')
| |
| Β Β Β Β Β Β f.write(results)
| |
| Β Β Β Β Β Β f.close()
| |
| Β Β Β Β except:
| |
| Β Β Β Β Β Β g.warn("Unable to create progress file:\n" + filename)
| |
| Β | |
| Β Β Β Β if payosha256_key is not None:
| |
| Β Β Β Β Β Β if (len(payosha256_key) > 0):
| |
| Β Β Β Β Β Β Β Β return catagolue_results(results, payosha256_key, "post_apgsearch_haul")
| |
| Β | |
| Β Β # Save soup RLE:
| |
| Β Β def save_soup(self, root, soupnum, symmetry):
| |
| Β | |
| Β Β Β Β # Soup pattern will be stored in a temporary directory:
| |
| Β Β Β Β souphash = hashlib.sha256(root + str(soupnum))
| |
| Β Β Β Β rlepath = souphash.hexdigest()
| |
| Β Β Β Β rlepath = g.getdir("temp") + rlepath + ".rle"
| |
| Β Β Β Β
| |
| Β Β Β Β results = "<a href=\"open:" + rlepath + "\">"
| |
| Β Β Β Β results += str(soupnum)
| |
| Β Β Β Β results += "</a>"
| |
| Β | |
| Β Β Β Β # Try to write soup patterns to file "rlepath":
| |
| Β Β Β Β try:
| |
| Β Β Β Β Β Β g.store(hashsoup(root + str(soupnum), symmetry), rlepath)
| |
| Β Β Β Β except:
| |
| Β Β Β Β Β Β g.warn("Unable to create soup pattern:\n" + rlepath)
| |
| Β | |
| Β Β Β Β return results
| |
| Β Β Β Β
| |
| Β Β # Display results in Help window:
| |
| Β Β def display_census(self, numsoups, root, symmetry):
| |
| Β | |
| Β Β Β Β dirname = g.getdir("data")
| |
| Β Β Β Β separator = dirname[-1]
| |
| Β Β Β Β apgpath = dirname + "apgsearch" + separator
| |
| Β Β Β Β objectspath = apgpath + "objects" + separator + self.rg.alphanumeric + separator
| |
| Β Β Β Β if not os.path.exists(objectspath):
| |
| Β Β Β Β Β Β os.makedirs(objectspath)
| |
| Β | |
| Β Β Β Β results = "<html>\n<title>Census results</title>\n<body bgcolor=\"#FFFFCE\">\n"
| |
| Β Β Β Β results += "<p>Census results after processing " + str(numsoups) + " soups (seed = " + root + ", symmetry = " + symmetry + "):\n"
| |
| Β | |
| Β Β Β Β tlist = sorted(self.objectcounts.iteritems(), key=operator.itemgetter(1), reverse=True)Β Β
| |
| Β Β Β Β results += "<p><center>\n"
| |
| Β Β Β Β results += "<table cellspacing=1 border=2 cols=2>\n"
| |
| Β Β Β Β results += "<tr><td> Object </td><td align=center> Common name </td>\n"
| |
| Β Β Β Β results += "<td align=right> Count </td><td> Sample occurrences </td></tr>\n"
| |
| Β Β Β Β for objname, count in tlist:
| |
| Β Β Β Β Β Β if (objname[0] == 'x'):
| |
| Β Β Β Β Β Β Β Β if (objname[1] == 'p'):
| |
| Β Β Β Β Β Β Β Β Β Β results += "<tr bgcolor=\"#CECECF\">"
| |
| Β Β Β Β Β Β Β Β elif (objname[1] == 'q'):
| |
| Β Β Β Β Β Β Β Β Β Β results += "<tr bgcolor=\"#CEFFCE\">"
| |
| Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β results += "<tr>"
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β results += "<tr bgcolor=\"#FFCECE\">"
| |
| Β Β Β Β Β Β results += "<td>"
| |
| Β Β Β Β Β Β results += " "
| |
| Β Β Β Β Β Β
| |
| Β Β Β Β Β Β # Using "open:" link enables one to click on the object name to open the pattern in Golly:
| |
| Β Β Β Β Β Β rlepath = objectspath + objname + ".rle"
| |
| Β Β Β Β Β Β if (objname[0] == 'x'):
| |
| Β Β Β Β Β Β Β Β results += "<a href=\"open:" + rlepath + "\">"
| |
| Β Β Β Β Β Β # If the name is longer than that of the block-laying switch engine:
| |
| Β Β Β Β Β Β if len(objname) > 51:
| |
| Β Β Β Β Β Β Β Β # Contract name and include ellipsis:
| |
| Β Β Β Β Β Β Β Β results += objname[:40] + "…" + objname[-10:]
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β results += objname
| |
| Β Β Β Β Β Β if (objname[0] == 'x'):
| |
| Β Β Β Β Β Β Β Β results += "</a>"
| |
| Β Β Β Β Β Β results += " "
| |
| Β | |
| Β Β Β Β Β Β if (objname[0] == 'x'):
| |
| Β Β Β Β Β Β Β Β # save object in rlepath if it doesn't exist
| |
| Β Β Β Β Β Β Β Β if not os.path.exists(rlepath):
| |
| Β Β Β Β Β Β Β Β Β Β # Canonised objects are at most 40-by-40:
| |
| Β Β Β Β Β Β Β Β Β Β rledata = "x = 40, y = 40, rule = " + self.rg.slashed + "\n"
| |
| Β Β Β Β Β Β Β Β Β Β # http://ferkeltongs.livejournal.com/15837.html
| |
| Β Β Β Β Β Β Β Β Β Β compact = objname.split('_')[1] + "z"
| |
| Β Β Β Β Β Β Β Β Β Β i = 0
| |
| Β Β Β Β Β Β Β Β Β Β strip = []
| |
| Β Β Β Β Β Β Β Β Β Β while (i < len(compact)):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β c = ord2(compact[i])
| |
| Β Β Β Β Β Β Β Β Β Β Β Β if (c >= 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (c < 32):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β # Conventional character:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(c)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (c == 35):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β # End of line:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (len(strip) == 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β for j in xrange(5):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β for d in strip:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β if ((d & (1 << j)) > 0):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β rledata += "o"
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β rledata += "b"
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β rledata += "$\n"
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip = []
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β # Multispace character:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (c >= 33):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β if (c == 34):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β i += 1
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β d = ord2(compact[i])
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β for j in xrange(d):
| |
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β strip.append(0)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β i += 1
| |
| Β Β Β Β Β Β Β Β Β Β # End of pattern representation:
| |
| Β Β Β Β Β Β Β Β Β Β rledata += "!\n"
| |
| Β Β Β Β Β Β Β Β Β Β try:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β f = open(rlepath, 'w')
| |
| Β Β Β Β Β Β Β Β Β Β Β Β f.write(rledata)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β f.close()
| |
| Β Β Β Β Β Β Β Β Β Β except:
| |
| Β Β Β Β Β Β Β Β Β Β Β Β g.warn("Unable to create object pattern:\n" + rlepath)
| |
| Β Β Β Β Β Β
| |
| Β Β Β Β Β Β results += "</td><td align=center> "
| |
| Β Β Β Β Β Β if (objname in self.commonnames):
| |
| Β Β Β Β Β Β Β Β results += self.commonnames[objname][0]
| |
| Β Β Β Β Β Β results += " </td><td align=right> " + str(count) + " "
| |
| Β Β Β Β Β Β results += "</td><td>"
| |
| Β Β Β Β Β Β if objname in self.alloccur:
| |
| Β Β Β Β Β Β Β Β results += " "
| |
| Β Β Β Β Β Β Β Β for soup in self.alloccur[objname]:
| |
| Β Β Β Β Β Β Β Β Β Β results += self.save_soup(root, soup, symmetry)
| |
| Β Β Β Β Β Β Β Β Β Β results += " "
| |
| Β Β Β Β Β Β results += "</td></tr>\n"
| |
| Β Β Β Β results += "</table>\n</center>\n"
| |
| Β | |
| Β Β Β Β ilist = sorted(self.soupscores.iteritems(), key=operator.itemgetter(1), reverse=True)
| |
| Β Β Β Β results += "<p><center>\n"
| |
| Β Β Β Β results += "<table cellspacing=1 border=2 cols=2>\n"
| |
| Β Β Β Β results += "<tr><td> Soup number </td><td align=right> Score </td></tr>\n"
| |
| Β Β Β Β for soupnum, score in ilist[:50]:
| |
| Β Β Β Β Β Β results += "<tr><td> "
| |
| Β Β Β Β Β Β results += self.save_soup(root, soupnum, symmetry)
| |
| Β Β Β Β Β Β results += " </td><td align=right> " + str(score) + " </td></tr>\n"
| |
| Β Β Β Β
| |
| Β Β Β Β results += "</table>\n</center>\n"
| |
| Β Β Β Β results += "</body>\n</html>\n"
| |
| Β Β Β Β
| |
| Β Β Β Β htmlname = apgpath + "latest_census.html"
| |
| Β Β Β Β try:
| |
| Β Β Β Β Β Β f = open(htmlname, 'w')
| |
| Β Β Β Β Β Β f.write(results)
| |
| Β Β Β Β Β Β f.close()
| |
| Β Β Β Β Β Β g.open(htmlname)
| |
| Β Β Β Β except:
| |
| Β Β Β Β Β Β g.warn("Unable to create html file:\n" + htmlname)
| |
| Β Β Β Β
| |
| Β | |
| # Converts a base-36 case-insensitive alphanumeric character into a
| |
| # numerical value.
| |
| def ord2(char):
| |
| Β | |
| Β Β x = ord(char)
| |
| Β | |
| Β Β if ((x >= 48) & (x < 58)):
| |
| Β Β Β Β return x - 48
| |
| Β | |
| Β Β if ((x >= 65) & (x < 91)):
| |
| Β Β Β Β return x - 55
| |
| Β | |
| Β Β if ((x >= 97) & (x < 123)):
| |
| Β Β Β Β return x - 87
| |
| Β | |
| Β Β return -1
| |
| Β | |
| Β | |
| def apg_verify(rulestring, symmetry, payoshakey):
| |
| Β | |
| Β Β verifysoup = Soup()
| |
| Β Β verifysoup.rg.setrule(rulestring)
| |
| Β Β verifysoup.rg.saveAllRules()
| |
| Β | |
| Β Β return_point = [None]
| |
| Β | |
| Β Β catagolue_results(rulestring+"\n"+symmetry+"\n", payoshakey, "verify_apgsearch_haul", endpoint="/verify", return_point=return_point)
| |
| Β | |
| Β Β if return_point[0] is not None:
| |
| Β | |
| Β Β Β Β resplist = return_point[0].split("\n")
| |
| Β | |
| Β Β Β Β if ((len(resplist) >= 4) and (resplist[1] == "yes")):
| |
| Β | |
| Β Β Β Β Β Β md5 = resplist[2]
| |
| Β Β Β Β Β Β passcode = resplist[3]
| |
| Β | |
| Β Β Β Β Β Β stringlist = resplist[4:]
| |
| Β | |
| Β Β Β Β Β Β stringlist = [s for s in stringlist if (len(s) > 0 and s[0] != '*')]
| |
| Β | |
| Β Β Β Β Β Β # g.exit(stringlist[0])
| |
| Β | |
| Β Β Β Β Β Β gsize = 3
| |
| Β | |
| Β Β Β Β Β Β pos = 0
| |
| Β | |
| Β Β Β Β Β Β while (len(stringlist) > 0):
| |
| Β | |
| Β Β Β Β Β Β Β Β while (gsize * gsize > len(stringlist)):
| |
| Β | |
| Β Β Β Β Β Β Β Β Β Β gsize -= 1
| |
| Β | |
| Β Β Β Β Β Β Β Β listhead = stringlist[:(gsize*gsize)]
| |
| Β Β Β Β Β Β Β Β stringlist = stringlist[(gsize*gsize):]
| |
| Β | |
| Β Β Β Β Β Β Β Β verifysoup.stabilise_soups_parallel_list(gsize, listhead, pos)
| |
| Β | |
| Β Β Β Β Β Β Β Β pos += (gsize * gsize)
| |
| Β | |
| Β Β Β Β Β Β # verifysoup.display_census(-1, "verify", "verify")
| |
| Β | |
| Β Β Β Β Β Β payload = "@MD5 "+md5+"\n"
| |
| Β Β Β Β Β Β payload += "@PASSCODE "+passcode+"\n"
| |
| Β Β Β Β Β Β payload += "@RULE "+rulestring+"\n"
| |
| Β Β Β Β Β Β payload += "@SYMMETRY "+symmetry+"\n"
| |
| Β | |
| Β Β Β Β Β Β tlist = sorted(verifysoup.objectcounts.iteritems(), key=operator.itemgetter(1), reverse=True)
| |
| Β | |
| Β Β Β Β Β Β for objname, count in tlist:
| |
| Β | |
| Β Β Β Β Β Β Β Β payload += objname + " " + str(count) + "\n"
| |
| Β | |
| Β Β Β Β Β Β catagolue_results(payload, payoshakey, "submit_verification", endpoint="/verify")
| |
| Β | |
| Β | |
| def apg_main():
| |
| Β | |
| Β Β # ---------------- Hardcode the following inputs if running without a user interface ----------------
| |
| Β Β orignumber = int(g.getstring("How many soups to search between successive uploads?", "5000000"))
| |
| Β Β rulestring = g.getstring("Which rule to use?", "B3/S23")
| |
| Β Β symmstring = g.getstring("What symmetries to use?", "C1")
| |
| Β Β payoshakey = g.getstring("Please enter your key (visit "+get_server_address()+"/payosha256 in your browser).", "#anon")
| |
| Β Β # ---------------------------------------------------------------------------------------------------
| |
| Β | |
| Β Β # Sanitise input:
| |
| Β Β #orignumber = max(orignumber, 100000)
| |
| Β Β #orignumber = min(orignumber, 100000000)
| |
| Β Β number = orignumber
| |
| Β Β initpos = 0
| |
| Β Β if symmstring not in ["UnbanMajestas32","BlockFloodTest", "wwei23BLOCKPARTYTEST","75p", "25p", "1X2x256X2", "1x256X2+1", "1x256X2", "32x32", "1x256", "2x128", "4x64", "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"]:
| |
| Β Β Β Β g.exit(symmstring+" is not a valid symmetry option")
| |
| Β | |
| Β Β quitapg = False
| |
| Β | |
| Β Β # Create associated rule tables:
| |
| Β Β soup = Soup()
| |
| Β Β soup.rg.setrule(rulestring)
| |
| Β Β soup.rg.saveAllRules()
| |
| Β | |
| Β Β # We have 100 soups per page, instead of one. This parallel approach
| |
| Β Β # was suggested by Tomas Rokicki, and results in approximately a
| |
| Β Β # fourfold increase in soup-searching speed!
| |
| Β Β sqrtspp_optimal = 10
| |
| Β | |
| Β Β # Initialise the census:
| |
| Β Β start_time = time.clock()
| |
| Β Β f = (lambda x : 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'[ord(x) % 56])
| |
| Β Β rootstring = ''.join(map(f, list(hashlib.sha256(payoshakey + datetime.datetime.now().isoformat()).digest()[:12])))
| |
| Β Β scount = 0
| |
| Β | |
| Β Β while (quitapg == False):
| |
| Β | |
| Β Β Β Β # Peer-review some soups:
| |
| Β Β Β Β #for i in xrange(5):
| |
| Β Β Β Β Β Β #apg_verify("b3s23", "C1", payoshakey)
| |
| Β | |
| Β Β Β Β # The 'for' loop has been replaced with a 'while' loop to allow sqrtspp
| |
| Β Β Β Β # to vary during runtime. The idea is that apgsearch can apply a basic
| |
| Β Β Β Β # form of machine-learning to dynamically locate the optimum sqrtspp:
| |
| Β Β Β Β while (scount < number):
| |
| Β | |
| Β Β Β Β Β Β delays = [0.0, 0.0, 0.0]
| |
| Β | |
| Β Β Β Β Β Β for i in xrange(1000):
| |
| Β | |
| Β Β Β Β Β Β Β Β page_time = time.clock()
| |
| Β | |
| Β Β Β Β Β Β Β Β sqrtspp = (sqrtspp_optimal + (i % 3) - 1) if (i < 150) else (sqrtspp_optimal)
| |
| Β | |
| Β Β Β Β Β Β Β Β # Don't overrun:
| |
| Β Β Β Β Β Β Β Β while (scount + sqrtspp * sqrtspp > number):
| |
| Β Β Β Β Β Β Β Β Β Β sqrtspp -= 1
| |
| Β | |
| Β Β Β Β Β Β Β Β meandelay = soup.stabilise_soups_parallel(rootstring, scount + initpos, sqrtspp, symmstring)
| |
| Β Β Β Β Β Β Β Β if (i < 150):
| |
| Β Β Β Β Β Β Β Β Β Β delays[i % 3] += meandelay
| |
| Β Β Β Β Β Β Β Β scount += (sqrtspp * sqrtspp)
| |
| Β | |
| Β Β Β Β Β Β Β Β current_speed = int((sqrtspp * sqrtspp)/(time.clock() - page_time))
| |
| Β Β Β Β Β Β Β Β alltime_speed = int((scount)/(time.clock() - start_time))
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β g.show(str(scount) + " soups processed (" + str(current_speed) +
| |
| Β Β Β Β Β Β Β Β Β Β Β " per second current; " + str(alltime_speed) + " overall)" +
| |
| Β Β Β Β Β Β Β Β Β Β Β " : (type 's' to see latest census or 'q' to quit).")
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β Β Β event = g.getevent()
| |
| Β Β Β Β Β Β Β Β if event.startswith("key"):
| |
| Β Β Β Β Β Β Β Β Β Β evt, ch, mods = event.split()
| |
| Β Β Β Β Β Β Β Β Β Β if ch == "s":
| |
| Β Β Β Β Β Β Β Β Β Β Β Β soup.save_progress(scount, rootstring, symmstring)
| |
| Β Β Β Β Β Β Β Β Β Β Β Β soup.display_census(scount, rootstring, symmstring)
| |
| Β Β Β Β Β Β Β Β Β Β elif ch == "q":
| |
| Β Β Β Β Β Β Β Β Β Β Β Β quitapg = True
| |
| Β Β Β Β Β Β Β Β Β Β Β Β break
| |
| Β | |
| Β Β Β Β Β Β Β Β if (scount >= number):
| |
| Β Β Β Β Β Β Β Β Β Β break
| |
| Β Β Β Β Β Β Β Β
| |
| Β Β Β Β Β Β if (quitapg == True):
| |
| Β Β Β Β Β Β Β Β break
| |
| Β | |
| Β Β Β Β Β Β # Change sqrtspp to a more optimal value:
| |
| Β Β Β Β Β Β if (scount < number):
| |
| Β Β Β Β Β Β Β Β sqrtspp_new = sqrtspp_optimal
| |
| Β | |
| Β Β Β Β Β Β Β Β if (delays[0] < delays[1]):
| |
| Β Β Β Β Β Β Β Β Β Β sqrtspp_new = sqrtspp_optimal - 1
| |
| Β Β Β Β Β Β Β Β if ((delays[2] < delays[1]) and (delays[2] < delays[0])):
| |
| Β Β Β Β Β Β Β Β Β Β sqrtspp_new = sqrtspp_optimal + 1
| |
| Β | |
| Β Β Β Β Β Β Β Β sqrtspp_optimal = sqrtspp_new
| |
| Β Β Β Β Β Β Β Β sqrtspp_optimal = max(sqrtspp_optimal, 5)
| |
| Β | |
| Β Β Β Β Β Β # Compactify highscore table:
| |
| Β Β Β Β Β Β soup.compactify_scores()
| |
| Β | |
| Β Β Β Β if (quitapg == False):
| |
| Β Β Β Β Β Β # Save progress, upload it to Catagolue, and reset the census if successful:
| |
| Β Β Β Β Β Β a = soup.save_progress(scount, rootstring, symmstring, payosha256_key=payoshakey)
| |
| Β Β Β Β Β Β if (a == 0):
| |
| Β Β Β Β Β Β Β Β # Reset the census:
| |
| Β Β Β Β Β Β Β Β soup.reset()
| |
| Β Β Β Β Β Β Β Β start_time = time.clock()
| |
| Β Β Β Β Β Β Β Β f = (lambda x : 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'[ord(x) % 56])
| |
| Β Β Β Β Β Β Β Β rootstring = ''.join(map(f, list(hashlib.sha256(rootstring + payoshakey + datetime.datetime.now().isoformat()).digest()[:12])))
| |
| Β Β Β Β Β Β Β Β scount = 0
| |
| Β Β Β Β Β Β Β Β number = orignumber
| |
| Β Β Β Β Β Β else:
| |
| Β Β Β Β Β Β Β Β number += orignumber
| |
| Β | |
| Β Β end_time = time.clock()
| |
| Β | |
| Β Β soup.save_progress(scount, rootstring, symmstring, payosha256_key=payoshakey)
| |
| Β | |
| Β Β soup.display_unids()
| |
| Β Β soup.display_census(scount, rootstring, symmstring)
| |
| Β | |
| def symmetry_test():
| |
| Β | |
| Β Β g.new("Symmetry test")
| |
|
| |
|
| Β Β symmetries = [["1X2x256X2", "1x256X2+1", "1x256X2", "32x32", "C1", "8x32", "4x64", "2x128", "1x256", "25p", "75p", "wwei23BLOCKPARTYTEST", "BlockFloodTest", "UnbanMajestas32"],
| | You can type { {userarticle} } (without the spaces) at the start, and it will look like this: |
| Β Β Β Β Β Β Β Β Β ["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"]]
| |
|
| |
|
| Β Β for i in range(len(symmetries)):
| | {{userarticle}} |
| Β Β Β Β for j in range(len(symmetries[i])):
| |
|
| |
|
| Β Β Β Β Β Β g.putcells(hashsoup("sym_test", symmetries[i][j]), 120 * j + 60 * (i % 2), 80 * i)
| | {{unsigned|Entity Valkyrie}} |
| Β Β g.fit()
| |
|
| |
|
| # Run the soup-searching script:
| | :EV, the <nowiki>{{unsigned}}</nowiki> template is only meant to be used to sign ''other people's'' talk page messages, not your own. For that you just need to do the tildes: <nowiki>~~~~</nowiki> |
| apg_main()
| | :Oh and also, please make sure you use the "New topic" button rather than the "Edit" button when you want to add a message that isn't going to part of an already existing topic. [[User:Ian07|Ian07]] ([[User talk:Ian07|talk]]) 22:25, 20 November 2018 (UTC) |
| # apg_verify()
| | ::Are you talking to me, or EV? [[User:Wwei23|Wwei23]] ([[User talk:Wwei23|talk]]) 15:45, 22 November 2018 (UTC) |
| | :::I'm talking to EV, you're okay. [[User:Ian07|Ian07]] ([[User talk:Ian07|talk]]) 15:54, 22 November 2018 (UTC) |