Census script (Python in Golly)

For scripts to aid with computation or simulation in cellular automata.
Post Reply
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Census script (Python in Golly)

Post by Nathaniel »

First off, I'm posting this in its own thread instead of the Golly Scripts thread because I want to encourage suggestions/improvements/modifications to this script from others, without derailing that thread (which has thus far mostly been for simple self-contained scripts).

So this is my first draft of a census script in Python, and it is what I'm planning on using for the online census program (see this thread). As such, there are a couple of things to note:

1) The script does not use a database of known objects, so it is pretty versatile for working in various rules.
2) The script is able to distinguish pretty well between close objects that do not interact (eg. a row of blinkers) and close objects that do interact (eg. it will count a block on table as one object). Because it's such a simple script though, it does sometimes group close but unrelated objects together.
3) If you have any suggestions for speed improvements or anything of the sort, please do let me know.
4) The output of the script is easiest to explain with an example (it uses the RLE format). {'2o$2o': 6, '3o': 4} as output would mean that the field has six blocks and four horizontal blinkers.
5) The script is painfully slow for very large censuses (such as those produced by HighLife after a replicator and some junk finally stabilizes).

Edit: I just realized that, as always, I didn't comment my code. The idea of the script is to loop through each cell on the field. If that cell has any other cells in its Moore neighbourhood, add those to the same "set" as the original cell. Repeat for any new cells that you add until you've exhausted that connected component. Now add any cells that are 2 cells away from at least 2 cells in the connected component you already found (this makes it possible to count disconnected objects like "toad" and "block on table" as single objects, while avoiding considering closely-spaced blinkers as single objects). Repeat these procedures until you can't anymore, and then store that set of cells as one object. Repeat until you've exhausted all the cells on the board.

Code: Select all

# A simple Python census script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# v1.04

# Collects statistics regarding how many of each type of object are on the field.
# The input pattern is assumed to be oscillatory (can include escaping gliders/spaceships as well as oscillators/still lifes).
# Works with any Life-like rule (though not always as well as with regular Life)
# Is able to distinguish between close objects that do not interact (eg. blinkers with one dead cell between them)
# and close objects that do interact (eg. block on table) reasonably well.
# It will never mistakenly split up an oscillator into two subpieces
# It may occassionally mistakenly group two unrelated objects together as one

import golly as g
cens_list = []
clist = []
axx = [1,1,-1,-1,0,0,0,0]
ayy = [1,-1,1,-1,0,0,0,0]
axy = [0,0,0,0,1,1,-1,-1]
ayx = [0,0,0,0,1,-1,1,-1]

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

def getRLE(rl_list):
   rle_res = ""
   rle_len = 1
   rl_list.sort(cmp = lambda x,y: (x[0]-y[0])+500*(x[1]-y[1]))
   rl_y = rl_list[0][1] - 1
   rl_x = 0
   for rl_i in rl_list:
      if rl_i[1] == rl_y:
         if rl_i[0] == rl_x + 1:
            rle_len += 1
         else:
            if rle_len == 1: rle_strA = ""
            else: rle_strA = str (rle_len)
            if rl_i[0] - rl_x - 1 == 1: rle_strB = ""
            else: rle_strB = str (rl_i[0] - rl_x - 1)

            rle_res = rle_res + rle_strA + "o" + rle_strB + "b"
            rle_len = 1
      else:
         if rle_len == 1: rle_strA = ""
         else: rle_strA = str (rle_len)
         if rl_i[1] - rl_y == 1: rle_strB = ""
         else: rle_strB = str (rl_i[1] - rl_y)
         if rl_i[0] == 1: rle_strC = "b"
         elif rl_i[0] == 0: rle_strC = ""
         else: rle_strC = str (rl_i[0]) + "b"
         
         rle_res = rle_res + rle_strA + "o" + rle_strB + "$" + rle_strC
         rle_len = 1

      rl_x = rl_i[0]
      rl_y = rl_i[1]
   
   if rle_len == 1: rle_strA = ""
   else: rle_strA = str (rle_len)
   rle_res = rle_res[2:] + rle_strA + "o"
   
   return rle_res

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

def chunks(l, n):
    for i in range(0, len(l), n):
        yield tuple(l[i:i+n])

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

