To that end, I will provide an explanation of a notation that can describe every rule within the rulespace, as well as provide a python script to be used in Golly to generate rulefiles for a given rulestring.
-----
Multistate INT is a family of non-totalistic cellular automata that obey D8 [sic] symmetry on the range-1 moore neighborhood. Transitions from one state to any other state can be specified.
Overview
The notation used for this rulespace is a series of constraints that a neighborhood configuration must fulfill for a non-default state transition to occur. By default, every cell will transition to state 0.
Transitions
Transitions signify a cell's state will change to upon advancing the universe a generation, given a constraint is met. A transition from state a to state n is notated as aTn, with a and n standing in for numbers. Also note Bn and Sn, which are shorthand for 0Tn and nTn respectively.
Transitions may be included and omitted freely, and in any order. As conflicting transitions are possible, transitions specified earlier in the rulestring will take priority.
Transitions are delimited by a '/' in front of them, except at the beginning of the rulestring, where it must be omitted.
Constraints
Constraints are the conditions under which a transition is allowed. Under a transition, only one full constraint needs to be satisfied for a state change to occur. Constraints work by specifying the a series of configurations that cells of specific states in the neighborhood must adhere to.
In a constraint, configurations are listed one after another, delimited by ';'. Configurations are specified in the format x+y+z:nh, with x, y, and z representing the cell states being considered, n representing the number of those cells in the neighborhood, h representing a specific shape as defined in hensel notation, and ':' acting as a delimiter between states and configuration. n and h can be omitted if you don't want it to be considered in the configuration.
States don't have to be specified explicitly: in a constraint, state 0, then 1, then 2... is assumed in configurations where states aren't explicitly specified. However, once a state is explicitly specified in a configuration, all following configurations must also be explicitly specified.
Constraints are delimited on both sides by '.', but these may be replaced by either '-' or '/'. '/' may only replace the end delimiter.
An example constraint: .;2a;1c;1+2:3i;2+3:3i;1+2+3:5a;3:2a.
- state 0 cells are unconstrained
- state 1 cells must occur in the 2a configuration
- state 2 cells must occur in the 1c configuration
- state 3 cells must occur in the 2a configuration
- state 1 and 2 cells must occur in the 3i configuration
- state 2 and 3 cells must occur in the 3i configuration
- state 1, 2, and 3 cells must occur in the 5a configuration
Code: Select all
1 1 2
0 o 3
0 0 3.1,2;3,4,5. expands to .1;3.1;4.1;5.2;3.2;4.2;5.
Subconfigurations
After a constraint, but before its delimiter, you may specify additional constrains in a series of parens ('(', ')'), which means that any neighborhood that satifies the main constraint must also have subconfigurations that would satisfy the constraints within each set of parens.
As an example: .(;2a;1c;1+2:3i;2+3:3i;1+2+3:5a;3:2a). (note: an empty constraint means that all neighborhoods satisfy it) represents
Code: Select all
1 1 2
x o 3
x x 3Negation
Under a transition, a '-' may be used to specify that neighborhoods that satisfy the constraints after it will not cause the transition to occur. These takes priority over non-negated constrains.
-----
Here is the script. Put it in the Rules folder so that it can work properly. Unfortunately, the rulefile conversion (specifically converting to a ruletree) is really slow, with noticeable slowdown by 4 states and is basically unusable by 7.
Code: Select all
basers = ''
#1-msint-ruletreegen.py
#If you are going to rename this script, make sure to also change the occurrences of the name later in the script, as those are how it stores the previously input rule.
#This script, based on RuleTreeGen.py, converts valid Multistate INT rulestrings into rulefiles. Unfortunately, the rulefile conversion (specifically converting to a ruletree) is really slow, with noticeable slowdown by 4 states and is basically unusable by 7.
import copy
import golly
int256 = [[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,'c'],
[0,0,0,0,0,0,1,0,1,'e'],
[0,0,0,0,0,0,1,1,2,'a'],
[0,0,0,0,0,1,0,0,1,'c'],
[0,0,0,0,0,1,0,1,2,'c'],
[0,0,0,0,0,1,1,0,2,'a'],
[0,0,0,0,0,1,1,1,3,'i'],
[0,0,0,0,1,0,0,0,1,'e'],
[0,0,0,0,1,0,0,1,2,'k'],
[0,0,0,0,1,0,1,0,2,'e'],
[0,0,0,0,1,0,1,1,3,'j'],
[0,0,0,0,1,1,0,0,2,'a'],
[0,0,0,0,1,1,0,1,3,'n'],
[0,0,0,0,1,1,1,0,3,'a'],
[0,0,0,0,1,1,1,1,4,'a'],
[0,0,0,1,0,0,0,0,1,'c'],
[0,0,0,1,0,0,0,1,2,'n'],
[0,0,0,1,0,0,1,0,2,'k'],
[0,0,0,1,0,0,1,1,3,'q'],
[0,0,0,1,0,1,0,0,2,'c'],
[0,0,0,1,0,1,0,1,3,'c'],
[0,0,0,1,0,1,1,0,3,'n'],
[0,0,0,1,0,1,1,1,4,'n'],
[0,0,0,1,1,0,0,0,2,'a'],
[0,0,0,1,1,0,0,1,3,'q'],
[0,0,0,1,1,0,1,0,3,'j'],
[0,0,0,1,1,0,1,1,4,'w'],
[0,0,0,1,1,1,0,0,3,'i'],
[0,0,0,1,1,1,0,1,4,'n'],
[0,0,0,1,1,1,1,0,4,'a'],
[0,0,0,1,1,1,1,1,5,'a'],
[0,0,1,0,0,0,0,0,1,'e'],
[0,0,1,0,0,0,0,1,2,'k'],
[0,0,1,0,0,0,1,0,2,'i'],
[0,0,1,0,0,0,1,1,3,'r'],
[0,0,1,0,0,1,0,0,2,'k'],
[0,0,1,0,0,1,0,1,3,'y'],
[0,0,1,0,0,1,1,0,3,'r'],
[0,0,1,0,0,1,1,1,4,'t'],
[0,0,1,0,1,0,0,0,2,'e'],
[0,0,1,0,1,0,0,1,3,'k'],
[0,0,1,0,1,0,1,0,3,'e'],
[0,0,1,0,1,0,1,1,4,'j'],
[0,0,1,0,1,1,0,0,3,'j'],
[0,0,1,0,1,1,0,1,4,'k'],
[0,0,1,0,1,1,1,0,4,'r'],
[0,0,1,0,1,1,1,1,5,'n'],
[0,0,1,1,0,0,0,0,2,'a'],
[0,0,1,1,0,0,0,1,3,'q'],
[0,0,1,1,0,0,1,0,3,'r'],
[0,0,1,1,0,0,1,1,4,'z'],
[0,0,1,1,0,1,0,0,3,'n'],
[0,0,1,1,0,1,0,1,4,'y'],
[0,0,1,1,0,1,1,0,4,'i'],
[0,0,1,1,0,1,1,1,5,'r'],
[0,0,1,1,1,0,0,0,3,'a'],
[0,0,1,1,1,0,0,1,4,'q'],
[0,0,1,1,1,0,1,0,4,'r'],
[0,0,1,1,1,0,1,1,5,'q'],
[0,0,1,1,1,1,0,0,4,'a'],
[0,0,1,1,1,1,0,1,5,'j'],
[0,0,1,1,1,1,1,0,5,'i'],
[0,0,1,1,1,1,1,1,6,'a'],
[0,1,0,0,0,0,0,0,1,'c'],
[0,1,0,0,0,0,0,1,2,'c'],
[0,1,0,0,0,0,1,0,2,'k'],
[0,1,0,0,0,0,1,1,3,'n'],
[0,1,0,0,0,1,0,0,2,'n'],
[0,1,0,0,0,1,0,1,3,'c'],
[0,1,0,0,0,1,1,0,3,'q'],
[0,1,0,0,0,1,1,1,4,'n'],
[0,1,0,0,1,0,0,0,2,'k'],
[0,1,0,0,1,0,0,1,3,'y'],
[0,1,0,0,1,0,1,0,3,'k'],
[0,1,0,0,1,0,1,1,4,'k'],
[0,1,0,0,1,1,0,0,3,'q'],
[0,1,0,0,1,1,0,1,4,'y'],
[0,1,0,0,1,1,1,0,4,'q'],
[0,1,0,0,1,1,1,1,5,'j'],
[0,1,0,1,0,0,0,0,2,'c'],
[0,1,0,1,0,0,0,1,3,'c'],
[0,1,0,1,0,0,1,0,3,'y'],
[0,1,0,1,0,0,1,1,4,'y'],
[0,1,0,1,0,1,0,0,3,'c'],
[0,1,0,1,0,1,0,1,4,'c'],
[0,1,0,1,0,1,1,0,4,'y'],
[0,1,0,1,0,1,1,1,5,'e'],
[0,1,0,1,1,0,0,0,3,'n'],
[0,1,0,1,1,0,0,1,4,'y'],
[0,1,0,1,1,0,1,0,4,'k'],
[0,1,0,1,1,0,1,1,5,'k'],
[0,1,0,1,1,1,0,0,4,'n'],
[0,1,0,1,1,1,0,1,5,'e'],
[0,1,0,1,1,1,1,0,5,'j'],
[0,1,0,1,1,1,1,1,6,'e'],
[0,1,1,0,0,0,0,0,2,'a'],
[0,1,1,0,0,0,0,1,3,'n'],
[0,1,1,0,0,0,1,0,3,'r'],
[0,1,1,0,0,0,1,1,4,'i'],
[0,1,1,0,0,1,0,0,3,'q'],
[0,1,1,0,0,1,0,1,4,'y'],
[0,1,1,0,0,1,1,0,4,'z'],
[0,1,1,0,0,1,1,1,5,'r'],
[0,1,1,0,1,0,0,0,3,'j'],
[0,1,1,0,1,0,0,1,4,'k'],
[0,1,1,0,1,0,1,0,4,'j'],
[0,1,1,0,1,0,1,1,5,'y'],
[0,1,1,0,1,1,0,0,4,'w'],
[0,1,1,0,1,1,0,1,5,'k'],
[0,1,1,0,1,1,1,0,5,'q'],
[0,1,1,0,1,1,1,1,6,'k'],
[0,1,1,1,0,0,0,0,3,'i'],
[0,1,1,1,0,0,0,1,4,'n'],
[0,1,1,1,0,0,1,0,4,'t'],
[0,1,1,1,0,0,1,1,5,'r'],
[0,1,1,1,0,1,0,0,4,'n'],
[0,1,1,1,0,1,0,1,5,'e'],
[0,1,1,1,0,1,1,0,5,'r'],
[0,1,1,1,0,1,1,1,6,'i'],
[0,1,1,1,1,0,0,0,4,'a'],
[0,1,1,1,1,0,0,1,5,'j'],
[0,1,1,1,1,0,1,0,5,'n'],
[0,1,1,1,1,0,1,1,6,'k'],
[0,1,1,1,1,1,0,0,5,'a'],
[0,1,1,1,1,1,0,1,6,'e'],
[0,1,1,1,1,1,1,0,6,'a'],
[0,1,1,1,1,1,1,1,7,'e'],
[1,0,0,0,0,0,0,0,1,'e'],
[1,0,0,0,0,0,0,1,2,'a'],
[1,0,0,0,0,0,1,0,2,'e'],
[1,0,0,0,0,0,1,1,3,'a'],
[1,0,0,0,0,1,0,0,2,'k'],
[1,0,0,0,0,1,0,1,3,'n'],
[1,0,0,0,0,1,1,0,3,'j'],
[1,0,0,0,0,1,1,1,4,'a'],
[1,0,0,0,1,0,0,0,2,'i'],
[1,0,0,0,1,0,0,1,3,'r'],
[1,0,0,0,1,0,1,0,3,'e'],
[1,0,0,0,1,0,1,1,4,'r'],
[1,0,0,0,1,1,0,0,3,'r'],
[1,0,0,0,1,1,0,1,4,'i'],
[1,0,0,0,1,1,1,0,4,'r'],
[1,0,0,0,1,1,1,1,5,'i'],
[1,0,0,1,0,0,0,0,2,'k'],
[1,0,0,1,0,0,0,1,3,'q'],
[1,0,0,1,0,0,1,0,3,'k'],
[1,0,0,1,0,0,1,1,4,'q'],
[1,0,0,1,0,1,0,0,3,'y'],
[1,0,0,1,0,1,0,1,4,'y'],
[1,0,0,1,0,1,1,0,4,'k'],
[1,0,0,1,0,1,1,1,5,'j'],
[1,0,0,1,1,0,0,0,3,'r'],
[1,0,0,1,1,0,0,1,4,'z'],
[1,0,0,1,1,0,1,0,4,'j'],
[1,0,0,1,1,0,1,1,5,'q'],
[1,0,0,1,1,1,0,0,4,'t'],
[1,0,0,1,1,1,0,1,5,'r'],
[1,0,0,1,1,1,1,0,5,'n'],
[1,0,0,1,1,1,1,1,6,'a'],
[1,0,1,0,0,0,0,0,2,'e'],
[1,0,1,0,0,0,0,1,3,'j'],
[1,0,1,0,0,0,1,0,3,'e'],
[1,0,1,0,0,0,1,1,4,'r'],
[1,0,1,0,0,1,0,0,3,'k'],
[1,0,1,0,0,1,0,1,4,'k'],
[1,0,1,0,0,1,1,0,4,'j'],
[1,0,1,0,0,1,1,1,5,'n'],
[1,0,1,0,1,0,0,0,3,'e'],
[1,0,1,0,1,0,0,1,4,'j'],
[1,0,1,0,1,0,1,0,4,'e'],
[1,0,1,0,1,0,1,1,5,'c'],
[1,0,1,0,1,1,0,0,4,'j'],
[1,0,1,0,1,1,0,1,5,'y'],
[1,0,1,0,1,1,1,0,5,'c'],
[1,0,1,0,1,1,1,1,6,'c'],
[1,0,1,1,0,0,0,0,3,'j'],
[1,0,1,1,0,0,0,1,4,'w'],
[1,0,1,1,0,0,1,0,4,'j'],
[1,0,1,1,0,0,1,1,5,'q'],
[1,0,1,1,0,1,0,0,4,'k'],
[1,0,1,1,0,1,0,1,5,'k'],
[1,0,1,1,0,1,1,0,5,'y'],
[1,0,1,1,0,1,1,1,6,'k'],
[1,0,1,1,1,0,0,0,4,'r'],
[1,0,1,1,1,0,0,1,5,'q'],
[1,0,1,1,1,0,1,0,5,'c'],
[1,0,1,1,1,0,1,1,6,'n'],
[1,0,1,1,1,1,0,0,5,'n'],
[1,0,1,1,1,1,0,1,6,'k'],
[1,0,1,1,1,1,1,0,6,'c'],
[1,0,1,1,1,1,1,1,7,'c'],
[1,1,0,0,0,0,0,0,2,'a'],
[1,1,0,0,0,0,0,1,3,'i'],
[1,1,0,0,0,0,1,0,3,'j'],
[1,1,0,0,0,0,1,1,4,'a'],
[1,1,0,0,0,1,0,0,3,'q'],
[1,1,0,0,0,1,0,1,4,'n'],
[1,1,0,0,0,1,1,0,4,'w'],
[1,1,0,0,0,1,1,1,5,'a'],
[1,1,0,0,1,0,0,0,3,'r'],
[1,1,0,0,1,0,0,1,4,'t'],
[1,1,0,0,1,0,1,0,4,'j'],
[1,1,0,0,1,0,1,1,5,'n'],
[1,1,0,0,1,1,0,0,4,'z'],
[1,1,0,0,1,1,0,1,5,'r'],
[1,1,0,0,1,1,1,0,5,'q'],
[1,1,0,0,1,1,1,1,6,'a'],
[1,1,0,1,0,0,0,0,3,'n'],
[1,1,0,1,0,0,0,1,4,'n'],
[1,1,0,1,0,0,1,0,4,'k'],
[1,1,0,1,0,0,1,1,5,'j'],
[1,1,0,1,0,1,0,0,4,'y'],
[1,1,0,1,0,1,0,1,5,'e'],
[1,1,0,1,0,1,1,0,5,'k'],
[1,1,0,1,0,1,1,1,6,'e'],
[1,1,0,1,1,0,0,0,4,'i'],
[1,1,0,1,1,0,0,1,5,'r'],
[1,1,0,1,1,0,1,0,5,'y'],
[1,1,0,1,1,0,1,1,6,'k'],
[1,1,0,1,1,1,0,0,5,'r'],
[1,1,0,1,1,1,0,1,6,'i'],
[1,1,0,1,1,1,1,0,6,'k'],
[1,1,0,1,1,1,1,1,7,'e'],
[1,1,1,0,0,0,0,0,3,'a'],
[1,1,1,0,0,0,0,1,4,'a'],
[1,1,1,0,0,0,1,0,4,'r'],
[1,1,1,0,0,0,1,1,5,'i'],
[1,1,1,0,0,1,0,0,4,'q'],
[1,1,1,0,0,1,0,1,5,'j'],
[1,1,1,0,0,1,1,0,5,'q'],
[1,1,1,0,0,1,1,1,6,'a'],
[1,1,1,0,1,0,0,0,4,'r'],
[1,1,1,0,1,0,0,1,5,'n'],
[1,1,1,0,1,0,1,0,5,'c'],
[1,1,1,0,1,0,1,1,6,'c'],
[1,1,1,0,1,1,0,0,5,'q'],
[1,1,1,0,1,1,0,1,6,'k'],
[1,1,1,0,1,1,1,0,6,'n'],
[1,1,1,0,1,1,1,1,7,'c'],
[1,1,1,1,0,0,0,0,4,'a'],
[1,1,1,1,0,0,0,1,5,'a'],
[1,1,1,1,0,0,1,0,5,'n'],
[1,1,1,1,0,0,1,1,6,'a'],
[1,1,1,1,0,1,0,0,5,'j'],
[1,1,1,1,0,1,0,1,6,'e'],
[1,1,1,1,0,1,1,0,6,'k'],
[1,1,1,1,0,1,1,1,7,'e'],
[1,1,1,1,1,0,0,0,5,'i'],
[1,1,1,1,1,0,0,1,6,'a'],
[1,1,1,1,1,0,1,0,6,'c'],
[1,1,1,1,1,0,1,1,7,'c'],
[1,1,1,1,1,1,0,0,6,'a'],
[1,1,1,1,1,1,0,1,7,'e'],
[1,1,1,1,1,1,1,0,7,'c'],
[1,1,1,1,1,1,1,1,8,0]]
henselmap = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 8, 2, 0, 0, 5, 0, 0, 7, 9, 0, 10, 0, 0, 11, 0, 6, 12, 0, 0, 0, 0]
def charmap(c):
if c == 'c':
return 99
if c == 'e':
return 101
if c == 'k':
return 107
if c == 'a':
return 97
if c == 'i':
return 105
if c == 'n':
return 110
if c == 'y':
return 121
if c == 'q':
return 113
if c == 'j':
return 106
if c == 'r':
return 114
if c == 't':
return 116
if c == 'w':
return 119
if c == 'z':
return 122
if c == 0:
return 0
if c == 127:
return 127
def find_first_of(targetstr,charset,index=0):
i = index
while i < len(targetstr):
for c in charset:
if targetstr[i] == c:
return i
i = i + 1
return -1
def find(lis,val):
for i in range(len(lis)):
if lis[i] == val:
return i
return -1
class TrLeaf:
def __init__(self):
self.num = 0
self.hensel = ''
self.subTrs = [[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]],[[],[],[],[],[],[],[],[],[],[],[],[],[]]]
class TrNode:
def __init__(self):
self.nextnode = [0,0]
self.leaf = TrLeaf()
tree = TrNode()
def set_up_tree():
config = [0,0,0,0,0,0,0,0]
for i in range(256):
currNode = tree
for j in range(8):
if currNode.nextnode[int256[i][j]] == 0:
currNode.nextnode[int256[i][j]] = TrNode()
currNode = currNode.nextnode[int256[i][j]]
lif = TrLeaf()
lif.num = int256[i][8]
lif.hensel = charmap(int256[i][9])
for j in range(256):
hasST = True
for k in range(8):
if int256[j][k] == 0:
continue
if int256[j][k] != int256[i][k]:
hasST = False
break
if hasST:
for k in range(8):
config[k] = int256[j][k]
if (lif.num == 0 and int256[j][8] == 0) or (lif.num != 0 and int256[j][8] != 0):
lif.subTrs[int256[j][8]][henselmap[charmap(int256[j][9])]].append(config[:])
currNode.leaf = lif
class ConstraintSection:
def __init__(self):
self.states = []
self.num = 127
self.hensel = 127
class SubcfgConstraintSect:
def __init__(self,con):
self.states = con.states
self.num = con.num
self.hensel = con.hensel
self.childcons = []
class Constraint:
def __init__(self):
self.endstate = 0
self.sections = []
self.subconfigs = []
self.subcfgs = []
class StateChanges:
def __init__(self):
self.beginState = 0
self.constraints = []
self.negconstraints = []
rule = []
newrule = []
tempnumstates = 0
numStates = 0
def decomp_rule(rs):
rulestring = rs + '/'
newrs = ""
inConstraint = False
currcons = []
currconsspec = []
constraintstr = ""
css = []
conspecstr = ""
issub = False
hassub = False
for c in rulestring:
if inConstraint:
if c == ':':
conspecstr = constraintstr
constraintstr = ""
elif c == ',':
css.append(constraintstr)
constraintstr = ""
elif c in ";().-/":
if c == ')':
continue
css.append(constraintstr)
currcons.append(css)
currconsspec.append(conspecstr)
constraintstr = ""
css = []
conspecstr = ""
if c in "(.-/":
if c == '(' and not issub:
hassub = True
pms = [0]*len(currcons)
tracker = 0
while tracker < len(pms):
for i in range(len(currcons)):
if currconsspec[i] != "":
newrs += currconsspec[i] + ":"
newrs += currcons[i][pms[i]]
if i != len(currcons) - 1:
newrs += ";"
if hassub:
newrs = newrs + "|"
while True:
pms[tracker] += 1
if pms[tracker] == len(currcons[tracker]):
pms[tracker] = 0
tracker += 1
if tracker == len(pms):
break
else:
tracker = 0
break
if issub:
newrs += ")"
if tracker < len(pms):
if issub:
newrs += "("
else:
newrs += "."
newrs += c
if c == '(':
issub = True
hassub = False
else:
issub = False
if c == '/':
inConstraint = False
currcons = []
currconsspec = []
else:
constraintstr += c
else:
newrs = newrs + c
if c in ".-":
inConstraint = True
continue
newnewrs = ""
inConstraint = False
inscfsect = False
scfsectstr = ""
for c in newrs:
if inConstraint:
if not inscfsect:
if c == '(':
inscfsect = True
else:
newnewrs += c
if inscfsect:
if c in ".-/":
inscfsect = False
newnewrs = newnewrs.replace('|',scfsectstr)
scfsectstr = ""
newnewrs += c
else:
scfsectstr += c
if c == '/':
inConstraint = False
else:
newnewrs = newnewrs + c
if c in ".-":
inConstraint = True
return newnewrs[:-1]
def valid_rulestring(rs):
rulestring = rs + "/"
numstates = 0
numStr = ""
stateChangeSpec = True
stateConSpec = False
numConStates = 0
preCol = False
numnums = 0
numhensels = 0
numnegs = 0
numBSTs = 0
hasBST = False
statesInCon = [[]]
for i in range(len(rulestring)):
if stateChangeSpec:
if rulestring[i] in "0123456789":
numStr = numStr + rulestring[i]
elif rulestring[i] in "BST.-/":
if rulestring[i] in "BS":
numBSTs = numBSTs + 1
hasBST = True
continue
if rulestring[i] == "T" and numStr == "":
return False
if int(numStr) > numstates:
numstates = int(numStr)
if rulestring[i] == "T":
numBSTs = numBSTs + 1
hasBST = True
numStr = ""
if rulestring[i] in ".-":
stateChangeSpec = False
if find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i) and rulestring.find(':',i) != -1:
stateConSpec = True
preCol = True
numStr = ""
numnegs = 0
if rulestring[i] == "-":
numnegs = numnegs + 1
if numBSTs > 1:
return False
else:
return False
else:
if not (rulestring[i] in ";()./-:+0123456789cekainyqjrtwz"):
return False
if rulestring[i] in ";()./-":
if numnums > 1 or numhensels > 1:
return False
numnums = 0
numhensels = 0
if rulestring[i] == "-":
numnegs = numnegs + 1
if numnegs > 1:
return False
if not stateConSpec:
if numConStates > numstates:
numstates = numConStates
if rulestring[i] == ';':
numConStates = numConStates + 1
else:
numConStates = 0
if find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i) and rulestring.find(':',i) != -1:
stateConSpec = True
if stateConSpec:
if preCol:
return False
preCol = True
if (rulestring[i] in "()./-") and not (find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i) and rulestring.find(':',i) != -1):
stateConSpec = False
preCol = False
numConStates = 0
if preCol and rulestring[i] not in ";()./-":
if rulestring[i] == ':':
if find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i+1) and rulestring.find(':',i+1) != -1:
return False
preCol = False
if numStr == "":
return False
numConStates = int(numStr)
if numConStates > numstates:
numstates = numConStates
numStr = ""
elif rulestring[i] == '+':
if numStr == "":
return False
numConStates = int(numStr)
if numConStates > numstates:
numstates = numConStates
numStr = ""
elif rulestring[i] in "0123456789":
numStr = numStr + rulestring[i]
else:
return False
elif rulestring[i] not in ";()./-":
if rulestring[i] in "012345678":
numnums = numnums + 1
elif rulestring[i] in "cekainyqjrtwz":
numhensels = numhensels + 1
else:
return False
if rulestring[i] == '/':
if not hasBST:
return False
stateChangeSpec = True
numStr = ""
numBSTs = 0
hasBST = False
inSubcon = False
for i in range(len(rulestring)):
if rulestring[i] == '(':
if inSubcon:
return False
inSubcon = True
elif rulestring[i] == ')':
if not inSubcon:
return False
inSubcon = False
elif inSubcon:
if rulestring[i] in "./-":
return False
global newrule
newrule = []
for i in range(numstates + 1):
newrule.append(StateChanges())
newrule[i].beginState = i
global tempnumstates
tempnumstates = numstates
return True
def parse_rulestring(rs):
rulestring = rs + "/"
global newrule
beginStateStr = ""
sameEnd = False
endStateStr = ""
inStateChange = True
doEndState = False
inConstraint = False
constraint = Constraint()
cs = ConstraintSection()
subconfig = []
sectionState = 0
sectionStateStr = ""
explicitStates = False
inSpecifyingStates = False
inSubconfig = False
negConstraint = False
constrained = False
for i in range(len(rulestring)):
if inStateChange:
if rulestring[i] in ".-":
if beginStateStr != "":
beginState = int(beginStateStr)
endState = int(endStateStr)
if sameEnd:
beginState = endState
beginStateStr = ""
endStateStr = ""
doEndState = False
sameEnd = False
inStateChange = False
inConstraint = True
if rulestring[i] == '-':
negConstraint = True
if find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i+1) and rulestring.find(':',i+1) != -1:
explicitStates = True
inSpecifyingStates = True
continue
if rulestring[i] == 'T':
doEndState = True
continue
if not doEndState:
if rulestring[i] == 'B':
beginState = 0
doEndState = True
elif rulestring[i] == 'S':
sameEnd = True
doEndState = True
else:
beginStateStr = beginStateStr + rulestring[i]
else:
endStateStr = endStateStr + rulestring[i]
if inConstraint:
constraint.endState = endState
if rulestring[i] in ";()./-":
if not explicitStates:
cs.states.append(sectionState)
sectionState = sectionState + 1
if find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i+1) and rulestring.find(':',i+1) != -1:
explicitStates = True
if explicitStates:
inSpecifyingStates = True
if constrained:
if not inSubconfig:
constraint.sections.append(copy.deepcopy(cs))
else:
subconfig.append(copy.deepcopy(cs))
constrained = False
cs = ConstraintSection()
if rulestring[i] == '(':
inSubconfig = True
if not (find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i+1) and rulestring.find(':',i+1) != -1):
explicitStates = False
inSpecifyingStates = False
sectionState = 0
elif rulestring[i] == ')':
constraint.subconfigs.append(copy.deepcopy(subconfig))
subconfig = []
elif rulestring[i] in "./-":
if not negConstraint:
newrule[beginState].constraints.append(copy.deepcopy(constraint))
else:
newrule[beginState].negconstraints.append(copy.deepcopy(constraint))
constraint = Constraint()
if not (find_first_of(rulestring,";()./-",i+1) > rulestring.find(':',i+1) and rulestring.find(':',i+1) != -1):
explicitStates = False
inSpecifyingStates = False
sectionState = 0
inSubconfig = False
if rulestring[i] == "-":
negConstraint = True
if inSpecifyingStates and not rulestring[i] in ";()./-":
if rulestring[i] == ':':
inSpecifyingStates = False
cs.states.append(int(sectionStateStr))
sectionStateStr = ""
elif rulestring[i] == '+':
cs.states.append(int(sectionStateStr))
sectionStateStr = ""
else:
sectionStateStr = sectionStateStr + rulestring[i]
else:
if rulestring[i] in "012345678":
cs.num = int(rulestring[i])
constrained = True
elif rulestring[i] in "cekainyqjrtwz":
cs.hensel = charmap(rulestring[i])
constrained = True
if rulestring[i] == '/':
inConstraint = False
inStateChange = True
negConstraint = False
beginStateStr = ""
endStateStr = ""
doEndState = False
sameEnd = False
def unique_states():
global newrule
for i in range(len(newrule)):
for j in range(len(newrule[i].constraints)):
for k in range(len(newrule[i].constraints[j].sections)):
for l in range(len(newrule[i].constraints[j].sections[k].states)):
for m in range(l+1,len(newrule[i].constraints[j].sections[k].states)):
if newrule[i].constraints[j].sections[k].states[l] == newrule[i].constraints[j].sections[k].states[m]:
return False
for k in range(len(newrule[i].constraints[j].subconfigs)):
for l in range(len(newrule[i].constraints[j].subconfigs[k])):
for m in range(len(newrule[i].constraints[j].subconfigs[k][l].states)):
for n in range(m+1,len(newrule[i].constraints[j].subconfigs[k][l].states)):
if newrule[i].constraints[j].subconfigs[k][l].states[m] == newrule[i].constraints[j].subconfigs[k][l].states[n]:
return False
for j in range(len(newrule[i].negconstraints)):
for k in range(len(newrule[i].negconstraints[j].sections)):
for l in range(len(newrule[i].negconstraints[j].sections[k].states)):
for m in range(l+1,len(newrule[i].negconstraints[j].sections[k].states)):
if newrule[i].negconstraints[j].sections[k].states[l] == newrule[i].negconstraints[j].sections[k].states[m]:
return False
for k in range(len(newrule[i].negconstraints[j].subconfigs)):
for l in range(len(newrule[i].negconstraints[j].subconfigs[k])):
for m in range(len(newrule[i].negconstraints[j].subconfigs[k][l].states)):
for n in range(m+1,len(newrule[i].negconstraints[j].subconfigs[k][l].states)):
if newrule[i].negconstraints[j].subconfigs[k][l].states[m] == newrule[i].negconstraints[j].subconfigs[k][l].states[n]:
return False
for i in range(len(newrule)):
for j in range(len(newrule[i].constraints)):
for k in range(len(newrule[i].constraints[j].sections)):
for l in range(k+1,len(newrule[i].constraints[j].sections)):
if len(newrule[i].constraints[j].sections[k].states) == len(newrule[i].constraints[j].sections[l].states):
allIn = True
for m in range(len(newrule[i].constraints[j].sections[l].states)):
if find(newrule[i].constraints[j].sections[k].states,newrule[i].constraints[j].sections[l].states[m]) == -1:
allIn = False
if allIn:
return False
for k in range(len(newrule[i].constraints[j].subconfigs)):
for l in range(len(newrule[i].constraints[j].subconfigs[k])):
for m in range(l+1,len(newrule[i].constraints[j].subconfigs[k])):
if len(newrule[i].constraints[j].subconfigs[k][l].states) == len(newrule[i].constraints[j].subconfigs[k][m].states):
allIn = True
for n in range(len(newrule[i].constraints[j].subconfigs[k][m].states)):
if find(newrule[i].constraints[j].subconfigs[k][l].states,newrule[i].constraints[j].subconfigs[k][m].states[n]) == -1:
allIn = False
if allIn:
return False
for j in range(len(newrule[i].negconstraints)):
for k in range(len(newrule[i].negconstraints[j].sections)):
for l in range(k+1,len(newrule[i].negconstraints[j].sections)):
if len(newrule[i].negconstraints[j].sections[k].states) == len(newrule[i].negconstraints[j].sections[l].states):
allIn = True
for m in range(len(newrule[i].negconstraints[j].sections[l].states)):
if find(newrule[i].negconstraints[j].sections[k].states,newrule[i].negconstraints[j].sections[l].states[m]) == -1:
allIn = False
if allIn:
return False
for k in range(len(newrule[i].negconstraints[j].subconfigs)):
for l in range(len(newrule[i].negconstraints[j].subconfigs[k])):
for m in range(l+1,len(newrule[i].negconstraints[j].subconfigs[k])):
if len(newrule[i].negconstraints[j].subconfigs[k][l].states) == len(newrule[i].negconstraints[j].subconfigs[k][m].states):
allIn = True
for n in range(len(newrule[i].negconstraints[j].subconfigs[k][m].states)):
if find(newrule[i].negconstraints[j].subconfigs[k][l].states,newrule[i].negconstraints[j].subconfigs[k][m].states[n]) == -1:
allIn = False
if allIn:
return False
return True
def process_rule():
global rule
largest = 0
temp = []
for i in range(len(rule)):
for j in range(len(rule[i].constraints)):
for k in range(len(rule[i].constraints[j].subconfigs)):
for l in range(len(rule[i].constraints[j].subconfigs[k])):
if len(rule[i].constraints[j].subconfigs[k][l].states) > largest:
largest = len(rule[i].constraints[j].subconfigs[k][l].states)
while largest > 0:
for l in range(len(rule[i].constraints[j].subconfigs[k])):
if len(rule[i].constraints[j].subconfigs[k][l].states) == largest:
temp.append(copy.deepcopy(rule[i].constraints[j].subconfigs[k][l]))
largest = largest - 1
rule[i].constraints[j].subconfigs[k] = copy.deepcopy(temp)
temp = []
for j in range(len(rule[i].negconstraints)):
for k in range(len(rule[i].negconstraints[j].subconfigs)):
for l in range(len(rule[i].negconstraints[j].subconfigs[k])):
if len(rule[i].negconstraints[j].subconfigs[k][l].states) > largest:
largest = len(rule[i].negconstraints[j].subconfigs[k][l].states)
while largest > 0:
for l in range(len(rule[i].negconstraints[j].subconfigs[k])):
if len(rule[i].negconstraints[j].subconfigs[k][l].states) == largest:
temp.append(copy.deepcopy(rule[i].negconstraints[j].subconfigs[k][l]))
largest = largest - 1
rule[i].negconstraints[j].subconfigs[k] = copy.deepcopy(temp)
temp = []
def p2_helper(curr,cs):
bottom = True
inthere = True
for i in range(len(curr.childcons)):
inthere = True
for j in range(len(cs.states)):
if find(curr.childcons[i].states,cs.states[j]) == -1:
inthere = False
break
if inthere:
bottom = False
p2_helper(curr.childcons[i],cs)
if bottom:
curr.childcons.append(SubcfgConstraintSect(cs))
def process_rule_p2():
temp = ConstraintSection()
temp.states.append(-1)
for i in range(len(rule)):
for j in range(len(rule[i].constraints)):
for k in range(len(rule[i].constraints[j].subconfigs)):
rule[i].constraints[j].subcfgs.append(SubcfgConstraintSect(temp))
for l in range(len(rule[i].constraints[j].subconfigs[k])):
p2_helper(rule[i].constraints[j].subcfgs[k],rule[i].constraints[j].subconfigs[k][l])
for j in range(len(rule[i].negconstraints)):
for k in range(len(rule[i].negconstraints[j].subconfigs)):
rule[i].negconstraints[j].subcfgs.append(SubcfgConstraintSect(temp))
for l in range(len(rule[i].negconstraints[j].subconfigs[k])):
p2_helper(rule[i].negconstraints[j].subcfgs[k],rule[i].negconstraints[j].subconfigs[k][l])
def mask_neighborhood(nh,target,unmaskedStates):
for i in range(8):
isUnmasked = False
for j in unmaskedStates:
if nh[i] == j:
isUnmasked = True
break
target[i] = isUnmasked
def has_subconstraint(nh,cs):
global tree
maskednh = [0,0,0,0,0,0,0,0]
mask_neighborhood(nh,maskednh,cs.states)
currNode = tree
for i in range(8):
currNode = currNode.nextnode[maskednh[i]]
if cs.num != 127 and cs.num != currNode.leaf.num:
return False
if cs.hensel != 127 and cs.hensel != currNode.leaf.hensel:
return False
return True
def semi_mask(nh,target,mask):
for i in range(8):
if mask[i]:
target[i] = nh[i]
else:
target[i] = -1
def in_subconfig(nh,subconfig,mask=[True,True,True,True,True,True,True,True]):
global tree
currTree = tree
maskedNH = [0,0,0,0,0,0,0,0]
doubleMasked = [True,True,True,True,True,True,True,True]
semi_mask(nh,maskedNH,mask)
mask_neighborhood(maskedNH,doubleMasked,subconfig.states)
for i in range(8):
currTree = currTree.nextnode[doubleMasked[i]]
if subconfig.states[0] == -1:
inNH = True
for i in subconfig.childcons:
if not in_subconfig(nh,i):
inNH = False
break
if inNH:
return True
elif subconfig.num == 127:
if len(subconfig.childcons) != 0:
for i in range(9):
for j in currTree.leaf.subTrs[i][henselmap[subconfig.hensel]]:
inNH = True
for k in subconfig.childcons:
if not in_subconfig(nh,k,j):
inNH = False
break
if inNH:
return True
else:
for i in range(9):
if len(currTree.leaf.subTrs[i][henselmap[subconfig.hensel]]) != 0:
return True
elif subconfig.hensel == 127:
if len(subconfig.childcons) != 0:
for i in range(13):
for j in currTree.leaf.subTrs[subconfig.num][i]:
inNH = True
for k in subconfig.childcons:
if not in_subconfig(nh,k,j):
inNH = False
break
if inNH:
return True
else:
for i in range(13):
if len(currTree.leaf.subTrs[subconfig.num][i]) != 0:
return True
else:
if len(subconfig.childcons) != 0:
for i in currTree.leaf.subTrs[subconfig.num][henselmap[subconfig.hensel]]:
inNH = True
for k in subconfig.childcons:
if not in_subconfig(nh,k,i):
inNH = False
break
if inNH:
return True
else:
if len(currTree.leaf.subTrs[subconfig.num][henselmap[subconfig.hensel]]) != 0:
return True
return False
def load_rule(rs):
global rule
global newrule
global numStates
global tempnumstates
set_up_tree()
r = decomp_rule(rs)
if not valid_rulestring(r):
return False
parse_rulestring(r)
if not unique_states():
return False
rule = newrule
numStates = tempnumstates
process_rule()
process_rule_p2()
return True
def transition(n,ne,e,se,s,sw,w,nw,center):
global numStates
global rule
nh = [n,ne,e,se,s,sw,w,nw]
negged = [False]*(numStates+1)
for i in rule[center].negconstraints:
if not negged[i.endState]:
hasConstraint = True
for j in i.sections:
hasConstraint = has_subconstraint(nh,j)
if not hasConstraint:
break
if hasConstraint:
for j in i.subcfgs:
hasConstraint = in_subconfig(nh,j)
if not hasConstraint:
break
negged[i.endState] = hasConstraint
end = 0
for i in rule[center].constraints:
if not negged[i.endState]:
hasConstraint = True
for j in i.sections:
hasConstraint = has_subconstraint(nh,j)
if not hasConstraint:
break
if hasConstraint:
for j in i.subcfgs:
hasConstraint = in_subconfig(nh,j)
if not hasConstraint:
break
if hasConstraint:
end = i.endState
break
return end
class GenerateRuleTree:
def __init__(self,numStates,numNeighbors,f):
self.numParams = numNeighbors + 1 ;
self.world = {}
self.r = []
self.params = [0]*self.numParams
self.nodeSeq = 0
self.numStates = numStates
self.numNeighbors = numNeighbors
self.f = f
self.recur(self.numParams)
#self.writeRuleTree()
def getNode(self,n):
if n in self.world:
return self.world[n]
else:
new_node = self.nodeSeq
self.nodeSeq += 1
self.r.append(n)
self.world[n] = new_node
return new_node
def recur(self,at):
if at == 0:
return self.f(self.params)
n = str(at)
for i in range(self.numStates):
self.params[self.numParams-at] = i
n += " " + str(self.recur(at-1))
return self.getNode(n)
def writeRuleTree(self):
t = ""
t = t + "num_states=" + str(self.numStates) + "\n"
t = t + "num_neighbors=" + str(self.numNeighbors) + "\n"
t = t + "num_nodes=" + str(len(self.r)) + "\n"
for rule in self.r:
t = t + str(rule) + "\n"
return t
# define your own transition function here:
def my_transition_function(a):
#n,ne,e,se,s,sw,w,nw,c
#nw,ne,sw,se,n,w,e,s,c
#4,1,6,3,7,2,5,0,8
#se,sw,ne,nw,s,e,w,n,c
#7,2,5,0,4,1,6,3,8
return transition(a[4],a[1],a[6],a[3],a[7],a[2],a[5],a[0],a[8])
# call the rule tree generator with your chosen parameters
rulestring = golly.getstring("Input Multistate INT rulestring:",basers)
#rulestring = "B1.;3/S1.;2.;3"
if not load_rule(rulestring):
golly.warn("Invalid rulestring!")
else:
s = GenerateRuleTree(numStates+1,8,my_transition_function)
rtr = s.writeRuleTree()
prunedrs = rulestring.replace('/','_').replace(':','_')[:100]
rftext = "@RULE " + prunedrs + "\n\nFull Rulestring:\n" + rulestring + "\n\n@TREE\n" + rtr
f = open(prunedrs + ".rule","w")
f.write(rftext)
f.close()
f = open("1-msint-ruletreegen.py","r")
n = 0
st = ""
for x in f:
if n == 0:
n = 1
continue
st = st + x
f.close()
f = open("1-msint-ruletreegen.py","w")
f.write("basers = '" + rulestring + "'\n" + st)
f.close()
golly.setrule(prunedrs)
-----
Here are some preexisting rules in this notation:
Code: Select all
B1.;3/S1.;2.;3 - Life
B1.;2/1T2. - Brian's Brain
B1.;3;0/B2.;0;3/S1.;;1,2,3,4,5,6,7,8.;2,3/S2.;1,2,3,4,5,6,7,8.;;2,3 - Symbiosis
1T2./2T3./3T1.;1.;2/S3. - Wireworld
B1.;2k,2a;;0,2,3.;1;;2,3/S1.;3a,3j,3k,3r;;0/1T2./S3. - Fireworld