Golly scripts

For scripts to aid with computation or simulation in cellular automata.
Post Reply
User avatar
dvgrn
Moderator
Posts: 10671
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » February 24th, 2015, 3:16 pm

Jackk wrote:Hi, I've installed Python and entered the location of the .dll file into Golly. However, when I try to start a script, it brings up the error message "The Python library does not have this symbol: Py_InitModule4".
While you're waiting for someone who knows what they're talking about to show up, here are a few more details worth adding to the trouble ticket:

Do you have 32- or 64-bit Golly? What version number?

Did you install 32- or 64-bit Python? What version number?

Based on a Google search on the error message, which turned up a thread related to Golly among other things, my uneducated guess would be that either you've installed Python 3.x where Golly needs 2.x (2.7.9 is the current version), or you have 32-bit Golly with 64-bit Python or vice versa. Just a guess, though --!

Jackk
Posts: 116
Joined: March 13th, 2012, 3:49 pm

Re: Golly scripts

Post by Jackk » February 24th, 2015, 3:21 pm

dvgrn wrote: Did you install 32- or 64-bit Python? What version number?

Based on a Google search on the error message, which turned up a thread related to Golly among other things, my uneducated guess would be that either you've installed Python 3.x where Golly needs 2.x (2.7.9 is the current version), or you have 32-bit Golly with 64-bit Python or vice versa. Just a guess, though --!
Oh, this makes sense. I'd installed Python 3.4.2, which it seems does not work with Golly. Thank you very much for helping me out!

User avatar
biggiemac
Posts: 515
Joined: September 17th, 2014, 12:21 am
Location: California, USA

Re: Golly scripts

Post by biggiemac » February 24th, 2015, 9:25 pm

Jackk wrote:
dvgrn wrote: Did you install 32- or 64-bit Python? What version number?

Based on a Google search on the error message, which turned up a thread related to Golly among other things, my uneducated guess would be that either you've installed Python 3.x where Golly needs 2.x (2.7.9 is the current version), or you have 32-bit Golly with 64-bit Python or vice versa. Just a guess, though --!
Oh, this makes sense. I'd installed Python 3.4.2, which it seems does not work with Golly. Thank you very much for helping me out!
In case any more info can help, I ran into your exact same error message recently, and my best interpretation of the problem was that I had installed 2.x and 3.x separately, and their libraries were stored in the same directory. Uninstalling both and reinstalling only 2.8 (I have no use for 3.x anyway) fixed the problem. 32- vs 64-bit was irrelevant.
Physics: sophistication from simplicity.

Jackk
Posts: 116
Joined: March 13th, 2012, 3:49 pm

Re: Golly scripts

Post by Jackk » February 25th, 2015, 8:50 am

[quote="biggiemac]
In case any more info can help, I ran into your exact same error message recently, and my best interpretation of the problem was that I had installed 2.x and 3.x separately, and their libraries were stored in the same directory. Uninstalling both and reinstalling only 2.8 (I have no use for 3.x anyway) fixed the problem. 32- vs 64-bit was irrelevant.[/quote]

Thanks! I've finally got it working completely fine now. I'm using Python 2.7.9.

User avatar
Scorbie
Posts: 1692
Joined: December 7th, 2013, 1:05 am

Re: Golly scripts

Post by Scorbie » February 26th, 2015, 11:17 am

I posted this short script in Tezcatlipoca's site a while ago. Just posting here for completeness.

Maybe save rule/save scripts feature would be nice to add in the next version of golly.

rule auto saver
gets the rule file from clipboard and automatically saves the rule in the rules directory

Code: Select all

import golly as g
import os

rulestring = g.getclipstr()
if not rulestring[:5]=='@RULE':
    g.exit('Invalid rule string.')
rulename = rulestring.splitlines()[0]
rulename = rulename[6:] + '.rule'
rulepath = os.path.join(g.getdir('rules'), rulename)
while os.path.isfile(rulepath):
    newrulename = g.getstring('The file already exists. Choose a different name or leave it to overwrite.', rulename)
    if(newrulename == rulename):
        break
    rulepath = os.path.join(g.getdir('rules'), rulename)
with open(rulepath, 'w') as rulefile:
    rulefile.write(rulestring)
g.show('file successfully saved at ' + rulepath)

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Golly scripts

Post by flipper77 » February 26th, 2015, 11:52 am

Scorbie wrote:I posted this short script in Tezcatlipoca's site a while ago. Just posting here for completeness.

Maybe save rule/save scripts feature would be nice to add in the next version of golly.

rule auto saver
gets the rule file from clipboard and automatically saves the rule in the rules directory

Code: Select all

import golly as g
import os

rulestring = g.getclipstr()
if not rulestring[:5]=='@RULE':
    g.exit('Invalid rule string.')
rulename = rulestring.splitlines()[0]
rulename = rulename[6:] + '.rule'
rulepath = os.path.join(g.getdir('rules'), rulename)
while os.path.isfile(rulepath):
    newrulename = g.getstring('The file already exists. Choose a different name or leave it to overwrite.', rulename)
    if(newrulename == rulename):
        break
    rulepath = os.path.join(g.getdir('rules'), rulename)
with open(rulepath, 'w') as rulefile:
    rulefile.write(rulestring)
g.show('file successfully saved at ' + rulepath)
You don't have to use a script just to do that, unless there's some advantage over the method I say next. You can just copy the rule in proper .rule format from somewhere on this forum, then go to Golly and paste as if you're pasting a pattern, and Golly does the work for you.

User avatar
Scorbie
Posts: 1692
Joined: December 7th, 2013, 1:05 am

Re: Golly scripts

Post by Scorbie » February 26th, 2015, 6:21 pm

flipper77 wrote:You can just copy the rule in proper .rule format from somewhere on this forum, then go to Golly and paste as if you're pasting a pattern, and Golly does the work for you.
Huh! Why didn't I know that! Thanks! Maybe a script-autosave script would be helpful? Or is there such a feature?

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Golly scripts

Post by flipper77 » February 26th, 2015, 10:35 pm

Scorbie wrote:
flipper77 wrote:You can just copy the rule in proper .rule format from somewhere on this forum, then go to Golly and paste as if you're pasting a pattern, and Golly does the work for you.
Huh! Why didn't I know that! Thanks! Maybe a script-autosave script would be helpful? Or is there such a feature?
Not that I know of, but it shouldn't be too hard to write such a script since you directly take the clipboard, write it to a filename specified by possibly g.savedialog(), and you're done.

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Golly scripts

Post by flipper77 » March 1st, 2015, 1:17 am

After Adam made a change to soups so that they also include the rule, I had to make one small change to the script, but since the script assumes the current rule the pattern is in is the rule to search through, it doesn't make much of a difference, so here's the revised script (EDIT: changed script to fix some problems):

Code: Select all

# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a 
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.

import golly as g
from glife import rect
import hashlib
import urllib2
import math

spacing = 250
maxsoups = 16
max_finite_period = 500

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                g.exit("Object encoding failed.")
            return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

def get_soups(symm):
    apgcode = "/" + encode(max_finite_period)
    broken = ['25%', '75%']
    skip = False
    rule = "/" + grule()
    req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
    response = urllib2.urlopen(req)
    page = response.read().splitlines()
    for line in page:
        if line[:13] == "<p>There are ":
            sentence = line.split()
            if "no" in sentence:
                return (0, 0)
            else:
                soups = []
                start = page.index(line) + 1
                numsoups = int(sentence[2])
                if symm == "All":
                    for sample in page[start:start+numsoups]:
                        for item in broken:
                            if item in line.split('/'):
                                skip = True
                                break

                        if (skip == True):
                            skip = False
                            continue

                        if len(soups) < maxsoups:
                            soups.append(sample.split('"')[3].replace(' ', '%20'))
                    return (numsoups, soups)
                for sample in page[start:start+numsoups]:
                    parts = sample.split("/")
                    if parts[2] != symm:
                        numsoups -= 1
                    elif len(soups) < maxsoups:
                        soups.append(sample.split('"')[3])
                return (numsoups, soups)

def generate_soups(souplist):
    main = "http://catagolue.appspot.com"
    total = len(souplist)
    width = int(math.ceil(math.sqrt(total)))
    g.addlayer()
    g.setname("Soups")
    for i in xrange(len(souplist)):
        g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
        req = urllib2.Request(main + souplist[i])
        response = urllib2.urlopen(req)
        page = response.read()
        g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
        g.update()
        g.fit()

def display(symm):
    numsoups, soups = get_soups(symm)
    if symm != "All":
        symm += " "
    else:
       symm = ""
    if numsoups == 1:
        verb = "is"
        mult = ""
    else:
        verb = "are"
        mult = "s"
    if numsoups == 0:
        g.exit("There are no %ssample soups available for this object yet." % symm)
    else:
        generate_soups(soups)
        g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))

