On a new Glider Database

A forum where anything goes. Introduce yourselves to other members of the forums, discuss how your name evolves when written out in the Game of Life, or just tell us how you found it. This is the forum for "non-academic" content.
Post Reply
AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

On a new Glider Database

Post by AforAmpere » July 4th, 2018, 6:56 pm

Can people give me their thoughts on a new, community-based version of the Glider Database? I have scripts written for Golly, the first is the program itself, which prints known ships to the grid. The second is a file that you can add to listing the known ships. The file consists of a list with two types of data: First, the RLE, and second, the rule string output by the third script. The third is a script that takes the maximum and minimum rules of a ship, and outputs the string to use in the second file as a rule number. Save all of them as the name given in the top line. I was thinking of having this as a community-based project, where people could add known ships to the database, a problem with the original. If this happens, where is a place that would be convenient for everyone to edit?

Code: Select all

# Glider_Database.py, run with Golly.
import golly as g
import os
import sys
import gliderlist ; reload(gliderlist)
g.new("")
class RuleGenerator:
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #    
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
    
    allneighbours = [  
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
        
    allneighbours_flat = [n for x in allneighbours for n in x]
    
    numneighbours = len(notationdict)
    
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
    
    # Save the isotropic rule
    def saveAllRules(self):    
        self.saveIsotropicRule()
    
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
        
        # Reverse the rule string to simplify processing
        part = part[::-1]
        
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                    
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
        
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):
    
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
        
        rulestring = rulestring.lower()
        
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
        
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
        
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + '_S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
            

    # Save a rule file:
    def saverule(self, name, comments, table, colours):
        
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"

        global results
	results = ""
        results += table

        # 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.
        

    # 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 = "\n"

        for name in namelist:
            block += self.newvar(name, vallist)

        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 isotropicline(self, chara, charb, left, right, n):

        line = str(left) + ","
        neighbours = self.notationdict[n]
        
        for i in xrange(8):
            if neighbours[i]:
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line
        
    def saveIsotropicRule(self):
    
  

        table = """"""

        for n in self.allneighbours_flat:
            if self.bee[n]:
                table += "1"
	    else:
		table += "0"

        for n in self.allneighbours_flat:
            if self.ess[n]:
                table += "1"
	    else:
		table += "0"
        
        colours = ""
	comments = ""
        self.saverule(self.rulename, comments, table, colours)

rulestring = g.getstring("Enter rule string in Alan Hensel's isotropic rule notation", 
                         "B2-a/S12")

rg = RuleGenerator()

rg.setrule(rulestring)
rg.saveIsotropicRule()
g.setrule(rulestring)

# g.show(results)
p=0

j=0
y=gliderlist.y
for z in range(len(y)):
    p = 0
    for x in range(len(results)):
        if (results[x] == "1" and y[z][1][x] == "0") or (results[x] == "0" and y[z][1][x] == "1"):
	    # g.show(rg.rulename + "False")
	    p = 1

    if p ==0:
	# g.show(rg.rulename + "True")
	g.setclipstr(y[z][0])
	g.paste(j,0,"or")
	j+=20	

Code: Select all

#gliderlist.py
y=[["b2o$obo$2bo!","000002202112211222220222222222222202222222222222222200112222211212222222222022222222222222222222222222"], 
["b3o$o2bo$3bo$3bo$obo!","000002002122211222222222222222222220222222222222022020211122111211222202222222220222022222022220202222"],
["2b3o$bo2bo$4bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022020211122111211222202222222220220022222022220222022"],
["2b3o$bo2bo$4bo$o3bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022000211122111211222202222222220220022222022220222020"],
["7b2obo$4b2obob2ob3o$b4o2b2o6bo$o4bo3bo3b2o$b2o!","000000002111111222200000002002222220220022202222222200111112111111112120000020022222022222222222222222"], 
["b2o$o2bo!","000112222222202222222222222222222222222222222222222220220222222222222222222222222222222222222222222222"], 
["b2o$$o2bo!","000121222222022222222222222222222222222222222222222002222222222222222222222222222222222222222222222222"], 
["bo$obo!","000012202022221122222222222222222222222222222222222220122212222220022222222222222222222222222222222222"],
["o$b2o$$2bo!","000102212200202222222222222222222222222222222222222011220222222222222222222222222222222222222222222222"],
["obo$2bo!","000012212200200222202222222222222222222222222222222011111222220222222222222222222222222222222222222222"],
["bo$3o!","000000202121211121202222222222222222222220222222222000122012122221121202222222202220222222222202222222"],
["b2o$o$3bo!","000111222222002222222222222222222222222222222222222000220222222222222222222222222222222222222222222222"],
["b2o$4o!","000002222122211222212222222222222222222022222222222222020202122222222211122222222222222222222022222222"],
["b3o$bo$o!","000002222122211222212222222222222220222222222222222211002222221111222222222220022222222222222222222222"],
["obo$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222202022222222222222222222"],
["3o$$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222212122222222222222222222"],
]

Code: Select all

# torulestring.py, run with Golly.
import golly as g
import os
import sys
g.new("")
class RuleGenerator:
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #    
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
    
    allneighbours = [  
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
        
    allneighbours_flat = [n for x in allneighbours for n in x]
    
    numneighbours = len(notationdict)
    
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
    
    # Save the isotropic rule
    def saveAllRules(self):    
        self.saveIsotropicRule()
    
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
        
        # Reverse the rule string to simplify processing
        part = part[::-1]
        
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                    
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
        
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):
    
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
        
        rulestring = rulestring.lower()
        
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
        
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
        
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + '_S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
            

    # Save a rule file:
    def saverule(self, name, comments, table, colours):
        
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"

        global results
	results = ""
        results += table

        # 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.
        

    # 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 = "\n"

        for name in namelist:
            block += self.newvar(name, vallist)

        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 isotropicline(self, chara, charb, left, right, n):

        line = str(left) + ","
        neighbours = self.notationdict[n]
        
        for i in xrange(8):
            if neighbours[i]:
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line
        
    def saveIsotropicRule(self):
    
  

        table = """"""

        for n in self.allneighbours_flat:
            if self.bee[n]:
                table += "1"
	    else:
		table += "0"

        for n in self.allneighbours_flat:
            if self.ess[n]:
                table += "1"
	    else:
		table += "0"
        
        colours = ""
	comments = ""
        self.saverule(self.rulename, comments, table, colours)

rulestring = g.getstring("Enter minimum rule string:")

rulestring2 = g.getstring("Enter maximum rule string:")

rg = RuleGenerator()

rg.setrule(rulestring)
rg.saveIsotropicRule()
q = results
rg.setrule(rulestring2)
rg.saveIsotropicRule()
t = results
g.setrule(rulestring)

# g.show(results)

final = ""
for z in range(len(q)):
    if q[z] == t[z]:
	final+=q[z]
    else:
	final+="2"
g.setclipstr(final)
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

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

Re: On a new Glider Database

Post by dani » July 4th, 2018, 7:17 pm

Hmm, maybe I could pour a little bit of money into hosting a website (this post is sponsored by squarespace) for storing this glider database and keeping it up-to-date? That or just pick up a google site. I'm getting birthday money soon, and may even get a job within a year.

Such a database would probably be incredibly big, and require some sort of minimum-maximum-transition breakdown, that would decompose rules into sets of transitions and output all ships with those transitions.

The problem then arises for what spaceships count as notable, exactly.

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 4th, 2018, 7:25 pm

danny wrote:Hmm, maybe I could pour a little bit of money into hosting a website (this post is sponsored by squarespace) for storing this glider database and keeping it up-to-date?
Sites may work, if it is editable by multiple people.
danny wrote:Such a database would probably be incredibly big, and require some sort of minimum-maximum-transition breakdown, that would decompose rules into sets of transitions and output all ships with those transitions.
That is exactly what the programs above do. All that needs to be hosted is a copy of the gliderlist.py that is editable by other people. Then, all that needs to be done is for people with copies of the above programs to copy the updated version online into their file on their PC, and run the scripts to display the ships.
danny wrote:The problem then arises for what spaceships count as notable, exactly.
I would say smallest of each speed for each rule at maximum, that can include different periods.
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 5th, 2018, 2:59 pm

Now the script has automatic ordering of gliders by speed and direction, and displacement between them is now based on width, not a fixed amount. There are two changes, Glider_Database.py is now the first file here, and the gliderlist.py now has a third pat of each sub-list, the speed, in [y displacement,x displacement,period] format:

Code: Select all

# Glider_Database.py, run with Golly.
import golly as g
import os
import sys
import gliderlist ; reload(gliderlist)
g.new("")
class RuleGenerator:
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #    
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
    
    allneighbours = [  
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
        
    allneighbours_flat = [n for x in allneighbours for n in x]
    
    numneighbours = len(notationdict)
    
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
    
    # Save the isotropic rule
    def saveAllRules(self):    
        self.saveIsotropicRule()
    
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
        
        # Reverse the rule string to simplify processing
        part = part[::-1]
        
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                    
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
        
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):
    
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
        
        rulestring = rulestring.lower()
        
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
        
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
        
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + '_S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
            

    # Save a rule file:
    def saverule(self, name, comments, table, colours):
        
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"

        global results
	results = ""
        results += table

        # 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.
        

    # 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 = "\n"

        for name in namelist:
            block += self.newvar(name, vallist)

        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 isotropicline(self, chara, charb, left, right, n):

        line = str(left) + ","
        neighbours = self.notationdict[n]
        
        for i in xrange(8):
            if neighbours[i]:
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line
        
    def saveIsotropicRule(self):
    
  

        table = """"""

        for n in self.allneighbours_flat:
            if self.bee[n]:
                table += "1"
	    else:
		table += "0"

        for n in self.allneighbours_flat:
            if self.ess[n]:
                table += "1"
	    else:
		table += "0"
        
        colours = ""
	comments = ""
        self.saverule(self.rulename, comments, table, colours)

rulestring = g.getstring("Enter rule string in Alan Hensel's isotropic rule notation", 
                         "B2-a/S12")

