Golly scripts

For scripts to aid with computation or simulation in cellular automata.
Post Reply
User avatar
Gustavo6046
Posts: 646
Joined: December 7th, 2013, 6:26 pm
Location: South of Brazil.

Re: Golly scripts

Post by Gustavo6046 » February 5th, 2015, 1:56 pm

Thanks! I finally got Python. But when I try to open python while running some script, Golly just closes itself!
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
Tezcatlipoca
Posts: 81
Joined: September 9th, 2014, 11:40 am

Re: Golly scripts

Post by Tezcatlipoca » February 12th, 2015, 2:27 am

Hi, is there something like a gofast.py that works for arbitrary rules loaded with RuleLoader? This would be very helpful for me.

User avatar
simsim314
Posts: 1766
Joined: February 10th, 2014, 1:27 pm

Re: Golly scripts

Post by simsim314 » February 12th, 2015, 4:52 am

Tezcatlipoca wrote:is there something like a gofast.py that works for arbitrary rules
If you just need fast forward many generations use g.setstep() and g.step(). While g.setstep() defines the step size as power of your base (default 8 ).

If you want to get to some very large specific generation see goto.py (comes with golly).

User avatar
Tezcatlipoca
Posts: 81
Joined: September 9th, 2014, 11:40 am

Re: Golly scripts

Post by Tezcatlipoca » February 12th, 2015, 7:00 am

simsim314 wrote:If you want to get to some very large specific generation see goto.py (comes with golly).
Thank you for the info! Trying this now. "Hit escape to abort..." I am trying to move forward by about 2,000,000 gen. It's a complex pattern--the population is at 550,000 and the rule set has 14 states. I am assuming this is significantly faster than increasing through the generations with the rendered display. Any idea at all about how much?

If this doesn't skip all that is involved in rendering and display--if it's just an end point at maximum speed doing everything. Is there anyway to skip all that stuff, just do the critical processing needed and then display the end point.

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

Re: Golly scripts

Post by dvgrn » February 12th, 2015, 12:41 pm

Tezcatlipoca wrote:I am trying to move forward by about 2,000,000 gen. It's a complex pattern--the population is at 550,000 and the rule set has 14 states. I am assuming this is significantly faster than increasing through the generations with the rendered display. Any idea at all about how much?
If I'm understanding you correctly -- using a script along the lines of goto.py will be very much faster than running 2M ticks with a step of 0 (i.e., step size 8^0 = 1 tick).

But running exactly 2097152 ticks in the GUI with a step size of 4 and a base of 8 will probably be just as fast or faster than any version of goto.py can get you to exactly 2000000 ticks. This is because Golly has to switch step size a few times to hit T=2000000 on the nose, and that takes some extra reworking of the hash table.

2097152 = 2^21, so there are no changes in step size needed in that case -- just 512 steps of size 8^4 = 4096. The display is redrawn only 512 times, which is probably a completely negligible amount of CPU use. Certainly step size 5, 64 redraws, will be not even a measurable difference... so you can use a script as simple as

Code: Select all

import golly as g
g.setbase(8)
g.setstep(5)
for i in range(64):
  g.step()
  g.fit()
  g.update()
If you want the absolute minimum display work while Golly is getting to T=2097152, you can leave out the last two lines. But beyond a certain point you don't really lose any time to speak of, and you get a better sense of whether Golly is struggling with the problem you've given it, or if it's humming right along and will get you your answer in a reasonable amount of time.

It's not clear that setting up Golly to take a single gigantic step would be even theoretically faster:

Code: Select all

import golly as g
g.setbase(2)
g.setstep(21)
g.step()
For many patterns there's an optimum speed, where the Hashlife speedup balances out the amount of time spent building the hash table at the beginning. With a larger step size than the optimum, Golly will cheerfully fill the entire available space with hash tiles, run out of memory, and start doing slow painful garbage collection so as to be able to finish the simulation.

User avatar
Tezcatlipoca
Posts: 81
Joined: September 9th, 2014, 11:40 am

Re: Golly scripts

Post by Tezcatlipoca » February 12th, 2015, 1:11 pm

Thank you, that is helpful and good info to have.