def fetch():
    if g.empty():
        g.exit("The universe is empty.")
    algo = g.getalgo()
    if ((algo != "QuickLife") & (algo != "HashLife")):
        g.exit("Please use a Life-like rule.")
    if g.getrule()[1] == '0':
        g.exit("Please use a rule without B0.")
    if g.numlayers() == g.maxlayers():
        g.exit("Please have an extra layer available.")
    symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
    if symm not in ["All", "C1", "C2_1", "C2_2", \
                    "C2_4", "C4_1", "C4_4", "D2_+1", \
                    "D2_+2", "D2_x", "D4_+1", "D4_+2", \
                    "D4_+4", "D4_x1", "D4_x4", "D8_1", "D8_4"\
                    "8x32", "25pct"]:
        g.exit("Please enter a valid symmetry.")
    display(symm)

fetch()
Like before in a previous post, just draw the pattern you want to search for, run the script, enter a symmetry to search through (or "All" to scan all the different symmetries for that rule), and the script places down the soups in another layer with a max of 16 soups to place down and a spacing of 250 between soups, both of which can be changed at the top of the script. It even works for linear-growth patterns, that is whatever linearlyse() can work on, so can the script, because it borrows some code from apgsearch.
Last edited by flipper77 on March 15th, 2015, 4:45 pm, edited 1 time in total.

User avatar
Gustavo6046
Posts: 647
Joined: December 7th, 2013, 6:26 pm
Location: Brazil.

Re: Golly scripts

Post by Gustavo6046 » March 7th, 2015, 8:16 pm

I need some catalysts to supress a reaction made by two gliders that normally result in a bugmeister (extremely common assortment of debris, in varied shapes, that resembles bugs, caused by all type of symetric reaction like two gliders, blocks with blinkers etc.)
Also how to run .cpp files?
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » March 15th, 2015, 4:11 pm

flipper77 wrote:After Adam made a change to soups so that they also include the rule, I had to make one small change to the script, but since the script assumes the current rule the pattern is in is the rule to search through, it doesn't make much of a difference, so here's the revised script:

Code: Select all

# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a 
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.

import golly as g
from glife import rect
import hashlib
import urllib2
import math

spacing = 250
maxsoups = 16
max_finite_period = 500

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                g.exit("Object encoding failed.")
            return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

def get_soups(symm):
    apgcode = "/" + encode(max_finite_period)
    rule = "/" + grule()
    req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
    response = urllib2.urlopen(req)
    page = response.read().splitlines()
    for line in page:
        if line[:13] == "<p>There are ":
            sentence = line.split()
            if "no" in sentence:
                return (0, 0)
            else:
                soups = []
                start = page.index(line) + 1
                numsoups = int(sentence[2])
                if symm == "All":
                    for sample in page[start:start+numsoups]:
                        if len(soups) < maxsoups:
                            soups.append(sample.split('"')[1])
                    return (numsoups, soups)
                for sample in page[start:start+numsoups]:
                    parts = sample.split("/")
                    if parts[2] != symm:
                        numsoups -= 1
                    elif len(soups) < maxsoups:
                        soups.append(sample.split('"')[1])
                return (numsoups, soups)