rg = RuleGenerator()

rg.setrule(rulestring)
rg.saveIsotropicRule()
g.setrule(rulestring)

# g.show(results)
q = []
p=0

j=0
y=gliderlist.y
for z in range(len(y)):
    p = 0
    for x in range(len(results)):
        if (results[x] == "1" and y[z][1][x] == "0") or (results[x] == "0" and y[z][1][x] == "1"):
	    # g.show(rg.rulename + "False")
	    p = 1

    if p ==0:
	q+=[y[z]]
	

q = sorted(q,key=lambda qe: (qe[2][0]/float(qe[2][2]))+qe[2][1]*200.0/qe[2][0])
g.setclipstr(str(q))
for v in q:	
    g.setclipstr(v[0])
    g.paste(j,0,"or")
    j=	g.getrect()[2]+15

Code: Select all

#gliderlist.py
y=[["b2o$obo$2bo!","000002202112211222220222222222222202222222222222222200112222211212222222222022222222222222222222222222", [1,1,4]], 
["b3o$o2bo$3bo$3bo$obo!","000002002122211222222222222222222220222222222222022020211122111211222202222222220222022222022220202222",[2,0,4]],
["2b3o$bo2bo$4bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022020211122111211222202222222220220022222022220222022",[2,0,4]],
["2b3o$bo2bo$4bo$o3bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022000211122111211222202222222220220022222022220222020",[2,0,4]],
["7b2obo$4b2obob2ob3o$b4o2b2o6bo$o4bo3bo3b2o$b2o!","000000002111111222200000002002222220220022202222222200111112111111112120000020022222022222222222222222",[1,0,3]], 
["b2o$o2bo!","000112222222202222222222222222222222222222222222222220220222222222222222222222222222222222222222222222",[1,0,1]], 
["b2o$$o2bo!","000121222222022222222222222222222222222222222222222002222222222222222222222222222222222222222222222222",[1,0,1]], 
["bo$obo!","000012202022221122222222222222222222222222222222222220122212222220022222222222222222222222222222222222",[1,0,2]],
["o$b2o$$2bo!","000102212200202222222222222222222222222222222222222011220222222222222222222222222222222222222222222222",[1,1,2]],
["obo$2bo!","000012212200200222202222222222222222222222222222222011111222220222222222222222222222222222222222222222",[1,1,6]],
["bo$3o!","000000202121211121202222222222222222222220222222222000122012122221121202222222202220222222222202222222",[1,0,5]],
["b2o$o$3bo!","000111222222002222222222222222222222222222222222222000220222222222222222222222222222222222222222222222",[1,0,1]],
["b2o$4o!","000002222122211222212222222222222222222022222222222222020202122222222211122222222222222222222022222222",[1,0,3]],
["b3o$bo$o!","000002222122211222212222222222222220222222222222222211002222221111222222222220022222222222222222222222",[1,1,4]],
["obo$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222202022222222222222222222",[1,0,11]],
["3o$$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222212122222222222222222222",[2,0,6]],
["5b3o10b3o$3obo7b2o7bob3o$4bo3bo2bo2bo2bo3bo$4bo5bo4bo5bo$10b2o2b2o$7bo3bo2bo3bo$7bobo6bobo$8b10o$10bo4bo$8bo8bo$7bo10bo$8bo8bo!","000000200111111111100000002000022000000022000200202000111111111111121100000000000220022022000200022222",[1,0,6]],
["25b2o$23bo2bo$22bo3bo$22b3o$17b4o6b2o$17b4o4b2obo$15b3o6bo4bo$15bo3b2o4b4o$19b2o9bo$25bo3bo$15bo2b2o2b3o$14bo4bo7b2o$14b2obo$12bo6b2o$12bob3ob2o$12bo2bo3b2o$14b2o2bobo$13bobobo2bo$11bo6b3o$11bo3bobobo$11bobob2o$10b3o6bo2$9bo9bo$8bo6bo3bo$8b5o5bo$16b3o$13b2o$12bo2b3o$13bob3obo$13bo2bo3bo$9b3ob2o4bo$6b2o4bob4o$6b2o4b4obo$11bo$6b2o2bo$9b2o$5b5o$4b2o$2bo6b3o$2bobo3bobo$3bo3bo3bo$6b2o3bo$b3obo6bo$b2o3bo3b2o$bo2bo2b4o$3bo3b2o$9bo$6bob2o$10bo$7b5o$6bo4bo$6b3ob3o$6b5obo$12bo$10bo$6b4o4bo$4b2ob4o$6bo4b3o$4bobo$2bo$2b2o2bo$3b3o$7b2o$bo5b3o$obo2b2o$obob3o2bo$2bo2bob2o$2o2bobo$3b2o$bo4b3o$bo4b3o$3o3b2o$2b2ob2o$4b2o$5bo2$5b2o$4bo!","000000000111111111100000000000000000000000000000000000111111111111111100000000000220000000000000000000",[2,1,6]],
["7bo$6bobo$5bo2bo$6b2o2$2bo5bo$bobo3bo$o2bo3b2o$o2b2o3bo!","000000000111111121100200000000220022020002220020222000111111111111111100002220202220002022222222222222",[1,0,7]],
["bo$bo8bo$obo5bo3bo$8bo3b2o$5bob2o5b2o$b6o2bo6bo$2b2o6bo3b3o$10bo3bob2o$13bo$18bo$17bo$17bo!","000000000111111121100000002200220200020222020222222000111121111111111200200200022220200000222002202022",[1,0,4]],
["9bo7bo9b$3b2obobob2o3b2obobob2o3b$3obob3o9b3obob3o$o3bobo5bobo5bobo3bo$4b2o6bobo6b2o4b$b2o9bobo9b2ob$b2ob2o15b2ob2ob$5bo15bo!","000000000111111111100200200000220000000002020000002000111111111111112200200000000220222000222220020202",[1,0,5]],
["b2o2b2o$3b2o$3b2o$obo2bobo$o6bo2$o6bo$b2o2b2o$2b4o2$3b2o$3b2o!","000000000111111222200200000000222202000022200220202200111111111112121200022002200220020000022022222022",[1,0,10]],
["b2o$b2o$o2bo$o2bob2o$o$o2bo3b2o$bo5b2o$b5obo$6bo3$7b3o$9bo$10b2o$11bo4b2o$11bo3b3o$14bo4bo$15bo3bo$15bo$15bobob2o$15bo5b2o$15b2o4b2o$17b4o!","000000000111111122100200000000022000020200020220020000111111111111111100000002000220002020022000220002",[1,1,5]],
["bo$obo$o2b2o2$5bo$3b2o2b2o$7b2o$8bo$8bobo$9b2o$9bo2bo$8bo4bo$8bo5bo$13bo3bo$11b2o3bobo$15b2ob2o$12bobo4b2o$12bo7b2o$12bo9bo$20b2o$20bo$19bo3bo$22bobo$17b3obo3bo$21bo$26b2o$25b2o$23b2o2b4o$29b2o2$31bo$30bo2bo$30bo2bo$34bo$35bo$33b2o!","000000000111111111100000000000020000020002000002002000111111111111211100000000000220000020222200222220",[1,1,6]],
["11b3o$13bo$8b2o2bo$8b2o$12b2o$11b2o$10bo2bo2$8bo2bo$7bo3bo$6bob3o$5bo$5bo13bobo2b2o$6bo13b2obobo$b2o13b2o2bo4bo$o2b2o2b2o6bo3bo$5bo2bo6bo6b2o$9b2o4bobo4b2o$2bo3bo3bo5bo$6b2o4bo2bo$bobo5bo3b2o$2o8bo$5bo4bo$7bo3bo$4b2o5bo$4bo5bo!","000000200111111111100000000000002000000002000222022000111111111111111100000000000220000020220022202222",[1,1,7]],
["20bo$19bobo2b2o7b2o$18bo3b4o6b3o$21bo7bobob2o$16b2obo10b2o$17bobo$8b2o8bo$8b2o4$46bo$44b2ob2o$46bo$30b2o$29bo2bo$28b2ob2o11b2o$31bo11bo2bo$b2o26b3o10bo3b2o$b2o3b2o34bo4bo$2o2b3o36bo3bo$3o2b2o36bo2bo$45bo2$29bo$29bo3bo$29bo2bo$28b2ob2o$29bobo$30bo2$41b2o$41b2o7$33b2o$33b2o!","000000000111111111100000000000000000000000000000000000111111111111111100000000000000000000000000000000",[1,1,12]],
]
The new gliderlist.py also now has many of the Life ships put in in random order, to demonstrate the ordering that it does. All we need to do is find some way for people to be able to submit entries to some document that updates when they do, so that everyone has access to the most up-to-date file. If Danny gets his idea working, or if I or someone else sets up a document for any of us to edit, it can finally take effect.
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

wwei23

Re: On a new Glider Database

Post by wwei23 » July 5th, 2018, 11:52 pm

Why can't we use the LifeWiki to do this? We could have a page with the database in it.

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

Re: On a new Glider Database

Post by calcyman » July 6th, 2018, 4:50 am

I could make Catagolue dynamically display rulemin and rulemax on pattern pages for low-period oscillators and spaceships, in the same way it displays minpop and maxpop.
What do you do with ill crystallographers? Take them to the mono-clinic!

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 6th, 2018, 12:30 pm

wwei23 wrote:Why can't we use the LifeWiki to do this? We could have a page with the database in it.
Maybe we should do that, if anyone else agrees. Anyone, please say something if you either agree, disagree, or don't care.
calcyman wrote:I could make Catagolue dynamically display rulemin and rulemax on pattern pages for low-period oscillators and spaceships, in the same way it displays minpop and maxpop.
That would be a really nice feature, as it would allow people to quickly get the information needed to input into the script. That would be neat in general, as it lets you see the versatility of the pattern as well.

Also, here is an updated database:

Code: Select all