So I know how to increase the steps and run the GUI to pretty good effect. I use a step size of 2 for increased specificity. And I actually timed with a stopwatch at different steps and recorded the generation increases to nail down the optimal step exponent on my hardware (at least up to a certain population size; it's variable, but that's another story) which happens to be about the speed right between fluid display and small "jumps" or stutters in the pattern view. A little jumping got me there faster if I recall, just in case anyone is looking for that sweet spot.

If I read you right, it doesn't really matter that the states in the middle don't really matter to me, that I just need to get to an arbitrary end point from a complex starting pattern for much of what I'm trying to do. Do I understand correctly that all the geometry display and rendering is negligible when it comes to resources in Golly? Maybe it's a matter of not being able to separate all the display from the computation with the provided functions and scripts, or perhaps I misunderstand the relationship between computation and display. In any case, I think I have got it. Sounds like I'm doing the best I can do for now. Thank you!

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

Re: Golly scripts

Post by dvgrn » February 12th, 2015, 1:23 pm

Tezcatlipoca wrote:If I read you right, it doesn't really matter that the states in the middle don't really matter to me, that I just need to get to an arbitrary end point from a complex starting pattern for much of what I'm trying to do.

Do I understand correctly that all the geometry display and rendering is negligible when it comes to resources in Golly?
It's negligible for moderate step sizes. It would matter a whole lot for really low step sizes. The contrast I tried to draw was something like this:
  • step size = 1, base = 8 -- you'd have to refresh the screen a quarter million times. Of course that will slow down your simulation enormously, for no benefit at all.
  • step = 5, base = 8, Golly only refreshes the screen 64 times. That's probably going to be hard to tell apart from step=6.
  • step = 6, base = 8, Golly refreshes the screen 8 times. Not much CPU savings, and less feedback to the user to show if Golly is working well or getting hung up due to lack of memory.

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

Re: Golly scripts

Post by Gustavo6046 » February 12th, 2015, 2:58 pm

I need some copy of a goslow.py so I can continue my research about glider syntheses, to confirm GOL-SSRP's existance!
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
simsim314
Posts: 1766
Joined: February 10th, 2014, 1:27 pm

Re: Golly scripts

Post by simsim314 » February 13th, 2015, 4:32 am

dvgrn wrote:you'd have to refresh the screen a quarter million times.
Regarding the refreshes. It has been confirmed on several machines now, that minimizing golly increase performance (at around x2-x4 rates). I guess golly is smart enough not to draw anything in minimized mode.

I know it's kinda strange to improve performance pretty drastically by minimizing the screen - nevertheless this is one of the "cheapest" and best ways to improve golly performance.

---

There is probably another issue regarding step, except the visual refresh. While stepping golly contains information in special format (HashTable), when you try to present this information in visually accessible way, even if not drawing - this conversion takes time.

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

Re: Golly scripts

Post by dvgrn » February 13th, 2015, 4:47 am

simsim314 wrote:Regarding the refreshes. It has been confirmed on several machines now, that minimizing golly increase performance (at around x2-x4 rates). I guess golly is smart enough not to draw anything in minimized mode.

I know it's kinda strange to improve performance pretty drastically by minimizing the screen - nevertheless this is one of the "cheapest" and best ways to improve golly performance.
Right, I should have mentioned that -- thanks for the reminder.

The slowdown is much more pronounced on the Windows version of Golly than on Mac or Linux. Oddly enough, you can get most of the speed back simply by shifting the focus away from the Golly window -- even if the pattern is still visible and even if it's being updated as you watch, scripts like apgsearch will still run noticeably faster if another window has the focus. It appears that Windows Golly is simply polling for keyboard input much too often, when it has the focus.

This will probably be improved in Golly 2.7, whenever that comes out -- I have a candidate fix from Andrew Trevorrow, but haven't taken the time yet to run it through its paces and make sure no new problems appear.

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

Re: Golly scripts

Post by Gustavo6046 » February 13th, 2015, 3:43 pm

Anyways I need a goslow.py script, once again.
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

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

Re: Golly scripts

Post by dvgrn » February 13th, 2015, 4:13 pm

Gustavo6046 wrote:Anyways I need a goslow.py script, once again.
Did you get some version of Golly working with a compatible Python? I hope you solved the problem one way or another. You mentioned that you had a version of Python that was intended to be run from a USB drive, so it looked like you must not be able to access python.org downloads from your location. I can't tell from the filename whether your PythonPortable.zip is 32-bit or 64-bit, and I'm not familiar with Portable Python, so can't really troubleshoot much further than that.

