Golly scripts

For scripts to aid with computation or simulation in cellular automata.

Re: Golly scripts

Eylrid wrote:I can't seem to get it to work for some reason.

PM 2Ring is using a very old version of Golly so some of his scripts don't work with newer versions (2.0 or later). Here is a fixed version:
`# Golly Python script. Written by PM 2Ring, March 2009. Updated August 2009.# Modified for Golly 2.0+ by Andrew Trevorrow.''' Create a 'bitmap printer'.    Print a bitmap in LWSSs, using the current selection as the bitmap source.'''import gollyfrom glife import *def get_bitmap():    ''' Get current selection & turn it into a bitmap. '''    selrect = golly.getselrect()    if len(selrect) == 0: golly.exit("There is no selection, aborting.")    #Get bitmap size    w, h = selrect[2:]    #Adjust width, so it's in the form of 4m, m>1    w = (w + 3) // 4 * 4    w = max(8, w)    #Initialize empty bitmap    row = w * [0]    bm = [row[:] for i in xrange(h)]    #Populate bitmap with cell data    u, v = selrect[:2]    cells = golly.getcells(selrect)    cellsxy = [(cells[i] - u, cells[i+1] - v) for i in xrange(0, len(cells), 2)]    for x, y in cellsxy:        bm[y][x] = 1    return bmdef linemaker(loopm):    ''' Create an empty LineMaker '''    LM = pattern('''34b2o\$34bo\$25b2o5bobo\$24b3o5b2o\$12bo8bob2o\$12bobo6bo2bo\$2o11bobo5bob2o\$2o11bo2bo7b3o21b2o\$13bobo9b2o21b2o\$12bobo\$12bo9bo\$23b2o\$22b2o6\$47b3o\$24b2o20bo3bo\$24b2o19bo5bo\$46bo3bo\$47b3o\$47b3o5\$49b3o\$44bo3b2ob2o\$42b2o4b2ob2o\$35b2o6b2o3b5o\$35b2o10b2o3b2o7\$45b2o5b2o\$45bo6bo\$46b3o4b3o\$48bo6bo!''')    LMTop = LM + pattern('2b2o\$2bo\$obo\$2o!', 32, 7)    LMBase = pattern('''11bo\$10bo\$10b3o12\$23b2o\$23bobo\$10bobo13bo\$9bo2bo2b2o6bo2bo12b2o\$8b2o5bobo8bo11b3o\$2o4b2o3bo3bo3bo3bobo9bob2o15bo\$2o6b2o5b3ob2o2b2o10bo2bo8b3o4bobo\$9bo2bo3b2o17bob2o16bobo\$10bobo25b3o2b2o2bo7bo2bo3b2o\$39b2o2bo3bo7bobo4b2o\$44bo2bo6bobo\$26bo17b2o8bo\$24bobo\$25b2o2\$36bo\$36bobo\$36b2o7\$33bo7bo\$32b4o5bobo\$27b2o2bo2b2o8b2o4b2o\$27b2o2b2o11b2o4b2o\$16b2o6b2o10bo7b2o\$16b2o5b3o10bo4bobo\$24b2o10bo4bo\$27b2o\$27b2o!''', 14, 29)    LMBase += LM(55, 42, flip)     return LMTop(8 + loopm, -21 - loopm) + LMBasedef dmprinter(pdy, copies=1):    ''' Generate & display a dot matrix printer for named bitmap pattern '''    #Horizontal pixel separation between LineMakers. minimum = 5    LMsx = 5    #Horizontal distance between bitmap pixels. Constant, due to LineMaker period    pdx = 15    #Distance between LineMakers    LMdx, LMdy = -LMsx * pdx, pdy    #Get bitmap pattern from current selection.    bm = get_bitmap()    #Make printer in a new layer    golly.duplicate()    golly.setoption("syncviews", False)    #Build new window title from old    title = golly.getname().split('.')[0]    title = '%s_Printer [%d] v0.10' % (title, pdy)    golly.new(title)    #Make sure we're using the standard Life generation rule    golly.setrule("B3/S23")    #Bitmap dimensions. Width MUST be of form 4m, for m >=1    bmheight = len(bm)    bmwidth = len(bm[0])    mid = (bmheight + 1) // 2    loopw = (bmwidth - 8) // 4    loopm = pdx * loopw    #Gliders, heading north-east    g0 = pattern('2bo\$2o\$b2o!')    g1 = pattern('obo\$2o\$bo!')    gliders = [g0, g1, g1(0, 2, rccw), g0(0, 2, rccw),        g0(2, 2, flip), g1(2, 2, flip), g1(2, 0, rcw), g0(2, 0, rcw)]            #Create full Glider loop.    gg = []    ox, oy = 35, 23    for j in xrange(loopw + 1):        dd = pdx * j        gg += [gliders[0](ox + dd, oy - dd), gliders[1](ox + 8 + dd, oy - 7 - dd)]    dd = loopm    gg += [gliders[2](45 + dd, 4 - dd), gliders[3](37 + dd, -3 - dd)]    ox, oy = 26 + loopm, -4 - loopm    for j in xrange(loopw + 1):        dd = pdx * j        gg += [gliders[4](ox - dd, oy + dd), gliders[5](ox - 8 - dd, oy + 7 + dd)]    dd = loopm    gg += [gliders[6](16, 15), gliders[7](24, 22)]    parity = 2*((loopw + 1)*(0, 0) + (1, 1))    def buildLM():        ''' Build a complete LineMaker '''        #Populate glider loop. (jj, ii) are bitmap coords        gloop = pattern()        for j in xrange(bmwidth):            jj = (j - delta + bmwidth - 1) % bmwidth            #Is there a pixel at this location?            if bm[ii][jj] == parity[j]:                                gloop += gg[j]  #Add glider to the loop        #Only put LineMaker if glider loop isn't empty        if len(gloop) > 0:            (LMBlank + gloop).put(Lx, Ly, trans)    #Assemble blank LineMaker    LMBlank = linemaker(loopm)    #Do upper LineMakers    trans = identity    for i in xrange(mid):        ii = mid - (i + 1)        io = mid + (i + 1)        Lx, Ly = io * LMdx, ii * LMdy        delta = LMsx * io        buildLM()    #Do lower LineMakers    trans = flip_y    for i in xrange(mid, bmheight):        ii = i        io = i + 2        Lx, Ly = io * LMdx + pdx, ii * LMdy + 128        delta = LMsx * io - 1        buildLM()    #Eaters facing south-east & north-east    eaterSE = pattern('2o\$bo\$bobo\$2b2o!')    eaterNE = eaterSE(0, 10, flip_y)    eY = 59    eaters = (eaterNE(0, eY), eaterSE(0, eY))        #Eater horizontal displacement. Must be odd    #bmwidth muliplier determines number of copies visible 'outside' printer.    eX = (bmheight + 1) * LMdx - (copies * bmwidth - 1) * pdx       eX = 1 + eX // 2 * 2    #Adjust parity    all = pattern()    for i in xrange(bmheight):        all += eaters[i % (1 + LMsx % 2)](eX, i * LMdy)    all.put()       #Centre view over bitmap output area    golly.setpos(str(-pdx * bmwidth // 2 + (bmheight - 1) * LMdx), str(Ly//2))    #golly.setmag(-2)    #Aliasing effects happen at smaller scales than -2 :(    golly.fit()def main():    #Vertical distance between pixels. Maybe get this from user. minimum = 16    pdy = 16    #Generate & display a dot matrix printer from current selection    dmprinter(pdy, copies=1)    golly.setcursor("Zoom In")    golly.setalgo("HashLife")    golly.setbase(2)    golly.setstep(6)main()`

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Eylrid wrote:I can't seem to get it to work for some reason.