#gliderlist.py
y=[["b2o$obo$2bo!","000002202112211222220222222222222202222222222222222200112222211212222222222022222222222222222222222222", [1,1,4]], 
["b3o$o2bo$3bo$3bo$obo!","000002002122211222222222222222222220222222222222022020211122111211222202222222220222022222022220202222",[2,0,4]],
["2b3o$bo2bo$4bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022020211122111211222202222222220220022222022220222022",[2,0,4]],
["2b3o$bo2bo$4bo$o3bo$o3bo$4bo$bobo!","000000002122211222222222222222222220222222222222022000211122111211222202222222220220022222022220222020",[2,0,4]],
["7b2obo$4b2obob2ob3o$b4o2b2o6bo$o4bo3bo3b2o$b2o!","000000002111111222200000002002222220220022202222222200111112111111112120000020022222022222222222222222",[1,0,3]], 
["b2o$o2bo!","000112222222202222222222222222222222222222222222222220220222222222222222222222222222222222222222222222",[1,0,1]], 
["b2o$$o2bo!","000121222222022222222222222222222222222222222222222002222222222222222222222222222222222222222222222222",[1,0,1]], 
["bo$obo!","000012202022221122222222222222222222222222222222222220122212222220022222222222222222222222222222222222",[1,0,2]],
["o$b2o$$2bo!","000102212200202222222222222222222222222222222222222011220222222222222222222222222222222222222222222222",[1,1,2]],
["obo$2bo!","000012212200200222202222222222222222222222222222222011111222220222222222222222222222222222222222222222",[1,1,6]],
["bo$3o!","000000202121211121202222222222222222222220222222222000122012122221121202222222202220222222222202222222",[1,0,5]],
["b2o$o$3bo!","000111222222002222222222222222222222222222222222222000220222222222222222222222222222222222222222222222",[1,0,1]],
["b2o$4o!","000002222122211222212222222222222222222022222222222222020202122222222211122222222222222222222022222222",[1,0,3]],
["b3o$bo$o!","000002222122211222212222222222222220222222222222222211002222221111222222222220022222222222222222222222",[1,1,4]],
["obo$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222202022222222222222222222",[1,0,11]],
["3o$$bo$$$bo!","000111112002200020212222222212222222222222222222222100222112222220220222222222212122222222222222222222",[2,0,6]],
["5b3o10b3o$3obo7b2o7bob3o$4bo3bo2bo2bo2bo3bo$4bo5bo4bo5bo$10b2o2b2o$7bo3bo2bo3bo$7bobo6bobo$8b10o$10bo4bo$8bo8bo$7bo10bo$8bo8bo!","000000200111111111100000002000022000000022000200202000111111111111121100000000000220022022000200022222",[1,0,6]],
["25b2o$23bo2bo$22bo3bo$22b3o$17b4o6b2o$17b4o4b2obo$15b3o6bo4bo$15bo3b2o4b4o$19b2o9bo$25bo3bo$15bo2b2o2b3o$14bo4bo7b2o$14b2obo$12bo6b2o$12bob3ob2o$12bo2bo3b2o$14b2o2bobo$13bobobo2bo$11bo6b3o$11bo3bobobo$11bobob2o$10b3o6bo2$9bo9bo$8bo6bo3bo$8b5o5bo$16b3o$13b2o$12bo2b3o$13bob3obo$13bo2bo3bo$9b3ob2o4bo$6b2o4bob4o$6b2o4b4obo$11bo$6b2o2bo$9b2o$5b5o$4b2o$2bo6b3o$2bobo3bobo$3bo3bo3bo$6b2o3bo$b3obo6bo$b2o3bo3b2o$bo2bo2b4o$3bo3b2o$9bo$6bob2o$10bo$7b5o$6bo4bo$6b3ob3o$6b5obo$12bo$10bo$6b4o4bo$4b2ob4o$6bo4b3o$4bobo$2bo$2b2o2bo$3b3o$7b2o$bo5b3o$obo2b2o$obob3o2bo$2bo2bob2o$2o2bobo$3b2o$bo4b3o$bo4b3o$3o3b2o$2b2ob2o$4b2o$5bo2$5b2o$4bo!","000000000111111111100000000000000000000000000000000000111111111111111100000000000220000000000000000000",[2,1,6]],
["7bo$6bobo$5bo2bo$6b2o2$2bo5bo$bobo3bo$o2bo3b2o$o2b2o3bo!","000000000111111121100200000000220022020002220020222000111111111111111100002220202220002022222222222222",[1,0,7]],
["bo$bo8bo$obo5bo3bo$8bo3b2o$5bob2o5b2o$b6o2bo6bo$2b2o6bo3b3o$10bo3bob2o$13bo$18bo$17bo$17bo!","000000000111111121100000002200220200020222020222222000111121111111111200200200022220200000222002202022",[1,0,4]],
["9bo7bo9b$3b2obobob2o3b2obobob2o3b$3obob3o9b3obob3o$o3bobo5bobo5bobo3bo$4b2o6bobo6b2o4b$b2o9bobo9b2ob$b2ob2o15b2ob2ob$5bo15bo!","000000000111111111100200200000220000000002020000002000111111111111112200200000000220222000222220020202",[1,0,5]],
["b2o2b2o$3b2o$3b2o$obo2bobo$o6bo2$o6bo$b2o2b2o$2b4o2$3b2o$3b2o!","000000000111111222200200000000222202000022200220202200111111111112121200022002200220020000022022222022",[1,0,10]],
["b2o$b2o$o2bo$o2bob2o$o$o2bo3b2o$bo5b2o$b5obo$6bo3$7b3o$9bo$10b2o$11bo4b2o$11bo3b3o$14bo4bo$15bo3bo$15bo$15bobob2o$15bo5b2o$15b2o4b2o$17b4o!","000000000111111122100200000000022000020200020220020000111111111111111100000002000220002020022000220002",[1,1,5]],
["bo$obo$o2b2o2$5bo$3b2o2b2o$7b2o$8bo$8bobo$9b2o$9bo2bo$8bo4bo$8bo5bo$13bo3bo$11b2o3bobo$15b2ob2o$12bobo4b2o$12bo7b2o$12bo9bo$20b2o$20bo$19bo3bo$22bobo$17b3obo3bo$21bo$26b2o$25b2o$23b2o2b4o$29b2o2$31bo$30bo2bo$30bo2bo$34bo$35bo$33b2o!","000000000111111111100000000000020000020002000002002000111111111111211100000000000220000020222200222220",[1,1,6]],
["11b3o$13bo$8b2o2bo$8b2o$12b2o$11b2o$10bo2bo2$8bo2bo$7bo3bo$6bob3o$5bo$5bo13bobo2b2o$6bo13b2obobo$b2o13b2o2bo4bo$o2b2o2b2o6bo3bo$5bo2bo6bo6b2o$9b2o4bobo4b2o$2bo3bo3bo5bo$6b2o4bo2bo$bobo5bo3b2o$2o8bo$5bo4bo$7bo3bo$4b2o5bo$4bo5bo!","000000200111111111100000000000002000000002000222022000111111111111111100000000000220000020220022202222",[1,1,7]],
["20bo$19bobo2b2o7b2o$18bo3b4o6b3o$21bo7bobob2o$16b2obo10b2o$17bobo$8b2o8bo$8b2o4$46bo$44b2ob2o$46bo$30b2o$29bo2bo$28b2ob2o11b2o$31bo11bo2bo$b2o26b3o10bo3b2o$b2o3b2o34bo4bo$2o2b3o36bo3bo$3o2b2o36bo2bo$45bo2$29bo$29bo3bo$29bo2bo$28b2ob2o$29bobo$30bo2$41b2o$41b2o7$33b2o$33b2o!","000000000111111111100000000000000000000000000000000000111111111111111100000000000000000000000000000000",[1,1,12]],
["bo12bob$bo12bob$obo10bobo$bo12bob$bo12bob$2bo3b4o3bo2b$6b4o6b$2b4o4b4o2b2$4bo6bo4b$5b2o2b2o!","000000002111111122100200002000020020020220220220220000111111111111111200002202002220020022222000222020",[2,0,7]],
["8b3o5b3o$8bobo5bobo$8bobo5bobo$6bob2o3bo3b2obo$6b2o4bobo4b2o$10b2obob2o$9bo7bo$9bobo3bobo$5b5o7b5o$4bo2bo11bo2bo$5bob3o7b3obo$7bob2o5b2obo$6b2obobo3bobob2o$6b3obo5bob3o2$10b2o3b2o$12bobo$9bo7bo$9b2o5b2o$6b2o11b2o$4bob2o11b2obo$4b2o2b2o7b2o2b2o$4bo2bo2bo5bo2bo2bo$5bo4bo5bo4bo$5bo2bo2bo3bo2bo2bo$2bo5bo9bo5bo$3bobo15bobo$7bo11bo$3bo3bobo7bobo3bo$3bo2bo3bo5bo3bo2bo$4b2o2b2o7b2o2b2o$8bo9bo2$8b5ob5o$bo6b2ob2ob2ob2o6bo$3o7bo5bo7b3o$o2b2o5bo5bo5b2o2bo$2bo3b5o5b5o3bo$7bob2o5b2obo$bo3bo15bo3bo$bob2o2bo11bo2b2obo$bob4o13b4obo$4bo17bo2$2bo21bo$bobo19bobo$o25bo$o3bo17bo3bo$5bo15bo$2o23b2o$2bo3bo2bo7bo2bo3bo$2bo3bobobo5bobobo3bo$2bo5bob2o3b2obo5bo$2bo3b2obo7bob2o3bo$6b2o11b2o$4bo17bo$3bo19bo$3bo4bo9bo4bo$2b2o3b2o9b2o3b2o$2b2o3bobo7bobo3b2o$2b2o3b2o3b3o3b2o3b2o$2b3o2b3obo3bob3o2b3o$6bob2obo3bob2obo$2b2o3b2obo5bob2o3b2o$3bob2o3bobobobo3b2obo$11bobobo$8bo9bo$8b3o5b3o$10b2obob2o$10b7o$8b3o5b3o$7b2obobobobob2o$6bo3bo5bo3bo$11b2ob2o$5bo2bobobobobobo2bo$6b4o7b4o$9bo7bo$9bo7bo$6b2obo2bobo2bob2o2$9b2o5b2o3$9bo7bo$9b3o3b3o$8bo2bo3bo2bo$9bo7bo$8bo2bo3bo2bo$11b2ob2o$12bobo$10bobobobo$9bo3bo3bo$9bo7bo$12bobo$7b2obo5bob2o$7b2o2bo3bo2b2o$7bo11bo$8bo9bo$6bobo9bobo$5b4o9b4o$5b2obobo5bobob2o$4bo2bo11bo2bo$9bobo3bobo$8b2obo3bob2o$4bo2bo3b2ob2o3bo2bo$9bo2bobo2bo$6bo2bob2ob2obo2bo$7bobobobobobobo$8b2o2bobo2b2o$9bobo3bobo$10b2o3b2o$7b2o9b2o$7b3o7b3o$7bobo7bobo$5b2o2bo7bo2b2o$5b2o13b2o$11bo3bo$6bo4bo3bo4bo$6b2o3bo3bo3b2o$7bo2bo5bo2bo$7b3o7b3o$6bobo9bobo$6b2o11b2o$6bobo4bo4bobo$6b2o4b3o4b2o$6b2o3bo3bo3b2o$5b3o4b3o4b3o$3b2o17b2o$2bo5b2o2bobo2b2o5bo2$2bo2bob3ob2ob2ob3obo2bo$8b3o5b3o$10b3ob3o$5bo4b2obob2o4bo$11bo3bo2$11b2ob2o!","000000000111111111100000000000000000000000000000000000111111111111111100000000000000000000000000000000",[3,0,7]],
["4bo8b$3b3o7b$2b2ob2o6b2$bobobobo2bo2b$2o3bo3b3ob$2o3bo6bo$10bobo$8bobo2b$9bo2bo$12bo!","000000000111111122200200000000000000020000220222222000111111111111112100000202000020002002022002222222",[2,0,5]],
["4b2o$2obo2bob2o$2o6b2o$obo4bobo$2b2o2b2o$b2ob2ob2o$3bo2bo$bo6bo$bo6bo2$b8o$2o6b2o!","000000000111111221100200000020220022002220222222222000111112121111211200020002022220022202222002220000",[1,0,3]],
["3bobob2o$2b2obob2o$2bo2bo3bo$o3bo$o3bo$2bo2bo2$2bobo$2b2o$3bo4b3o$2bo6bo$3bo$2b3o$2bobob4o$2bo2b2obobo$5bob2obo!","000000000111111111100000000020222000000000121122022000111112111111111102000000020220202020002000202202",[1,0,4]],
["4bo6bo$4bo6bo$2bo3bo2bo3bo$2b2ob2o2b2ob2o2$5b2o2b2o$3bo8bo$3bo3b2o3bo$bobo3b2o3bobo$o14bo$bo3bob2obo3bo$4bobo2bobo$3b2o6b2o$6bo2bo$7b2o!","000000000111111121100000000020022000000220121222202000111111111111111100020200002000002000222022202220",[1,0,5]],
["bo15bo$3o13b3o$obo13bobo$2bobo9bobo$2bo2bo7bo2bo$2bo3bo5bo3bo$2bo4bo3bo4bo$7bo3bo$4b2ob2ob2ob2o$4bobob3obobo$5bo3bo3bo$8bobo$5b2o5b2o$5b2o5b2o3$6b3ob3o$6b3ob3o$3b4o5b4o$bo2bo9bo2bo$bo3bo7bo3bo$b2o2bo7bo2b2o$b5o7b5o$2bobo9bobo!","000000000111111111100000000000200200000002111111020000111111111111111100000000000220000000020000000202",[2,0,5]],
["5bob3obo$5bo5bo$3b2o7b2o$2bob2o5b2obo$3bob2o3b2obo$8bo2$6bobobo$6b5o$7bobo$6b2ob2o$6bo3bo$4b2o5b2o$4b2o5b2o$5bo5bo2$3b2o7b2o$5bo5bo$5bo5bo$2bo11bo$bobo9bobo$3bobo5bobo$3b2o7b2o$b2o11b2o$o2bo9bo2bo$bo4bo3bo4bo$bo4b5o4bo$6bobobo$2bo4bobo4bo$bobobo5bobobo$bo3bo2bo2bo3bo$5bobobobo$5b2o3b2o$4bobo3bobo$4bo7bo$3b3o5b3o$3b2o7b2o$7b3o$7b3o$3b3o5b3o4$7b3o$8bo$5bo5bo$3b2ob2ob2ob2o$7bobo$3b2ob2ob2ob2o$5bo5bo2$3b4obob4o$2bob2o5b2obo$2b2o9b2o$4b9o2$5b3ob3o$6b2ob2o$4bo7bo$5b2obob2o$7bobo$4b2ob3ob2o$7b3o3$7bobo$8bo!","000000000111111111100000000000000000000000111211002000111111111111111100000000000000000000000000002200",[1,0,6]],
["7b2o$6bo2bo$6bo2bo$7b2o$7b2o$2b2obo4bob2o$2bo3bo2bo3bo$7b2o$2bo3b4o3bo$7b2o$3b3o4b3o$3bob2o2b2obo$5bo4bo2$2bo2bob2obo2bo$3bo3b2o3bo$2b5o2b5o$6bo2bo$2bo10bo$3bo8bo$2b2o8b2o3$4b2o4b2o$3bobo4bobo$3b2obo2bob2o2$6bo2bo$5b2o2b2o$6bo2bo$4bob4obo$3b2o6b2o$2b2o8b2o$b2o10b2o$b3o8b3o$2bobo6bobo$2b3o6b3o$4b2o4b2o$2o12b2o!","000000000111111111100000000000200000000000111111002000111111111111111200000000000020000000002000202000",[1,0,8]],
["bo13bo$2bo11bo$o2bo9bo2bo$obo11bobo$o15bo2$2b2o9b2o$2bobo7bobo$3bo9bo!","000000000111111111100000000000000000000000111111000000111111111111111100000000000000000000000000000000",[1,0,98]],
["2bo$3b2o$2o3bo$2b2obo$4bo2$5bo$5b2o$6bo$7b2o$8b2o2b2o$11bo2bo$12bobo$12bo2bo$13bo$13bo!","000000200111111112200222000020202222020200211221202000111111111111121200002020220220000222202222222222",[1,1,5]],
["10bo$10b2o$10bobo$11b3o2$5b3o$6bobo$3o4b2o$8bo!","000000000111111112100200200020000000022220221212020000111111111111111100002000000020000002000000022022",[8,8,48]],
["4b3o4b3o$4b3o4b3o4$3b5o2b5o$7bo2bo$5bo2b2o2bo$4b2o6b2o$3bo10bo$2bo12bo2$3o12b3o$b3o10b3o$3bo10bo$3b3o6b3o$6bo4bo$4bo8bo$3bo2bo4bo2bo$2bo2bo6bo2bo$8b2o$2bo2bobo2bobo2bo$8b2o$3b3o6b3o$4bo8bo$4bo8bo$2bo3bo4bo3bo$2bo3bo4bo3bo$bo2bo2bo2bo2bo2bo!","000000000111111111100000000000200020020000020220222000111111111111122200000200002200000000222022202022",[1,0,6]],
["3bo3bo$3bo3bo$2bobobobo2$2bobobobo$4bobo$bobo3bobo$bobo3bobo$bo2b3o2bo2$2o7b2o$o9bo$bo7bo$2ob5ob2o$bo2bobo2bo$2bobobobo2$3b2ob2o$3bo3bo$2b2o3b2o$2b2o3b2o!","000000002111111121100200002000200002000000202200002000111111111111111100000000200000022022200000222202",[1,0,4]],
["2b3o5b3o$2bo3bobo3bo$6bobo$4b3ob3o$6bobo$3bo2bobo2bo$3b2obobob2o$5bobobo$2bo2bobobo2bo$2o4bobo4b2o$bo2bobobobo2bo$5b2ob2o$bobo2bobo2bobo$bob2obobob2obo$2bo3bobo3bo$2bobobobobobo$6bobo$3bo2bobo2bo$4bobobobo$4bobobobo$2b3obobob3o$bo2bobobobo2bo$bo2bobobobo2bo$bobo2bobo2bobo$3b2obobob2o$6bobo$6bobo$5b2ob2o$2b2o2bobo2b2o$b2ob3ob3ob2o$bo4bobo4bo$5b2ob2o$2ob3o3b3ob2o!","000000000111111111100000000000000000000000100020202000111111111111111100000000000000002000020200020022",[1,0,5]],
["o13bo$3o9b3o$2b2o3bo3b2o$b4obobob4o$6bobo$6bobo$2bo3bobo3bo$b2o3bobo3b2o$b2o2b2ob2o2b2o$2bo2bo3bo2bo2$4bo5bo$5bo3bo$7bo$4bobobobo$2b2ob2ob2ob2o$4b3ob3o$5b2ob2o$4bo5bo$2b2o7b2o$2b2o7b2o$4b3ob3o$4bobobobo$2bobo5bobo$bo11bo$b2ob3ob3ob2o$b2obo5bob2o$4bo5bo$bo2bo5bo2bo$bob2o5b2obo$3bo7bo$2bo9bo!","000000000111111111100000000000000000000002020010002000111111111111111100000000020220000020002000022222",[1,0,5]],
["4b2o5b2o$3bo2bo3bo2bo$3bo2bo3bo2bo$4b2o5b2o$4b2o5b2o$3bo2bo3bo2bo3$3bo2bo3bo2bo$4bo7bo$3bo2b2ob2o2bo$bo4b2ob2o4bo$o2b2obo3bob2o2bo$bo4bo3bo4bo$2bobo2bobo2bobo$6b2ob2o$6b5o$5b2obob2o$7bobo2$4bo2bobo2bo$4bob2ob2obo$3b2o7b2o$5b3ob3o$5b3ob3o2$7bobo$5bobobobo$3bobobobobobo$2bo2bobobobo2bo$b2ob2obobob2ob2o$4bo2bobo2bo$3b3obobob3o$7bobo$o6bobo6bo$b3obobobobob3o$5bobobobo$6bo3bo$4b2o5b2o$3b3o5b3o2$bo13bo$b2o11b2o$4bo7bo$2bobo7bobo2$3bo9bo$2bob2o5b2obo2$6bo3bo$4bobo3bobo$5bo5bo!","000000000111111111100000000000200000000000001020202000111111111111111100000000000220000000000000222002",[1,0,5]],
["3o$3bo$4bo$2bobo$3bo!","000000202122211222200222122022222222222222222222222200121111112212222220222222220222220022222222222222",[1,1,4]],
["5b3o2$4bo3bo$3b2o3b2o$3b2obob2o$4bobobo2$2b3o3b3o2$5bobo$5bobo$3bobobobo$4b2ob2o4$4b5o$4bobobo$4b2ob2o$4b2ob2o2$b3o5b3o$3bo5bo$o3bo3bo3bo$o4bobo4bo$o11bo$5o3b5o!","000000000111111111100000000000200200020000020100002000111111111111121200002002000020002020020020222002",[1,0,5]],
["5bo5bo$4bobo3bobo$7bobo$b2ob2obobob2ob2o$o3bobo3bobo3bo$obo11bobo$bo2b3o3b3o2bo$4bo7bo$4b3o3b3o$5bobobobo$3bob3ob3obo$b2o2bo5bo2b2o$b2obo7bob2o$bo2bo7bo2bo$4bobo3bobo$7bobo$4bo2bobo2bo$4bo3bo3bo$6b5o$3b3o5b3o$5bob3obo$7b3o$6bobobo$4bo2bobo2bo$6bo3bo$4b2o5b2o$4b2o5b2o$4b3o3b3o$4bo2bobo2bo$3b2o2bobo2b2o$4b2obobob2o$b2obo2bobo2bob2o$obobo7bobobo$b2obo7bob2o$3b2o2bobo2b2o$3b2ob2ob2ob2o$4b3o3b3o$4b2o5b2o2$2bo3bo3bo3bo$bo13bo$o2b2o7b2o2bo$b2o11b2o$5b2o3b2o$3b3obobob3o$2b2obobobobob2o$2b2obobobobob2o$3bobobobobobo$3bobobobobobo$b2o4bobo4b2o$7bobo$b2obo2bobo2bob2o$b3ob2o3b2ob3o2$2b2o9b2o$4bo7bo$2bo11bo$2bo2bo2bo2bo2bo$5b3ob3o$3bo3bobo3bo$4b2obobob2o$4b2obobob2o$5bobobobo$4b2obobob2o$7bobo$4bo2bobo2bo$5bo5bo$5bo5bo$5b7o$5bo5bo$8bo$8bo$6b5o$b4obobobob4o$ob5obob5obo$o15bo$7bobo$bo3bo5bo3bo$2b4o5b4o$4b2o5b2o!","000000000111111111100000000000000000000000000000102000111111111111111100000000000020000000000000222202",[1,0,5]],
["7b2o$6b4o$4bo6bo$3bo3b2o3bo$3bo3b2o3bo$7b2o$5bo4bo$5bo4bo2$6bo2bo$5bo4bo$5bo4bo$b3o2b4o2b3o$b3o3b2o3b3o$4bo6bo$b4o6b4o$o3bo6bo3bo$b2o10b2o$5bo4bo$6bo2bo$3bo2bo2bo2bo$4b2o4b2o2$5b2o2b2o$6bo2bo$2bo10bo$2bo4b2o4bo$2bo10bo$4bobo2bobo$3bob6obo$3bob6obo!","000000000111111111100000000000220000000002020000212000111111111111111100000000000220000000200000022002",[1,0,5]],
["3bo$b2ob2o$b2ob2o$bo3bo$2o3b2o!","000002202111211222202202222222220222222022222222222200112212122112222200102220222222222222222222222222",[1,0,2]],
["3o3b3o$o2bobo2bo$o2bobo2bo$2obobob2o$o7bo!","000000002121111222202202202220222220222222200220222000111022211111121202222222020220202022002022202222",[2,0,4]],
["2b3o$bo2bo$bo2bo2$2o8b3o$o9bo2bo$10bo2bo2$13b2o$14bo2$4bo$5bo$3b2o5bo$bo2bo$4bo8b2o$5bo5bo$13b2o$13bo!","000000000111111111100000000000220000000000000000112000111111111111111100000000000020000000002000002002",[9,0,28]],
["2bo5bo$b3o3b3o$o2b2ob2o2bo$ob2o3b2obo$2bo5bo$o2bo3bo2bo$3bo3bo$bobo3bobo$3bo3bo!","000000000111111112200200200200202020222202022220222000111112111111222202022022020220222022022022222222",[2,0,4]],
["7b3o$10bo$5bo5bo$5bo5bo$7bob2o$3b2obo2b3o$4bo5bo$3bo$9b2o$9b3o$6b2o3bo$9b3o$b2o7b2o$o3bo5bo$bo2b2obob2o$5bo4bo$bo2bo4bo$2bobo$2b2obo$3bobo$3b2o$3bo$3bo$4bo$2b2o!","000000000111111111100000000000200000000020020002002000111111111111111100000000020220000000202000002022",[1,1,5]],
["2bo$bobo$bobo$o3bo2$2ob2o2$3b2o$bob2o$bobo$bobo!","000000000111111121100202220202200022220020220220202000111012111111122200102202222220222020222022222200",[2,0,6]],
["6bo$4bo2bo$b4o2b2o$o7bobo$o2bo4bo$o2bo4bo$8bo$2o$8b2o$2bo$2o8bo$obo5b2o$8bobo!","000000200111111112100200000000222200220222222022222000111011211111221100220202022222200002222222222222",[1,0,3]],
["9bo$7b2ob2o$7b5o$6bo5bo2$6bobobobo$7bo3bo$7bo3bo$4b5ob5o$2b2ob2obobob2ob2o$2bo5bobo5bo$2bo2bob2ob2obo2bo$2b3o3bobo3b3o$2bo5bobo5bo$6bobobobo2$3b3ob2ob2ob3o$2bo2bob2ob2obo2bo$2bobobobobobobobo$2bo4b2ob2o4bo$2bo5bobo5bo$3b2ob2o3b2ob2o$2bobobo5bobobo$7bo3bo$3bo11bo$5bo7bo$bob3o7b3obo$3o13b3o2$2o15b2o$2o15b2o$bo15bo$2bo13bo$2bo13bo$2bo13bo!","000000000111111111100000000000000000000000000220022000111011111111111100100000000000000020000020222022",[2,0,5]],
["5bo$4bo2bo$4bo2bo$3bo3b2o$2bo2bo2bo$2bo3bo$o$3o$b2o!","000000202111111111200002002020220022200022220220202000121011111112122100202202222220020022222220222222",[1,0,4]],
["40b2o$40b2o2$32b2obo$33bo2bo$36bo2$20bo$19bobo$18bo3bo$19bobo$20bo6$15bo$13b2ob2o$13bo2bo$14b2o$b2o$obo$bo!","000000000111111111100000000000000000000000000000001000111111111111111100000000000000000000000000000000",[5,2,190]],
["3bo$b2ob2o$o2bo2bo$obobobo$3bo$bo3bo$bobobo!","000000002111211211202220222002001222222211222222222002221212122211221220202222220202220022022222222222",[1,0,2]],
["2bo$b3o$o3bo$bo2bo$3b2obo$6bo$4bobo!","000000202111211121200020000000222221222221222222222000111111111111122202022222020220202022222222222222",[1,0,3]],
["2b3o$bo3bo$o2bo2bo$3ob3o!","000000200121211122202222000200221211222212002222222200111111111111121202222022220220202000022222222202",[2,0,5]],
["5bo$5bo$4bobo2$2b4o$3bobo$2bo2bo$2o3bo$bo2bo$2b2o2$3bo$3bobo$3bobo$5bo2$3b2o!","000000000111111112100200200200221121121222020202002000111111111111111200002202000222002000202002200222",[1,0,4]],
["4b3ob3o$3bo2bobo2bo$3bo2bobo2bo$2bo9bo$b4o5b4o$ob3o5b3obo$bo2b3ob3o2bo$bo4bobo4bo$2obo2bobo2bob2o$2ob3o3b3ob2o3$5b2ob2o$3bo2bobo2bo$2b5ob5o$2bo9bo$b2o9b2o$bo11bo$3bo2b3o2bo$3b2ob3ob2o$6bobo$7bo$6bobo$5b2ob2o$5bobobo$3bo2b3o2bo$3bobo3bobo$5bo3bo2$6b3o$6bobo$5bo3bo2$5bo3bo$4bob3obo$3b2o5b2o$4bo2bo2bo$4bo5bo$5bo3bo$6b3o$6bobo$7bo$6b3o2$4b2o3b2o$4b2o3b2o2$5bo3bo$6bobo$3bo2bobo2bo$3bo2bobo2bo$4b2o3b2o$5bo3bo$5b2ob2o$3b2o5b2o$bo4bobo4bo$bo11bo$2bobo5bobo$5bo3bo$3b3o3b3o$3b2obobob2o$4bobobobo$4bobobobo2$4b3ob3o!","000000000111111111100000000000001111111121000000000000111111111111111100000000000200000000000000020000",[1,0,5]],
["48bo$48b2o$41b2o4b4o$40bo2bo6b2o$41bobo6b3o$43b3o4bo$43bob2o$42bo2b3o$39b2obo4bo$46b4o$38bo5b2o4bo$38bo2bo6bobo$38b3o3bo4bo$34b2obo2bo3bo$36bobob3o$36bo2bo$31b3obob2o$26b3ob3o3bo2bo$32bo6bo$24bo2b2obo5bo$23b2o2bobo4b3o$23bo2bobo2bo3b2o$22bob2o2bo4bobo$24b2obo4bo$20b2o4b3ob2obobo$18bob3o5b2o2b2obo$21bo6bo2bo3bo$18bo3bo6b2o$18b2o3bo5b2ob2o$18b2obo2bo6b2o$18bob3o2bobo2bo$18b2o2b2o2b3o$18b2o2bo4b2o$13b2o3b4ob2o$11bo2bo3b8obo$10b3obo$9bob3o$7bob2o3bo$6b2o7bob3o$6b2o8bo2bo$11bo4b2o$2b6o4bo3b3o$4b4o7bobo$3bo4bo6b2o$obo3bo2bo$bo8b2ob3o$8bob2ob2o$10b2o$10b2o$9bobo$8bo2bo$7bo$8bo!","000000000111111111100000000000001111111111000002022000111111111111111100000000000020000000000000200020",[1,1,4]],
]
It contains a lot of close Life relatives in birth conditions, including tlife, HighLife, DryLife, B38/S23, and B35/S23.
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