def generate_soups(souplist):
    main = "http://catagolue.appspot.com"
    width = int(math.ceil(math.sqrt(len(souplist))))
    total = len(souplist)
    g.addlayer()
    g.setname("Soups")
    for i in xrange(len(souplist)):
        g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
        req = urllib2.Request(main + souplist[i])
        response = urllib2.urlopen(req)
        page = response.read()
        g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
        g.update()
        g.fit()

def display(symm):
    numsoups, soups = get_soups(symm)
    if symm != "All":
        symm += " "
    else:
       symm = ""
    if numsoups == 1:
        verb = "is"
        mult = ""
    else:
        verb = "are"
        mult = "s"
    if numsoups == 0:
        g.exit("There are no %ssample soups available for this object yet." % symm)
    else:
        generate_soups(soups)
        g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))

def fetch():
    if g.empty():
        g.exit("The universe is empty.")
    algo = g.getalgo()
    if ((algo != "QuickLife") & (algo != "HashLife")):
        g.exit("Please use a Life-like rule.")
    if g.getrule()[1] == '0':
        g.exit("Please use a rule without B0.")
    if g.numlayers() == g.maxlayers():
        g.exit("Please have an extra layer available.")
    symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
    if symm not in ["All", "C1", "C2_1", "C2_2", \
                    "C2_4", "C4_1", "C4_4", "D2_+1", \
                    "D2_+2", "D2_x", "D4_+1", "D4_+2", \
                    "D4_+4", "D4_x1", "D4_x4", "D8_1", "D8_4"]:
        g.exit("Please enter a valid symmetry.")
    display(symm)

fetch()
I tried running it and got this error:

Code: Select all

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/eric/golly/Scripts/find-object.py", line 302, in <module>
    fetch()
  File "/home/eric/golly/Scripts/find-object.py", line 300, in fetch
    display(symm)
  File "/home/eric/golly/Scripts/find-object.py", line 281, in display
    generate_soups(soups)
  File "/home/eric/golly/Scripts/find-object.py", line 260, in generate_soups
    response = urllib2.urlopen(req)
  File "/usr/lib/python2.7/urllib2.py", line 127, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 404, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 422, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1199, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1138, in do_open
    h = http_class(host, timeout=req.timeout) # will parse host:port
  File "/usr/lib/python2.7/httplib.py", line 712, in __init__
    (self.host, self.port) = self._get_hostport(host, port)
  File "/usr/lib/python2.7/httplib.py", line 754, in _get_hostport
    raise InvalidURL("nonnumeric port: '%s'" % host[i+1:])
httplib.InvalidURL: nonnumeric port: 'black'

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Golly scripts

Post by flipper77 » March 15th, 2015, 4:49 pm

It would be more beneficial for me to know what what object you were searching for, as well as the symmetry, so I could tell if I found the problem or not. Anyways, I still fixed other problems in the script in the previous post, so the script shouldn't have problems, especially with some of the censuses that went haywire such as "D4 +4" without the underscore and "25%" and "75%" censuses not having anything recorded as far as I can tell.

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » March 15th, 2015, 5:00 pm

flipper77 wrote:It would be more beneficial for me to know what what object you were searching for, as well as the symmetry, so I could tell if I found the problem or not. Anyways, I still fixed other problems in the script in the previous post, so the script shouldn't have problems, especially with some of the censuses that went haywire such as "D4 +4" without the underscore and "25%" and "75%" censuses not having anything recorded as far as I can tell.
Well, every object I search for either turns up no soups (such as objects to common to have sample soups stored for them) or gives an error (seems to be the same error each time), but it was this object that I reported it on:

Code: Select all

#C [[ THUMBNAIL VIEWONLY ZOOM 20 ]]
x = 9, y = 9, rule = B3/S23
3bo$2bobo$2bo2bo$3b2obo$5bobo$b3obo2bo$o2bo2b2o$o2bo$b2o!

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Golly scripts

Post by flipper77 » March 15th, 2015, 5:31 pm

gameoflifeboy wrote: Well, every object I search for either turns up no soups (such as objects to common to have sample soups stored for them) or gives an error (seems to be the same error each time), but it was this object that I reported it on: ...
I ran the new script on that object in standard life scanning all symmetry types, and changed maxsoups to 49 so I can get all of them, and didn't get a single problem. I don't know what the problem could be, but could others test out the script to see if it works for them as well? I have a Windows 7 64-bit computer with Golly 2.6 and Python 2.7.9 installed, maybe you have a different version of Python that doesn't work well with this script, but I have no idea what could be causing the error.

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » March 20th, 2015, 9:08 pm

I have tried the script on both an Ubuntu 14.10 and Windows Vista (?) computer and both times gave an error. I think I have Python 2.7.8 on both of them, at least on the Ubuntu computer.

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » March 22nd, 2015, 10:41 pm

Now I know the problem. The program was trying to read

Code: Select all

<a href="/hashsoup/C1/uUbUqDjWs3jz396091/b3s23" style="color:black">
while the HTML of the page was formatted like

Code: Select all

<a style="color:black" href="/hashsoup/C1/uUbUqDjWs3jz396091/b3s23">
Here is the fixed script, at least for my particular instance. If the original script worked for you, your HTML reader might have alphabetized the attributes href and style, so this might not work for you.

Code: Select all

# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.

import golly as g
from glife import rect
import hashlib
import urllib2
import math

spacing = 250
maxsoups = 16
max_finite_period = 500

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                g.exit("Object encoding failed.")
            return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