If Golly and Python still aren't getting along, send me a private message -- it shouldn't be difficult to get a copy of the standard 64-bit Python installer to you, one way or another.

Your request for a "goslow.py" script doesn't make much sense. You'd have to explain specifically what you want the script to do -- and once you have that figured out, try writing the script yourself.

You've mentioned this script in the context of your "GOL-SSRP" hoax. It really seems to me that this is not a good way to get attention; eventually you'll simply train everyone not to bother to read anything you post.

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

Re: Golly scripts

Post by flipper77 » February 16th, 2015, 3:39 am

I've modified the change-state.py script by Andrew found in Golly's online database to make another version that runs very fast for sparse patterns:

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.
# Modifications by Flipper77, Feb 2015

import golly as g
from glife import rect
from time import time

r = rect( g.getselrect() )
if r.empty: g.exit("There is no selection.")
clist = g.getcells(r)

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
oldsecs = time()
if fromstate != 0:
   total = int(len(clist)/3)
   for cell in xrange(2, len(clist), 3):
      # might be large pattern so show percentage done each second
      x = cell - 2
      y = cell - 1
      newsecs = time()
      if newsecs - oldsecs >= 1.0:
         oldsecs = newsecs
         g.show("Changing cell states: " +
            str(int(100 * x /(3 * total))) + "%")
         g.update()

      # allow keyboard interaction
      g.dokey( g.getkey() )


      if clist[cell] == fromstate:
         g.setcell(clist[x], clist[y], tostate)
         changed += 1

else:
   total = float(r.wd) * float(r.ht)
   for row in xrange(r.top, r.top + r.height):
      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()

      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))
The script takes advantage of cell lists, which is based on population count, instead of bounding box area, which cuts down execution time by quite a bit when used on very sparse patterns. It's quite obvious that with this method, it won't work if 'fromstate' is 0, so I kept some of the old code in case that happens.

Also, here's a similar script to the one above that swaps cell states, which is actually quite useful, especially with rules that have some states inverted:

Code: Select all

# Swaps cells between the two given states.
# Works in any rule with more than 2 states.
# Author: Andrew Trevorrow (andrew@trevorrow.com), Oct 2009.
# Modifications by Flipper77, Feb 2015

import golly as g
from glife import rect
from time import time

r = rect( g.getselrect() )
if r.empty: g.exit("There is no selection.")
clist = g.getcells(r)

maxstate = g.numstates() - 1
if maxstate == 1: g.exit("The current rule only has 2 states.")

result = g.getstring(
"Swaps all the cells of one state (the 1st number)\n" +
"with a different state (the 2nd number) where both\n" +
"numbers must be from 0 to " + str(maxstate) + ":",
"1 2", "Swap state")

try:
   s1, s2 = result.split()
except:
   g.exit("Enter 2 numbers separated by a space.")
try:
   state1 = int(s1)
except:
   g.exit("1st state is not a number: " + s1)
try:
   state2 = int(s2)
except:
   g.exit("2nd state is not a number: " + s2)
if state1 < 0 or state1 > maxstate:
   g.exit("1st state is out of range: " + s1)
if state2 < 0 or state2 > maxstate:
   g.exit("2nd state is out of range: " + s2)
if state1 == state2:
   g.exit("The given states are the same.")

changed = 0
oldsecs = time()
if (state1 != 0) and (state2 != 0):
   total = int(len(clist)/3)
   for cell in xrange(2, len(clist), 3):
      # might be large pattern so show percentage done each second
      x = cell - 2
      y = cell - 1
      newsecs = time()
      if newsecs - oldsecs >= 1.0:
         oldsecs = newsecs
         g.show("Changing cell states: " +
            str(int(100 * x /(3 * total))) + "%")
         g.update()

      # allow keyboard interaction
      g.dokey( g.getkey() )

      if clist[cell] == state1:
         g.setcell(clist[x], clist[y], state2)
         changed += 1

      elif clist[cell] == state2:
         g.setcell(clist[x], clist[y], state1)
         changed += 1