User avatar
77topaz
Posts: 1496
Joined: January 12th, 2018, 9:19 pm

Re: On a new Glider Database

Post by 77topaz » July 7th, 2018, 3:18 am

AforAmpere wrote:
calcyman wrote:I could make Catagolue dynamically display rulemin and rulemax on pattern pages for low-period oscillators and spaceships, in the same way it displays minpop and maxpop.
That would be a really nice feature, as it would allow people to quickly get the information needed to input into the script. That would be neat in general, as it lets you see the versatility of the pattern as well.
I second (third?) this! :D

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

Re: On a new Glider Database

Post by Naszvadi » July 7th, 2018, 9:18 am

My 2cents:

A content tracker tool + Reviewing tool would be nice for managing a glider database. Including still lifes+oscillators.

Proposal for content trackingin this particular casee: a git repository, which contains a CSV file similar to this: http://fano.ics.uci.edu/glider.db ( used by http://fano.ics.uci.edu/ca/rules/ )

Pushing (sending) a change should trigger events like checks, where each altered line of the CSV file is checked if it is valid:
  • oscillator or glider with the smallest period, movement etc.
  • works strictly in the committed rule interval
  • inserted in the correct row if it is new
  • one of the smallest phases are inserted
IMHO different CSV database is needed for the 2-state 2 dimensional rules with the following neighbourhoods:
  • Hextiles (6 neighbours per cell, isotropic)
  • Moore ("classic")
  • Neumann (only B0 for gliders, basically this NH is a subset of Moore automata)
  • Simplectic tiling
