Page 1 of 1

MAPper, a script to efficiently change MAP rules

Posted: July 1st, 2017, 12:37 pm
by Rhombic
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)

Re: MAPper, a script to efficiently change MAP rules

Posted: July 1st, 2017, 3:13 pm
by dvgrn
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.

Re: MAPper, a script to efficiently change MAP rules

Posted: July 1st, 2017, 4:50 pm
by Rhombic
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.

Re: MAPper, a script to efficiently change MAP rules

Posted: July 1st, 2017, 5:13 pm
by dvgrn
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!

Re: MAPper, a script to efficiently change MAP rules

Posted: July 14th, 2017, 7:31 am
by Saka
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?

Re: MAPper, a script to efficiently change MAP rules

Posted: July 14th, 2017, 7:51 am
by dvgrn
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...?

Re: MAPper, a script to efficiently change MAP rules

Posted: July 14th, 2017, 3:53 pm
by Rhombic
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.

Re: MAPper, a script to efficiently change MAP rules

Posted: March 18th, 2018, 1:58 pm
by dvgrn
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 or current rule) without the MAP prefix. Leave blank to start from scratch (B/S)", mapstr)
if rule == "" then rule="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" end
changestr = g.getstring("How many changes are you planning?","1")
if changestr == "" then changestr = 0 end
changes = tonumber(changestr)
if changes == 0 then g.exit() end
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)

Re: MAPper, a script to efficiently change MAP rules

Posted: March 27th, 2018, 11:40 am
by shouldsee
WoW you guys are onto non-isotropic rules already!

Re: MAPper, a script to efficiently change MAP rules

Posted: May 18th, 2018, 3:21 am
by LaundryPizza03
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.