def census():
   keylist = []

   # Set cct_limit to the maximum number of generations that you want to look for oscillation
   # when building the census. Can be made precise in conjunction with Golly's included oscar.py script
   cct_limit = 10
   
   cct = 1
   cens = {}
   clist = list (chunks (g.getcells (g.getrect()), 2))
   
   cens_list = set(clist[:])
   while cct <= cct_limit:
      g.run(1)
      clist = list (chunks (g.getcells (g.getrect()), 2))
      cens_list = cens_list | set (clist)
      cct += 1
   cens_list = list(cens_list)
   
   while len(cens_list) > 0:
      wt = {}
      curcells = [cens_list.pop(0)]
      for j in curcells:
         g.dokey( g.getkey() )            # allow keyboard interaction
         wu = {}
         tlist=cens_list[:]
         for i in tlist:
            c_dist = [abs(i[0] - j[0]), abs(i[1] - j[1])]
            if (max(c_dist) == 2):
               try:
                  if(wt[i] == 1):
                     curcells.append (i)
                     cens_list.remove (i)
               except:
                  wt[i] = 1
               wu[i] = 1
            elif (max(c_dist) <= 1):         
               curcells.append (i)
               cens_list.remove (i)
         if(len(wu) >= 2):
	    for i in wu:
	       if not i in curcells:
                  curcells.append (i)
                  cens_list.remove (i)
	       
      tccells = curcells[:]
      for j in tccells:
         if not j in clist:
            curcells.remove(j)
      
      if len(curcells) > 0:
         pllist = []
         rlelist = []
         for i in range(0,len(curcells)):
            pllist.append(curcells[i][0])
            pllist.append(curcells[i][1])

         for i in range(0,8):
            rotlist = list (chunks (g.transform(pllist,0,0,axx[i],axy[i],ayx[i],ayy[i]), 2))
            mcc = min(rotlist)
            rotlist = [[x[0]-mcc[0],x[1]-mcc[1]] for x in rotlist]
            curRLE = getRLE(rotlist)
            rlelist.append(curRLE)

         intlist = list(set(keylist) & set(rlelist))
         
         if(len(intlist) > 0):
            cens[intlist[0]] += 1
         else:
            keylist.append(curRLE)
            cens[curRLE] = 1
         
   g.show (str (cens))
   
census()
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

The first problem that I've found with the script is that it doesn't know what to do with the disconnected part of beacon -- it reports it as two tiny L-shaped pieces, rather than as one object. I could do some tinkering on the server to manually fix that, but does anyone have any suggestions for a more systematic way that will take care of this?
hkoenig
Posts: 299
Joined: June 20th, 2009, 11:40 am

Re: Census script (Python in Golly)

Post by hkoenig »

You are going to have problems with objects that contain sparks (spaceships) or disconnected phases (Pentadecathlon). Also pseudo-objects will be considered as single objects, which may or may not be what you want. Worse, some Toad pair pseudo-objects can appear as separate objects in one phase, and as a pseudo-object in another. Close spaced Glider fleets can cause problems to (I speak from experience here, and I never did solve all these problems.)

Escaping Glider detection seems to work better with a diagonal bounding box instead of an orthogonal one.

<i>The reason for this is that it would have been the slowest part of the script, and it is easily handled by the server when the data is submitted online.</i>

Unless you are assuming you never want to scale this up, you should be offloading as much work onto the clients as possible. To really speed up the client, see about getting features you want/need incorporated into Golly, also. Partitioning a bitpattern into a list of sub-patterns, for example, would seem to be useful in a lot of applications. The same with converting such patterns into a canonical form. (for this you need to define how to compare patterns and determine which one sorts first. )

In Life, there are only 20 or so objects that you are going to encounter with any frequency. Anything else is worthy of interest. My first census program simply hard-coded them in all their phases and did a compare down the list. And since 2/3rds are blonks, the compare was pretty fast. What didn't match got written out in bitmap format.

If you are going to stick to passing back a simple bitmap, why not use a simple encoding like my SOF format instead of passing back a long string of coordinates? Or are you also assuming that the server's network connection is always going to accommodate verbose messages? You may think this is a proof of concept or a test,and you'll just throw it all away at some point, but it's surprising how hard it is to undo bad decisions made early.

(Your Beacon problem: you need to consider the birth cells as alive. That helps connect a lot of disjoint oscillators.)
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