A reviewer tool (p.ex. Gerrit) can invite both real and technical users, the latter can automatically vote for changes performing checks and propose automatic corrections if available.

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

Re: On a new Glider Database

Post by Saka » July 7th, 2018, 9:43 am

calcyman wrote:I could make Catagolue dynamically display rulemin and rulemax on pattern pages for low-period oscillators and spaceships, in the same way it displays minpop and maxpop.
The only criticism I have about this is that Catagolue then wouldn't be solely for soup searching so the name does not fit as much anymore, if anybody knows what I mean.

But I guess businesses do change without changing their name, even though their name is fit for the thing they did before changing.

Perhaps changing the name of the page the database is on to something like "Catagolue Glider DB".

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

Re: On a new Glider Database

Post by calcyman » July 7th, 2018, 10:49 am

Saka wrote:
calcyman wrote:I could make Catagolue dynamically display rulemin and rulemax on pattern pages for low-period oscillators and spaceships, in the same way it displays minpop and maxpop.
The only criticism I have about this is that Catagolue then wouldn't be solely for soup searching so the name does not fit as much anymore, if anybody knows what I mean.
I certainly don't. The name Catagolue is a pun on 'catalogue' and 'GoL', and it was always intended to be a database of objects in cellular automata. How does the name relate to soup searching?

