Turing into Golly Rule Converter

For scripts to aid with computation or simulation in cellular automata.
Post Reply
User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Turing into Golly Rule Converter

Post by simsim314 » April 14th, 2014, 7:12 pm

Using the syntax from this site: http://morphett.info/turing/turing.html to define any Turing machine rules, I made a small script that will convert Turing rules into Golly rule table.

Copy some valid Turing machine (from this site) into your clipboard and run the script. After that your clipboard will have the golly rule. You can paste it into some text editor, or directly into golly (during script execution pasting the script is not allowed).

It could cause an error stating that state is out of range, it's because the script trying to setup an initial state into your currently loaded rule, if number of states of your current rule is smaller golly will alert. Don't worry just paste the rule (which is in your clipboard), and copy the Turing rule again, run the script again - now everything will work fine.

Code: Select all

import golly as g 
import string

class GollyParser:
	def __init__(self):
		self.rules = ""
		self.rule = ""
		self.initState = ""
		self.allStates = ["0"]
	
	def ParseClipboard(self):
		
		str = g.getclipstr()
		lines = str.splitlines()
		
		rulesWithStar = ""
		rulesWithStars = ""
		
		for line in lines:
			
			str = self.TranslateToRules(line)
			
			if str.startswith("@RULE"):
				self.rule = str
				continue 
				
			if str.startswith("init_"):
				self.initState = str.strip("init_")
				continue
				
			if "* *" in str:
				rulesWithStars += str
				continue
			if "*" in str:
				rulesWithStar += str
				continue 
				
			self.rules += str
	
		self.rules += rulesWithStar
		self.rules += rulesWithStars
		
		
	def AppendNewStates(self, state):
		if state == "" or state == "anyS" or state == "0":
			return
		
		if not state in self.allStates:
			self.allStates.append(state)
		
	def TranslateToRules(self, str):
		str = str.strip()
		
		if str.startswith(';'):
			if str.startswith(';RULE'): 
				return "@" + str.strip(";")
			if len(str.split("$INITIAL_TAPE:")) == 2:
				return "init_" + str.split("$INITIAL_TAPE:")[1].strip()
			else:
				return ""
		
		str = str.split(';')[0]
		str = str.strip()
		
		vals = str.split(None)
		
		if len(vals) != 5:
			return ""
			
		Hx = "H" + vals[0]
		Sx = "A" + vals[1]
		Sy = "A" + vals[2]
		lr = vals[3]
		Hy = "H" + vals[4]
		
		if lr != "l" and lr != "r" and lr != "x" and lr != "*":
			return ""
		
		if Sx == "A*":
			Sx = "anyS"
		
		if Sy == "A*":
			Sy = "anyS"
		
		if Hy == "Hhalt": 
			Hy = "0"
		
		if Sx == "A_":	
			Sx = "0"
		
		if Sy == "A_":	
			Sy = "0"
		
		self.AppendNewStates(Sx)
		self.AppendNewStates(Sy)
		self.AppendNewStates(Hx)
		self.AppendNewStates(Hy)
		
		result = "" 
		result += "\n# {0} \n".format(str)
		
		if lr == "r": 
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,Hy)
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,0)
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,0)
		if lr == "l": 
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,Hy)
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,0)
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,0)
		if lr == "x" or lr == "*":
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,Hy)
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,0)
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,0)
			
		result += "{0},{1},0,any1,0,0,0,any2,0,{2}\n".format(Sx,Hx,Sy)
				
		return result
	
	def Rule(self):
	
		if self.rule == "": 
			self.rule = "@RULE SomeTuringMachine"
		
		self.allStates.remove("0")
		self.allStates.sort()
		self.allStates.insert(0, "0")
		
		result = ""
		
		result += self.rule + "\n"
		result += "@TABLE\n"
		result += "n_states:{0}\n".format(len(self.allStates))
		result += "neighborhood:Moore\n"
		result += "symmetries:none\n"
		
		any = "0,"
		anyS = "0,"
		anyH = ""
		for i in xrange(1, len(self.allStates)):
			result += "var " +  self.allStates[i] + " = {" + str(i) + "}\n"
			any += str(i) + ","
			
			if self.allStates[i].startswith("A"): 
				anyS += str(i) + ","
				
			if self.allStates[i].startswith("H"): 
				anyH += str(i) + ","
				
		any = any.strip(",")
		anyS = anyS.strip(",")
		anyH = anyH.strip(",")
		
		result += "var any1 = {" + any + "}\n"
		result += "var any2 = {" + any + "}\n"
		result += "var anyS = {" + anyS + "}\n"
		result += "var anyH1 = {" + anyH + "}\n"
		result += "var anyH2 = {" + anyH + "}\n"
		
		result += self.rules
		
		result += "\n#Not valid Turing Head will die\n"
		result += "anyH1, 0,0,0,any1, any2, anyS, anyH2, 0, 0, 0\n"
		result += "anyH1, 0,0,anyH2,any1, any2, anyS, 0, 0, 0, 0\n"
		
		return result
				
	def SetGollyRule(self):
		
		self.ParseClipboard()
		rule = self.Rule()
		g.setclipstr(rule)
		
		istates =  list(self.initState)
		i = 0 
		
		for istate in istates:	
			if istate != " ":
				idx = self.allStates.index("A" + istate)
				g.setcell(i, 0, idx)
				
			i += 1
			
		idx = self.allStates.index("H0")
		g.setcell(0, -1, idx)
		
		