hkoenig wrote:Unless you are assuming you never want to scale this up, you should be offloading as much work onto the clients as possible.
The difference is that the server only has to do it once per client submit (so say every thousand patterns or so), but if the client does it then it has to be done every time the pattern is encountered. I suppose I could have the client do it right before doing the submit rather than during the counting process, but either way I don't think it belongs in the script above.
hkoenig wrote:In Life, there are only 20 or so objects that you are going to encounter with any frequency. Anything else is worthy of interest. My first census program simply hard-coded them in all their phases and did a compare down the list. And since 2/3rds are blonks, the compare was pretty fast. What didn't match got written out in bitmap format.
I could have the script retrieve the (some number) most common patterns in the current rule from the database and do that, but I'm not seeing how the comparison will be any faster. The information submitted to the server will be smaller, which is a plus, but keep in mind that information isn't constantly getting sent to the server; it accumulates on the user's machine and then is sent when they have enough of it.
hkoenig wrote:If you are going to stick to passing back a simple bitmap, why not use a simple encoding like my SOF format instead of passing back a long string of coordinates?
Ideally I'd want to submit an RLE, but that slows things down unfortunately. The format submitted right now is just the format that things happen to be in after going through the algorithm. I'm not overly familiar with the specifics of your SOF format, so I'll look into how well that works.

Edit: Nevermind, I can just have the script compute the RLEs right before it does the upload, rather than every time it picks out a pattern. I'll do that definitely for the next version.
User avatar
Macbi
Posts: 919
Joined: March 29th, 2009, 4:58 am
Contact:

Re: Census script (Python in Golly)

Post by Macbi »

How fast does censusing need to be? Won't actually running the patterns take much more time?
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

I've edited the original post to have an updated version of the script. This new version outputs the data as RLE instead of the weird cell list format used before.
Macbi wrote:How fast does censusing need to be? Won't actually running the patterns take much more time?
Good point.
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

I've updated the script again, this time using the idea from hkoenig's post about considering the birth cells as alive. This makes the beacon now correctly detected, as well as the pulsar in all of its phases and I *think* the pentadecathlon in all of its phases, as well as the LWSS. The most common pattern that fails is the MWSS (its middle spark gets counted as a separate object). Also, the script is *much* slower now (slow enough that it's noticeable) after adding this birth feature. Any recommendations on how to speed it up are welcome, and I'll have another pass over the code tonight as well.

PS: Yes, I do want to count pseudo-objects as one object, for the following reason: from pseudo-object counts you can derive "strict" object counts, but not vice-versa.
User avatar
dvgrn
Moderator
Posts: 12034
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Census script (Python in Golly)

Post by dvgrn »

I've been following the development of this census-tool idea with great interest. A year or so ago I started messing around with the idea of adding a census utility to Golly, and I did do some work on it. But it turned into a fairly ugly nest of interlocked Python scripts. I wasn't inspired to complete the project, since it wasn't clear anyone but me would be able to figure out how to use it...

The architecture of my vaporware census tool was quite different from what's been described here so far: in particular, the intention was to have a pattern library be an integral part of the utility. (The utility can still be rule-agnostic -- you just need a different library file for each rule you're interested in.)

One of the key pieces of functionality was to have Golly highlight all the parts of a pattern that weren't recognizable according to the current library. A user could then select any object and add it to the library -- the tool would figure out the period, if any. Maybe there'd be a way to automatically name and add new still lifes, as well.

Once all the pieces of a given pattern were recognizable, the census tool would be capable of writing out a Python or Perl script defining the entire pattern in terms of the recognized subpatterns. This can be a fairly effective way to compress large engineered patterns, in many cases -- say, if you're constructing complicated signal circuitry and you add all the component parts to the library. The library file itself would probably be a Python/Perl script as well.

One of the problems was that a single script can't conveniently do all the things that the census tool needs to be able to do: seems like you'd need, say, take-census.py, add-to-library.py, save-as-script.py, and maybe a few other maintenance functions.

How does that sound? Worth coming up with a working prototype? Any better ideas for the architecture?
User avatar
Lewis
Posts: 337
Joined: March 17th, 2009, 5:26 pm
Contact:

Re: Census script (Python in Golly)

Post by Lewis »

Whenever I try running the script, this happens:
error.PNG
error.PNG (8.06 KiB) Viewed 23929 times
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

