## Golly scripts

For scripts to aid with computation or simulation in cellular automata.
Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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:

Code: Select all

``````# 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 golly
from 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 bm

def 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\$42b2o
4b2ob2o\$35b2o6b2o3b5o\$35b2o10b2o3b2o7\$45b2o5b2o\$45bo6bo\$46b3o4b3o\$48bo
6bo!''')
LMTop = LM + pattern('2b2o\$2bo\$obo\$2o!', 32, 7)
LMBase = pattern('''
11bo\$10bo\$10b3o12\$23b2o\$23bobo\$10bobo13bo\$9bo2bo2b2o6bo2bo12b2o\$8b2o5b
obo8bo11b3o\$2o4b2o3bo3bo3bo3bobo9bob2o15bo\$2o6b2o5b3ob2o2b2o10bo2bo8b
3o4bobo\$9bo2bo3b2o17bob2o16bobo\$10bobo25b3o2b2o2bo7bo2bo3b2o\$39b2o2bo
3bo7bobo4b2o\$44bo2bo6bobo\$26bo17b2o8bo\$24bobo\$25b2o2\$36bo\$36bobo\$36b2o
7\$33bo7bo\$32b4o5bobo\$27b2o2bo2b2o8b2o4b2o\$27b2o2b2o11b2o4b2o\$16b2o6b2o
10bo7b2o\$16b2o5b3o10bo4bobo\$24b2o10bo4bo\$27b2o\$27b2o!''', 14, 29)
LMBase += LM(55, 42, flip)
return LMTop(8 + loopm, -21 - loopm) + LMBase

def 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

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()
``````

Nathaniel
Posts: 617
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

### 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.

Eylrid
Posts: 30
Joined: October 8th, 2009, 10:28 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:

Code: Select all

``````# 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 golly
from 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 bm

def 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\$42b2o
4b2ob2o\$35b2o6b2o3b5o\$35b2o10b2o3b2o7\$45b2o5b2o\$45bo6bo\$46b3o4b3o\$48bo
6bo!''')
LMTop = LM + pattern('2b2o\$2bo\$obo\$2o!', 32, 7)
LMBase = pattern('''
11bo\$10bo\$10b3o12\$23b2o\$23bobo\$10bobo13bo\$9bo2bo2b2o6bo2bo12b2o\$8b2o5b
obo8bo11b3o\$2o4b2o3bo3bo3bo3bobo9bob2o15bo\$2o6b2o5b3ob2o2b2o10bo2bo8b
3o4bobo\$9bo2bo3b2o17bob2o16bobo\$10bobo25b3o2b2o2bo7bo2bo3b2o\$39b2o2bo
3bo7bobo4b2o\$44bo2bo6bobo\$26bo17b2o8bo\$24bobo\$25b2o2\$36bo\$36bobo\$36b2o
7\$33bo7bo\$32b4o5bobo\$27b2o2bo2b2o8b2o4b2o\$27b2o2b2o11b2o4b2o\$16b2o6b2o
10bo7b2o\$16b2o5b3o10bo4bobo\$24b2o10bo4bo\$27b2o\$27b2o!''', 14, 29)
LMBase += LM(55, 42, flip)
return LMTop(8 + loopm, -21 - loopm) + LMBase

def 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

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.)

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

### 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.

Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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.

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

### 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?

Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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()).

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

### 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

Code: Select all

``````# 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 golly
from 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()
``````

Axaj
Posts: 232
Joined: September 26th, 2009, 12:23 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?

Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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

Code: Select all

``````# 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 g
from glife import rect
from time import time

r = rect( g.getrect() )
if r.empty: g.exit("There is no pattern.")

maxstate = g.numstates() - 1
if 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 = 0
total = 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 += 1

if 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))
``````

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

### 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.)

Axaj
Posts: 232
Joined: September 26th, 2009, 12:23 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

Code: Select all

``````# 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 g
from glife import rect
from time import time

r = rect( g.getrect() )
if r.empty: g.exit("There is no pattern.")

maxstate = g.numstates() - 1
if 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 = 0
total = 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 += 1

if 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?

Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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.".

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

### 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!

PM 2Ring
Posts: 152
Joined: March 26th, 2009, 11:18 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

Code: Select all

``````# Golly python script.
# Written by PM 2Ring, November 2009

''' Fill current selection with a random glider field '''

import golly
from glife import *
from random import seed, random, choice

def 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 args

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 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

Code: Select all

``````# Golly Python script
# Display a numbered grid of generations of currently selected pattern
# Written by PM 2Ring, 2010.3.22

import golly
from 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 args

def 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.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()
``````

Awesomeness
Posts: 126
Joined: April 5th, 2009, 7:30 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

Code: Select all

``````#Golly Python script
#Written by PM 2Ring 2009.3.24

''' Advance generation count using binary hashing '''

from glife import getstring, validint
import golly as g

def 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:

Code: Select all

``````#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 g
import os

def 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')
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()``````

PM 2Ring
Posts: 152
Joined: March 26th, 2009, 11:18 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 ).

Awesomeness
Posts: 126
Joined: April 5th, 2009, 7:30 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.

dvgrn
Moderator
Posts: 6737
Joined: May 17th, 2009, 11:00 pm
Contact:

### 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.

Code: Select all

``````import golly as g
r = 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.

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

### Re: Golly scripts

Yay! I've already made my first script!
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.)

Code: Select all

``````################
#### box.py ####
################

# A script by Awesomeness 9.27.10
# conwaylife.com

import golly as g # Use golly
r = 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 line
x = r[0]
y = r[1]
while x <= r[0] + r[2] - 1:
g.setcell(x,y,1)
x = x + 2

# Draw bottom line
x = r[0]
y = r[1] + r[3] - 1
while x <= r[0] + r[2] - 1:
g.setcell(x,y,1)
x = x + 2

# Draw left line
x = r[0]
y = r[1]
while y <= r[1] + r[3] - 1:
g.setcell(x,y,1)
y = y + 2

# Draw right line
x = r[0] + r[2]-1
y = 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.

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

### 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:

Code: Select all

``````..... .....
.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:

Code: Select all

``````oo.
o.o
.oo
``````
Becomes this:

Code: Select all

``````.oooo.
.oooo.
oo..oo
oo..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

Andrew
Moderator
Posts: 771
Joined: June 2nd, 2009, 2:08 am
Location: Melbourne, Australia
Contact:

### 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/ruletablerepos ... ighborhood.

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 ... ts/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.

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

### 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.