else:
   total = float(r.wd) * float(r.ht)
   for row in xrange(r.top, r.top + r.height):
      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()

      g.dokey( g.getkey() )

      for col in xrange(r.left, r.left + r.width):
         if g.getcell(col, row) == state1:
            g.setcell(col, row, state2)
            changed += 1

         elif g.getcell(col, row) == state2:
            g.setcell(col, row, state1)
            changed += 1

if changed == 0:
   # better to beep in this case
   g.exit("There are no cells in state %d." % state1)
else:
   g.show("%d cells were swapped between state %d and state %d."
          % (changed, state1, state2))

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

Re: Golly scripts

Post by Gustavo6046 » February 17th, 2015, 10:24 am

Yep, anyway it's actually goslow.lua but I think Golly can handle Lua script too, because I born in Brazil, country with the Rio de Janeiro, place where is the PUC-RJ (Pontífica Universidade Católica do Rio de Janeiro, i.e. Pontific Catholic University of Rio de Janeiro), where were developed the Lua, which is an Brazilian Portuguese for "Moon"!!!! *arf* *arf* *arf* I am STILL in Brazil, i live here, only I hate it, thanks to crappy politics.

RETURNING TO TOPIC.... *arf* I think Golly should support Lua, C++, JS and Java scripts as well, because they're so common, easy to learn and to do, and rather appropiate for autodidacts like me. :P Anyway I think the only exception is the titanic C++, which I hate because it's syntax is a shit I never wish to stay with. *arf* goslow.lua or goslow.java OR goslow.js would be okay, now Python is surely badass here and won't work thanks to its very low priority given my deep research in the [font=cambria_math]non-hoax[/font] GOL-SSRP, which is now still occurring. Anyway my Python is a heck cause it won't work! Also my system don't accept setups for some reason... either an very incomodating one...
___________________________________________
"Do you want this app to make changes in your PC?"
"Name: python27setup.exe"
Pass: >idk<
_____
Wrong pass!
"Sorry but you need admin permissions to run it. :(3"
____________________________________________
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

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

Re: Golly scripts

Post by dvgrn » February 17th, 2015, 11:29 am

Gustavo6046 wrote:Anyway my Python is a heck cause it won't work! Also my system don't accept setups for some reason... either an very incomodating one...
"Sorry but you need admin permissions to run it."
Ah, there's the actual problem. Try to mention specific details along these lines as early as possible in the future.

If you're set up as a non-administrative user on the computer you're using, you won't be able to install Python -- it's as simple as that, a permissions issue, and the error messages you posted are telling you exactly what you need to know. Someone with an admin account could do the installation for you. So who has admin access to your machine?

Python seems like a very appropriate "common, easy to learn" scripting language. If you try Python for a few years and you find it just isn't good enough for you for some reason, then theoretically you could learn enough C++ to add support in Golly for Lua, C++, JS and Java. Golly is an open-source project and anyone can try their hand at contributing code. There has been some mention of working toward supporting embedded Lua or JS, but no actual plans to do the coding as far as I know -- it will be quite a bit of work, even for an experienced programmer.

Now, code contributions don't necessarily get accepted into Golly's main codebase. Just supporting Python and Perl is really a plenty big enough headache as it is -- no point in adding more complexity if it will just make Golly buggier and less reliable. So you'd probably have to write a lot of test code along with the scripting support code, to prove that your contribution isn't going to cause more problems than it solves.

Otherwise, well, it's possible you might just end up with your own private build of Golly... but at least you'd have the scripting support you want (and lots of good programming experience).

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

Re: Golly scripts

Post by flipper77 » February 19th, 2015, 9:00 am

For those who like to find certain apgsearch objects with a certain symmetry, here's a script that does that:

Code: Select all

import golly as g
import os

def check(string):
    symmetries = {"C1": [],
                  "C2": ["1", "2", "4"],
                  "C4": ["1", "4"],
                  "D2": ["+1", "+2", "x"],
                  "D4": ["+1", "+2", "+4", "x1", "x4"],
                  "D8": ["1", "4"]}

    if len(string.split("_")) != 2:
        if string == "C1":
            return "C1"
        g.exit("Please enter a valid symmetry.")

    pr, z = string.split("_")
    if symmetries.has_key(pr):
        if z in symmetries[pr]:
            return string

    g.exit("Please enter a valid symmetry.")

