Non-totalistic Rules and Weighted Life (Scripts)

For scripts to aid with computation or simulation in cellular automata.
Post Reply
EricG
Posts: 199
Joined: August 19th, 2011, 5:41 pm
Location: Chicago-area, USA

Non-totalistic Rules and Weighted Life (Scripts)

Post by EricG » July 20th, 2012, 3:27 pm

This script converts MCell's Weighted Life rules into Golly's Ruletree format. It will allow Golly users to experiment with their own Weighted Life rules.

Code: Select all

# Weightedlife->Ruletree(1.0).py
#  by Eric Goldstein, July 20, 2012.

import golly
from glife.RuleTree import *

dialog_box_message = '''  
This script will allow you to enter the same kind of Weighted Life rule specifications that are 
used by Mirek Mirek Wojtowicz's MCell program.  A corresponding ruletree will be created in Golly's rules folder.

An example rule is given below as a default. Separate each rule specification with a comma, a space, or 
a carriage return/line feed.  

Names:  Rules can be named, for example, "Foo", by using the optional specification Name=Foo with no spaces.   
Rule names should follow Golly's naming conventions (they should start with a capital letter and contain no spaces).  

Visit http://www.mirekw.com/ca/rullex_wlif.html or search the web for "MCell weighted life" to learn more.
Rules listed on MCell's Weighted Life webpage can be cut and pasted into the space below just after the "Name=" term.
'''


# Default values
RS = []
RB = []
ME_weight = 0
NW_weight = 1 
NN_weight = 1    
NE_weight = 1
WW_weight = 1     
EE_weight = 1
SW_weight = 1         
SS_weight = 1
SE_weight = 1

CR = chr(13)
LF = chr(10)
   
name = "Temporary-rule-name" 

WeightedRulestring = golly.getstring(dialog_box_message, "Name=Temporary-rule-name NW4,NN1,NE0,WW1, ME0,EE4,SW0,SS4, SE1,HI0,RS1,RS6,RS8,RB5,RB6") 

WeightedRulestring = WeightedRulestring.replace("ame = ", "ame=")
WeightedRulestring = WeightedRulestring.replace(" ", ",")
WeightedRulestring = WeightedRulestring.replace(CR, ",")
WeightedRulestring = WeightedRulestring.replace(LF, ",")
 
for x in WeightedRulestring.split(","):
   if x.startswith("Name=") or x.startswith("name="):
      name = x.split("ame=")[1]
   else:
      x = x.upper()
      if x.startswith("NW"):
         NW_weight = int(x.split("NW")[1])
      elif x.startswith("NN"):
         NN_weight = int(x.split("NN")[1])
      elif x.startswith("NE"):
         NE_weight = int(x.split("NE")[1])
      elif x.startswith("WW"):
         WW_weight = int(x.split("WW")[1])
      elif x.startswith("EE"):
         EE_weight = int(x.split("EE")[1])
      elif x.startswith("SW"):
         SW_weight = int(x.split("SW")[1])
      elif x.startswith("SS"):
         SS_weight = int(x.split("SS")[1])
      elif x.startswith("SE"):
         SE_weight = int(x.split("SE")[1])
      elif x.startswith("ME"):
         ME_weight = int(x.split("ME")[1])
      elif x.startswith("RS"):
         RS.append(int(x.split("RS")[1]))
      elif x.startswith("RB"):
         RB.append(int(x.split("RB")[1]))
      elif x.startswith("HI"):
         n_states = (int(x.split("HI")[1]))

# python probably has a nicer way to say the following, using max or min
if n_states < 3:
      n_states = 2
if n_states > 256:
    n_states = 256
if n_states > 8:
    golly.getstring('''Ruletrees with more than 8 or so states can take a long time to compute.  

Save your work before continuing. Once this script starts computing your rule, 
if you want to stop the calculation, you may have to abort Golly itelf.

Choose the cancel option now to stop this script.''', "Proceed")

n_neighbors = 8

def transition_function(a):
    # order for 8 neighbors is NW, NE, SW, SE, N, W, E, S, C

    n = NW_weight*(a[0] == 1) + NE_weight*(a[1] == 1) + SW_weight*(a[2] == 1) + SE_weight*(a[3] == 1) + NN_weight*(a[4] == 1) + WW_weight*(a[5] == 1) + EE_weight*(a[6] == 1) + SS_weight*(a[7] == 1) + ME_weight*(a[8] == 1)

    if  a[8] == 1 and n in RS:
        return 1 
    
    if a[8] == 0 and n in RB:
        return 1

    if a[8] > 0 and a[8] < (n_states - 1):
        return a[8] + 1
    
    return 0

golly.show("Please wait while ruletree is being created...")

# The code below this line is copied from make-ruletree.py

try:
   MakeRuleTreeFromTransitionFunction( n_states, n_neighbors, transition_function, golly.getdir("rules")+name+".tree" )
   golly.setalgo("RuleTree")   # in case name.table exists
   golly.setrule(name)
   golly.show("Created "+name+".tree in "+golly.getdir("rules"))
   
