MAPper, a script to efficiently change MAP rules

For scripts to aid with computation or simulation in cellular automata.
Post Reply
User avatar
Rhombic
Posts: 1056
Joined: June 1st, 2013, 5:41 pm

MAPper, a script to efficiently change MAP rules

Post by Rhombic » July 1st, 2017, 12:37 pm

You specify an initial rule, the number of transitions you change and then each transition (one by one). The script changes the rule for you and copies it to your clipboard. Fairly straightforward.
In Python.

Code: Select all

# MAPper.py
# Changes a given MAP rule one state at a time, much faster than by hand.
# The way it works will switch the existing transition input.
# Solution is shown and copied to clipboard, prefixed with MAP.
# Author: Rhombic

import golly as g
rule = g.getstring("Initial MAP rule to modify (default is CGoL) without the MAP prefix. Leave blank to start from scratch (B/S)","ARYXfhZofugWaH7oaIDogBZofuhogOiAaIDogIAAgAAWaH7oaIDogGiA6ICAAIAAaIDogIAAgACAAIAAAAAAAA")
if rule == "":
    rule="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
changes = int(g.getstring("How many changes are you planning?","1"))
for i in range(changes):
    newcase = g.getstring("Binary code to implement:","000000000")
    defect = int(newcase,2)
    base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    rulepos = (defect-(defect%6))/6
    checkcase = int(base64.index(rule[rulepos]))
    bcheckcase = "{0:06b}".format(checkcase)
    if bcheckcase[defect%6] == "1":
        newdec = int(bin(base64.index(rule[rulepos]))[2:])-int(bin(2**(5-(defect%6)))[2:])
    elif bcheckcase[defect%6] == "0":
        newdec = int(bin(base64.index(rule[rulepos]))[2:])+int(bin(2**(5-(defect%6)))[2:])
    newdecnum = int(str(newdec), 2)
    pointdefect = base64[newdecnum]
    newrule = rule[0:rulepos]+pointdefect+rule[(rulepos+1):len(rule)]
    g.show("Last transition changed: "+str(pointdefect)+" replaced "+str(rule[rulepos])+" at position "+str(rulepos)+", new rule is MAP"+newrule)
    rule = newrule
    
g.setclipstr("MAP"+newrule)
Last edited by Rhombic on July 1st, 2017, 4:55 pm, edited 1 time in total.
SoL : FreeElectronics : DeadlyEnemies : 6a-ite : Rule X3VI
what is “sesame oil”?

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

Re: MAPper, a script to efficiently change MAP rules

Post by dvgrn » July 1st, 2017, 3:13 pm

Rhombic wrote:You specify an initial rule, the number of transitions you change and then each transition (one by one). The script changes the rule for you and copies it to your clipboard. Fairly straightforward.
Hmm, not quite straightforward enough for my simple mind yet, I'm afraid. How would you add, say, an ON transition toward the end of the MAP string -- say the S8 bit at the very end (but before the last four bits of padding)?

When I try the script, it seems like the first character keeps getting changed no matter how long a binary string I put in. Or I get errors from strange things happening during the conversion of my length-512 binary string.

User avatar
Rhombic
Posts: 1056
Joined: June 1st, 2013, 5:41 pm

Re: MAPper, a script to efficiently change MAP rules

Post by Rhombic » July 1st, 2017, 4:50 pm

dvgrn wrote:
Rhombic wrote:You specify an initial rule, the number of transitions you change and then each transition (one by one). The script changes the rule for you and copies it to your clipboard. Fairly straightforward.
Hmm, not quite straightforward enough for my simple mind yet, I'm afraid. How would you add, say, an ON transition toward the end of the MAP string -- say the S8 bit at the very end (but before the last four bits of padding)?

When I try the script, it seems like the first character keeps getting changed no matter how long a binary string I put in. Or I get errors from strange things happening during the conversion of my length-512 binary string.
First of all, you have to either use CGoL (and leave the first getstring as is) or paste your MAP rule in, without the prefix.
Then, for S8, the next getstring input will be 111111111 because

Code: Select all

111
111
111
keeps the central cell alive. Golly will change the MAP string accordingly.

This script does not convert length 512 binary strings to the mapcode. It only switches birth/survival conditions with 9-digit codes.
If yu want to work from scratch, use AAAAAAAAAAAAA... ...AAAA and then add each condition.
Last edited by Rhombic on July 1st, 2017, 5:31 pm, edited 1 time in total.
SoL : FreeElectronics : DeadlyEnemies : 6a-ite : Rule X3VI
what is “sesame oil”?

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