parser = GollyParser()
parser.SetGollyRule()
Here is an example for convention of binary counter:

Turing:

Code: Select all

;RULE BinaryAdder
0 * * r 0
0 _ _ l A
A _ 1 r 0 
A 0 1 r 0
A 1 0 l A 

;$INITIAL_TAPE: 111
Golly Rule:

Code: Select all

@RULE BinaryAdder
@TABLE
n_states:5
neighborhood:Moore
symmetries:none
var A0 = {1}
var A1 = {2}
var H0 = {3}
var HA = {4}
var any1 = {0,1,2,3,4}
var any2 = {0,1,2,3,4}
var anyS = {0,1,2}
var anyH1 = {3,4}
var anyH2 = {3,4}

# 0 _ _ l A 
0,0,0,H0,0,any1,any2,0,0,HA
0,0,0,0,any1,any2,0,H0,0,0
H0,0,0,0,any1,0,any2,0,0,0
0,H0,0,any1,0,0,0,any2,0,0

# A _ 1 r 0 
0,0,0,0,any1,any2,0,HA,0,H0
0,0,0,HA,0,any1,any2,0,0,0
HA,0,0,0,any1,0,any2,0,0,0
0,HA,0,any1,0,0,0,any2,0,A1

# A 0 1 r 0 
0,0,0,0,any1,any2,A0,HA,0,H0
0,0,0,HA,A0,any1,any2,0,0,0
HA,0,0,0,any1,A0,any2,0,0,0
A0,HA,0,any1,0,0,0,any2,0,A1

# A 1 0 l A 
0,0,0,HA,A1,any1,any2,0,0,HA
0,0,0,0,any1,any2,A1,HA,0,0
HA,0,0,0,any1,A1,any2,0,0,0
A1,HA,0,any1,0,0,0,any2,0,A0

# 0 * * r 0 
0,0,0,0,any1,any2,anyS,H0,0,H0
0,0,0,H0,anyS,any1,any2,0,0,0
H0,0,0,0,any1,anyS,any2,0,0,0
anyS,H0,0,any1,0,0,0,any2,0,anyS

#Not valid Turing Head will die
anyH1, 0,0,0,any1, any2, anyS, anyH2, 0, 0, 0
anyH1, 0,0,anyH2,any1, any2, anyS, 0, 0, 0, 0
The scripts also creates an initial state that can be changed by ;$INITIAL_TAPE parameter:

Code: Select all

x = 3, y = 2, rule = BinaryAdder
C$3B!
My only issue is with golly rule loader. I couldn't find a simple way to Load the rule from the script. Trying golly.setrule was throwing an error because this rule is not in the list. Creating a file with the rule will require some OS specific libraries (although with python is less an issue), and Golly installation folder, which the current API doesn't provides as I can tell. And trying to direct paste the clipboard into golly, is also alerting that the rule can't be changed during script execution.

So I'm currently stuck to just copy the rule into clipboard which is ugly but works...

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