When reporting difficulty with scripts, please let us know *what* doesn't work. For example: do you get an error message? If so, what's the message? If not, what happens? Does nothing at all happen? Etc.

Nathaniel

Posts: 442
Joined: December 10th, 2008, 3:48 pm

Re: Golly scripts

Andrew wrote:
Eylrid wrote:I can't seem to get it to work for some reason.

PM 2Ring is using a very old version of Golly so some of his scripts don't work with newer versions (2.0 or later). Here is a fixed version:
`# Golly Python script. Written by PM 2Ring, March 2009. Updated August 2009.# Modified for Golly 2.0+ by Andrew Trevorrow.''' Create a 'bitmap printer'.    Print a bitmap in LWSSs, using the current selection as the bitmap source.'''import gollyfrom glife import *def get_bitmap():    ''' Get current selection & turn it into a bitmap. '''    selrect = golly.getselrect()    if len(selrect) == 0: golly.exit("There is no selection, aborting.")    #Get bitmap size    w, h = selrect[2:]    #Adjust width, so it's in the form of 4m, m>1    w = (w + 3) // 4 * 4    w = max(8, w)    #Initialize empty bitmap    row = w * [0]    bm = [row[:] for i in xrange(h)]    #Populate bitmap with cell data    u, v = selrect[:2]    cells = golly.getcells(selrect)    cellsxy = [(cells[i] - u, cells[i+1] - v) for i in xrange(0, len(cells), 2)]    for x, y in cellsxy:        bm[y][x] = 1    return bmdef linemaker(loopm):    ''' Create an empty LineMaker '''    LM = pattern('''34b2o\$34bo\$25b2o5bobo\$24b3o5b2o\$12bo8bob2o\$12bobo6bo2bo\$2o11bobo5bob2o\$2o11bo2bo7b3o21b2o\$13bobo9b2o21b2o\$12bobo\$12bo9bo\$23b2o\$22b2o6\$47b3o\$24b2o20bo3bo\$24b2o19bo5bo\$46bo3bo\$47b3o\$47b3o5\$49b3o\$44bo3b2ob2o\$42b2o4b2ob2o\$35b2o6b2o3b5o\$35b2o10b2o3b2o7\$45b2o5b2o\$45bo6bo\$46b3o4b3o\$48bo6bo!''')    LMTop = LM + pattern('2b2o\$2bo\$obo\$2o!', 32, 7)    LMBase = pattern('''11bo\$10bo\$10b3o12\$23b2o\$23bobo\$10bobo13bo\$9bo2bo2b2o6bo2bo12b2o\$8b2o5bobo8bo11b3o\$2o4b2o3bo3bo3bo3bobo9bob2o15bo\$2o6b2o5b3ob2o2b2o10bo2bo8b3o4bobo\$9bo2bo3b2o17bob2o16bobo\$10bobo25b3o2b2o2bo7bo2bo3b2o\$39b2o2bo3bo7bobo4b2o\$44bo2bo6bobo\$26bo17b2o8bo\$24bobo\$25b2o2\$36bo\$36bobo\$36b2o7\$33bo7bo\$32b4o5bobo\$27b2o2bo2b2o8b2o4b2o\$27b2o2b2o11b2o4b2o\$16b2o6b2o10bo7b2o\$16b2o5b3o10bo4bobo\$24b2o10bo4bo\$27b2o\$27b2o!''', 14, 29)    LMBase += LM(55, 42, flip)     return LMTop(8 + loopm, -21 - loopm) + LMBasedef dmprinter(pdy, copies=1):    ''' Generate & display a dot matrix printer for named bitmap pattern '''    #Horizontal pixel separation between LineMakers. minimum = 5    LMsx = 5    #Horizontal distance between bitmap pixels. Constant, due to LineMaker period    pdx = 15    #Distance between LineMakers    LMdx, LMdy = -LMsx * pdx, pdy    #Get bitmap pattern from current selection.    bm = get_bitmap()    #Make printer in a new layer    golly.duplicate()    golly.setoption("syncviews", False)    #Build new window title from old    title = golly.getname().split('.')[0]    title = '%s_Printer [%d] v0.10' % (title, pdy)    golly.new(title)    #Make sure we're using the standard Life generation rule    golly.setrule("B3/S23")    #Bitmap dimensions. Width MUST be of form 4m, for m >=1    bmheight = len(bm)    bmwidth = len(bm[0])    mid = (bmheight + 1) // 2    loopw = (bmwidth - 8) // 4    loopm = pdx * loopw    #Gliders, heading north-east    g0 = pattern('2bo\$2o\$b2o!')    g1 = pattern('obo\$2o\$bo!')    gliders = [g0, g1, g1(0, 2, rccw), g0(0, 2, rccw),        g0(2, 2, flip), g1(2, 2, flip), g1(2, 0, rcw), g0(2, 0, rcw)]            #Create full Glider loop.    gg = []    ox, oy = 35, 23    for j in xrange(loopw + 1):        dd = pdx * j        gg += [gliders[0](ox + dd, oy - dd), gliders[1](ox + 8 + dd, oy - 7 - dd)]    dd = loopm    gg += [gliders[2](45 + dd, 4 - dd), gliders[3](37 + dd, -3 - dd)]    ox, oy = 26 + loopm, -4 - loopm    for j in xrange(loopw + 1):        dd = pdx * j        gg += [gliders[4](ox - dd, oy + dd), gliders[5](ox - 8 - dd, oy + 7 + dd)]    dd = loopm    gg += [gliders[6](16, 15), gliders[7](24, 22)]    parity = 2*((loopw + 1)*(0, 0) + (1, 1))    def buildLM():        ''' Build a complete LineMaker '''        #Populate glider loop. (jj, ii) are bitmap coords        gloop = pattern()        for j in xrange(bmwidth):            jj = (j - delta + bmwidth - 1) % bmwidth            #Is there a pixel at this location?            if bm[ii][jj] == parity[j]:                                gloop += gg[j]  #Add glider to the loop        #Only put LineMaker if glider loop isn't empty        if len(gloop) > 0:            (LMBlank + gloop).put(Lx, Ly, trans)    #Assemble blank LineMaker    LMBlank = linemaker(loopm)    #Do upper LineMakers    trans = identity    for i in xrange(mid):        ii = mid - (i + 1)        io = mid + (i + 1)        Lx, Ly = io * LMdx, ii * LMdy        delta = LMsx * io        buildLM()    #Do lower LineMakers    trans = flip_y    for i in xrange(mid, bmheight):        ii = i        io = i + 2        Lx, Ly = io * LMdx + pdx, ii * LMdy + 128        delta = LMsx * io - 1        buildLM()    #Eaters facing south-east & north-east    eaterSE = pattern('2o\$bo\$bobo\$2b2o!')    eaterNE = eaterSE(0, 10, flip_y)    eY = 59    eaters = (eaterNE(0, eY), eaterSE(0, eY))        #Eater horizontal displacement. Must be odd    #bmwidth muliplier determines number of copies visible 'outside' printer.    eX = (bmheight + 1) * LMdx - (copies * bmwidth - 1) * pdx       eX = 1 + eX // 2 * 2    #Adjust parity    all = pattern()    for i in xrange(bmheight):        all += eaters[i % (1 + LMsx % 2)](eX, i * LMdy)    all.put()       #Centre view over bitmap output area    golly.setpos(str(-pdx * bmwidth // 2 + (bmheight - 1) * LMdx), str(Ly//2))    #golly.setmag(-2)    #Aliasing effects happen at smaller scales than -2 :(    golly.fit()def main():    #Vertical distance between pixels. Maybe get this from user. minimum = 16    pdy = 16    #Generate & display a dot matrix printer from current selection    dmprinter(pdy, copies=1)    golly.setcursor("Zoom In")    golly.setalgo("HashLife")    golly.setbase(2)    golly.setstep(6)main()`