Re: MAPper, a script to efficiently change MAP rules

Post by dvgrn » July 1st, 2017, 5:13 pm

Rhombic wrote:First of all, you have to either use CGoL (and leave the first getstring as is) or paste your MAP rule in, without the prefix.
Then, for S8, the next getstring input will be 111111111 because

Code: Select all

111
111
111
keeps the central cell alive. The program will change the MAP string accordingly.

This script does not convert length 512 binary strings to the mapcode. It only switches birth/survival conditions with 9-digit codes.
Ah, now it makes sense. Thanks!

Saka
Posts: 3138
Joined: June 19th, 2015, 8:50 pm
Location: In the kingdom of Sultan Hamengkubuwono X

Re: MAPper, a script to efficiently change MAP rules

Post by Saka » July 14th, 2017, 7:31 am

I want to make it so cells don't get born with 3i at the bottom. What binary code do I enter? I guessed it is the same as in ruleTables, but when I enter 0110000010 it does not do what I want to do. What is the binary code?
Airy Clave White It Nay

Code: Select all

x = 17, y = 10, rule = B3/S23
b2ob2obo5b2o$11b4obo$2bob3o2bo2b3o$bo3b2o4b2o$o2bo2bob2o3b4o$bob2obo5b
o2b2o$2b2o4bobo2b3o$bo3b5ob2obobo$2bo5bob2o$4bob2o2bobobo!
(Check gen 2)

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

Re: MAPper, a script to efficiently change MAP rules

Post by dvgrn » July 14th, 2017, 7:51 am

Saka wrote:I want to make it so cells don't get born with 3i at the bottom. What binary code do I enter? I guessed it is the same as in ruleTables, but when I enter 0110000010 it does not do what I want to do. What is the binary code?
No guessing necessary -- docs for the rule table are in Golly Help, and the order is

C,N,NE,E,SE,S,SW,W,NW,C'

whereas the MAP neighbor ordering is straight left-to-right top-to-bottom. See rule integer on the LifeWiki. So therefore it's

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