Re: Turing into Golly Rule Converter

Post by dvgrn » April 14th, 2014, 8:03 pm

simsim314 wrote:My only issue is with golly rule loader. I couldn't find a simple way to Load the rule from the script. Trying golly.setrule was throwing an error because this rule is not in the list. Creating a file with the rule will require some OS specific libraries (although with python is less an issue), and Golly installation folder, which the current API doesn't provides as I can tell. And trying to direct paste the clipboard into golly, is also alerting that the rule can't be changed during script execution.
Sounds like you may be looking for golly.getdir("rules"), which should be reasonably OS-independent. I've used g.getdir("data") routinely without any cross-platform troubles, e.g. in writing a file to remember the previous setting for goto.py.

I'm not sure whether it would work to write a rule to a temporary directory -- g.getdir("temp") -- and then golly.open() that file. It might recognize a rule and copy it to the right folder, just as the GUI does. If so, that would allow for the usual pop-up requests for permission to overwrite, if the file already exists. But writing directly to the Rules folder should be fine, maybe with a quick check for an already-existing file.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Turing into Golly Rule Converter

Post by simsim314 » April 14th, 2014, 9:57 pm

dvgrn wrote:Sounds like you may be looking for...
Wow thanks for the help! Now it's working nicely. I also clear (the initial pattern) and fit, and still copy to clipboard the rule so that it would be easier to check out the corresponding states.

As previously save the script into the script folder, copy to clipboard some Turing rule, and see it in golly.

Code: Select all

import golly as g 
import string

class GollyParser:
	def __init__(self):
		self.rules = ""
		self.rule = ""
		self.initState = ""
		self.allStates = ["0"]
		
	def ParseClipboard(self):
		
		str = g.getclipstr()
		lines = str.splitlines()
		
		rulesWithStar = ""
		rulesWithStars = ""
		
		for line in lines:
			
			str = self.TranslateToRules(line)
			
			if str.startswith("@RULE"):
				self.rule = str
				continue 
				
			if str.startswith("init_"):
				self.initState = str.strip("init_")
				continue
				
			if "* *" in str:
				rulesWithStars += str
				continue
			if "*" in str:
				rulesWithStar += str
				continue 
				
			self.rules += str
	
		self.rules += rulesWithStar
		self.rules += rulesWithStars
		
		
	def AppendNewStates(self, state):
		if state == "" or state == "anyS" or state == "0":
			return
		
		if not state in self.allStates:
			self.allStates.append(state)
		
	def TranslateToRules(self, str):
		str = str.strip()
		
		if str.startswith(';'):
			if str.startswith(';RULE'): 
				return "@" + str.strip(";")
			if len(str.split("$INITIAL_TAPE:")) == 2:
				return "init_" + str.split("$INITIAL_TAPE:")[1].strip()
			else:
				return ""
		
		str = str.split(';')[0]
		str = str.strip()
		
		vals = str.split(None)
		
		if len(vals) != 5:
			return ""
			
		Hx = "H" + vals[0]
		Sx = "A" + vals[1]
		Sy = "A" + vals[2]
		lr = vals[3]
		Hy = "H" + vals[4]
		
		if lr != "l" and lr != "r" and lr != "x" and lr != "*":
			return ""
		
		if Sx == "A*":
			Sx = "anyS"
		
		if Sy == "A*":
			Sy = "anyS"
		
		if Hy == "Hhalt": 
			Hy = "0"
		
		if Sx == "A_":	
			Sx = "0"
		
		if Sy == "A_":	
			Sy = "0"
		
		self.AppendNewStates(Sx)
		self.AppendNewStates(Sy)
		self.AppendNewStates(Hx)
		self.AppendNewStates(Hy)
		
		result = "" 
		result += "\n# {0} \n".format(str)
		
		if lr == "r": 
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,Hy)
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,0)
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,0)
		if lr == "l": 
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,Hy)
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,0)
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,0)
		if lr == "x" or lr == "*":
			result += "{0},0,0,0,any1,{1},any2,0,0,{2}\n".format(Hx,Sx,Hy)
			result += "0,0,0,{0},{1},any1,any2,0,0,{2}\n".format(Hx,Sx,0)
			result += "0,0,0,0,any1,any2,{0},{1},0,{2}\n".format(Sx,Hx,0)
			
		result += "{0},{1},0,any1,0,0,0,any2,0,{2}\n".format(Sx,Hx,Sy)
				
		return result
	
	def Rule(self):
	
		if self.rule == "": 
			self.rule = "@RULE SomeTuringMachine"
		
		self.allStates.remove("0")
		self.allStates.sort()
		self.allStates.insert(0, "0")
		
		result = ""
		
		result += self.rule + "\n"
		result += "@TABLE\n"
		result += "n_states:{0}\n".format(len(self.allStates))
		result += "neighborhood:Moore\n"
		result += "symmetries:none\n"
		
		any = "0,"
		anyS = "0,"
		anyH = ""
		for i in xrange(1, len(self.allStates)):
			result += "var " +  self.allStates[i] + " = {" + str(i) + "}\n"
			any += str(i) + ","
			
			if self.allStates[i].startswith("A"): 
				anyS += str(i) + ","
				
			if self.allStates[i].startswith("H"): 
				anyH += str(i) + ","
				
		any = any.strip(",")
		anyS = anyS.strip(",")
		anyH = anyH.strip(",")
		
		result += "var any1 = {" + any + "}\n"
		result += "var any2 = {" + any + "}\n"
		result += "var anyS = {" + anyS + "}\n"
		result += "var anyH1 = {" + anyH + "}\n"
		result += "var anyH2 = {" + anyH + "}\n"
		
		result += self.rules
		
		result += "\n#Not valid Turing Head will die\n"
		result += "anyH1, 0,0,0,any1, any2, anyS, anyH2, 0, 0, 0\n"
		result += "anyH1, 0,0,anyH2,any1, any2, anyS, 0, 0, 0, 0\n"
		
		return result
				
	def SetGollyRule(self):
		
		self.ParseClipboard()
		rule = self.Rule()
		
		ruleName = self.rule.strip("@RULE")
		ruleName = ruleName.strip()
		 
		f = open(g.getdir("rules") + ruleName + ".rule", 'w')
		f.write(rule)
		f.close()
		
		g.setrule(ruleName)
		
		g.select([0,0,1,1])
		g.clear(0)
		g.clear(1)
		g.select([])
		istates =  list(self.initState)
		
		i = 0 
		
		for istate in istates:	
			if istate != " ":
				idx = self.allStates.index("A" + istate)
				g.setcell(i, 0, idx)
				
			i += 1
			
		idx = self.allStates.index("H0")
		g.setcell(0, -1, idx)
		
		g.fit()
		
		g.setclipstr(rule)
		