except:
   import sys
   import traceback
   exception, msg, tb = sys.exc_info()
   golly.warn(\
'''There was an error.   Please see http://www.mirekw.com/ca/rullex_wlif.html for more information on how to specify rules for this script.'''+ '\n'.join(traceback.format_exception(exception, msg, tb)))
   golly.exit()


Last edited by EricG on July 22nd, 2012, 3:33 am, edited 3 times in total.

EricG
Posts: 199
Joined: August 19th, 2011, 5:41 pm
Location: Chicago-area, USA

Re: Non-totalistic Rules and Weighted Life (Scripts)

Post by EricG » July 20th, 2012, 3:33 pm

This script converts Alan Hensel's notation for non-totalistic rules into Golly's Ruletable format. It will allow Golly users to experiment with their own non-totalistic rules. It also can be used to read Life32 format files, and in particular, it was intended to allow Golly users to browse Emmanuel Sapin's archive of non-totalistic patterns .

Alan Hensel's notation is discussed here: viewtopic.php?f=11&t=936

Emmanuel Sapin's work and archive is discussed here: viewtopic.php?f=11&t=936#p6836

Code: Select all

# HenselNotation->Ruletable(1.3).py
#  by Eric Goldstein, July 20, 2012.

import golly

dialog_box_message =  '''This script will allow you to enter the non-totalistic rule notation 
used by Johan Bontes' Life32 program, based on work by Alan Hensel.
Please look in the script file to see how the notation works.
Newly created rules will be placed in Golly's Rules directory (set in Golly"s preferences). 

Note:  Alan Hensel's notation for r and y are swapped in Life32.   Life32's rule dialog box
shows the correct arrangements, but they are swapped in the implementation.
What should work as 3r, 4r, and 5r, actually work as 3y, 4y, and 5y, and vice versa.
This script swaps the definition of r and y just like Life32 does, so that rules will
run the same way using either Golly or Life32.

An example rule is given below as a default.  Rules are case-insensitive.
B2a/S12, S12/B2a, and 12/2a are valid and equivalent.  

Inverse specifications are allowed.  For example, David Bell's Just Friends rule can be 
expressed B2-a/S12 indicating that all B2 arrangements are included except 2a.


There are two ways to use this script.  Both ways will create a new ruletable, and 
set the active layer of Golly to use the new rule. 
1) You can enter a rule below.
2) You can copy the contents of a life32 file starting with "#r" and then click
on this script, after which you'll be able to paste the pattern into Golly's active layer.
 
Enter a rule here:
   '''


 
# Golly"s ruletable format use the following notation:  
#  C,N,NE,E,SE,S,SW,W,NW,C' 
# where C is the current state, and C' is the new state, and
# where the eight neighbors in the Moore neighborhood are as shown below.

#     NW  N  NE 
#     W   C  E  
#     SW  S  SE 

# The lookup table below for Life32"s non-totalistic notation uses the eight neighbor values:
#   N,NE,E,SE,S,SW,W,NW

# The following code shows how the non-totalistic notation works:

notationdict = {  
                 "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
                 "2v" : "0,1,0,0,0,1,0,0",  #   NE, SW
                 "3a" : "1,1,1,0,0,0,0,0",  #   N,  NE, E
                 "3v" : "1,1,0,1,0,0,0,0",  #   N,  NE, SE 
                 "3y" : "1,1,0,0,1,0,0,0",  #   N,  NE, S      (3r in non-swapped notation)
                 "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
                 "3r" : "1,0,0,1,0,1,0,0",  #   N,  SE, SW     (3y in non-swapped notation)
                 "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
                 "4y" : "1,1,1,0,1,0,0,0",  #   N,  NE, E,  S  (4r in non-swapped notation)
                 "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
                 "4r" : "1,1,0,1,0,1,0,0",  #   N,  NE, SE, SW (4y in non-swapped notation)
                 "4k" : "1,1,0,1,0,0,1,0",  #   N,  NE, SE, W
                 "4v" : "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
                 "5a" : "0,0,0,1,1,1,1,1",  #   SE, S,  SW, W,  NW
                 "5v" : "0,0,1,0,1,1,1,1",  #   E,  S,  SW, W,  NW
                 "5y" : "0,0,1,1,0,1,1,1",  #   E,  SE, SW, W,  NW (5r in non-swapped notation)
                 "5q" : "0,0,1,1,1,0,1,1",  #   E,  SE, S,  W,  NW
                 "5j" : "0,0,1,1,1,1,0,1",  #   E,  SE, S,  SW, NW 
                 "5i" : "0,0,1,1,1,1,1,0",  #   E,  SE, S,  SW, W 
                 "5e" : "0,1,0,1,0,1,1,1",  #   NE, SE, SW, W,  NW, 
                 "5k" : "0,1,0,1,1,0,1,1",  #   NE, SE, S,  W,  NW
                 "5r" : "0,1,1,0,1,0,1,1",  #   NE, E,  S,  W, NW  (5y in non-swapped notation)
                 "5c" : "1,0,1,0,1,0,1,1",  #   N,  E,  S,  W,  NW
                 "6a" : "0,0,1,1,1,1,1,1",  #   E,  SE, S,  SW, W,  NW
                 "6e" : "0,1,0,1,1,1,1,1",  #   NE, SE, S,  SW, W,  NW
                 "6k" : "0,1,1,0,1,1,1,1",  #   NE, E,  S,  SW, W,  NW
                 "6i" : "0,1,1,1,0,1,1,1",  #   NE, E,  SE, SW, W,  NW
                 "6c" : "1,0,1,0,1,1,1,1",  #   N,  E,  S,  SW, W,  NW
                 "6v" : "1,0,1,1,1,0,1,1",  #   N,  E,  SE, S,  W,  NW
                 "7e" : "0,1,1,1,1,1,1,1",  #   NE, E,  SE, S,  SW, W,  NW 
                 "7c" : "1,0,1,1,1,1,1,1"   #   N,  E,  SE, S,  SW, W,  NW
                }