def shift_list(rect, clist):
    for x in xrange(0, len(clist), 2):
        clist[x] -= rect[0]
        clist[x+1] -= rect[1]
    return clist

def diagonal(rect, clist):
    d = 0
    d1 = 0
    d2 = 0
    l = len(clist)

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (clist[i+1]) == (clist[j]) ) and ( (clist[i]) == (clist[j+1]) ):
                d1 += 1
                break
    if d1 == l/2:
        d += 1

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (rect[2]-1-clist[i+1]) == (clist[j]) ) and ( (rect[2]-1-clist[i]) == (clist[j+1]) ):
                d2 += 1
                break
    if d2 == l/2:
        d += 1

    return d

def orthogonal(rect, clist):
    xline = rect[3]-1
    yline = rect[2]-1
    l = len(clist)
    x = 0
    y = 0
    o = 0

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (yline-clist[i]) == (clist[j]) ) and ( (clist[i+1]) == (clist[j+1]) ):
                x += 1
                break

    if x == l/2:
        o += 1

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (clist[i]) == (clist[j]) ) and ( (xline-clist[i+1]) == (clist[j+1]) ):
                y += 1
                break
    if y == l/2:
        o += 1

    if o == 1:
        return (o + 2 + (y > x))

    return o

def rotation(rect, clist):
    l = len(clist)
    r2 = 0
    r4 = 0

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (clist[i+1]) == (clist[j]) ) and ( (rect[2]-1-clist[i]) == (clist[j+1]) ):
                r4 += 1
                break

    if r4 == l/2:
        return 2

    for i in xrange(0, l, 2):
        for j in xrange(0, l, 2):
            if ( (rect[2]-1-clist[i]) == (clist[j]) ) and ( (rect[3]-1-clist[i+1]) == (clist[j+1]) ):
                r2 += 1
                break

    if r2 == l/2:
        return 1

    return 0

def get_symmetry():
    rect = g.getrect()
    patt = shift_list(rect, g.getcells(rect))

    if rect[2] == rect[3]:
        d = diagonal(rect, patt)
    else:
        d = 0
    o = orthogonal(rect, patt)
    r = rotation(rect, patt)

    a = 2-(rect[2]%2)
    b = 2-(rect[3]%2)

    if d and o:
        return "D8_%d" % (a**2)
    if d == 1:
        return "D2_x"
    if d == 2:
        return "D4_x%d" % (a**2)
    if o == 2:
        return "D4_+%d" % (a*b)
    if o > 2:
        return "D2_+%d" % (2-(rect[o-1]%2))
    if r == 1:
        return "C2_%d" % (a*b)
    if r == 2:
        return "C4_%d" % (a**2)

    return "C1"

# ------------------------------------------------------------------------

count = 0
rule = g.getstring("Enter rule.", "B3/S23").replace("/", "")
all = {}
dir = g.getdir("data")
s = dir[-1]
place = dir + "Symmetrical_objects" + s
path = dir + "apgsearch" + s + "objects" + s + rule + s

if not os.path.exists(path):
    g.exit("The specified directory doesn't exist: %s" % path)
if not os.path.exists(place):
    os.mkdir(place)

for obj in os.listdir(path):
    g.open(path + obj)
    objname = obj.partition(".")[0]
    symm = get_symmetry()
    if all.has_key(symm):
        all[symm] += "\n"
        all[symm] += objname
    else:
        all[symm] = objname
    if (count % 100) == 0:
        g.show("Objects processed: %d" % count)
        g.update()
    count += 1

for key in all:
    out = open(place + rule + "-" + key + ".txt", 'w')
    out.write(all[key])
    out.close()

g.show("Objects processed: %d" % count)
It's really useful, especially for finding asymmetrical objects with a high bit count. Just so people know, it uses the same symmetry notation as the latest hacked version of apgsearch with symmetry options, and it will generate a bunch of .txt files within a folder with the name "Symmetrical_objects". Each .txt file starts with the rule, then a hyphen(-), then the appropriate symmetry.

Chances are the script could have been written more efficiently, but it works fast enough as it is. Also, the script analyses phase-specific symmetry, so an object like a glider is interpreted as having C1 symmetry, instead of having non phase-specific D2_x symmetry.

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