def get_soups(symm):
    apgcode = "/" + encode(max_finite_period)
    rule = "/" + grule()
    req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
    response = urllib2.urlopen(req)
    page = response.read().splitlines()
    for line in page:
        if line[:13] == "<p>There are ":
            sentence = line.split()
            if "no" in sentence:
                return (0, 0)
            else:
                soups = []
                start = page.index(line) + 1
                numsoups = int(sentence[2])
                if symm == "All":
                    for sample in page[start:start+numsoups]:
                        if len(soups) < maxsoups:
                            soups.append(sample.split('"')[3])
                    return (numsoups, soups)
                for sample in page[start:start+numsoups]:
                    parts = sample.split("/")
                    if parts[2] != symm:
                        numsoups -= 1
                    elif len(soups) < maxsoups:
                        soups.append(sample.split('"')[3])
                return (numsoups, soups)

def generate_soups(souplist):
    main = "http://catagolue.appspot.com"
    width = int(math.ceil(math.sqrt(len(souplist))))
    total = len(souplist)
    g.addlayer()
    g.setname("Soups")
    for i in xrange(len(souplist)):
        g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
        req = urllib2.Request(main + souplist[i])
        response = urllib2.urlopen(req)
        page = response.read()
        g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
        g.update()
        g.fit()

def display(symm):
    numsoups, soups = get_soups(symm)
    if symm != "All":
        symm += " "
    else:
       symm = ""
    if numsoups == 1:
        verb = "is"
        mult = ""
    else:
        verb = "are"
        mult = "s"
    if numsoups == 0:
        g.exit("There are no %ssample soups available for this object yet." % symm)
    else:
        generate_soups(soups)
        g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))

def fetch():
    if g.empty():
        g.exit("The universe is empty.")
    algo = g.getalgo()
    if ((algo != "QuickLife") & (algo != "HashLife")):
        g.exit("Please use a Life-like rule.")
    if g.getrule()[1] == '0':
        g.exit("Please use a rule without B0.")
    if g.numlayers() == g.maxlayers():
        g.exit("Please have an extra layer available.")
    symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
    if symm not in ["All", "C1", "C2_1", "C2_2", \
                    "C2_4", "C4_1", "C4_4", "D2_+1", \
                    "D2_+2", "D2_x", "D4_+1", "D4_+2", \
                    "D4_+4", "D4_x1", "D4_x4", "D8_1", "D8_4"]:
        g.exit("Please enter a valid symmetry.")
    display(symm)

fetch()
EDIT: Here is my hacked script, which lets the user choose the number of soups to show:

Code: Select all

# find-object.py
# This script goes to http://catagolue.appspot.com
# and tells you if there are sample soups for the
# object in the universe under the
# current rule and specified symmetry.
#
# If there are any soups, it fetches them
# from the site, and places them down in a
# seperate layer.
#
# There are special variables before the function
# definitions for modifying if necessary.

import golly as g
from glife import rect
import hashlib
import urllib2
import math

spacing = 2048
max_finite_period = 500

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                g.exit("Object encoding failed.")
            return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

def get_soups(symm):
    apgcode = "/" + encode(max_finite_period)
    rule = "/" + grule()
    req = urllib2.Request('http://catagolue.appspot.com/object' + apgcode + rule)
    response = urllib2.urlopen(req)
    page = response.read().splitlines()
    for line in page:
        if line[:13] == "<p>There are ":
            sentence = line.split()
            if "no" in sentence:
                return (0, 0)
            else:
		maxsoupstring = g.getstring("How many soups do you want to show?", "1")
                maxsoups = int(maxsoupstring)
                if maxsoups < 0:
                    return (0, 0)
                soups = []
                start = page.index(line) + 1
                numsoups = int(sentence[2])
                if symm == "All":
                    for sample in page[start:start+numsoups]:
                        if len(soups) < maxsoups:
                            soups.append(sample.split('"')[3])
                    return (numsoups, soups)
                for sample in page[start:start+numsoups]:
                    parts = sample.split("/")
                    if parts[2] != symm:
                        numsoups -= 1
                    elif len(soups) < maxsoups:
                        soups.append(sample.split('"')[3])
                return (numsoups, soups)

def generate_soups(souplist):
    main = "http://catagolue.appspot.com"
    width = int(math.ceil(math.sqrt(len(souplist))))
    total = len(souplist)
    g.addlayer()
    g.setname("Soups")
    for i in xrange(len(souplist)):
        g.show("Placing down soups: "+str(100*i/total)+"%" + " complete...")
        req = urllib2.Request(main + souplist[i])
        response = urllib2.urlopen(req)
        page = response.read()
        g.putcells(g.parse(page.split("\n", 1)[-1], (i%width)*spacing, int(i/width)*spacing))
        g.update()
        g.fit()

def display(symm):
    numsoups, soups = get_soups(symm)
    if symm != "All":
        symm += " "
    else:
       symm = ""
    if numsoups == 1:
        verb = "is"
        mult = ""
    else:
        verb = "are"
        mult = "s"
    if numsoups == 0:
        g.exit("There are no %ssample soups available for this object." % symm)
    else:
        generate_soups(soups)
        g.show("There %s %d %ssample soup%s available." % (verb, numsoups, symm, mult))

def fetch():
    if g.empty():
        g.exit("The universe is empty.")
    algo = g.getalgo()
    if ((algo != "QuickLife") & (algo != "HashLife")):
        g.exit("Please use a Life-like rule.")
    if g.getrule()[1] == '0':
        g.exit("Please use a rule without B0.")
    if g.numlayers() == g.maxlayers():
        g.exit("Please have an extra layer available.")
    symm = g.getstring('What symmetry do you want to check?\n(Enter "All" for all sample occurences)', "C1")
    if symm not in ["All", "C1", "C2_1", "C2_2", \
                    "C2_4", "C4_1", "C4_4", "D2_+1", \
                    "D2_+2", "D2_x", "D4_+1", "D4_+2", \
                    "D4_+4", "D4_x1", "D4_x4", "D8_1", "D8_4"]:
        g.exit("Please enter a valid symmetry.")
    display(symm)