#  Here's a graphical depiction of the notation.
dummyvariable = '''
x = 147, y = 97, rule = B/S012345678
21b3o6b5o5bo3bo6b3o6b5o5bo3bo5bo3bo6b3o10bo5b4o6b5o5bobobo5b5o$20bo3bo
5bo9bo2bo6bo3bo7bo7bo3bo5bo3bo5bo3bo9bo5bo3bo7bo7bobobo9bo$20bo9bo9bob
o7bo3bo7bo7bo3bo5bo3bo5bo3bo9bo5bo3bo7bo7bobobo8bo$20bo9b3o7b2o8b5o7bo
7bo3bo6bobo6bo3bo9bo5b4o8bo7bobobo7bo$20bo9bo9bobo7bo3bo7bo7bo3bo7bo7b
obobo5bo3bo5bo3bo7bo7bobobo6bo$20bo3bo5bo9bo2bo6bo3bo7bo8bobo8bo7bo2bo
6bo3bo5bo3bo7bo7bobobo5bo$21b3o6b5o5bo3bo5bo3bo5b5o7bo9bo8b2obo6b3o6bo
3bo7bo8bobo6b5o4$b3o6b7o$o3bo5bo5bo$o2b2o5bo5bo$obobo5bo2bo2bo$2o2bo5b
o5bo$o3bo5bo5bo$b3o6b7o4$b2o17b7o3b7o$obo17bo5bo3bo5bo$2bo17bobo3bo3bo
2bo2bo$2bo17bo2bo2bo3bo2bo2bo$2bo17bo5bo3bo5bo$2bo17bo5bo3bo5bo$5o15b
7o3b7o4$b3o16b7o3b7o3b7o3b7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo
5bo3bo5bo$4bo15bobobobo3bo2bo2bo3bobo3bo3bobo3bo3bo2bo2bo3bo3bobo$3bo
16bo2bo2bo3bob2o2bo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2bo2bo$2bo17bo5bo3bo
5bo3bo2bo2bo3bo5bo3bo2bo2bo3bobo3bo$bo18bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo$5o15b7o3b7o3b7o3b7o3b7o3b7o4$b3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b
7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo$4bo15bobobobo3bo2bo2bo3bobo3bo3bob2o2bo3bo3bobo3bobobobo3bo2b2o
bo3bo3bobo3bo3bobo3bobobobo$2b2o16bo2bo2bo3bob2o2bo3bo2b2obo3bob2o2bo
3bo2b2obo3bob2o2bo3bo2bo2bo3bo2bo2bo3bo2b2obo3bo2bo2bo$4bo15bobo3bo3bo
2bo2bo3bo2bo2bo3bo5bo3bo3bobo3bo5bo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2bo2b
o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$b
3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$2b2o16b7o3b7o3b7o3b7o3b7o
3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o$bobo16bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3b
o5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$o2bo16bobobobo3bo2bo2bo
3bo2b2obo3bob3obo3bob2o2bo3bobobobo3bob2o2bo3bob2o2bo3bo3bobo3bobobobo
3bob3obo3bobo3bo3bob2o2bo$5o15bo2bo2bo3bob3obo3bob2o2bo3bob2o2bo3bo2bo
2bo3bob2o2bo3bob2o2bo3bob2o2bo3bob3obo3bo2bo2bo3bo2bo2bo3bob2o2bo3bo2b
o2bo$3bo16bobobobo3bo2bo2bo3bo3bobo3bo5bo3bob2o2bo3bobo3bo3bo2bo2bo3bo
3bobo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2b2obo3bo2b2obo$3bo16bo5bo3bo5bo3bo
5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$3bo16b
7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$5o15b7o3b7o3b7o3b
7o3b7o3b7o3b7o3b7o3b7o3b7o$o19bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5b
o3bo5bo3bo5bo3bo5bo$4o16bo2bo2bo3bobobobo3bo2b2obo3bo3bobo3bob2o2bo3bo
bo3bo3bobobobo3bob2o2bo3bob2o2bo3bobobobo$4bo15bob3obo3bo2b2obo3bob2o
2bo3bo2b2obo3bob2o2bo3bob3obo3bob3obo3bob2o2bo3bob2o2bo3bob3obo$4bo15b
o2b2obo3bobobobo3bobobobo3bob3obo3bob2o2bo3bob2o2bo3bobo3bo3bo2b2obo3b
obobobo3bo2bo2bo$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo3bo5bo$b3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$b3o16b7o3b
7o3b7o3b7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$o19bo2bo
2bo3bobobobo3bo2b2obo3bo2b2obo3bobobobo3bob2o2bo$4o16bob3obo3bo2b2obo
3bob3obo3bo2b2obo3bob3obo3bob3obo$o3bo15bob3obo3bob3obo3bobobobo3bob3o
bo3bobobobo3bo2b2obo$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$b3o16b
7o3b7o3b7o3b7o3b7o3b7o4$5o15b7o3b7o$4bo15bo5bo3bo5bo$3bo16bo2b2obo3bob
3obo$3bo16bob3obo3bo2b2obo$2bo17bob3obo3bob3obo$2bo17bo5bo3bo5bo$2bo
17b7o3b7o4$b3o6b7o$o3bo5bo5bo$o3bo5bob3obo$b3o6bob3obo$o3bo5bob3obo$o
3bo5bo5bo$b3o6b7o!
'''