parser = GollyParser()
parser.SetGollyRule()
Some details:

- Adding ";RULE" in the beginning of the Turing rule, will save it in golly with the following name. For exmplae placing ";RULE BinaryAdder" in the Turing rule will save in Golly into BinaryAdder.rule. Otherwise the script will save the Turing rule into "SomeTuringMachine.rule" automatically.

- The states are sorted by symbols first, and then head states - in the .rule file states are with A prefix and head values are with H prefix. To see the values of the states in golly, the simplest way is to paste the rule (after running the script) into some text editor. Here is a sample:

Code: Select all

var A0 = {1}
var A1 = {2}
var Ax = {3}
var Ay = {4}
var H0 = {5}
var H1 = {6}
var H2 = {7}
var H3x = {8}
var H3y = {9}
var H4x = {10}
var H4y = {11}
var H5 = {12}
var H6 = {13}
var H7 = {14}
var any1 = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
var any2 = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
var anyS = {0,1,2,3,4}
var anyH1 = {5,6,7,8,9,10,11,12,13,14}
var anyH2 = {5,6,7,8,9,10,11,12,13,14}
This machine has 0-1-x-y symbols, which are represented in golly as 1-2-3-4 states. And the head has 0-1-2-3x-3y etc. states which correspondingly shown in the .rule with their values.

- The "$INITIAL_TAPE: 11 1" will setup the initial state to "AA.A". It parses by chars, so it works only for states that represented by one characters. spaces are considered "0".

- The halt state is handled inside the script as 0 state in golly (i.e. the head just dies).

-Inside .rule file a source Turing rule that based the Golly rule is present above the rules.

Post Reply