It currently features known glider syntheses of small still-lifes (does anyone use this feature?) in b3s23, which is also unrelated to its function of collecting censuses.
What do you do with ill crystallographers? Take them to the mono-clinic!

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

Re: On a new Glider Database

Post by dvgrn » July 7th, 2018, 12:30 pm

AforAmpere wrote:
wwei23 wrote:Why can't we use the LifeWiki to do this? We could have a page with the database in it.
Maybe we should do that, if anyone else agrees. Anyone, please say something if you either agree, disagree, or don't care.
Seems simple enough. Here's a version of Glider_Database.py that runs off a page I added to my LifeWiki user space.

I ran into a few problems with the original code -- the indentation seems to be a little wacky. (Maybe a TAB-to-space conversion problem?) Also, the script as written clobbered the clipboard unnecessarily, so I replaced the copy/paste pattern handline with a call to g.parse().

Obviously LIFEWIKI_URL can be moved to another LifeWiki page, or there could be multiple pages for different collections.

The next trick might be to build a stamp collection in B/S012345678 showing all the spaceships in the current collection. When you click on any one of them, a new "Spaceship" layer could open in Golly with the chosen spaceship in the correct rule.

Code: Select all

# Glider_Database.py v 1.1.  Run with Golly.
# Updated by dvgrn to read contents of LIFEWIKI_URL,  7 July 2018
# -- also fixed whitespace, unclobbered clipboard, other trivial changes.

import golly as g
import os
import sys
import urllib2

LIFEWIKI_URL = 'http://conwaylife.com/wiki/User:Dvgrn/gliderlist'

response = urllib2.urlopen(LIFEWIKI_URL)
html = response.read()
gliderlist = html[html.find("<p>")+3:html.find("</p>")]
g.new("")
class RuleGenerator:
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #    
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
    
    allneighbours = [  
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
        
    allneighbours_flat = [n for x in allneighbours for n in x]
    
    numneighbours = len(notationdict)
    
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
    
    # Save the isotropic rule
    def saveAllRules(self):    
        self.saveIsotropicRule()
    
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
        
        # Reverse the rule string to simplify processing
        part = part[::-1]
        
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                    
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
        
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):    
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
        rulestring = rulestring.lower()
        
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
        
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
        
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + '_S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
            

    # Save a rule file:
    def saverule(self, name, comments, table, colours):        
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"
        global results
        results = ""
        results += table

        # 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.
        

    # 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 = "\n"
        for name in namelist:
            block += self.newvar(name, vallist)
        return block

    def scoline(self, chara, charb, left, right, amount):
        line = str(left) + ","
        for i in xrange(8):
            line += chara if (i < amount) else charb
            line += chr(97 + i)
            line += ","
        line += str(right) + "\n"
        return line

    def isotropicline(self, chara, charb, left, right, n):
        line = str(left) + ","
        neighbours = self.notationdict[n]       
        for i in xrange(8):
            line += chara if neighbours[i] else charb
            line += chr(97 + i)
            line += ","
        line += str(right) + "\n"
        return line
        
    def saveIsotropicRule(self):
        table = ""
        for n in self.allneighbours_flat:
            table += "1" if self.bee[n] else "0"
        for n in self.allneighbours_flat:
            table += "1" if self.ess[n] else "0"        
        colours = ""
        comments = ""
        self.saverule(self.rulename, comments, table, colours)

rulestring = g.getstring("Enter rule string in Alan Hensel's isotropic rule notation", 
                         "B2-a/S12")

rg = RuleGenerator()
rg.setrule(rulestring)
rg.saveIsotropicRule()
g.setrule(rulestring)

# g.show(results)
q = []
p=0

j=0
y=eval("["+gliderlist.replace("\n","")+"]")
for z in range(len(y)):
    p = 0
    for x in range(len(results)):
        if (results[x] == "1" and y[z][1][x] == "0") or (results[x] == "0" and y[z][1][x] == "1"):
            # g.show(rg.rulename + "False")
            p = 1
    if p ==0:
        q+=[y[z]]  

q = sorted(q,key=lambda qe: (qe[2][0]/float(qe[2][2]))+qe[2][1]*200.0/qe[2][0])
for v in q:   
    g.putcells(g.parse(v[0]),j,0)
    j=g.getrect()[2]+15
g.fit()

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 7th, 2018, 1:02 pm

dvgrn wrote: Seems simple enough. Here's a version of Glider_Database.py that runs off a page I added to my LifeWiki user space.

I ran into a few problems with the original code -- the indentation seems to be a little wacky. (Maybe a TAB-to-space conversion problem?) Also, the script as written clobbered the clipboard unnecessarily, so I replaced the copy/paste pattern handline with a call to g.parse().

Obviously LIFEWIKI_URL can be moved to another LifeWiki page, or there could be multiple pages for different collections.
Nice! this will work well, as only registered LifeWiki users will be able to edit, which will reduce any spam. I just hope the page is able to handle large amounts of ships, as I would like to import the 5s project at some point. Can I try adding ships?
dvgrn wrote: The next trick might be to build a stamp collection in B/S012345678 showing all the spaceships in the current collection. When you click on any one of them, a new "Spaceship" layer could open in Golly with the chosen spaceship in the correct rule.
The problem with that is that the collection would soon get massive, and finding a ship would be practically impossible, especially as there are ships in different rules with different evolution that look the same. Maybe there can be some way to display them so the rule that they work in is displayed above them, or something like that?
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

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

Re: On a new Glider Database

Post by dvgrn » July 7th, 2018, 1:39 pm

AforAmpere wrote:Nice! this will work well, as only registered LifeWiki users will be able to edit, which will reduce any spam. I just hope the page is able to handle large amounts of ships, as I would like to import the 5s project at some point. Can I try adding ships?
Go right ahead -- and/or take the script back over, change LIFEWIKI_URL to point somewhere else, and experiment from there.
AforAmpere wrote:The problem with that is that the collection would soon get massive, and finding a ship would be practically impossible, especially as there are ships in different rules with different evolution that look the same. Maybe there can be some way to display them so the rule that they work in is displayed above them, or something like that?
Sure -- could add labels in a big font, maybe, like in the Elementary Conduits Collection? That would be a step in the wrong direction in terms of making the stamp collection massive, but it probably doesn't matter since it the pattern would be generated from the script -- wouldn't really have to keep it anywhere in stamp-collection form.