# The following function takes a rule element like B2a or B2-a and creates the appropriate ruletable row.
# Parameters:
# bs is a string with one of two values "0," to indicate birth, and "1," to indicates survival.
# totalistic_num is one character string "0" ... "8"
# notation_letter is a one character string indicating Hensel's notation "a", 
# inverse_list is a list of notation letters for inverse notation 

def create_table_row(bs,totalistic_num, notation_letter, inverse_list):
   result = ""
   if totalistic_num == "0":
     result = bs + "0,0,0,0,0,0,0,0" + ",1"+"\n"  
   elif totalistic_num == "8":
     result = bs + "1,1,1,1,1,1,1,1" + ",1" +"\n"  
   elif notation_letter != "none":
      result = bs + notationdict[totalistic_num+notation_letter] + ",1" +"\n"
   elif inverse_list != []:
      for i in notationdict: 
         if not (i[1] in inverse_list) and i.startswith(totalistic_num):
            result = result +  bs + notationdict[i] + ",1" +"\n"
   else: 
      for i in notationdict:
         if i.startswith(totalistic_num):
            result = result +  bs + notationdict[i] + ",1" + "\n"
   return result


CR = chr(13)
LF = chr(10)
pastepattern = "none"

yourclipboard = golly.getclipstr()
yourclipboard = yourclipboard.replace(CR+LF,LF)
yourclipboard = yourclipboard.replace(CR,LF)

if yourclipboard.startswith("#r "):
  rulestring = yourclipboard.split(" ")[1].split("\n")[0]
  pastepattern = yourclipboard.split("#r "+rulestring)[1] 
else:
  rulestring = golly.getstring(dialog_box_message, "B2a/S12") 


# The following code cleans up the rulestring
# so that it makes a valid and somewhat readable file name - eg "B2-a_S12.table"

rulestring = rulestring.replace(" ", "")
rulestring = rulestring.lower()
rulestring = rulestring.replace("b", "B")
rulestring = rulestring.replace("s", "S")

# The variable named rulestring will be parsed.  
# The variable named rule_name will be the name of the new file.
# Valid rules contain a slash character but filenames can not include slashes.

rule_name = rulestring.replace("/", "_")  

# To do:  Allow the user to specify their own name for a rule.

#  The following code cleans up the rule string to 
#  make life easier for the parser.

if rulestring.startswith("B") or rulestring.startswith("S"):
    rulestring = rulestring.replace("/", "")
else:
    rulestring = rulestring.replace("/", "B")

rulestring = rulestring + "\n"  

# Lets make a new file

f = open(golly.getdir("rules")+rule_name+".table", "w")

# Now create the header for the rule table

neighborhood = "Moore"  
# Incorporate Paul Callahan's hexagonal non-totaistic notation next?
f.write("neighborhood:"+neighborhood+"\n")

# The next line is where the magic happens! 
f.write("symmetries:rotate4reflect\n") 


# Lets stick with 2 states for now, but it would be interesting 
# to add generations-style decay states, as MCell's Weighted Life does.

n_states = 2   
f.write("n_states:"+str(n_states)+"\n\n")

f.write("""
var a={0,1}
var b={0,1}
var c={0,1}
var d={0,1}
var e={0,1}
var f={0,1}
var g={0,1}
var h={0,1}

""")

# Now that the header for the rule table has been created, 
# parse the rulestring, and add rows to the ruletable as we go.

# Lets say rule strings contain "rule elements", such as B2i, or B2-a, which are composed of: 
# 1) a birth or survival flag 
# 2) a "totalistic context" consisting of an integer between zero and 8
# 3) a "notation_letter".   
# 4) a flag for "positive" or "inverse" notation

bs = "1,"                        # Use "1," for survival or "0," for birth
totalistic_context = "none"      # "none" will be replaced with "0" through
                                 # "8" by the parser.