Re: Golly scripts

Post by Gustavo6046 » February 19th, 2015, 9:38 am

Now I want to see if someone has a program that places some eaters in a selected unstable pattern so to supress the junk into something useful... like some eaters around a R-pentomino could eat the R-pentomino and only remain the eaters.
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

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

Re: Golly scripts

Post by dvgrn » February 19th, 2015, 1:00 pm

Gustavo6046 wrote:Now I want to see if someone has a program that places some eaters in a selected unstable pattern so to supress the junk into something useful... like some eaters around a R-pentomino could eat the R-pentomino and only remain the eaters.
The two existing (C/C++) search utilities are Paul Callahan's 'ptbsearch' and Gabriel Nivasch's 'catalyst'. Both are command-line utilities and there's a steep learning curve, but definitely worth learning if you're interested in this kind of thing... simsim314 has written a Python-based front end for 'catalyst', so if you get Python installed that might be helpful.

You've already posted on the 'Catalyst improvements WIP' thread, though, so presumably you already know about the program.

It's a good sign that you're saying that the eaters should remain. You've posted patterns lately where an eater is destroyed to produce the effect you're advertising. Unless you provide a way to rebuild the eater, and some reason why it's worth bothering to do that, any reaction where a catalyst is destroyed is a guaranteed dead end -- or at least the "catalyst" is not really a catalyst!

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

Re: Golly scripts

Post by Gustavo6046 » February 19th, 2015, 3:30 pm

No I don't know what is catalysts. But I wish something to auto put these eaters im8
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

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

Re: Golly scripts

Post by Scorbie » February 20th, 2015, 5:29 am

Here's a giffer script translated in python in case someone doesn't have perl working in golly.

Code: Select all

# Runs the current selection for a given number of steps and
# creates a black and white animated GIF file.
# Based on giffer.pl, which is based on code by Tony Smith.

import golly as g
import os
import struct

rect=g.getselrect()
if len(rect)==0:
    g.exit("Nothing in selection.")