Probably a better idea would be some Lua-based browser method of allowing users to pick rules by clicking (from a list of whatever rules are available). Maybe a scrollable list on the left side, and a click displays those spaceships in a window on the right side...? I definitely need to play around with Lua more, because clearly this kind of thing is pretty easy but offhand I don't know exactly how to do it.

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 7th, 2018, 6:27 pm

Here are two modified scripts to aid in getting the rulestring for the database. The first is a version of the getallisorule.py, which was modified by Naszvadi first. Save it as getallisorule2.py in Golly. The second script is a better version of torulestring.py. This version takes whatever pattern is on the grid, which for this should be a spaceship, and outputs the rulestring for the given period, a lot simpler than using multiple scripts. Again, there may be whitespace issues, because I used Notepad to edit it. It should work, though. torulestring.py will put the rulestring in your keyboard.

Code: Select all

# Rule computation script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# Updated by: Peter, NASZVADI (), June 2017.

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

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

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

# Python versions < 2.4 don't have "sorted" built-in
try:
    sorted
except NameError:
    def sorted(inlist):
        outlist = list(inlist)
        outlist.sort()
        return outlist

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

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

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

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

clist = []
rule = g.getrule().split(':')[0]

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

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

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

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

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

g.select(g.getrect())
g.copy()
s = int(s)

for i in range(0,s):
    g.run(1)
    clist.append(list(chunks(g.getcells(g.getrect()), 2)))
    mcc = min(clist[i])
    clist[i] = [[x[0] - mcc[0], x[1] - mcc[1]] for x in clist[i]]

g.show('Processing...')

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

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

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

for i in [iter2 for iter1 in Hensel for iter2 in iter1]:
    if not i in b_OK:
        b_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ''.join(b_OK) + '/S' + ruleArr[1]))
        except:
            b_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_OK.remove(i)
                    break
            except:
                b_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_OK.sort()

    if not i in s_OK:
        s_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_OK)))
        except:
            s_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_OK.remove(i)
                    break
            except:
                s_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_OK.sort()

    if i in b_need:
        b_need.remove(i)
        g.setrule(rulestringopt('B' + ''.join(b_need) + '/S' + ruleArr[1]))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_need.append(i)
                    break
            except:
                b_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_need.sort()

    if i in s_need:
        s_need.remove(i)
        g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_need)))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_need.append(i)
                    break
            except:
                s_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_need.sort()

g.setrule(oldrule)
ruleres = 'B' + ''.join(sorted(b_need)) + '/S' + ''.join(sorted(s_need)) + \
    ' - B' + ''.join(sorted(b_OK)) + '/S' + ''.join(sorted(s_OK))
ruleres = rulestringopt(ruleres)
if __name__ == "__builtin__":    
    g.show(ruleres)
    g.getstring('Pattern works in rules:', ruleres, 'Rules calculator')

Code: Select all

# torulestring.py, run with Golly.
import golly as g
import os
import sys
#g.new("")
class RuleGenerator:
    notationdict = {
        "0"  : [0,0,0,0,0,0,0,0],   #    
        "1e" : [1,0,0,0,0,0,0,0],   #   N
        "1c" : [0,1,0,0,0,0,0,0],   #   NE
        "2a" : [1,1,0,0,0,0,0,0],   #   N,  NE
        "2e" : [1,0,1,0,0,0,0,0],   #   N,  E
        "2k" : [1,0,0,1,0,0,0,0],   #   N,  SE
        "2i" : [1,0,0,0,1,0,0,0],   #   N,  S
        "2c" : [0,1,0,1,0,0,0,0],   #   NE, SE
        "2n" : [0,1,0,0,0,1,0,0],   #   NE, SW
        "3a" : [1,1,1,0,0,0,0,0],   #   N,  NE, E
        "3n" : [1,1,0,1,0,0,0,0],   #   N,  NE, SE
        "3r" : [1,1,0,0,1,0,0,0],   #   N,  NE, S
        "3q" : [1,1,0,0,0,1,0,0],   #   N,  NE, SW
        "3j" : [1,1,0,0,0,0,1,0],   #   N,  NE, W
        "3i" : [1,1,0,0,0,0,0,1],   #   N,  NE, NW
        "3e" : [1,0,1,0,1,0,0,0],   #   N,  E,  S
        "3k" : [1,0,1,0,0,1,0,0],   #   N,  E,  SW
        "3y" : [1,0,0,1,0,1,0,0],   #   N,  SE, SW
        "3c" : [0,1,0,1,0,1,0,0],   #   NE, SE, SW
        "4a" : [1,1,1,1,0,0,0,0],   #   N,  NE, E,  SE
        "4r" : [1,1,1,0,1,0,0,0],   #   N,  NE, E,  S
        "4q" : [1,1,1,0,0,1,0,0],   #   N,  NE, E,  SW
        "4i" : [1,1,0,1,1,0,0,0],   #   N,  NE, SE, S
        "4y" : [1,1,0,1,0,1,0,0],   #   N,  NE, SE, SW
        "4k" : [1,1,0,1,0,0,1,0],   #   N,  NE, SE, W
        "4n" : [1,1,0,1,0,0,0,1],   #   N,  NE, SE, NW
        "4z" : [1,1,0,0,1,1,0,0],   #   N,  NE, S,  SW
        "4j" : [1,1,0,0,1,0,1,0],   #   N,  NE, S,  W
        "4t" : [1,1,0,0,1,0,0,1],   #   N,  NE, S,  NW
        "4w" : [1,1,0,0,0,1,1,0],   #   N,  NE, SW, W
        "4e" : [1,0,1,0,1,0,1,0],   #   N,  E,  S,  W
        "4c" : [0,1,0,1,0,1,0,1],   #   NE, SE, SW, NW
        "5i" : [1,1,1,1,1,0,0,0],   #   N,  NE, E,  SE, S
        "5j" : [1,1,1,1,0,1,0,0],   #   N,  NE, E,  SE, SW
        "5n" : [1,1,1,1,0,0,1,0],   #   N,  NE, E,  SE, W
        "5a" : [1,1,1,1,0,0,0,1],   #   N,  NE, E,  SE, NW
        "5q" : [1,1,1,0,1,1,0,0],   #   N,  NE, E,  S,  SW
        "5c" : [1,1,1,0,1,0,1,0],   #   N,  NE, E,  S,  W
        "5r" : [1,1,0,1,1,1,0,0],   #   N,  NE, SE, S,  SW
        "5y" : [1,1,0,1,1,0,1,0],   #   N,  NE, SE, S,  W
        "5k" : [1,1,0,1,0,1,1,0],   #   N,  NE, SE, SW, W
        "5e" : [1,1,0,1,0,1,0,1],   #   N,  NE, SE, SW, NW
        "6a" : [1,1,1,1,1,1,0,0],   #   N,  NE, E,  SE, S,  SW
        "6c" : [1,1,1,1,1,0,1,0],   #   N,  NE, E,  SE, S,  W
        "6k" : [1,1,1,1,0,1,1,0],   #   N,  NE, E,  SE, SW, W
        "6e" : [1,1,1,1,0,1,0,1],   #   N,  NE, E,  SE, SW, NW
        "6n" : [1,1,1,0,1,1,1,0],   #   N,  NE, E,  S,  SW, W
        "6i" : [1,1,0,1,1,1,0,1],   #   N,  NE, SE, S,  SW, NW
        "7c" : [1,1,1,1,1,1,1,0],   #   N,  NE, E,  SE, S,  SW, W
        "7e" : [1,1,1,1,1,1,0,1],   #   N,  NE, E,  SE, S,  SW, NW
        "8"  : [1,1,1,1,1,1,1,1],   #   N,  NE, E,  SE, S,  SW, W,  NW
        }
    
    allneighbours = [  
        ["0"],
        ["1e", "1c"],
        ["2a", "2e", "2k", "2i", "2c", "2n"],
        ["3a", "3n", "3r", "3q", "3j", "3i", "3e", "3k", "3y", "3c"],
        ["4a", "4r", "4q", "4i", "4y", "4k", "4n", "4z", "4j", "4t", "4w", "4e", "4c"],
        ["5i", "5j", "5n", "5a", "5q", "5c", "5r", "5y", "5k", "5e"],
        ["6a", "6c", "6k", "6e", "6n", "6i"],
        ["7c", "7e"],
        ["8"],
        ]
        
    allneighbours_flat = [n for x in allneighbours for n in x]
    
    numneighbours = len(notationdict)
    
    # Use dict to store rule elements, initialised by setrule():
    bee = {}
    ess = {}
    alphanumeric = ""
    rulename = ""
    
    # Save the isotropic rule
    def saveAllRules(self):    
        self.saveIsotropicRule()
    
    # Interpret birth or survival string
    def ruleparts(self, part):

        inverse = False
        nlist = []
        totalistic = True
        rule = { k: False for k, v in self.notationdict.iteritems() }
        
        # Reverse the rule string to simplify processing
        part = part[::-1]
        
        for c in part:
            if c.isdigit():
                d = int(c)
                if totalistic:
                    # Add all the neighbourhoods for this value
                    for neighbour in self.allneighbours[d]:
                        rule[neighbour] = True
                elif inverse:
                    # Add all the neighbourhoods not in nlist for this value
                    for neighbour in self.allneighbours[d]:
                        if neighbour[1] not in nlist:
                            rule[neighbour] = True
                else:
                    # Add all the neighbourhoods in nlist for this value
                    for n in nlist:
                        neighbour = c + n
                        if neighbour in rule:
                            rule[neighbour] = True
                        else:
                            # Error
                            return {}
                    
                inverse = False
                nlist = []
                totalistic = True

            elif (c == '-'):
                inverse = True

            else:
                totalistic = False
                nlist.append(c)
        
        return rule

    # Set isotropic, non-totalistic rule
    # Adapted from Eric Goldstein's HenselNotation->Ruletable(1.3).py
    def setrule(self, rulestring):
    
        # neighbours_flat = [n for x in neighbours for n in x]
        b = {}
        s = {}
        sep = ''
        birth = ''
        survive = ''
        
        rulestring = rulestring.lower()
        
        if '/' in rulestring:
            sep = '/'
        elif '_' in rulestring:
            sep = '_'
        elif (rulestring[0] == 'b'):
            sep = 's'
        else:
            sep = 'b'
        
        survive, birth = rulestring.split(sep)
        if (survive[0] == 'b'):
            survive, birth = birth, survive
        survive = survive.replace('s', '')
        birth = birth.replace('b', '')
        
        b = self.ruleparts(birth)
        s = self.ruleparts(survive)

        if b and s:
            self.alphanumeric = 'B' + birth + 'S' + survive
            self.rulename = 'B' + birth + '_S' + survive
            self.bee = b
            self.ess = s
        else:
            # Error
            g.note("Unable to process rule definition.\n" +
                    "b = " + str(b) + "\ns = " + str(s))
            g.exit()
            

    # Save a rule file:
    def saverule(self, name, comments, table, colours):
        
        ruledir = g.getdir("rules")
        filename = ruledir + name + ".rule"

        global results
	results = ""
        results += table

        # 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.
        

    # 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 = "\n"

        for name in namelist:
            block += self.newvar(name, vallist)

        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 isotropicline(self, chara, charb, left, right, n):

        line = str(left) + ","
        neighbours = self.notationdict[n]
        
        for i in xrange(8):
            if neighbours[i]:
                line += chara
            else:
                line += charb
            line += chr(97 + i)
            line += ","

        line += str(right) + "\n"

        return line
        
    def saveIsotropicRule(self):
    
  

        table = """"""

        for n in self.allneighbours_flat:
            if self.bee[n]:
                table += "1"
	    else:
		table += "0"

        for n in self.allneighbours_flat:
            if self.ess[n]:
                table += "1"
	    else:
		table += "0"
        
        colours = ""
	comments = ""
        self.saverule(self.rulename, comments, table, colours)