last_totalistic_context = "none" # Lets the parser remember the previous  
                                 # integer it encountered.
notation_letter = "none"         # "none","a", "e", "i", etc.
positive_or_inverse = "positive" 
inverse_list = []

for x in rulestring:
  if x == "S" or x == "B" or x.isdigit() or x == "\n":
     last_totalistic_context = totalistic_context   
     totalistic_context = x                         
     if last_totalistic_context != "none"  and notation_letter == "none":
         f.write(create_table_row(bs, last_totalistic_context, "none",[]))             
     if last_totalistic_context != "none" and  positive_or_inverse == "inverse":
         f.write(create_table_row(bs, last_totalistic_context, "none", inverse_list))  
     # Now lets get ready to move on to the next character.
     notation_letter = "none"
     inverse_list = []
     positive_or_inverse = "positive"
     if x == "S" or x == "B":
         totalistic_context = "none"
     if x == "S":
         bs = "1,"
     if x == "B": 
         bs = "0,"
  elif x == "-":
    positive_or_inverse = "inverse"
  elif x in ["c", "a", "e", "k", "i", "v", "j", "y", "q", "r", "w", "t", "z"] and totalistic_context != "none":
     if positive_or_inverse == "positive":
        notation_letter = x
        f.write(create_table_row(bs, totalistic_context, notation_letter, []))    
     else:
        notation_letter = x
        inverse_list.append(x) 
 

f.write("# Death otherwise"+"\n")
f.write("1,a,b,c,d,e,f,g,h,0"+"\n\n")
f.flush()
f.close()
golly.setalgo("RuleTable")
golly.setrule(rule_name)
if pastepattern != "none":
  golly.setclipstr(pastepattern)
  golly.show("Paste the pattern when you are ready....")
else:
  golly.show("Created "+rule_name+".table in "+golly.getdir("rules"))

Last edited by EricG on July 22nd, 2012, 3:33 am, edited 2 times in total.

EricG
Posts: 199
Joined: August 19th, 2011, 5:41 pm
Location: Chicago-area, USA

Re: Non-totalistic Rules and Weighted Life (Scripts)

Post by EricG » July 20th, 2012, 3:43 pm

This script converts Weighted Life rules into Alan Hensel's non-totalistic notation. It should be helpful for comparing two isotropic (symmetric) Weighted Life rules, such as comparing MCell's SimpleInverse rule to MCell's SimpleInverseFire rule. It also may eventually be helpful when using Paul Tooke's version of gfind, modified for non-totalistic rules, currently under development, as well as Paul Tooke's modified gencols.

Code: Select all

# Weightedlife->HenselNotation(1.0).py
#  by Eric Goldstein, July 20, 2012.

import golly

dialog_box_message = '''   

This script converts Weighted Life rules to Alan Hensel's non-totalistic notation, as implemented in the Life32 program by Johan Bontes.

Note:  Alan Hensel's notation only works with isotropic rules -- rules in which patterns work the same way after they are 
rotated 90 or 180 degrees, and after they are reflected left-right and top-bottom.  This script does not check 
whether a weighted life rule is compatible.    

If (but not only if) a rule is weighted such that NW = NE = SW = SE, and N = E = S = W, then the rule will be isotropic.   

Also note:  Alan Hensel's notation for r and y are swapped in Life32.   
Life32's rule dialog box shows the correct arrangements, but they are swapped in the implementation.
What should work as 3r, 4r, and 5r, actually work as 3y, 4y, and 5y, and vice versa.
This script swaps the definition of r and y just like Life32 does, so that rules will run the same way using either Golly or Life32.
                                                          

Enter a Weighted Life rule here:
'''


# Default values
RS = []
RB = []
ME_weight = 0
NW_weight = 1 
NN_weight = 1    
NE_weight = 1
WW_weight = 1     
EE_weight = 1
SW_weight = 1         
SS_weight = 1
SE_weight = 1

CR = chr(13)
LF = chr(10)

# The lookup table below for Life32"s non-totalistic notation uses the eight neighbor values:
#   N,NE,E,SE,S,SW,W,NW