Lewis wrote:Whenever I try running the script, this happens:
Python is very picky about whitespace and indentation. When copying a script from the forums, make sure that the whitespace in your file matches the whitespace on the forums exactly (in particular, make sure that there aren't any spaces in front of the "import golly as g" line).
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: Census script (Python in Golly)

Post by apg »

I've updated the script again, this time using the idea from hkoenig's post about considering the birth cells as alive. This makes the beacon now correctly detected, as well as the pulsar in all of its phases and I *think* the pentadecathlon in all of its phases, as well as the LWSS. The most common pattern that fails is the MWSS (its middle spark gets counted as a separate object). Also, the script is *much* slower now (slow enough that it's noticeable) after adding this birth feature. Any recommendations on how to speed it up are welcome, and I'll have another pass over the code tonight as well.

PS: Yes, I do want to count pseudo-objects as one object, for the following reason: from pseudo-object counts you can derive "strict" object counts, but not vice-versa.

Instead, just count connected islands of live cells in this super-rule. All known natural objects (including HWSSes, pulsars, queen bee shuttles and tumblers), as well as inductors, pseudo-stills etc. are all detected. The rule only fails for objects with periods > 30, e.g. the twin bees shuttle. I don't think that any objects that fall into this category occur naturally.

Unfortunately, gliders following each other in close proximity may be regarded as a single object.

Code: Select all

n_states:32
neighborhood:Moore
symmetries:rotate8reflect

var a={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var b={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var c={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var d={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var e={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var f={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var g={0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var h={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var i={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var j={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var k={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var l={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var m={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var n={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var o={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
var p={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}

# 3-neighbour birth/survival
h,1,1,1,a,b,c,d,e,1
h,1,1,a,1,b,c,d,e,1
h,1,1,a,b,1,c,d,e,1
h,1,a,1,b,1,c,d,e,1
h,1,a,1,b,c,1,d,e,1

# 2-neighbour survival
1,1,1,a,b,c,d,e,f,1
1,1,a,1,b,c,d,e,f,1
1,1,a,b,1,c,d,e,f,1
1,1,a,b,c,1,d,e,f,1

# Bridge inductors
e,1,1,1,1,h,i,j,k,2
e,1,1,1,h,1,i,j,k,2
e,1,1,1,h,i,1,j,k,2
e,1,1,h,1,1,i,j,k,2
e,1,1,h,1,i,1,j,k,2
e,1,1,h,1,i,j,1,k,2
e,1,1,h,i,1,1,j,k,2
e,1,h,1,i,1,j,1,k,2

# On state dies and becomes the history state
1,i,j,k,l,m,n,o,p,2
2,i,j,k,l,m,n,o,p,3
3,i,j,k,l,m,n,o,p,4
4,i,j,k,l,m,n,o,p,5
5,i,j,k,l,m,n,o,p,6
6,i,j,k,l,m,n,o,p,7
7,i,j,k,l,m,n,o,p,8
8,i,j,k,l,m,n,o,p,9
9,i,j,k,l,m,n,o,p,10
10,i,j,k,l,m,n,o,p,11
11,i,j,k,l,m,n,o,p,12
12,i,j,k,l,m,n,o,p,13
13,i,j,k,l,m,n,o,p,14
14,i,j,k,l,m,n,o,p,15
15,i,j,k,l,m,n,o,p,16
16,i,j,k,l,m,n,o,p,17
17,i,j,k,l,m,n,o,p,18
18,i,j,k,l,m,n,o,p,19
19,i,j,k,l,m,n,o,p,20
20,i,j,k,l,m,n,o,p,21
21,i,j,k,l,m,n,o,p,22
22,i,j,k,l,m,n,o,p,23
23,i,j,k,l,m,n,o,p,24
24,i,j,k,l,m,n,o,p,25
25,i,j,k,l,m,n,o,p,26
26,i,j,k,l,m,n,o,p,27
27,i,j,k,l,m,n,o,p,28
28,i,j,k,l,m,n,o,p,29
29,i,j,k,l,m,n,o,p,30
30,i,j,k,l,m,n,o,p,31
31,i,j,k,l,m,n,o,p,0


# Off state remains in off state by default
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

@Calcyman - Actually I've got an updated script that works even for things like twin bees shuttles. The downside is that, as with yours, it considers some closely-spaced objects as one object, but that's fixed by a separate script with database thing that I hope to finish up tonight. I'll post the updated census script within a day or two, when I post the updated soup search with census script for people to download.

Edit: OK, I just updated the original post to include v1.03 of the census script, which is the script I spoke of just now. It *always* groups together separate pieces of an oscillator, not matter how disjoint (eg. queen bee shuttle, twin bees shuttle). As a trade-off, it sometimes accidentally groups things like close gliders together, which I fix by some scripts that only I have access to and some database code. I'll post results and whatnot shortly on the General Discussion Forum.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: Census script (Python in Golly)

Post by apg »

Can your script differentiate between these two pentadecathlons, given that the rotors overlap?

x = 20, y = 16, rule = B3/S23
2$3bo2bob2obo2bo$3b4ob2ob4o$3bo2bob2obo2bo6$5bobo2bobo$b2obo2bo2bo2bob
2o$5bobo2bobo!
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

calcyman wrote:Can your script differentiate between these two pentadecathlons, given that the rotors overlap?

x = 20, y = 16, rule = B3/S23
2$3bo2bob2obo2bo$3b4ob2ob4o$3bo2bob2obo2bo6$5bobo2bobo$b2obo2bo2bo2bob
2o$5bobo2bobo!
Nope, they have to be separated manually, just like quite a few other objects. I would much rather have it accidentally group unrelated objects together though, as that can be fixed on the server's end of things. If, on the other hand, I were to get half of an object, I have no way of knowing what the piece belonged to.
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

I just uploaded v1.04 of the script - with this new update, it *does* group objects by reflection/rotation. This is done primarily to reduce the amount of data that has to be uploaded to the server, though the server still does a check to place the object in its correct orientation when it is uploaded.
Elithrion
Posts: 100
Joined: February 3rd, 2009, 4:02 pm

Re: Census script (Python in Golly)

Post by Elithrion »

Wait, what happened to v1.02-1.03? Also, the front page still only has the 1.01 file as far as I can see :(
Vi veri veniversum vivus vici.
User avatar
Nathaniel
Site Admin
Posts: 905
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Census script (Python in Golly)

Post by Nathaniel »

Elithrion wrote:Wait, what happened to v1.02-1.03? Also, the front page still only has the 1.01 file as far as I can see :(
Sorry, I'm being perhaps a bit confusing. The census script that I'm showing here has different version numbers than the overall soup search script. The soup search script is still at v1.01 (I'll be releasing 1.02 at the end of this week, and it will use the census v1.04 script).
Elithrion
Posts: 100
Joined: February 3rd, 2009, 4:02 pm

Re: Census script (Python in Golly)

Post by Elithrion »

Ah, makes sense! I've been meaning to look over that and be helpful, but seem to constantly have other random (equally non-vital) thing to do >.<
Vi veri veniversum vivus vici.
Axaj
Posts: 231
Joined: September 26th, 2009, 12:23 am

Re: Census script (Python in Golly)

Post by Axaj »

Do you think you could add the rule B36/S245? It appears to show class 4 behavior, and has some very interesting objects.
Image
Axaj
Posts: 231
Joined: September 26th, 2009, 12:23 am

Re: Census script (Python in Golly)

Post by Axaj »

Could someone who has a working copy of the script attach it to their post? I keep getting syntax errors.
Image
Sokwe
Moderator
Posts: 3384
Joined: July 9th, 2009, 2:44 pm

Re: Census script (Python in Golly)

Post by Sokwe »

Axaj wrote:Could someone who has a working copy of the script attach it to their post? I keep getting syntax errors.
Lines 110 and 111 of Nathaniel's code are insufficiently indented (both lines need five more spaces of indentation). Here is a copy of the script that should work:

Code: Select all

# A simple Python census script for use with Golly.
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
# v1.04

# Collects statistics regarding how many of each type of object are on the field.
# The input pattern is assumed to be oscillatory (can include escaping gliders/spaceships as well as oscillators/still lifes).
# Works with any Life-like rule (though not always as well as with regular Life)
# Is able to distinguish between close objects that do not interact (eg. blinkers with one dead cell between them)
# and close objects that do interact (eg. block on table) reasonably well.
# It will never mistakenly split up an oscillator into two subpieces
# It may occassionally mistakenly group two unrelated objects together as one

import golly as g
cens_list = []
clist = []
axx = [1,1,-1,-1,0,0,0,0]
ayy = [1,-1,1,-1,0,0,0,0]
axy = [0,0,0,0,1,1,-1,-1]
ayx = [0,0,0,0,1,-1,1,-1]

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

def getRLE(rl_list):
   rle_res = ""
   rle_len = 1
   rl_list.sort(cmp = lambda x,y: (x[0]-y[0])+500*(x[1]-y[1]))
   rl_y = rl_list[0][1] - 1
   rl_x = 0
   for rl_i in rl_list:
      if rl_i[1] == rl_y:
         if rl_i[0] == rl_x + 1:
            rle_len += 1
         else:
            if rle_len == 1: rle_strA = ""
            else: rle_strA = str (rle_len)
            if rl_i[0] - rl_x - 1 == 1: rle_strB = ""
            else: rle_strB = str (rl_i[0] - rl_x - 1)

            rle_res = rle_res + rle_strA + "o" + rle_strB + "b"
            rle_len = 1
      else:
         if rle_len == 1: rle_strA = ""
         else: rle_strA = str (rle_len)
         if rl_i[1] - rl_y == 1: rle_strB = ""
         else: rle_strB = str (rl_i[1] - rl_y)
         if rl_i[0] == 1: rle_strC = "b"
         elif rl_i[0] == 0: rle_strC = ""
         else: rle_strC = str (rl_i[0]) + "b"
         
         rle_res = rle_res + rle_strA + "o" + rle_strB + "$" + rle_strC
         rle_len = 1

      rl_x = rl_i[0]
      rl_y = rl_i[1]
   
   if rle_len == 1: rle_strA = ""
   else: rle_strA = str (rle_len)
   rle_res = rle_res[2:] + rle_strA + "o"
   
   return rle_res

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

def chunks(l, n):
    for i in range(0, len(l), n):
        yield tuple(l[i:i+n])

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

def census():
   keylist = []

   # Set cct_limit to the maximum number of generations that you want to look for oscillation
   # when building the census. Can be made precise in conjunction with Golly's included oscar.py script
   cct_limit = 10
   
   cct = 1
   cens = {}
   clist = list (chunks (g.getcells (g.getrect()), 2))
   
   cens_list = set(clist[:])
   while cct <= cct_limit:
      g.run(1)
      clist = list (chunks (g.getcells (g.getrect()), 2))
      cens_list = cens_list | set (clist)
      cct += 1
   cens_list = list(cens_list)
   
   while len(cens_list) > 0:
      wt = {}
      curcells = [cens_list.pop(0)]
      for j in curcells:
         g.dokey( g.getkey() )            # allow keyboard interaction
         wu = {}
         tlist=cens_list[:]
         for i in tlist:
            c_dist = [abs(i[0] - j[0]), abs(i[1] - j[1])]
            if (max(c_dist) == 2):
               try:
                  if(wt[i] == 1):
                     curcells.append (i)
                     cens_list.remove (i)
               except:
                  wt[i] = 1
               wu[i] = 1
            elif (max(c_dist) <= 1):         
               curcells.append (i)
               cens_list.remove (i)
         if(len(wu) >= 2):
            for i in wu:
               if not i in curcells:
                  curcells.append (i)
                  cens_list.remove (i)
          
      tccells = curcells[:]
      for j in tccells:
         if not j in clist:
            curcells.remove(j)
      
      if len(curcells) > 0:
         pllist = []
         rlelist = []
         for i in range(0,len(curcells)):
            pllist.append(curcells[i][0])
            pllist.append(curcells[i][1])

         for i in range(0,8):
            rotlist = list (chunks (g.transform(pllist,0,0,axx[i],axy[i],ayx[i],ayy[i]), 2))
            mcc = min(rotlist)
            rotlist = [[x[0]-mcc[0],x[1]-mcc[1]] for x in rotlist]
            curRLE = getRLE(rotlist)
            rlelist.append(curRLE)

         intlist = list(set(keylist) & set(rlelist))
         
         if(len(intlist) > 0):
            cens[intlist[0]] += 1
         else:
            keylist.append(curRLE)
            cens[curRLE] = 1
         
   g.show (str (cens))
   
census()
-Matthias Merzenich
Axaj
Posts: 231
Joined: September 26th, 2009, 12:23 am

Re: Census script (Python in Golly)

Post by Axaj »

I'm now getting errors saying "global name 'set' is not defined".
Image
Sokwe
Moderator
Posts: 3384
Joined: July 9th, 2009, 2:44 pm

Re: Census script (Python in Golly)

Post by Sokwe »

Axaj wrote:I'm now getting errors saying "global name 'set' is not defined".
I'm not very familiar with python, but I've looked around a bit and have seen 'set' related errors due to use of python 2.3 (apparently a change was made in 2.3.1). What version of python are you using? The current version is 2.6.4, which can be downloaded from the official python website here. Hopefully someone with more python experience can give you a better explanation.
-Matthias Merzenich
Axaj
Posts: 231
Joined: September 26th, 2009, 12:23 am

Re: Census script (Python in Golly)

Post by Axaj »

I just upgraded from 2.6.3 to 2.6.4, but the error is still the same.
Image
Post Reply