fetch()

User avatar
Kazyan
Posts: 1247
Joined: February 6th, 2014, 11:02 pm

Re: Golly scripts

Post by Kazyan » March 31st, 2015, 6:09 pm

I would like to request a script. I do not know how to write Python, myself, so I can't write this script without putting a ton of brainpower into something relatively simple. The desired script would enumerate the evolution of every possible state of the universe within a 5x5 bounding box, and record the top 1000-or-so lifespans of the resultant ash, along with the micro-soups that produced them. Basically, I'm going to set my CPU to solve the question of methuselahs within a 5x5 box, if I can get a script for that. I imagine that some code from apgsearch could be adapted for this?

There's 33.5 million soups to search, using brute force, but it can be cut down to 21 million rather simply. If the upper-left hand corner cell is OFF, enumerate everything. If it's ON, the other three corners also have to be ON; otherwise you can rotate and reflect to get something already checked. Something similar can be done for other sets of four cells that have four-fold symmetry around the central square; that would cut things down fairly quickly...
Tanner Jacobi
Coldlander, a novel, available in paperback and as an ebook. Now on Amazon.

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

Re: Golly scripts

Post by dvgrn » March 31st, 2015, 10:26 pm

Kazyan wrote:The desired script would enumerate the evolution of every possible state of the universe within a 5x5 bounding box, and record the top 1000-or-so lifespans of the resultant ash, along with the micro-soups that produced them.... I imagine that some code from apgsearch could be adapted for this?
Actually there are much quicker ways than Python scripts of doing this kind of exhaustive enumeration. Paul Callahan did a complete survey of 5x5 patterns in 1997, looking for infinite-growth patterns but also collecting methuselahs. The longest-lived 5x5s were all 'rabbits' ancestors.

In 1997, optimized C/C++ search code could apparently do this kind of search in a day or so. I don't really know what the time requirement would be on a modern CPU, but it should be pretty quick -- the great majority of 5x5 patterns will fade out or settle to ash pretty much immediately.

If an exhaustive 5x5 search takes an hour or two nowadays, then an exhaustive 6x6 search should now be within reach, at least with a multi-CPU distributed search. Luckily this kind of enumeration is absolutely trivial to divide up between processors. 7x7 would need 2^13=8K times as many processors as 6x6, so a good high-performance cluster could handle the job eventually, I suppose.

wildmyron
Posts: 1544
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » April 2nd, 2015, 12:29 am

dvgrn wrote:
Kazyan wrote:The desired script would enumerate the evolution of every possible state of the universe within a 5x5 bounding box, and record the top 1000-or-so lifespans of the resultant ash, along with the micro-soups that produced them.... I imagine that some code from apgsearch could be adapted for this?
Actually there are much quicker ways than Python scripts of doing this kind of exhaustive enumeration. Paul Callahan did a complete survey of 5x5 patterns in 1997, looking for infinite-growth patterns but also collecting methuselahs. The longest-lived 5x5s were all 'rabbits' ancestors.
I guess gsearch would be a good starting point for this kind of task, although a number of changes would be required to adapt it to exhaustive methuselah searching. There may be other programs available too, but this is the only one I'm aware of with source code available.
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » May 13th, 2015, 11:32 pm

I just made a script that gives a Catagolue object page for the displayed pattern.

Code: Select all

# Goes to catagolue and finds an object page for the shown pattern

import golly as g
import hashlib
from glife import rect

max_finite_period = 500

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                g.exit("Object encoding failed.")
            return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

def return_url(maxperiod):
    apgcode = encode(maxperiod)
    rule = grule()
    return "http://catagolue.appspot.com/object/" + apgcode + "/" + rule

def display_link():
    url = return_url(max_finite_period)
    htmlfile = g.getdir('temp') + 'object-page.html'
    f = open(htmlfile, 'w')
    f.write('<html>\n<title>Catagolue page for object</title>\n')
    f.write('<body><a href="' + url + '">' + url + '</a></body>\n')
    f.write('</html>')
    f.close()
    g.show('All done!')
    g.open(htmlfile)

display_link()

User avatar
gameoflifeboy
Posts: 474
Joined: January 15th, 2015, 2:08 am

Re: Golly scripts

Post by gameoflifeboy » May 18th, 2015, 10:00 pm

I made a script that tells how many times an object occurred naturally, as well as its frequency.

Code: Select all

# Tells how many times an object occurred naturally, as well as its frequency.
# Works for linear and chaotic growth patterns.

import golly as g
from glife import rect
import hashlib
import urllib2
import re
import math

max_finite_period = 500

def grule():
    return g.getrule().split(":")[0].replace('/', '').replace('B', 'b').replace('S', 's')

textcensus_prefix = 'http://catagolue.appspot.com/textcensus/'
census_prefix = 'http://catagolue.appspot.com/census/'
rule_sym = grule() + '/C1'

def canonise(duration):

    representation = "#"

    # We need to compare each phase to find the one with the smallest
    # description:
    for t in xrange(duration):

        r = rect(g.getrect())
        if r.empty:
            return "0"

        if ((r.wd <= 40) & (r.ht <= 40)):
            # Fits within a 40-by-40 bounding box, so eligible to be canonised.
            # Choose the orientation which results in the smallest description:
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y, 1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y, -1, 0, 0, 1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x, r.y+r.ht-1, 1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.wd, r.ht, r.x+r.wd-1, r.y+r.ht-1, -1, 0, 0, -1))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y, 0, 1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y, 0, -1, 1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x, r.y+r.ht-1, 0, 1, -1, 0))
            representation = compare_representations(representation, canonise_orientation(r.ht, r.wd, r.x+r.wd-1, r.y+r.ht-1, 0, -1, -1, 0))

        g.run(1)

    g.setgen('0')

    return representation