notationdict = {  
                 "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
                 "2v" : [0,1,0,0,0,1,0,0],  #   NE, SW
                 "3a" : [1,1,1,0,0,0,0,0],  #   N,  NE, E
                 "3v" : [1,1,0,1,0,0,0,0],  #   N,  NE, SE 
                 "3y" : [1,1,0,0,1,0,0,0],  #   N,  NE, S      (3r in non-swapped notation)
                 "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
                 "3r" : [1,0,0,1,0,1,0,0],  #   N,  SE, SW     (3y in non-swapped notation)
                 "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
                 "4y" : [1,1,1,0,1,0,0,0],  #   N,  NE, E,  S  (4r in non-swapped notation)
                 "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
                 "4r" : [1,1,0,1,0,1,0,0],  #   N,  NE, SE, SW (4y in non-swapped notation)
                 "4k" : [1,1,0,1,0,0,1,0],  #   N,  NE, SE, W
                 "4v" : [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
                 "5a" : [0,0,0,1,1,1,1,1],  #   SE, S,  SW, W,  NW
                 "5v" : [0,0,1,0,1,1,1,1],  #   E,  S,  SW, W,  NW
                 "5y" : [0,0,1,1,0,1,1,1],  #   E,  SE, SW, W,  NW (5r in non-swapped notation)
                 "5q" : [0,0,1,1,1,0,1,1],  #   E,  SE, S,  W,  NW
                 "5j" : [0,0,1,1,1,1,0,1],  #   E,  SE, S,  SW, NW 
                 "5i" : [0,0,1,1,1,1,1,0],  #   E,  SE, S,  SW, W 
                 "5e" : [0,1,0,1,0,1,1,1],  #   NE, SE, SW, W,  NW, 
                 "5k" : [0,1,0,1,1,0,1,1],  #   NE, SE, S,  W,  NW
                 "5r" : [0,1,1,0,1,0,1,1],  #   NE, E,  S,  W, NW  (5y in non-swapped notation)
                 "5c" : [1,0,1,0,1,0,1,1],  #   N,  E,  S,  W,  NW
                 "6a" : [0,0,1,1,1,1,1,1],  #   E,  SE, S,  SW, W,  NW
                 "6e" : [0,1,0,1,1,1,1,1],  #   NE, SE, S,  SW, W,  NW
                 "6k" : [0,1,1,0,1,1,1,1],  #   NE, E,  S,  SW, W,  NW
                 "6i" : [0,1,1,1,0,1,1,1],  #   NE, E,  SE, SW, W,  NW
                 "6c" : [1,0,1,0,1,1,1,1],  #   N,  E,  S,  SW, W,  NW
                 "6v" : [1,0,1,1,1,0,1,1],  #   N,  E,  SE, S,  W,  NW
                 "7e" : [0,1,1,1,1,1,1,1],  #   NE, E,  SE, S,  SW, W,  NW 
                 "7c" : [1,0,1,1,1,1,1,1]   #   N,  E,  SE, S,  SW, W,  NW
                }

#  Here's a graphical depiction of the notation.
dummyvariable = '''
x = 147, y = 97, rule = B/S012345678
21b3o6b5o5bo3bo6b3o6b5o5bo3bo5bo3bo6b3o10bo5b4o6b5o5bobobo5b5o$20bo3bo
5bo9bo2bo6bo3bo7bo7bo3bo5bo3bo5bo3bo9bo5bo3bo7bo7bobobo9bo$20bo9bo9bob
o7bo3bo7bo7bo3bo5bo3bo5bo3bo9bo5bo3bo7bo7bobobo8bo$20bo9b3o7b2o8b5o7bo
7bo3bo6bobo6bo3bo9bo5b4o8bo7bobobo7bo$20bo9bo9bobo7bo3bo7bo7bo3bo7bo7b
obobo5bo3bo5bo3bo7bo7bobobo6bo$20bo3bo5bo9bo2bo6bo3bo7bo8bobo8bo7bo2bo
6bo3bo5bo3bo7bo7bobobo5bo$21b3o6b5o5bo3bo5bo3bo5b5o7bo9bo8b2obo6b3o6bo
3bo7bo8bobo6b5o4$b3o6b7o$o3bo5bo5bo$o2b2o5bo5bo$obobo5bo2bo2bo$2o2bo5b
o5bo$o3bo5bo5bo$b3o6b7o4$b2o17b7o3b7o$obo17bo5bo3bo5bo$2bo17bobo3bo3bo
2bo2bo$2bo17bo2bo2bo3bo2bo2bo$2bo17bo5bo3bo5bo$2bo17bo5bo3bo5bo$5o15b
7o3b7o4$b3o16b7o3b7o3b7o3b7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo
5bo3bo5bo$4bo15bobobobo3bo2bo2bo3bobo3bo3bobo3bo3bo2bo2bo3bo3bobo$3bo
16bo2bo2bo3bob2o2bo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2bo2bo$2bo17bo5bo3bo
5bo3bo2bo2bo3bo5bo3bo2bo2bo3bobo3bo$bo18bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo$5o15b7o3b7o3b7o3b7o3b7o3b7o4$b3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b
7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo$4bo15bobobobo3bo2bo2bo3bobo3bo3bob2o2bo3bo3bobo3bobobobo3bo2b2o
bo3bo3bobo3bo3bobo3bobobobo$2b2o16bo2bo2bo3bob2o2bo3bo2b2obo3bob2o2bo
3bo2b2obo3bob2o2bo3bo2bo2bo3bo2bo2bo3bo2b2obo3bo2bo2bo$4bo15bobo3bo3bo
2bo2bo3bo2bo2bo3bo5bo3bo3bobo3bo5bo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2bo2b
o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$b
3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$2b2o16b7o3b7o3b7o3b7o3b7o
3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o$bobo16bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3b
o5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$o2bo16bobobobo3bo2bo2bo
3bo2b2obo3bob3obo3bob2o2bo3bobobobo3bob2o2bo3bob2o2bo3bo3bobo3bobobobo
3bob3obo3bobo3bo3bob2o2bo$5o15bo2bo2bo3bob3obo3bob2o2bo3bob2o2bo3bo2bo
2bo3bob2o2bo3bob2o2bo3bob2o2bo3bob3obo3bo2bo2bo3bo2bo2bo3bob2o2bo3bo2b
o2bo$3bo16bobobobo3bo2bo2bo3bo3bobo3bo5bo3bob2o2bo3bobo3bo3bo2bo2bo3bo
3bobo3bo2bo2bo3bob2o2bo3bo2bo2bo3bo2b2obo3bo2b2obo$3bo16bo5bo3bo5bo3bo
5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$3bo16b
7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$5o15b7o3b7o3b7o3b
7o3b7o3b7o3b7o3b7o3b7o3b7o$o19bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5b
o3bo5bo3bo5bo3bo5bo$4o16bo2bo2bo3bobobobo3bo2b2obo3bo3bobo3bob2o2bo3bo
bo3bo3bobobobo3bob2o2bo3bob2o2bo3bobobobo$4bo15bob3obo3bo2b2obo3bob2o
2bo3bo2b2obo3bob2o2bo3bob3obo3bob3obo3bob2o2bo3bob2o2bo3bob3obo$4bo15b
o2b2obo3bobobobo3bobobobo3bob3obo3bob2o2bo3bob2o2bo3bobo3bo3bo2b2obo3b
obobobo3bo2bo2bo$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo
3bo5bo3bo5bo$b3o16b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o3b7o4$b3o16b7o3b
7o3b7o3b7o3b7o3b7o$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$o19bo2bo
2bo3bobobobo3bo2b2obo3bo2b2obo3bobobobo3bob2o2bo$4o16bob3obo3bo2b2obo
3bob3obo3bo2b2obo3bob3obo3bob3obo$o3bo15bob3obo3bob3obo3bobobobo3bob3o
bo3bobobobo3bo2b2obo$o3bo15bo5bo3bo5bo3bo5bo3bo5bo3bo5bo3bo5bo$b3o16b
7o3b7o3b7o3b7o3b7o3b7o4$5o15b7o3b7o$4bo15bo5bo3bo5bo$3bo16bo2b2obo3bob
3obo$3bo16bob3obo3bo2b2obo$2bo17bob3obo3bob3obo$2bo17bo5bo3bo5bo$2bo
17b7o3b7o4$b3o6b7o$o3bo5bo5bo$o3bo5bob3obo$b3o6bob3obo$o3bo5bob3obo$o
3bo5bo5bo$b3o6b7o!
'''
   

WeightedRulestring = golly.getstring(dialog_box_message, "NW5,NN1,NE5,WW1,ME0, EE1,SW5,SS1,SE5,HI0,RS1,RS5,RB2,RB10") 

WeightedRulestring = WeightedRulestring.replace("ame = ", "ame=")
WeightedRulestring = WeightedRulestring.replace(" ", ",")
WeightedRulestring = WeightedRulestring.replace(CR, ",")
WeightedRulestring = WeightedRulestring.replace(LF, ",")
 
for x in WeightedRulestring.split(","):
   if x.startswith("Name=") or x.startswith("name="):
      name = x.split("ame=")[1]
   else:
      x = x.upper()
      if x.startswith("NW"):
         NW_weight = int(x.split("NW")[1])
      elif x.startswith("NN"):
         NN_weight = int(x.split("NN")[1])
      elif x.startswith("NE"):
         NE_weight = int(x.split("NE")[1])
      elif x.startswith("WW"):
         WW_weight = int(x.split("WW")[1])
      elif x.startswith("EE"):
         EE_weight = int(x.split("EE")[1])
      elif x.startswith("SW"):
         SW_weight = int(x.split("SW")[1])
      elif x.startswith("SS"):
         SS_weight = int(x.split("SS")[1])
      elif x.startswith("SE"):
         SE_weight = int(x.split("SE")[1])
      elif x.startswith("ME"):
         ME_weight = int(x.split("ME")[1])
      elif x.startswith("RS"):
         RS.append(int(x.split("RS")[1]))
      elif x.startswith("RB"):
         RB.append(int(x.split("RB")[1]))
      elif x.startswith("HI"):
         n_states = (int(x.split("HI")[1]))

notation_numbers = ["1", "2", "3", "4", "5", "6", "7"]
notationletters = ["c", "e", "k", "a", "v", "y", "q", "j", "r", "t", "w", "z"]

howmanydict = {"1": 2,   "2": 6,   "3": 10,   "4": 13,   "5" : 10,   "6": 6,   "7": 2}


#if not( n_states == 0 and NW == NE and NW == SE and NW == SW and NN == EE and NN = SS and NN == SW):
#   golly.note("Sorry, the weighted life rule entered is not compatible with Alan Hensel's isotropic #notation")
#else:

def handlehalf (RB_or_RS):
   results = ""

   for rulevalue in RB_or_RS:
     if rulevalue == 0:
          results = results + "0"

   for j in notation_numbers:
     subresults = ""
     for rulevalue in RB_or_RS:
       for i in notationdict:
           if i.startswith(j):
                if rulevalue == NN_weight*notationdict[i][0]+NE_weight*notationdict[i][1]+EE_weight*notationdict[i][2]+SE_weight*notationdict[i][3]+SS_weight*notationdict[i][4]+SW_weight*notationdict[i][5]+WW_weight*notationdict[i][6]+NW_weight*notationdict[i][7]:
                   subresults = subresults + i[1]

     if subresults != "":
         if len(subresults) != howmanydict[j]:
            results = results + j + "".join(sorted(subresults))
         else:
            results = results + j    
 
   for rulevalue in RB_or_RS:
     if rulevalue ==  NW_weight + NE_weight + SW_weight + SE_weight + NN_weight + WW_weight + EE_weight + SS_weight:
          results = results + "8"    

   return results





golly.getstring('''Converted rule:                                                                                                                               .                                              
'               ''', "B"+handlehalf(RB)+"/S"+handlehalf(RS))

Last edited by EricG on July 22nd, 2012, 3:40 am, edited 2 times in total.

EricG
Posts: 199
Joined: August 19th, 2011, 5:41 pm
Location: Chicago-area, USA

Re: Non-totalistic Rules and Weighted Life (Scripts)

Post by EricG » July 20th, 2012, 4:18 pm

This script lets you run Generations rules in a six neighbor hexagonal neighborhood. This is a common use of Weighted Life rules, and this script allows you to express the rules in Generations' more human-readable format.

Code: Select all

# HexGenerations->Ruletree(1.0).py
#  by Eric Goldstein, July 20, 2012.

import golly
from glife.RuleTree import *


# Default values
RS = []
RB = []

# Setting the NorthEast and SouthWest weights to zero simulates a hexagonal neighborhood.
NW_weight = 1 
NN_weight = 1    
NE_weight = 0
WW_weight = 1     
EE_weight = 1
SW_weight = 0         
SS_weight = 1
SE_weight = 1

ME_weight = 0


CR = chr(13)
LF = chr(10)

   
name = "Temporary-rule-name" 

WeightedRulestring = golly.getstring(''' Enter a rule for Generations in a six neighbor hexagonal neighborhood:
''', "B2345/S245/C5") 

WeightedRulestring = WeightedRulestring.upper()
WeightedRulestring = WeightedRulestring.replace(" ", "")
WeightedRulestring = WeightedRulestring.replace(CR, "")
WeightedRulestring = WeightedRulestring.replace(LF, "")
name = "HexGens_"+WeightedRulestring
name = name.replace("/","_")
WeightedRulestring = WeightedRulestring.split("/")
firstpart = WeightedRulestring[0]
secondpart = WeightedRulestring[1]
thirdpart = WeightedRulestring[2]
useletters = True


if firstpart.startswith("B"):
   firstpart = firstpart.split("B")[1]
   for x in firstpart:
      RB.append(int(x))
else:
   useletters = False
   for x in firstpart:
      RS.append(int(x))

if useletters:
   secondpart = secondpart.split("S")[1]
   for x in secondpart:
      RS.append(int(x))
else:
   for x in secondpart:
      RB.append(int(x))

if useletters:
   thirdpart = thirdpart.split("C")[1]
n_states = int(thirdpart)

 
if n_states < 3:
      n_states = 2
if n_states > 256:
    n_states = 256
if n_states > 8:
    golly.getstring('''Ruletrees with more than 8 or so states can take a long time to compute.  

Save your work before continuing. Once this script starts computing your rule, 
if you want to stop the calculation, you may have to abort Golly itelf.

Choose the cancel option now to stop this script.''', "Proceed")

n_neighbors = 8

def transition_function(a):
    # order for 8 neighbors is NW, NE, SW, SE, N, W, E, S, C

    n = NW_weight*(a[0] == 1) + NE_weight*(a[1] == 1) + SW_weight*(a[2] == 1) + SE_weight*(a[3] == 1) + NN_weight*(a[4] == 1) + WW_weight*(a[5] == 1) + EE_weight*(a[6] == 1) +  SS_weight*(a[7] == 1) + ME_weight*(a[8] == 1)

    if  a[8] == 1 and n in RS:
        return 1 
    
    if a[8] == 0 and n in RB:
        return 1

    if a[8] > 0 and a[8] < (n_states - 1):
        return a[8] + 1

    return 0

golly.show("Please wait while ruletree is being created...")

# The code below this line is copied from make-ruletree.py


try:
   MakeRuleTreeFromTransitionFunction( n_states, n_neighbors, transition_function, golly.getdir("rules")+name+".tree" )
   golly.setalgo("RuleTree")   # in case name.table exists
   golly.setrule(name)
   golly.show("Created "+name+".tree in "+golly.getdir("rules"))
   
except:
   import sys
   import traceback
   exception, msg, tb = sys.exc_info()
   golly.warn(\
'''There was an error.   Please see http://www.mirekw.com/ca/rullex_wlif.html for more information on how to specify rules for this script.'''+ '\n'.join(traceback.format_exception(exception, msg, tb)))
   golly.exit()



Post Reply