[x,y,width,height]=rect
if(width>=65536 or height>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels.")

s = g.getstring( "Enter:\nthe number of frames,\n"
                +"the pause time of each frame(in centicecs),\n"
                +"the size of each cell(in pixels), and\n"
                +"the file name.\n"
                +"ex) 100 1 10 out.gif"
                ,"","Create Animated GIF");
frames, pause, cellsize, filename = s.split()
if(frames==""): frames = 100
if(pause==""): pause = 1
if(cellsize==""): cellsize = 1
try:
    frames = int(frames)
except:
    g.exit("Number of frames is not an integer: " + frames)
try:
    pause = int(pause)
except:
    g.exit("Pause time is not an integer: " + pause)
try:
    cellsize = int(cellsize)
except:
    g.exit("Cell size is not an integer: " + cellsize)
if(cellsize*height>=65536 or cellsize*width>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels.")
# ------------------------------------------------------------------------------
def getdata():
    lines = []
    #each array element is a line of 0 and 1 characters
    for row in xrange(y, y+height):
        line = ""
        for col in xrange(x, x+width):
            line += str(g.getcell(col,row))*cellsize 
        lines += [line]*cellsize
    return lines
# ------------------------------------------------------------------------------
def compress(lines):
    table = {'0': 0, '1': 1}
    curr = cc = 4
    used = eoi = 5
    bits = size = 3
    mask = 7
    output = code = ""
    for line in lines:
        for i in xrange(len(line)):
            next = line[i]
            if (code + next) in table:
                code += next
            else:
                used += 1
                table[(code + next)] = used
                curr += table[code] << bits
                bits += size
                while(bits >= 8):
                    output += chr(curr & 255)
                    curr >>= 8
                    bits -= 8
                if(used > mask):
                    if(size < 12):
                        size += 1
                        mask = mask*2 + 1
                    else:
                        curr += cc << bits # output cc in current width
                        bits += size
                        while(bits >= 8):
                            output += chr(curr & 255)
                            curr >>= 8
                            bits -= 8
                        table = {'0': 0, '1': 1} #reset table
                        used = 5
                        bits = 3
                        mask = 7
                code = next
    curr += table[code] << bits
    bits += size
    while(bits >= 8):
        output += chr(curr & 255)
        curr >>= 8
        bits -= 8
    output += chr(curr)
    subbed = ""
    while(len(output) > 255):
        subbed += chr(255) + output[:255]
        output = output[255:]
    return subbed + chr(len(output)) + output + chr(0)
# ----------------------------------------------------------------------
# GIF formatting
# Useful information of GIF formats in:
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
# I'm a novice both at python and perl.
# Anybody is welcome in modifying the "part that I wrote",
# especially the pack implementations.
# (Added the original perl code in comments)
# ----------------------------------------------------------------------
header = "GIF89a"
screendesc = struct.pack("<2HB2b", cellsize*width, cellsize*height, 0x80, 0, 0)
#global is a keyword in python.
#my $global = pack('v2B8c2', $width, $height, '10000000', 0, 0);
colortable = "\xFF\xFF\xFF\x00\x00\x00"
#Sadly, I don't know an elegant way in python.
#my $colortable = pack('H*', 'FFFFFF000000');
applic = chr(11) + "NETSCAPE2.0" + struct.pack("<2bHb", 3, 1, 0, 0)
#my $applic = chr(11) . 'NETSCAPE2.0' . pack('c2vc', 3, 1, 0, 0);
imagedesc = struct.pack("<4HB", 0, 0, cellsize*width, cellsize*height, 0x00)
#my $descriptor = pack('v4B8', 0, 0, $width, $height, '00000000');

try:
    gif = open(os.getcwd() + "\\" + filename,"wb")
except:
    g.exit("Unable to open file.")

gif.write(header + screendesc + colortable)
gif.write("!" + chr(0xFF) + applic)
for f in xrange(frames):
    gif.write("!" + chr(0xF9) + struct.pack("<bBH2b", 4, 0x00, pause, 0, 0))
    #print GIF '!', chr(0xF9), pack('cB8vc2', 4, '00000000', $pause, 0, 0);
    #get data for this frame
    gif.write("," + imagedesc + chr(2) + compress(getdata()))
    g.show(str(f+1)+"/"+str(frames))
    if(f+1 < frames):
        g.run(1)
        g.update()
gif.close()
g.show("GIF animation saved in " + filename)

Best wishes to you, Scorbie

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

Re: Golly scripts

Post by Kazyan » February 21st, 2015, 12:39 pm

I'd like to run apgsearch 1.0, but Golly shows an error message regarding trying to import urllib2, httplib, socket, and _socket. It's saying that a DLL load failed and that %1 is not a valid Win32 application. Previous versions of apgsearch work fine. What did I do wrong when installing Python?
Tanner Jacobi
Coldlander, a novel, available in paperback and as an ebook.

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

Re: Golly scripts

Post by Scorbie » February 21st, 2015, 1:01 pm

Uh, oh. I saw a similar msg when I was trying to use pillow. I could not fix that. Do you have an older version of python?
Best wishes to you, Scorbie

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

Re: Golly scripts

Post by Kazyan » February 21st, 2015, 1:45 pm

Scorbie wrote:Uh, oh. I saw a similar msg when I was trying to use pillow. I could not fix that. Do you have an older version of python?
Python 2.7.
Tanner Jacobi
Coldlander, a novel, available in paperback and as an ebook.

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

Re: Golly scripts

Post by flipper77 » February 24th, 2015, 2:05 pm

Here's a script that figures out if the specified object has been encountered by catagolue's records, and if so, finds the proper soups that generate the object (I borrowed some parts of apgsearch to assist):

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]:
                        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, (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()
What you do is draw the object you want to find (hopefully, what you draw is stabilised), then using some apgsearch code, encodes the object, it even works for patterns that linearlyse() can identify. Also, the script assumes the current rule, if life-like, to be the rule to search under, symmetry part of the census you specify when you run it. There are some variables at the top to modify if you like to use different numbers(i.e. spacing is the amount of space between soups when placed down.)

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

Re: Golly scripts

Post by Jackk » February 24th, 2015, 2:54 pm

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". Does anyone know why this is happening and how I can fix it?
Screenshot 2015-02-24 18.52.26.png
Screenshot 2015-02-24 18.52.26.png (145.42 KiB) Viewed 17080 times
Thanks in advance!

Post Reply