# A subroutine used by canonise:
def canonise_orientation(length, breadth, ox, oy, a, b, c, d):

    representation = ""

    chars = "0123456789abcdefghijklmnopqrstuvwxyz"

    for v in xrange(int((breadth-1)/5)+1):
        zeroes = 0
        if (v != 0):
            representation += "z"
        for u in xrange(length):
            baudot = 0
            for w in xrange(5):
                x = ox + a*u + b*(5*v + w)
                y = oy + c*u + d*(5*v + w)
                baudot = (baudot >> 1) + 16*g.getcell(x, y)
            if (baudot == 0):
                zeroes += 1
            else:
                if (zeroes > 0):
                    if (zeroes == 1):
                        representation += "0"
                    elif (zeroes == 2):
                        representation += "w"
                    elif (zeroes == 3):
                        representation += "x"
                    else:
                        representation += "y"
                        representation += chars[zeroes - 4]
                zeroes = 0
                representation += chars[baudot]
    return representation

# Compares strings first by length, then by lexicographical ordering.
# A hash character is worse than anything else.
def compare_representations(a, b):

    if (a == "#"):
        return b
    elif (b == "#"):
        return a
    elif (len(a) < len(b)):
        return a
    elif (len(b) < len(a)):
        return b
    elif (a < b):
        return a
    else:
        return b

# Gets the period of an interleaving of degree-d polynomials:
def deepperiod(sequence, maxperiod, degree):

    for p in xrange(1, maxperiod, 1):

        good = True

        for i in xrange(maxperiod):

            diffs = [0] * (degree + 2)
            for j in xrange(degree + 2):

                diffs[j] = sequence[i + j*p]

            # Produce successive differences:
            for j in xrange(degree + 1):
                for k in xrange(degree + 1):
                    diffs[k] = diffs[k] - diffs[k + 1]

            if (diffs[0] != 0):
                good = False
                break

        if (good):
            return p
    return -1

def simple_osc(r, h, p):
    rect = g.getrect()
    hash = g.hash(rect)
    pop = g.getpop()
    if ((r[2] != rect[2]) | (r[3] != rect[3])):
        return False
    if (h != hash):
        return False
    if (p != pop):
        return False

    return True

# Analyses a linear-growth pattern, returning a hash:
def linearlyse(maxperiod):

    poplist = [0]*(3*maxperiod)

    for i in xrange(3*maxperiod):

        g.run(1)
        poplist[i] = int(g.getpop())

    p = deepperiod(poplist, maxperiod, 1)

    if (p == -1):
        return "unidentified"

    difflist = [0]*(2*maxperiod)

    for i in xrange(2*maxperiod):

        difflist[i] = poplist[i + p] - poplist[i]

    q = deepperiod(difflist, maxperiod, 0)

    moments = [0, 0, 0]

    for i in xrange(p):

        moments[0] += (poplist[i + q] - poplist[i])
        moments[1] += (poplist[i + q] - poplist[i]) ** 2
        moments[2] += (poplist[i + q] - poplist[i]) ** 3

    prehash = str(moments[1]) + "#" + str(moments[2])

    # Linear-growth patterns with growth rate zero are clearly errors!
    if (moments[0] == 0):
        return "unidentified"

    return "yl" + str(p) + "_" + str(q) + "_" + str(moments[0]) + "_" + hashlib.md5(prehash).hexdigest()

def encode(maxperiod):
    period = 0
    irect = g.getrect()
    ihash = g.hash(irect)
    ipop = g.getpop()
    ipatt = g.getcells(irect)

    oscillating = False
    while not oscillating:
        if (period == maxperiod):
            g.setgen('0')
            repr = linearlyse(1500)
            if repr == "unidentified":
                return repr
        g.run(1)
        period += 1
        if (simple_osc(irect, ihash, ipop)):
            oscillating = True
            crect = g.getrect()
            cpatt = g.getcells(crect)
            for i in xrange(0, int(ipop), 2):
                if ((ipatt[i] - irect[0] == cpatt[i] - crect[0]) \
                  & (ipatt[i+1] - irect[1] == cpatt[i+1] - crect[1])):
                    continue
                oscillating = False
                break

    if ((irect[0] == crect[0]) & (irect[1] == crect[1])):
        if (period == 1):
            return "xs" + str(ipop) + '_' + canonise(period)
        else:
            return "xp" + str(period) + '_' + canonise(period)
    else:
        return "xq" + str(period) + '_' + canonise(period)

def regress(pairlist):

    cumx = 0.0
    cumy = 0.0
    cumvar = 0.0
    cumcov = 0.0

    for x,y in pairlist:

        cumx += x
        cumy += y

    cumx = cumx / len(pairlist)
    cumy = cumy / len(pairlist)

    for x,y in pairlist:

        cumvar += (x - cumx)*(x - cumx)
        cumcov += (x - cumx)*(y - cumy)

    return (cumcov / cumvar)

# Analyses a pattern whose average population follows a power-law:
def powerlyse(stepsize, numsteps):

    g.setalgo("HashLife")
    g.setbase(2)
    g.setstep(stepsize)

    poplist = [0]*numsteps

    poplist[0] = int(g.getpop())

    pointlist = []

    for i in xrange(1, numsteps, 1):

        g.step()
        poplist[i] = int(g.getpop()) + poplist[i-1]

        if (i % 50 == 0):

            g.fit()
            g.update()

        if (i > numsteps/2):

            pointlist.append((math.log(i),math.log(poplist[i]+1.0)))

    power = regress(pointlist)

    if (power < 1.10):
        return "unidentified"
    elif (power < 1.65):
        return "zz_REPLICATOR"
    elif (power < 2.05):
        return "zz_LINEAR"
    elif (power < 2.8):
        return "zz_EXPLOSIVE"
    else:
        return "zz_QUADRATIC"