import golly as g
import getallisorule2; reload(getallisorule2)
a = str(getallisorule2.ruleres)
min,max = a.split("-")
min = str(min.strip())
max = str(max.strip())
rulestring = min #g.getstring("Enter minimum rule string:")

rulestring2 = max #g.getstring("Enter maximum rule string:")

rg = RuleGenerator()

rg.setrule(rulestring)
rg.saveIsotropicRule()
q = results
rg.setrule(rulestring2)
rg.saveIsotropicRule()
t = results
g.setrule(rulestring)

# g.show(results)

final = ""
for z in range(len(q)):
    if q[z] == t[z]:
	final+=q[z]
    else:
        final+="2"
g.setclipstr(final)	
Tell me if this does not work, please.
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

User avatar
Majestas32
Posts: 549
Joined: November 20th, 2017, 12:22 pm
Location: 'Merica

Re: On a new Glider Database

Post by Majestas32 » July 9th, 2018, 1:04 am

AforAmpere wrote:
wwei23 wrote:Why can't we use the LifeWiki to do this? We could have a page with the database in it.
Maybe we should do that, if anyone else agrees. Anyone, please say something if you either agree, disagree, or don't care.
Agree.
Searching:
b2-a5k6n7cs12-i3ij4k5j8
b2-a3c7cs12-i

Currently looking for help searching these rules.

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » July 9th, 2018, 1:19 pm

Majestas32 wrote: Agree.
Then please test out dvgrn's and my scripts. Try adding ships to the database on dvgrn's page, because if I am the only one who will contribute, then this will be pointless.
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

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

Re: On a new Glider Database

Post by Saka » May 5th, 2019, 7:22 am

Sorry to bring this thread back up, but a little conversation about this has happened on discord about this.
A few questions:
1. How does the rulerange format work?
2. How about an online browser like in the original fano database?

AforAmpere
Posts: 1334
Joined: July 1st, 2016, 3:58 pm

Re: On a new Glider Database

Post by AforAmpere » May 5th, 2019, 10:44 am

Saka wrote:Sorry to bring this thread back up, but a little conversation about this has happened on discord about this.
A few questions:
1. How does the rulerange format work?
2. How about an online browser like in the original fano database?
Rulerange format has each transition, ordered like it is on the wiki, labeled with a 0,1,or 2. If the transition cannot be active and the pattern works, it gets a 0, if the pattern needs that transition, it gets a 1. If the transition does not affect the evolution, it gets a 2. Do that for all 102 transitions, and put them in a string.

I have no idea how to get an online browser to do this. Maybe somebody else has a better idea.

Also, torulestring.py was a little broken because of an error in the getallisorules.

Here's fixed getallisorule2:

Code: Select all

# Rule computation script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# Updated by: Peter, NASZVADI (), June 2017.

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

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

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

# Python versions < 2.4 don't have "sorted" built-in
try:
    sorted
except NameError:
    def sorted(inlist):
        outlist = list(inlist)
        outlist.sort()
        return outlist

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

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

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

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

clist = []
rule = g.getrule().split(':')[0]

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

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

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

if __name__ == "__builtin__" or __name__ == "getallisorule2":    
    s = g.getstring('Enter the period:', '', 'Rules calculator')

if not validint(s):
    g.exit('Bad number: %s' % s)

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

g.select(g.getrect())
g.copy()
s = int(s)

for i in range(0,s):
    g.run(1)
    clist.append(list(chunks(g.getcells(g.getrect()), 2)))
    mcc = min(clist[i])
    clist[i] = [[x[0] - mcc[0], x[1] - mcc[1]] for x in clist[i]]

g.show('Processing...')

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

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

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

for i in [iter2 for iter1 in Hensel for iter2 in iter1]:
    if not i in b_OK:
        b_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ''.join(b_OK) + '/S' + ruleArr[1]))
        except:
            b_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_OK.remove(i)
                    break
            except:
                b_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_OK.sort()

    if not i in s_OK:
        s_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_OK)))
        except:
            s_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_OK.remove(i)
                    break
            except:
                s_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_OK.sort()

    if i in b_need:
        b_need.remove(i)
        g.setrule(rulestringopt('B' + ''.join(b_need) + '/S' + ruleArr[1]))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_need.append(i)
                    break
            except:
                b_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_need.sort()

    if i in s_need:
        s_need.remove(i)
        g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_need)))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_need.append(i)
                    break
            except:
                s_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_need.sort()

g.setrule(oldrule)
ruleres = 'B' + ''.join(sorted(b_need)) + '/S' + ''.join(sorted(s_need)) + \
    ' - B' + ''.join(sorted(b_OK)) + '/S' + ''.join(sorted(s_OK))
#g.getstring(ruleres)
ruleres = rulestringopt(ruleres)
if __name__ == "__builtin__":    
    g.show(ruleres)
    g.getstring('Pattern works in rules:', ruleres, 'Rules calculator')
else:
    g.show(__name__)
Here's fixed getallisorule3:

Code: Select all

# Rule computation script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# Updated by: Peter, NASZVADI (), June 2017.

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

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

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

# Python versions < 2.4 don't have "sorted" built-in
try:
    sorted
except NameError:
    def sorted(inlist):
        outlist = list(inlist)
        outlist.sort()
        return outlist

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

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

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

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

clist = []
rule = g.getrule().split(':')[0]

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

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

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

s = "100"

if not validint(s):
    g.exit('Bad number: %s' % s)

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

g.select(g.getrect())
g.copy()
s = int(s)

for i in range(0,s):
    g.run(1)
    clist.append(list(chunks(g.getcells(g.getrect()), 2)))
    mcc = min(clist[i])
    clist[i] = [[x[0] - mcc[0], x[1] - mcc[1]] for x in clist[i]]

#g.show('Processing...')

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

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

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

for i in [iter2 for iter1 in Hensel for iter2 in iter1]:
    if not i in b_OK:
        b_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ''.join(b_OK) + '/S' + ruleArr[1]))
        except:
            b_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_OK.remove(i)
                    break
            except:
                b_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_OK.sort()

    if not i in s_OK:
        s_OK.append(i)
        execfor = 1
        # B0 and nontotalistic rulestrings are mutually exclusive
        try:
            g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_OK)))
        except:
            s_OK.remove(i)
            execfor = 0
        for j in range(0, s * execfor):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_OK.remove(i)
                    break
            except:
                s_OK.remove(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_OK.sort()

    if i in b_need:
        b_need.remove(i)
        g.setrule(rulestringopt('B' + ''.join(b_need) + '/S' + ruleArr[1]))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    b_need.append(i)
                    break
            except:
                b_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        b_need.sort()

    if i in s_need:
        s_need.remove(i)
        g.setrule(rulestringopt('B' + ruleArr[0] + '/S' + ''.join(s_need)))
        for j in range(0, s):
            g.run(1)
            try:
                dlist = list(chunks(g.getcells(g.getrect()), 2))
                mcc = min(dlist)
                dlist = [[x[0] - mcc[0], x[1] - mcc[1]] for x in dlist]
                if not(clist[j] == dlist):
                    s_need.append(i)
                    break
            except:
                s_need.append(i)
                break
        g.new('')
        g.paste(0, 0, 'or')
        g.select(g.getrect())
        s_need.sort()

g.setrule(oldrule)
ruleres = 'B' + ''.join(sorted(b_need)) + '/S' + ''.join(sorted(s_need)) + \
    ' - B' + ''.join(sorted(b_OK)) + '/S' + ''.join(sorted(s_OK))
ruleres = rulestringopt(ruleres)
if __name__ == "__builtin__":    
    g.show(ruleres)
    g.getstring('Pattern works in rules:', ruleres, 'Rules calculator')
else:
    g.show(__name__)
I manage the 5S project, which collects all known spaceship speeds in Isotropic Non-totalistic rules. I also wrote EPE, a tool for searching in the INT rulespace.

Things to work on:
- Find (7,1)c/8 and 9c/10 ships in non-B0 INT.
- EPE improvements.

Post Reply