Thankyou! That is really cool!

Nathaniel wrote:
Eylrid wrote:I can't seem to get it to work for some reason.

When reporting difficulty with scripts, please let us know *what* doesn't work. For example: do you get an error message? If so, what's the message? If not, what happens? Does nothing at all happen? Etc.

Will do. (That one wasn't doing anything at all.)
Eylrid

Posts: 30
Joined: October 8th, 2009, 10:28 pm

Re: Golly scripts

Eylrid wrote:Thankyou! That is really cool!

I'm glad you like it, Eylrid.

Some of my scripts print error messages to the console, so it can be useful when running my scripts to launch Golly from a terminal, rather than via the icon.

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

I've added some new Python scripts to the Golly Scripts Database:

fast-duplicate.py -- Duplicates the current pattern and settings in a new layer, but doesn't copy the undo/redo history. Much faster than the Duplicate Layer menu command, especially for very large patterns.

save-as-mc.py -- Golly's Save Pattern dialog always uses .rle as the default format, so this script brings up the same dialog but with .mc as the default format. Works with all algorithms except QuickLife.

save-image.py -- Saves the current selection or pattern in a specified image file (.png/.bmp/.gif/.tif/.jpg). Requires the Python Imaging Library.

The new scripts all require Golly 2.1, and you can download them via Help > Online Archives.

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Andrew,

Is there a Golly Python command to select the whole current pattern, like the Edit/Select All menu? Currently, I use
golly.select([-maxint//2, -maxint//2, maxint, maxint]); golly.shrink()
Is there a better way?

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

PM 2Ring wrote:Is there a Golly Python command to select the whole current pattern, like the Edit/Select All menu?

Yes, just do golly.select(golly.getrect()).

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Andrew wrote:
PM 2Ring wrote:Is there a Golly Python command to select the whole current pattern, like the Edit/Select All menu?

Yes, just do golly.select(golly.getrect()).

Ah, thanks, Andrew! I must be going blind.

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

LWSS & MWSS maker

`# Golly python script.# Written by PM 2Ring, October 2009''' Create a LWSS or MWSS spaceship gun The spaceship gun is created from the currently loaded pattern, which is assumed to be some sort of glider gun. The pattern is built in the current layer. The gun's glider must be about to leave the guns bounding box.'''import gollyfrom glife import *def toxy(cells):    ''' Convert cell list to a sorted list of (x, y) tuples '''    xy = zip(cells[::2], cells[1::2])    xy.sort()    return xy            def MakeSSGun(sstype):    ''' Make a LWSS or MWSS gun '''        #Set up glider identification stuff    #Glider heading SE _\    glider = pattern('bo\$2bo\$3o!')        #Glider directions & associated transforms    xforms = {'SE':identity, 'SW':flip_x, 'NE':flip_y, 'NW':flip}        #Glider transformed by j with phase i, as a tuple-ised cell list    def gliderxy(j, i):         g = toxy(list(glider[i].apply(j)))        #Adjust origin to top left corner        ox, oy = g[0][0], min([i[1] for i in g])        return sum([(x-ox, y-oy) for x,y in g], ())        #All 16 gliders, with direction string & phase    glidict = dict([(gliderxy(j, i), (k, i))         for k,j in xforms.iteritems() for i in xrange(4)])        #Select entire pattern    selrect = golly.getrect()    if len(selrect) == 0: golly.exit("No pattern!")    gun0 = pattern(golly.getcells(selrect))    #Emit one glider, to act as a positioning key    golly.run(16)    golly.select(selrect)    golly.clear(0)    #Find the "key" glider    selrect = golly.getrect()    if len(selrect) == 0: golly.exit("No glider!")    #Get the glider bounding box & cell data    ox, oy = selrect[:2]    cells = golly.getcells(selrect)        #Convert cell list to a tuple & make top left cell the glider's origin       cells = sum([(x-ox, y-oy) for x,y in toxy(cells)], ())        #Determine glider direction & current phase    direction, phase = glidict[cells]    xform = xforms[direction]    dx, dy = xform[::3]    golly.show('Direction=%s, Phase=%d' % (direction, phase))    #Reorient gun if the key glider isn't heading SE        if direction != 'SE':        ox += 1 - dx        oy += 1 - dy              gun0 = gun0.apply(xform)        #Move the gun's origin to the new origin of the key glider    ox -= 4 * dx    oy -= 4 * dy        gun0 = gun0(-ox * dx, -oy * dy)    gun0bb = getminbox(gun0)    #Determine minimum glider gun separation    mx, my = gun0bb[0] + gun0bb[2], gun0bb[1] + gun0bb[3]        d = max(0, mx, my)     #print gun0bb, mx, my, 'd =', d    #Phase based adjustment for gun2 & eater        gx = (phase==0 or phase==3) and -2 or 0                 #An eater to catch the SS stream    eater = pattern('2b2o\$3bo\$3o\$o!', gx//2 - 3, 0)    #Adjust the glider guns' orientation, phase & position         if sstype == 'L':        #LWSS gun. Min period=23        #Bottom left gun        gun1 = gun0(-4, 4, flip_y)            #Top right gun. Horizontal position depends on phase of initial glider        gun2 = gun0[1](5 + gx, 4, flip_x)            else:   #sstype == 'M'        #MWSS gun. Min period=44         #Bottom left gun. Shifted SW by d1 units, to allow MWSS to pass safely        d1 = 3           gun1 = gun0[4*d1 + 3](-1 - d1, 7 + d1, flip_y)                #Top right gun. Horizontal position depends on phase of initial glider        gun2 = gun0(5 + gx, -3, flip_x)                eater = eater(0, 1)    #Clear the layer    golly.new(sstype + 'WSS' + golly.getname())         #Build the SS gun.     all = gun0(-d, -d) + gun1(-d, d) + gun2(d, -d)    all += eater(0, (2*d + gun0bb[3])//2*2)    all.put()        golly.fit()     #Select the "key" glider of gun0        #golly.select([-d, -d, 3, 3])          def main():    #Get input data    prompt = 'Enter M or L (default) to select spaceship type:'    sstype = getstring(prompt).upper() == 'M' and 'M' or 'L'    MakeSSGun(sstype)if __name__ == '__main__':    main()`

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

Can someone who knows how to script make one for lifecolor to convert all cells that are one color to another?
Axaj

Posts: 232
Joined: September 26th, 2009, 12:23 am

Re: Golly scripts

Axaj wrote:Can someone who knows how to script make one for lifecolor to convert all cells that are one color to another?

The following script should do the job. I've also added it to the Golly Scripts Database.

change-state.py
`# Change all cells from one given state to another given state.# Works in any rule with more than 2 states.# Author: Andrew Trevorrow (andrew@trevorrow.com), Oct 2009.import golly as gfrom glife import rectfrom time import timer = rect( g.getrect() )if r.empty: g.exit("There is no pattern.")maxstate = g.numstates() - 1if maxstate == 1: g.exit("The current rule only has 2 states.")result = g.getstring("Change all the cells in one state (the 1st number)\n" +"to a different state (the 2nd number) where both\n" +"numbers must be from 0 to " + str(maxstate) + ":","1 2", "Change state")try:   s1, s2 = result.split()except:   g.exit("Enter 2 numbers separated by a space.")try:   fromstate = int(s1)except:   g.exit("1st state is not a number: " + s1)try:   tostate = int(s2)except:   g.exit("2nd state is not a number: " + s2)if fromstate < 0 or fromstate > maxstate:   g.exit("1st state is out of range: " + s1)if tostate < 0 or tostate > maxstate:   g.exit("2nd state is out of range: " + s2)if fromstate == tostate:   g.exit("The given states are the same.")changed = 0total = float(r.wd) * float(r.ht)oldsecs = time()for row in xrange(r.top, r.top + r.height):   # might be large pattern so show percentage done each second   newsecs = time()   if newsecs - oldsecs >= 1.0:      oldsecs = newsecs      g.show("Changing cell states: " +             str(int(100 * (row - r.top) * r.wd / total)) + "%")      g.update()   # allow keyboard interaction   g.dokey( g.getkey() )   for col in xrange(r.left, r.left + r.width):      if g.getcell(col, row) == fromstate:         g.setcell(col, row, tostate)         changed += 1if changed == 0:   # better to beep in this case   g.exit("There are no cells in state %d." % fromstate)else:   g.show("%d cells were changed from state %d to state %d."          % (changed, fromstate, tostate))`

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Andrew wrote:PM 2Ring is using a very old version of Golly so some of his scripts don't work with newer versions (2.0 or later). Here is a fixed version:

Thanks very much for fixing that, Andrew. I really would like to run the latest version, and take advantage of undo/redo (and the new scripting features) but my distro's too old. I just tried running Golly 2.1, but it complains that I'm missing libgio. I guess I really need to get a new distro. (apt-get won't update or upgrade my system - it says I don't have enough memory.)

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

Andrew wrote:
Axaj wrote:Can someone who knows how to script make one for lifecolor to convert all cells that are one color to another?

The following script should do the job. I've also added it to the Golly Scripts Database.

change-state.py
`# Change all cells from one given state to another given state.# Works in any rule with more than 2 states.# Author: Andrew Trevorrow (andrew@trevorrow.com), Oct 2009.import golly as gfrom glife import rectfrom time import timer = rect( g.getrect() )if r.empty: g.exit("There is no pattern.")maxstate = g.numstates() - 1if maxstate == 1: g.exit("The current rule only has 2 states.")result = g.getstring("Change all the cells in one state (the 1st number)\n" +"to a different state (the 2nd number) where both\n" +"numbers must be from 0 to " + str(maxstate) + ":","1 2", "Change state")try:   s1, s2 = result.split()except:   g.exit("Enter 2 numbers separated by a space.")try:   fromstate = int(s1)except:   g.exit("1st state is not a number: " + s1)try:   tostate = int(s2)except:   g.exit("2nd state is not a number: " + s2)if fromstate < 0 or fromstate > maxstate:   g.exit("1st state is out of range: " + s1)if tostate < 0 or tostate > maxstate:   g.exit("2nd state is out of range: " + s2)if fromstate == tostate:   g.exit("The given states are the same.")changed = 0total = float(r.wd) * float(r.ht)oldsecs = time()for row in xrange(r.top, r.top + r.height):   # might be large pattern so show percentage done each second   newsecs = time()   if newsecs - oldsecs >= 1.0:      oldsecs = newsecs      g.show("Changing cell states: " +             str(int(100 * (row - r.top) * r.wd / total)) + "%")      g.update()   # allow keyboard interaction   g.dokey( g.getkey() )   for col in xrange(r.left, r.left + r.width):      if g.getcell(col, row) == fromstate:         g.setcell(col, row, tostate)         changed += 1if changed == 0:   # better to beep in this case   g.exit("There are no cells in state %d." % fromstate)else:   g.show("%d cells were changed from state %d to state %d."          % (changed, fromstate, tostate))`

Thanks a ton! Do you think you could change it to do only the current selection?
Axaj

Posts: 232
Joined: September 26th, 2009, 12:23 am

Re: Golly scripts

Axaj wrote:Thanks a ton! Do you think you could change it to do only the current selection?

Just change getrect() to getselrect(), and the error message on the next line to "There is no selection.".

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Golly scripts

Andrew wrote:
Axaj wrote:Thanks a ton! Do you think you could change it to do only the current selection?

Just change getrect() to getselrect(), and the error message on the next line to "There is no selection.".

Great, it works! Thanks again!
Axaj

Posts: 232
Joined: September 26th, 2009, 12:23 am

Re: Golly scripts

Fill current selection with a random glider field. Density must be a random floating point number between 0 & 1. Separation & seed are integers >= 0

`# Golly python script.# Written by PM 2Ring, November 2009''' Fill current selection with a random glider field '''import gollyfrom glife import *from random import seed, random, choicedef getargs():    ''' Get input data '''    prompt = 'Enter density, separation and seed'    usage = 'Create a random grid of gliders.\n' + prompt + ', separated by spaces.'    #Loop until we get correct args, or are aborted by ESC    while True:        try:            args = getstring(prompt + ' :').split()            if len(args) < 3:                raise ValueError, 'Not enough data.'                        args = float(args[0]), int(args[1]), int(args[2])                    except ValueError, s:            golly.warn('%s\n\n%s' % (s, usage))        else:            break    return argsdef toxy(cells):    ''' Convert cell list to a sorted list of (x, y) tuples '''    xy = zip(cells[::2], cells[1::2])    xy.sort()    return xydef MakeGliders():    ''' Create a list of all gliders '''    #Glider heading SE _\    glider = pattern('bo\$2bo\$3o!')    #Glider transforms    xforms = [identity, flip_x, flip_y, flip]    #Glider transformed by j with phase i, as a cell list    def gliderxy(j, i):        g = toxy(list(glider[i].apply(j)))        #Adjust origin to top left corner        ox, oy = g[0][0], min([i[1] for i in g])        return sum([[x-ox, y-oy] for x,y in g], [])    #All 16 gliders    return [pattern(gliderxy(j, i)) for j in xforms for i in xrange(4)]def RandGliders():    #Get density, separation & randomizer seed from user    density, dd, rseed = getargs()    dd += 3    seed(rseed)    selrect = golly.getselrect()    if len(selrect) == 0: golly.exit("No area selected")    ox, oy, wx, wy = selrect    wx -= 2; wy -= 2        #A list of all 16 gliders    gliders = MakeGliders()    #Fill grid    all = pattern()    for y in xrange(0, wy, dd):        for x in xrange(0, wx, dd):            if random() < density:                g = choice(gliders)                all += g(ox + x, oy + y)    all.put()    #golly.fit()def main():    RandGliders()if __name__ == '__main__':    main()`

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

This script creates a numbered grid of multiple generations of the currently selected pattern. Each grid entry is numbered using the Snakial font. It was written to display all phases of an oscillator (or guns) in a single pattern.

PatGrid0.py
`# Golly Python script# Display a numbered grid of generations of currently selected pattern# Written by PM 2Ring, 2010.3.22import gollyfrom glife import *def snakenum(n): return make_text(str(n))def makegrid(patA, count, dx, dy, cols):    ''' Build a numbered grid of pattern states '''        def slots():        ''' Return co-ords of next grid slot '''        xr = xrange(0, dx*cols, dx)        y = 0        while 1:            for x in xr:                yield x, y            y += dy                #Adjust height & width to allow for Snakial font             dy += 20        dx = max(30, dx)       slot = slots()    for i in xrange(count):        num = snakenum(i)        pat = patA[i] + num(0, -5)                pat.put(*slot.next())        if i % cols == 0:            #golly.fit()            golly.update()    golly.fit()def getargs():    ''' Get input data: generation count and the number of columns in the grid '''    prompt = 'Enter number of gens and columns'    usage = prompt + ' , separated by spaces.'    #Loop until we get correct args, or are aborted by ESC    while True:        try:            args = getstring(prompt + ' :').split()            if args == []:                raise ArgError            #Check that all args are valid integers            for s in args:                if not validint(s):                    raise ArgError, '[%s] is not a valid integer.' % s            if len(args) < 2:                raise ArgError, 'Not enough data.'            #Convert arguments to integer            args = [int(s) for s in args]            for i in args:                if i<0:                    raise ArgError, 'Data must be >= 0, not %d!' % i        except ArgError, s:            g.warn('%s\n\n%s' % (s, usage))        else:            break    return argsdef main():    #Get pattern to evolve    selrect = golly.getselrect()    if len(selrect) == 0: golly.exit('No pattern selected.')    #Get generation count & number of columns    count, cols = getargs()        #count, cols = (12, 4)                #Get pattern data from selection    ox, oy = selrect[:2]    #Pattern origin    dx, dy = selrect[2:]    #Pattern size    pat = pattern(golly.getcells(selrect))(-ox, -oy)    #Initialize universe    #rule()    title = golly.getname().split('.')[0] + 'Grid'        golly.addlayer()    golly.new(title)    golly.setcursor(zoomin)    #Set viewport    golly.select([0, 0, dx*cols, dy])    golly.fitsel()    golly.update()    golly.select([])        makegrid(pat, count, dx, dy, cols)if __name__ == '__main__':    main()`

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

Nathaniel wrote:If you have any custom Golly python or perl scripts, feel free to post them here. The first one is a blazingly-fast version of the "goto.py" script that comes with Golly:

gofast.py
`#Golly Python script#Written by PM 2Ring 2009.3.24''' Advance generation count using binary hashing ''' from glife import getstring, validint import golly as gdef intbits(n):  ''' Convert integer n to a bit list '''  bits = []  while n:    bits += [n & 1]    n >>= 1  return bits        def gofast(num):  ''' Fast goto '''  #Save current settings    oldbase = g.getbase()  oldstep = g.getstep()  oldhash = g.setoption("hashing", True)    g.show('Hit ESC to abort')  #Advance by binary powers, to maximize advantage of hashing    g.setbase(2)  for i, b in enumerate(intbits(num)):    if b:      g.setstep(i)       g.step()      g.dokey(g.getkey())      g.update()      if golly.empty():         break          g.show('')    #Restore settings    g.setoption("hashing", oldhash)  g.setbase(oldbase)  g.setstep(oldstep)       def main():  numstr = getstring('How many gens to advance? :')  if not validint(numstr):     g.exit('Bad number: %s' % numstr)  num = int(numstr)   if num<1:    g.exit('Number must be positive!')  gofast(num)main()`

I decided to try out this, however there were problems. At line 4, you referenced golly as golly instead of g, giving me an error. If you fix that, (On my mac, I don't know about PCs) it stops giving errors but is still unusable because when I try to type into the little input thing at the top infobar, it triggers hotkeys on the menu which change the scale. Since the scale goes on powers of two, numbers such as 3, 5, 6, 7, and 9 are not affected, but it doesn't type anyway for those numbers, suggesting a bug in Golly. But anyway, the result is you cannot type anything, instead it just zooms in and out like the magnifying glass is having a seizure. Using the same input system as goto.py, (which makes a text box in a new window instead) I fixed it. After all the problems were fixed, I realized just how fast gofast.py was... Oh my gosh! I never expected it to do hundreds of thousands of generations of a universal constructor in five seconds! That's ridiculous!

Anyway, this is the code that worked for me:
`#Golly Python script#Written by PM 2Ring 2009.3.24#Improved (at least in his opinion) by Awesomeness 2010.7.9''' Advance generation count using binary hashing ''' from glife import validint import golly as gimport osdef intbits(n):   ''' Convert integer n to a bit list '''   bits = []   while n:      bits += [n & 1]      n >>= 1   return bits        def gofast(num):   ''' Fast goto '''   #Save current settings      oldbase = g.getbase()   oldstep = g.getstep()   oldhash = g.setoption("hashing", True)      g.show('Hit ESC to abort')   #Advance by binary powers, to maximize advantage of hashing      g.setbase(2)   for i, b in enumerate(intbits(num)):      if b:         g.setstep(i)          g.step()         g.dokey(g.getkey())         g.update()         if g.empty():             break            g.show('')      #Restore settings      g.setoption("hashing", oldhash)   g.setbase(oldbase)   g.setstep(oldstep)       def main():   GotoINIFileName = g.getdir("data") + "goto.ini"   previousgen = ""   if os.access(GotoINIFileName, os.R_OK):      f = open(GotoINIFileName, 'r')      previousgen = f.readline()      f.close()      if not validint(previousgen):         previousgen = ""      numstr = g.getstring("Enter the desired generation number.", previousgen, "Go to generation")      if not validint(numstr):          g.exit('Bad number: %s' % numstr)      num = int(numstr)       if num<1:         g.exit('Number must be positive!')      gofast(num)      previousgen = numstr   if os.access(GotoINIFileName, os.W_OK) or not os.access(GotoINIFileName, os.F_OK):      try:         f = open(GotoINIFileName, 'w')         f.write(previousgen)         f.close()      except:         g.show("Unable to save defaults to " + GotoINIFileName)   main()`
Awesomeness

Posts: 126
Joined: April 5th, 2009, 7:30 am

Re: Golly scripts

Thanks for fixing that, Awesomeness.

I'm still using Golly 1.2 on this ancient machine. I need to do major "renovations" before I can run later versions. So there are a few differences in the way scripts behave on my system.

I don't know if this has been changed in later versions, but if you do a Python import in a Golly session, then the module stays imported for subsequent scripts until you quit from Golly. This can be handy, but it can also mask bugs like the one you discovered (and the rest of us missed ).

PM 2Ring

Posts: 152
Joined: March 26th, 2009, 11:18 am

Re: Golly scripts

I'd like to get started with Python (Gosh, I haven't used it in years!) in Golly, and I was wondering:

How should I get started? Is there anything special I need to know? (Like what weird coordinate system Golly must use since it's an infinite plane?)

And just as an example, could someone create a script for me that turns the four corner cells of the current selection on? That would help so much and teach me not only how to use a selection but how to change cells as well.
Awesomeness

Posts: 126
Joined: April 5th, 2009, 7:30 am

Re: Golly scripts

Awesomeness wrote:How should I get started? Is there anything special I need to know? (Like what weird coordinate system Golly must use since it's an infinite plane?)

Read through Help > Python Scripting carefully before you start writing your scripts -- especially the Cell Lists section toward the bottom, explaining how coordinate lists are handled. The coordinate system is straightforward, but cell lists are a little tricky... most of my weird bugs recently have been caused by a script that builds a multi-state list and passes it to a Golly function that treats it as a one-state list (because there happen to be an even number of cells in the list, and therefore the list has an even number of elements, and I forgot to add a [0] at the end to indicate it's a multi-state list.)

Of course, if you aren't writing scripts that use LifeHistory or some other multi-state rule, you may never run into this particular problem -- but it's good to understand how cell lists work, anyway.

And just as an example, could someone create a script for me that turns the four corner cells of the current selection on? That would help so much and teach me not only how to use a selection but how to change cells as well.

`import golly as gr = g.getselrect()if len(r)==0: g.exit("There is no selection.")for x in [r[0],r[0]+r[2]-1]:   for y in [r[1],r[1]+r[3]-1]:      g.setcell(x,y,1)`

I love how Python scripts so often work the first time I try them... The quickest way to run the above is to copy it and choose File > Run From Clipboard.

dvgrn
Moderator

Posts: 4973
Joined: May 17th, 2009, 11:00 pm

Re: Golly scripts

Draws a box around the current selection, which has to have odd x and y values. (Is there a way I could fix that? I could up the spacing to two for certain directions when the width and height are odd or even, but that'd be inconsistent.)
`#################### box.py ##################### A script by Awesomeness 9.27.10# conwaylife.comimport golly as g # Use gollyr = g.getselrect() # Get our selection as a list of four numbers# If r has no items in it, that means there's no selection, which we need.if len(r) == 0:   g.exit("There is no selection.")# If the numbers are even.  %2 means the remainder when divided by 2.if r[2]%2 == 0 or r[3]%2 == 0:   g.exit("Length and width must be odd!")   # r[0] = x of top left corner of selection# r[1] = y of top left corner of selection# r[2] = width of selection# r[3] = height of selection# Draw top linex = r[0]y = r[1]while x <= r[0] + r[2] - 1:   g.setcell(x,y,1)   x = x + 2# Draw bottom linex = r[0]y = r[1] + r[3] - 1while x <= r[0] + r[2] - 1:   g.setcell(x,y,1)   x = x + 2   # Draw left linex = r[0]y = r[1]while y <= r[1] + r[3] - 1:   g.setcell(x,y,1)   y = y + 2# Draw right linex = r[0] + r[2]-1y = r[1]while y <= r[1] + r[3 ]- 1:   g.setcell(x,y,1)   y = y + 2`

Yeah, I always comment my code heavily, especially when other people are going to see it.
Awesomeness

Posts: 126
Joined: April 5th, 2009, 7:30 am

Utility Scripts for Hexagonal Neighborhoods

I was playing around with the Patterson's Worms examples in Golly and started thinking about rules with hexagonal neighborhoods and how to implement them in Golly. I had two rather different ideas, but in the end I decided the best way was to use a Moore neighborhood in which two of the cells where ignored.

There are two basic options for such a rule, which are:

`..... ......oo.. .oo...oOo. .oOo...oo. .oo....... .....`

I decided to go with the first option, because it's symmetry leads to patterns that look more hexagonal. The second option is more common in programs with true hexagonal displays for technical reasons.

I implemented a rule table using the standard Life rule (B3/S23) and found that it worked, but that the rule wasn't very satisfying. After playing with a few other rules, I decided to write a script to generate rule tables as it was a little tedious to hand roll them. The script is currently a command line utility, but I plan to add an (optional) interface to allow it to be used from within Golly as well.

It's slightly optimized in that birth and survival rules share the same transitions where possible. I've thought about a further optimization, but I haven't implemented it yet. Currently, the resulting rules default to having all live cells die and the rules are implemented as exceptions to this. However, for some rules, it may be possible to have a smaller set of transitions if all cells are assumed to come to life unless otherwise specified. Furthermore, for some rules, the smallest ruleset might be result from having differing default behaviors for live and dead cells.

While playing around with various rules, I grew decided to write a Golly script to fake a hexagonal display. It works by grabbing the cells from the current layer and expanding them into two by two blocks in a second layer. By offsetting the rows, the result looks approximately hexagonal. For example, something like this:

`oo.o.o.oo`

Becomes this:

`.oooo..oooo.oo..oooo..oo.oooo..oooo.`

With a little extra programming, I added functionality to step and run patterns while displaying the results like this. The speed isn't the greatest when the number of cells gets large, but it's fairly functional.

With a four state dummy rule and some properly designed icons, this could be made to look even better, but I'm not sure that it would work very well for interactive use. As Golly doesn't allow each layer to have it's own rules, the script would have to switch rules every update, which I'm sure would be abysmally slow. However, I plan on making a non-interactive version, for grabbing screen shots and whatnot.

It would probably be possible to work around this by adding four dummy states to the hexagonal rule tables, but I'm afraid that that would slow things down. How badly is the speed of the RuleTable algorithm affected by additional states that have no transition rules?

I've also got some ideas for scripts to assist in editing hexagonal patterns. Basically, they'd perform the opposite function of the orthogonal to hexagon transformation that powers the hex-view script.

P.S. The other idea I for implementing hexagonal neighborhoods was to use a multi-state rule in which groups of four cells (laid out in offset rows) coordinate their behavior and act as meta-cells for a hexagonal rule. While possible, the number of states and rules required quickly got rather large. I abandoned this idea, but might produce a proof of concept example at some point.
Attachments
hex-tools-1.0.zip
Karatorian

Posts: 21
Joined: September 18th, 2010, 10:56 am
Location: Rindge, NH, USA

Re: Utility Scripts for Hexagonal Neighborhoods

Karatorian wrote:I was playing around with the Patterson's Worms examples in Golly and started thinking about rules with hexagonal neighborhoods and how to implement them in Golly.

Quite a lot of work on this has already been done -- see http://code.google.com/p/ruletablerepository/wiki/TheRules#Hexagonal_neighborhood.

Tim Hutton (author of the RuleTable algo) has also done some work on suitable icons -- see his blog http://ferkeltongs.livejournal.com/31455.html. He's written some scripts that can create suitable .table and .icons files for hexagonal neighborhood rules. These scripts will be included in Golly 2.2 but if you want to play with them now you could probably just download all the stuff in the Scripts/Python directory from CVS (http://golly.cvs.sourceforge.net/viewvc/golly/golly/src/Scripts/Python/). From memory most of Tim's new stuff should work with Golly 2.1. In particular, see the AbsoluteHexTurmite-gen.py script and the glife module it relies on called EmulateHexagonal.py.

Golly doesn't allow each layer to have it's own rules...

Sure it does. If there is some situation where that isn't working then that's a bug, so let me know. (Cloned layers can't have different rules of course.)

Apologies to all who have been waiting patiently for Golly 2.2. Hopefully I can finish off support for bounded grids by the end of October (it was a lot more complicated than I thought!) and so 2.2 will be out soon after that.

Andrew
Moderator

Posts: 681
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia

Re: Utility Scripts for Hexagonal Neighborhoods

Andrew wrote:
Karatorian wrote:Golly doesn't allow each layer to have it's own rules...

Sure it does. If there is some situation where that isn't working then that's a bug, so let me know. (Cloned layers can't have different rules of course.)

Could they in 1.4? I recently upgraded and am not quite sure where I got that mistaken impression. Anyway, that makes my someof my ideas more viable. Although, I suppose I should look into what's already been done first.
Karatorian

Posts: 21
Joined: September 18th, 2010, 10:56 am
Location: Rindge, NH, USA

PreviousNext