def num_occurs():
    apgcode = encode(max_finite_period)
    if apgcode == "unidentified":
        apgcode = powerlyse(8, 1500)
    if apgcode == "unidentified":
        g.exit("The object could not be identified.")
    page = textcensus_prefix + rule_sym
    url = urllib2.urlopen(page)
    obj_data = url.readlines()
    occurs = 0
    total = 0
    for line in obj_data:
        obj_name, num = map(lambda arg: arg.replace('"', ''), line.split(','))
        if obj_name == 'apgcode':
            continue
        current_occurs = int(num)
        if obj_name == apgcode:
            occurs = current_occurs
        total += current_occurs
    return occurs, total

def main():
    occurs, total = num_occurs()
    if (occurs == 0):
        g.show("This object occurred 0 times.")
    else:
        invfreq = total / occurs
        g.show("This object occurred %d times (1 in %d objects)." % (occurs, invfreq))

main()
EDIT: Added hashlib. I can't believe I never tried this on linearly growing objects when I was testing it.

c0b0p0
Posts: 645
Joined: February 26th, 2014, 4:48 pm

Re: Golly scripts

Post by c0b0p0 » May 20th, 2015, 4:13 pm

Here is a script that randomly creates isotropic, hexagonal CAs and checks whether they are explosive. When it finds one that is not explosive that has a natural glider, puffer, or wickstretcher, the script sets the rule in Golly to the rule it found and stops running.

Code: Select all

import random as r
import golly as g
g.new("untitled")
def inear(k,l):
    j = k[0:len(k):l]
    p = j[1] - j[0]
    if p == 0:
        return False
    for m in range(1,len(j)):
        if (j[m] - j[m - 1]) != p:
            return False
    return True
def gli():
    g.new("untitled")
    g.select([0,0,3000,30])
    g.randfill(20)
    kde = True
    kdes = []
    for i in range(500):
        g.run(1)
        if g.getrect() == []:
            kde = False
            break
        kdes.append(g.getrect()[3])
    kdes = kdes[250:500]
    for i in range(1,50):
        if inear(kdes,i):
            break
        if i == 49:
            kde = False
    return kde
def explo():
    g.new("untitled")
    g.select([-50,-50,100,100])
    g.randfill(20)
    g.run(500)
    g.clear(1)
    g.run(500)
    if len(g.getrect())==4:
       return g.getrect()[2]*g.getrect()[3]>=22500
    return False
def boo(d):
    return d >= 0.6
def rulename(bool):
    qqq = "";
    for i in range(len(bool)):
        qqq += str(bool[i] * bool[i])
    return qqq
possiblerules = ["0o","1o","2o","2m","2p","3o","3m","3p","4o","4m","4p","5o","6o"]
bstring = [0 for v in range(13)]
sstring = [0 for v in range(13)]
closing = "@COLORS\r1   0 255   0"
possibletransitions = ["0,0,0,0,0,0", "1,0,0,0,0,0", "1,1,0,0,0,0", "1,0,1,0,0,0", "1,0,0,1,0,0", "1,1,1,0,0,0", "1,1,0,1,0,0", "1,0,1,0,1,0", "1,1,1,1,0,0", "1,1,1,0,1,0", "1,1,0,1,1,0", "1,1,1,1,1,0", "1,1,1,1,1,1"]
bstart = "0,"
bend = ",1"
dstart = "1,"
dend = ",0"
intro = "@TABLE\rn_states:2\rneighborhood:hexagonal\rsymmetries:rotate6reflect"
intintro = "@RULE"
ruletable = ""
transitiontable = ""
btransitions = ""
stransitions = ""
rulestring = "qqq"
for i in range(13):
     bstring[i] = boo(r.random())
for i in range(13):
    sstring[i] = not boo(r.random())
bstring[0] = 0
bstring[1] = 0
bstring[2] = 1
rulestring = rulename(bstring) + rulename(sstring)
intintro = intintro + " " + rulestring
intro = intintro + "\r" + intro
for i in range(len(bstring)):
    if bstring[i]:
        btransitions = btransitions + "\r" + bstart + possibletransitions[i] + bend
for i in range(len(sstring)):
    if sstring[i]:
        stransitions = stransitions + "\r" + dstart + possibletransitions[i] + dend     
transitiontable = stransitions + "\r" + btransitions
ruletable = intro + "\r" + transitiontable
ruletable = ruletable + "\r" + closing
f=open(g.getdir("rules") + rulestring + ".rule", "w")
for i in range(len(ruletable)):
    f.write(ruletable[i])
f.close()
g.setrule(rulestring)
while (explo() or (not gli())) == True:
    intro = "@TABLE\rn_states:2\rneighborhood:hexagonal\rsymmetries:rotate6reflect"
    intintro = "@RULE"
    ruletable = ""
    transitiontable = ""
    btransitions = ""
    stransitions = ""
    rulestring = "qqq"
    for i in range(13):
        bstring[i] = boo(r.random())
    for i in range(13):
        sstring[i] = not boo(r.random())
    bstring[0] = 0
    bstring[1] = 0
    bstring[2] = 1
    rulestring = rulename(bstring) + rulename(sstring)
    intintro = intintro + " " + rulestring
    intro = intintro + "\r" + intro
    for i in range(len(bstring)):
        if bstring[i]:
            btransitions = btransitions + "\r" + bstart + possibletransitions[i] + bend
    for i in range(len(sstring)):
        if sstring[i]:
            stransitions = stransitions + "\r" + dstart + possibletransitions[i] + dend     
    transitiontable = stransitions + "\r" + btransitions
    ruletable = intro + "\r" + transitiontable
    ruletable = ruletable + "\r" + closing
    f=open(g.getdir("rules") + rulestring + ".rule", "w")
    for i in range(len(ruletable)):
        f.write(ruletable[i])
    f.close()
    g.setrule(rulestring)



User avatar
Alexey_Nigin
Posts: 326
Joined: August 4th, 2014, 12:33 pm
Location: Ann Arbor, MI
Contact:

Re: Golly scripts

Post by Alexey_Nigin » May 21st, 2015, 4:13 am

c0b0p0 wrote:Here is a script that randomly creates isotropic, hexagonal CAs and checks whether they are explosive. When it finds one that is not explosive that has a natural glider, puffer, or wickstretcher, the script sets the rule in Golly to the rule it found and stops running.
It tests about 100 rules per second and doesn't bother to delete uninteresting rule tables. As a result, my rules folder is now full of <some binary number>.rule files. Could you change it?
There are 10 types of people in the world: those who understand binary and those who don't.

c0b0p0
Posts: 645
Joined: February 26th, 2014, 4:48 pm

Re: Golly scripts

Post by c0b0p0 » May 27th, 2015, 3:00 pm

I fixed a fatal bug with rules in which random soups left no ash, and I made the script delete uninteresting rule files. The result is below.

Code: Select all

import random as r
import os
import golly as g
g.new("untitled")
def inear(k,l):
    j = k[0:len(k):l]
    p = j[1] - j[0]
    if p == 0:
        return False
    for m in range(1,len(j)):
        if (j[m] - j[m - 1]) != p:
            return False
    return True
def gli():
    g.new("untitled")
    g.select([0,0,3000,30])
    g.randfill(20)
    kde = True
    kdes = []
    for i in range(500):
        g.run(1)
        if g.getrect() == []:
            return False
            break
        kdes.append(g.getrect()[3])
    kdes = kdes[250:500]
    for i in range(1,50):
        if inear(kdes,i):
            break
        if i == 49:
            kde = False
    return kde
def explo():
    g.new("untitled")
    g.select([-50,-50,100,100])
    g.randfill(20)
    g.run(500)
    g.clear(1)
    g.run(500)
    if len(g.getrect())==4:
       return g.getrect()[2]*g.getrect()[3]>=22500
    return False
def boo(d):
    return d >= 0.6
def rulename(bool):
    qqq = "";
    for i in range(len(bool)):
        qqq += str(bool[i] * bool[i])
    return qqq
possiblerules = ["0o","1o","2o","2m","2p","3o","3m","3p","4o","4m","4p","5o","6o"]
bstring = [0 for v in range(13)]
sstring = [0 for v in range(13)]
closing = "@COLORS\r1   0 255   0"
possibletransitions = ["0,0,0,0,0,0", "1,0,0,0,0,0", "1,1,0,0,0,0", "1,0,1,0,0,0", "1,0,0,1,0,0", "1,1,1,0,0,0", "1,1,0,1,0,0", "1,0,1,0,1,0", "1,1,1,1,0,0", "1,1,1,0,1,0", "1,1,0,1,1,0", "1,1,1,1,1,0", "1,1,1,1,1,1"]
bstart = "0,"
bend = ",1"
dstart = "1,"
dend = ",0"
intro = "@TABLE\rn_states:2\rneighborhood:hexagonal\rsymmetries:rotate6reflect"
intintro = "@RULE"
ruletable = ""
transitiontable = ""
btransitions = ""
stransitions = ""
rulestring = "qqq"
for i in range(13):
     bstring[i] = boo(r.random())
for i in range(13):
    sstring[i] = not boo(r.random())
bstring[0] = 0
bstring[1] = 0
bstring[2] = 1
rulestring = rulename(bstring) + rulename(sstring)
intintro = intintro + " " + rulestring
intro = intintro + "\r" + intro
for i in range(len(bstring)):
    if bstring[i]:
        btransitions = btransitions + "\r" + bstart + possibletransitions[i] + bend
for i in range(len(sstring)):
    if sstring[i]:
        stransitions = stransitions + "\r" + dstart + possibletransitions[i] + dend     
transitiontable = stransitions + "\r" + btransitions
ruletable = intro + "\r" + transitiontable
ruletable = ruletable + "\r" + closing
f=open(g.getdir("rules") + rulestring + ".rule", "w")
for i in range(len(ruletable)):
    f.write(ruletable[i])
f.close()
g.setrule(rulestring)
while (explo() or (not gli())) == True:
    os.remove(g.getdir("rules") + rulestring + ".rule")
    intro = "@TABLE\rn_states:2\rneighborhood:hexagonal\rsymmetries:rotate6reflect"
    intintro = "@RULE"
    ruletable = ""
    transitiontable = ""
    btransitions = ""
    stransitions = ""
    rulestring = "qqq"
    for i in range(13):
        bstring[i] = boo(r.random())
    for i in range(13):
        sstring[i] = not boo(r.random())
    bstring[0] = 0
    bstring[1] = 0
    bstring[2] = 1
    rulestring = rulename(bstring) + rulename(sstring)
    intintro = intintro + " " + rulestring
    intro = intintro + "\r" + intro
    for i in range(len(bstring)):
        if bstring[i]:
            btransitions = btransitions + "\r" + bstart + possibletransitions[i] + bend
    for i in range(len(sstring)):
        if sstring[i]:
            stransitions = stransitions + "\r" + dstart + possibletransitions[i] + dend     
    transitiontable = stransitions + "\r" + btransitions
    ruletable = intro + "\r" + transitiontable
    ruletable = ruletable + "\r" + closing
    f=open(g.getdir("rules") + rulestring + ".rule", "w")
    for i in range(len(ruletable)):
        f.write(ruletable[i])
    f.close()
    g.setrule(rulestring)



User avatar
gmc_nxtman
Posts: 1150
Joined: May 26th, 2015, 7:20 pm

Re: Golly scripts

Post by gmc_nxtman » June 2nd, 2015, 11:05 am

I've always wanted a string to table converter. Can someone make a good string to table converter, possibly with support for vonNeumannNeighborhood rules and hexagonalNeighborhood rules? Nothing too fancy, just gives you a box to input your rule string, and gives you a string of text to paste into a .table file.... :roll:

Post Reply