(and the C' is the bit that the script is toggling).

Your "0110000010" looks like what I would call "3i at the top", but maybe I'm misunderstanding something... the equivalent would be 111000000, but maybe you really want 000000111 toggled off...?

User avatar
Rhombic
Posts: 1056
Joined: June 1st, 2013, 5:41 pm

Re: MAPper, a script to efficiently change MAP rules

Post by Rhombic » July 14th, 2017, 3:53 pm

To prevent a cell to be born with 3i(N), that is, immediately below a horizontal blinker, just write 111000000:

111
000
000

The script basically checks if the existing transition was true or false and changes it. the input is therefore the same, regardless of whether you want it off or on.
Hope it helps. Any bugs or problems, or possible further development, just let me know.
SoL : FreeElectronics : DeadlyEnemies : 6a-ite : Rule X3VI
what is “sesame oil”?

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

Re: MAPper, a script to efficiently change MAP rules

Post by dvgrn » March 18th, 2018, 1:58 pm

Here's a Lua version of the MAPper script, with one handy improvement.

Instead of defaulting to B3/S23, the script now tries to default to the MAP string representing the rule currently set in Golly. This makes it easier to make minimal changes to specific Life-like rules, without having to go through a separate step to figure out what the MAP equivalent of the Life-like rule is.

This script also makes the rule change immediately in Golly, rather than reporting it to the clipboard.

If anyone runs into bugs, please let me know. I only tried it on muzik's two rules, and those seemed to work fine, but it would be easy for some problem to have crept in that I haven't thought of.

Code: Select all

-- MAPper.lua
-- Changes a given MAP rule one state at a time, much faster than by hand.
-- The way it works will switch the existing transition input.
-- Solution is shown and copied to clipboard, prefixed with MAP.
-- Lua version defaults to current rule if this can be determined.
-- Authors: Rhombic, dvgrn

local g = golly()
local gp = require "gplus"
local split = gp.split

function fixrule(s)
  return (gp.split(s,":")) -- remove bounded grid spec, if any
end

-- make reasonably sure that the current rule is something compatible with the MAP-rule-finding function
if g.numstates()==3 then
  g.addlayer("Default two-state")
  g.setrule("B/S")
end

g.addlayer("temp") -- keep and test the current rule
fixedrule=fixrule(g.getrule())
g.setrule(fixedrule)

bitlist={{-1,-1,256},{0,-1,128},{1,-1,64},{-1,0,32},{0,0,16},{1,0,8},{-1,1,4},{0,1,2},{1,1,1}}
for j = 1, 9 do
    x, y, bit = table.unpack(bitlist[j])
    for i=0, 511 do
        if i&bit>0 then g.setcell(i*5+x,y,1) end
    end
end

g.run(1)
bits={}
ind=1
local rulestr, invstr, revinvstr="","",""
for k=0,2555,5 do
    if g.getcell(k,0)==1 then
        rulestr=rulestr.."1"
        invstr=invstr.."0"
        revinvstr="0"..revinvstr   
    else
        rulestr=rulestr.."0"
        invstr=invstr.."1"
        revinvstr="1"..revinvstr
    end
end

-- build base64 lookup table
local lookupstr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
bdict={}
lookup={}
base64char={}
for i=1,64 do
    local w=""
    for j=0,5 do
        if (i-1)&2^j>0 then w="1"..w else w="0"..w end
    end
    bdict[w]=lookupstr:sub(i,i)
    lookup[lookupstr:sub(i,i)]=i-1
    base64char[i-1]=lookupstr:sub(i,i)
end

-- decide whether to use standard, inverted, or inverted-reversed bitstring, based on fixedrule
-- (assuming Golly is correctly simulating rules with B0, with or without S8, to avoid strobing)

-- the following won't work for anisotropic non-totalistic rules defined by MAP strings, so
-- we'd have to decode the MAP rulestring into a 512-bit string in that case, check first and last digits.

if string.match(fixedrule,"B0") then rulestr = string.match(fixedrule,"S.*8") and revinvstr or invstr end

g.dellayer()

toconvert=rulestr.."0000"
toconvertold=toconvert
mapstr=""
while #toconvert>0 do
    chunk=toconvert:sub(1,6)
    toconvert=toconvert:sub(7)
    mapstr=mapstr..bdict[chunk]
end
-- g.setclipstr(mapstr.."\n"..toconvert.."\n"..toconvertold)
rule = g.getstring("Initial MAP rule to modify (default is CGoL) without the MAP prefix. Leave blank to start from scratch (B/S)", mapstr)
if rule == "" then rule="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" end
changes = tonumber(g.getstring("How many changes are you planning?","1"))
for i= 1, changes do
    newcase = g.getstring("Binary code to implement:","000000000")
    if #newcase~=9 then
        g.exit("Input "..newcase.." does not have the required nine characters.")
    end
    defect = 0
    pow = 256
    for j = 1, string.len(newcase) do
        num = string.sub(newcase, j, j) == "1" and 1 or 0
        defect = defect + num * pow
        pow = pow / 2
    end
    rulepos = (defect-(defect%6))//6
--    g.note("rulepos="..rulepos)
    bitpos = defect % 6
    char=rule:sub(rulepos+1,rulepos+1)
--    g.note("rulepos character in rule="..char..", bitpos="..bitpos)
    bitpattern = lookup[char]
--    g.note(lookup[char])
--    g.note("bitpattern="..bitpattern)
    newbitpattern = bitpattern ~ (2^(5-bitpos))
--    g.note("newbitpattern="..newbitpattern)
    newrule = rule:sub(1,rulepos)..base64char[newbitpattern]..rule:sub(rulepos+2)
    g.show("Last transition changed: "..base64char[newbitpattern].." replaced "..base64char[bitpattern].." at position "..rulepos.." New rule is MAP"..newrule)
    rule = newrule
end
g.setrule("MAP"..newrule)

shouldsee
Posts: 406
Joined: April 8th, 2016, 8:29 am

Re: MAPper, a script to efficiently change MAP rules

Post by shouldsee » March 27th, 2018, 11:40 am

WoW you guys are onto non-isotropic rules already!

User avatar
LaundryPizza03
Posts: 457
Joined: December 15th, 2017, 12:05 am
Location: Unidentified location "https://en.wikipedia.org/wiki/Texas"

Re: MAPper, a script to efficiently change MAP rules

Post by LaundryPizza03 » May 18th, 2018, 3:21 am

dvgrn wrote: If anyone runs into bugs, please let me know. I only tried it on muzik's two rules, and those seemed to work fine, but it would be easy for some problem to have crept in that I haven't thought of.
When I ran the script to attempt to convert B2ce3ir/S12 to B2ce3ir7e/s12, I found no difference when a random 64*64 soup was run for 1100 generations.

I also ran to convert B017/S1 to B0178/S1 and evolved the same soup for 1000 generations.

It seems to work.

Code: Select all

x = 4, y = 3, rule = B3-q4z5y/S234k5j
2b2o$b2o$2o!
LaundryPizza03 at Wikipedia

Post Reply