.

In order to do that I've implemented a lua script. It allows to use golly to create the rule. But instead of stating the "states" your states in golly as input represent variables and functions to unroll some variable. For example I had majority gate - so it's a function that takes three arguments, they came at place 3, 5, 7. The script unrolls all the possibilities and generates a rule based on the options, while keeping other variables just as strings. You write the function in lua, which is relatively simple job usually. The maintenance and bug fixing of rule generated by the script reduced dramatically - I fix only the functions, or the input.

The compilation is done into Nutshell. I also hope to join forces and make a universal script for universal rule table generator, which is defined in golly visually only.

Code: Select all

```
--A script by michael simkin 2019 - auto generates rules using states in golly which represent variables.
--Insert your code only between user definition start and end definitions.
--Register your variables using the line: def:append("any", any)
--f1, f2, ..., fn - represent state 1,2,3...n in golly.
--index_function is the part which selects what part of the rule you want to unroll in order to use the functions.
--index_function[] = {2, 3} will unroll variables which placed in golly in the second and third place.
--In golly you fill 8 states around some x = 0 y = 10*N. You also place the function index in x = 10 y = 10*N next to the rule.
--Forum post here: http://conwaylife.com/forums/viewtopic.php?f=7&t=3361&p=59227&hilit=nutshell#p59227
------------------------------------Helper functions----------------------------------
-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base, init)
local c = {} -- a new class instance
if not init and type(base) == 'function' then
init = base
base = nil
elseif type(base) == 'table' then
-- our new class is a shallow copy of the base class!
for i,v in pairs(base) do
c[i] = v
end
c._base = base
end
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
c.__index = c
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
if init then
init(obj,...)
else
-- make sure that any stuff from the base class is initialized!
if base and base.init then
base.init(obj, ...)
end
end
return obj
end
c.init = init
c.is_a = function(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
end
return false
end
setmetatable(c, mt)
return c
end
Definitions = class(function(df)
df.table_of_everything = {}
df.table_of_keys = {}
df.table_of_idx = {}
end)
function Definitions:append(name, value)
self.table_of_everything[name] = value
self.table_of_keys[#self.table_of_keys + 1] = name
self.table_of_idx[name] = #self.table_of_keys
end
function interpret(f, params)
if #params == 0 then
return f()
elseif #params == 1 then
return f(params[1])
elseif #params == 2 then
return f(params[1], params[2])
elseif #params == 3 then
return f(params[1], params[2], params[3])
end
end
function index_to_function(idx)
if idx == 1 then
return f1
elseif idx == 2 then
return f2
elseif idx == 3 then
return f3
elseif idx == 4 then
return f4
elseif idx == 5 then
return f5
elseif idx == 6 then
return f6
elseif idx == 7 then
return f7
elseif idx == 8 then
return f8
elseif idx == 9 then
return f9
elseif idx == 10 then
return f10
elseif idx == 11 then
return f11
elseif idx == 12 then
return f12
elseif idx == 13 then
return f13
elseif idx == 14 then
return f14
elseif idx == 15 then
return f15
end
end
NdIterator = class(function(it, sizes)
it.box = sizes
it.cur = {}
it.flags = {}
for i = 1, #sizes do
it.cur[#it.cur + 1] = 1
it.flags[#it.flags + 1] = false
end
end)
function NdIterator:next()
for i = 1, #self.box do
self.cur[i] = self.cur[i] + 1
if self.cur[i] > self.box[i] then
self.cur[i] = 1
else
break
end
end
end
function NdIterator:total()
total = 1
for i = 1, #self.box do
total = total * self.box[i]
end
return total
end
local def = Definitions()
--------------------------------------User definitions Start--------------------------------------
--visual compiler
local g = golly()
--0 - empty space
--switch - 0,1,unk,A0,A1 : 1, 2, 3, 4, 5
--hold - 0,1,unk,A0,A1 : 6, 7, 8, 9, 10
--relese A0, A1, r, : 11, 12, 13
--relax - A0, A1, r, : 14, 15, 16
function from_idx(idx)
if idx <= 10 then
idx = (idx - 1) % 5 + 1
if idx == 1 then
return 0
elseif idx == 2 then
return 1
elseif idx == 3 then
return 2
elseif idx == 4 then
return 0
else
return 1
end
else
idx = idx - 10
idx = idx % 3
return idx
end
end
--state 1
any = {"any"}
--state 2
live = {"live"}
--state 3
switch = {1, 2, 3, 4, 5}
--state 4
hold = {6, 7, 8, 9, 10}
--state 5
relese = {11, 12, 13}
--state 6
relax = {14, 15, 16}
--state 7
switch_hold = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
--state 8
not_switch_hold = {0, 11, 12, 13, 14, 15, 16}
--state 9
undefined_relax = {16}
--state 10
defined_relax = {14, 15}
--state 11
not_switch = {0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
def:append("any", any)
def:append("live", live)
def:append("switch", switch)
def:append("hold", hold)
def:append("relese", relese)
def:append("relax", relax)
def:append("switch_hold", switch_hold)
def:append("not_switch_hold", not_switch_hold)
def:append("undefined_relax", undefined_relax)
def:append("defined_relax", defined_relax)
def:append("not_switch", not_switch)
--switch->hold
function f1(sw)
return sw + 5
end
--hold->relese
function f2(hld)
if hld < 9 then
return 13
else
return hld + 2
end
end
--release->relax
function f3(ease)
return ease + 3
end
--(defined_relax)->(defined_switch)
function f4(defax)
return defax - 10
end
--switch_hold->switch
function f5(sw)
return (from_idx(sw) + 1)
end
--(swr1, swr2)->switch (known or unknown depending)
function f6(sw1, sw2)
if from_idx(sw1) == from_idx(sw2) then
return from_idx(sw1) + 1
else
return 3
end
end
function f7(sw1, sw2)
if from_idx(sw1) == from_idx(sw2)then
if from_idx(sw2) ~= 2 then
return 2 - from_idx(sw1)
else
return 3
end
else
return 3
end
end
function f8(sw1, sw2, sw3)
arr = {sw1, sw2, sw3}
counter = {0,0,0}
for i = 1, 3 do
idx = from_idx(arr[i]) + 1
counter[idx] = counter[idx] + 1
if counter[idx] == 2 then
return idx
end
end
return 3
end
function f9(sw)
return 3
end
index_function = {{0}, {0}, {0}, {0}, {7}, {3, 7}, {4, 6}, {3, 5, 7}, {0}}
colors = {
"0 255 0: 1, 4, 6, 9, 11, 14",
"0 150 0: 6",
"255 255 0: 2, 5, 10, 12, 15",
"150 150 0: 7",
"192 192 192: 3, 8",
"64 64 64: 13, 16"
}
--------------------------------------User definitions End--------------------------------------
---------------------Compilation part don't touch--------------------------------
for i = 1, #index_function do
indeces = index_function[i]
for j = 1, #indeces do
index_function[i][j] = index_function[i][j] + 1
end
end
function toskip(x, y)
if #(g.getcells({x - 10, y, 20, 1})) >= 60 or #(g.getcells({x - 10, y, 20, 1})) == 0 then
return true
else
return false
end
end
golly_order = {{0, 0},{0, -1},{1, -1},{1, 0},{1, 1},{0, 1},{-1, 1},{-1, 0},{-1, -1}}
rules = {}
rect = g.getrect()
y0 = math.floor(rect[2] / 10) * 10 - 20
y1 = math.floor((rect[2] + rect[4]) / 10) * 10 + 20
y = y0
total = 0
while y < y1 do
rule = {{}, 0}
--Fill the variables
if toskip(0, y) == false then
for i = 1, 9 do
rule[1][#rule[1] + 1] = g.getcell(0 + golly_order[i][1], y + golly_order[i][2])
end
--This is the function index
rule[2] = g.getcell(10, y)
rules[#rules + 1] = rule
end
y = y + 10
end
name = g.getstring("Enter full path with file name")
local file = io.open(name, "w")
file:write("@NUTSHELL QdCA\n\n@TABLE\nstates: 17\nsymmetries: rotate4 reflect\nneighborhood: Moore\n")
for i = 1, #def.table_of_keys do
value = def.table_of_everything[def.table_of_keys[i]]
if (value[1] == "any" or value[1] == "live") == false then
file:write(def.table_of_keys[i].." = (")
for idx = 1, #value do
if idx == #value then
file:write(tostring(value[idx])..")\n")
else
file:write(tostring(value[idx])..", ")
end
end
end
end
for i = 1, #rules do
rule = rules[i]
vars = rule[1] --9 variables around me
func_idx = rule[2] -- function index
input_vars = index_function[func_idx]
local state_iterator = NdIterator({1, 1, 1, 1, 1, 1, 1, 1, 1})
for j = 1, #input_vars do
values = def.table_of_everything[def.table_of_keys[vars[input_vars[j]]]]
state_iterator.box[input_vars[j]] = #values
state_iterator.flags[input_vars[j]] = true
end
max_idx = state_iterator:total()
for state_iterator_idx = 1, max_idx do
input_func = {}
for j = 1, #state_iterator.box do
if state_iterator.flags[j] == false then
var_name = "0"
if vars[j] ~= 0 then
var_name = def.table_of_keys[vars[j]]
end
if j == #state_iterator.box then
file:write(var_name.."; ")
else
file:write(var_name..", ")
end
else
values = def.table_of_everything[def.table_of_keys[vars[j]]]
value = values[state_iterator.cur[j]]
input_func[#input_func + 1] = value
if j == #state_iterator.box then
file:write(tostring(value).."; ")
else
file:write(tostring(value)..", ")
end
end
end
value = interpret(index_to_function(func_idx), input_func)
file:write(tostring(value).."\n")
state_iterator:next()
end
end
file:write("@COLORS\n")
for i = 1,#colors do
file:write(colors[i].."\n")
end
file.close()
```

Input example (make sure all cells centers are at X = 0, Y = 10*N and the functions codes are at same Y, and X = 10) - better use the attached file.