Quadsymm Python Script

For scripts to aid with computation or simulation in cellular automata.
Post Reply
knightlife
Posts: 566
Joined: May 31st, 2009, 12:08 am

Quadsymm Python Script

Post by knightlife »

A very basic quad rotational symmetry generator. Select an area with live cells and run the script file. Should work for any life rules and any number of states. Requires Golly 2.0 and at least Python 2.4 although it might work with earlier versions also.

Code: Select all

# Quadsymm
# by knightlife -- Sept 2009 
# Create quad symmetry using the current selection.
# Current selection becomes the top left quadrant of resulting pattern.
# based on invert.py script from golly 2.0

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

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

oldsecs = time()
maxstate = g.numstates() - 1

for row in xrange(r.top, r.top + r.height):
   # if large selection then give some indication of progress
   newsecs = time()
   if newsecs - oldsecs >= 1.0:
      oldsecs = newsecs
      g.update()

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

   for col in xrange(r.left, r.left + r.width):
      g.setcell(r.left + r.width + r.height - 1 - (row - r.top), r.top - r.width + r.height + (col - r.left), g.getcell(col, row))
      g.setcell(r.left + 2*r.width - 1 - (col - r.left), r.top + 2*r.height - 1 - (row - r.top), g.getcell(col, row))
      g.setcell(r.left + r.width - r.height + (row - r.top), r.top + r.width + r.height - 1 - (col - r.left), g.getcell(col, row))

if not r.visible(): g.fitsel()
Edit:

Slight mod to make bilateral symmetry:

Code: Select all

# Bilatsymm
# by knightlife -- Sept 2009
# Create bilateral symmetry using the current selection.
# Current selection becomes the left half of resulting pattern.
# based on invert.py script from golly 2.0

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

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

oldsecs = time()
maxstate = g.numstates() - 1

for row in xrange(r.top, r.top + r.height):
   # if large selection then give some indication of progress
   newsecs = time()
   if newsecs - oldsecs >= 1.0:
      oldsecs = newsecs
      g.update()

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

   for col in xrange(r.left, r.left + r.width):
      g.setcell(r.left + 2*r.width - 1 - (col - r.left), r.top + (row - r.top), g.getcell(col, row))

if not r.visible(): g.fitsel()
Sokwe
Moderator
Posts: 3401
Joined: July 9th, 2009, 2:44 pm

Re: Quadsymm Python Script

Post by Sokwe »

I suppose you would need to write another script to create odd-width radially-symmetric patterns.
-Matthias Merzenich
knightlife
Posts: 566
Joined: May 31st, 2009, 12:08 am

Re: Quadsymm Python Script

Post by knightlife »

Here is Quadsymm_odd:

The center cell is not modified by the script:

Code: Select all

# Quadsymm_odd
# by knightlife -- Sept 2009
# Create quad symmetry using the current selection.
# Current selection becomes the top left quadrant of resulting pattern.
# center cell is not modified: it is the cell to the right of the SE corner cell of the current selection
# based on invert.py script from golly 2.0

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

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

oldsecs = time()
maxstate = g.numstates() - 1

for row in xrange(r.top, r.top + r.height):
   # if large selection then give some indication of progress
   newsecs = time()
   if newsecs - oldsecs >= 1.0:
      oldsecs = newsecs
      g.update()

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

   for col in xrange(r.left, r.left + r.width):
      g.setcell(r.left + r.width + r.height -1 - (row - r.top), r.top - r.width + r.height - 1 + (col - r.left), g.getcell(col, row))
      g.setcell(r.left + 2*r.width - (col - r.left), r.top + 2*r.height - 2 - (row - r.top), g.getcell(col, row))
      g.setcell(r.left + r.width - r.height + 1 + (row - r.top), r.top + r.width + r.height - 1 - (col - r.left), g.getcell(col, row))

if not r.visible(): g.fitsel()
Sokwe
Moderator
Posts: 3401
Joined: July 9th, 2009, 2:44 pm

Re: Quadsymm Python Script

Post by Sokwe »

Based on Knightlife's first script, I wrote a program that tests random 90-degree radially symmetric patterns in a finite universe until they oscillate, and stops when it finds an oscillator of a sufficiently high period. The oscillation detection is based on Andrew Trevorrow's oscar.py script. The patterns are run in a finite universe so that gliders would not need to be dealt with, as they would just turn into blocks when they hit the edge.

I have very little experience with python, and so the script may not be very good (it really isn't); it merely demonstrates the idea. Code improvements would be nice (actually, you should probably just write your own script, as this one isn't that great).

Code: Select all

from glife import rect, pattern
import golly as g

#These five variables define the search
px = 5			#This defines the width of the random pattern (pattern width = 2*px)
ux = 9			#This defines the width of the finite universe (universe width = 2*ux)
d = 35			#This defines the density of the randowm pattern (density = d/100)
minp = 5		#This is the minimum period oscillator to be searched for
maxgen = 150	#this is for double checking the pattern to see if it is really an oscillator

found = False

# initialize lists
hashlist = []        # for pattern hash values
genlist = []         # corresponding generation counts
poplist = []         # corresponding population counts
boxlist = []         # corresponding bounding boxes


#Create random 90-degree radially symmetric pattern; based on Knightlife's Quadsymm script
def rand_quad(psize,density):

	g.new("")
	g.select([0,0,psize,psize])
	g.randfill(density)
	r = rect( g.getselrect() )

	maxstate = g.numstates() - 1

	for row in xrange(r.top, r.top + r.height):
	   for col in xrange(r.left, r.left + r.width):
		  g.setcell(r.left + r.width + r.height - 1 - (row - r.top), r.top - r.width + r.height + (col - r.left), g.getcell(col, row))
		  g.setcell(r.left + 2*r.width - 1 - (col - r.left), r.top + 2*r.height - 1 - (row - r.top), g.getcell(col, row))
		  g.setcell(r.left + r.width - r.height + (row - r.top), r.top + r.width + r.height - 1 - (col - r.left), g.getcell(col, row))


#Run the pattern until it oscillates; based on Andrew Treverrow's oscar.py script

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

def show_spaceship_speed(period, deltax, deltay):
   # we found a moving oscillator
   if period == 1:
      g.show("Spaceship detected (speed = c)")
   elif (deltax == deltay) or (deltax == 0) or (deltay == 0):
      speed = ""
      if (deltax == 0) or (deltay == 0):
         # orthogonal spaceship
         if (deltax > 1) or (deltay > 1):
            speed += str(deltax + deltay)
      else:
         # diagonal spaceship (deltax == deltay)
         if deltax > 1:
            speed += str(deltax)
      g.show("Spaceship detected (speed = " + speed + "c/" +str(period) + ")")
   else:
      # deltax != deltay and both > 0
      speed = str(deltay) + "," + str(deltax)
      g.show("Knightship detected (speed = " + speed + "c/" + str(period) + ")")

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

def oscillating():
   # return True if the pattern is empty, stable or oscillating
   
   # first get current pattern's bounding box
   prect = g.getrect()
   pbox = rect(prect)
   if pbox.empty:
      g.show("The pattern is empty.")
      return 1
   
   # get current pattern and create hash of "normalized" version -- ie. shift
   # its top left corner to 0,0 -- so we can detect spaceships and knightships
   ## currpatt = pattern( g.getcells(prect) )
   ## h = hash( tuple( currpatt(-pbox.left, -pbox.top) ) )

   # use Golly's hash command (3 times faster than above code)
   h = g.hash(prect)
   
   # determine where to insert h into hashlist
   pos = 0
   listlen = len(hashlist)
   while pos < listlen:
      if h > hashlist[pos]:
         pos += 1
      elif h < hashlist[pos]:
         # shorten lists and append info below
         del hashlist[pos : listlen]
         del genlist[pos : listlen]
         del poplist[pos : listlen]
         del boxlist[pos : listlen]
         break
      else:
         # h == hashlist[pos] so pattern is probably oscillating, but just in
         # case this is a hash collision we also compare pop count and box size
         if (int(g.getpop()) == poplist[pos]) and \
            (pbox.wd == boxlist[pos].wd) and \
            (pbox.ht == boxlist[pos].ht):
            period = int(g.getgen()) - genlist[pos]
            if period == 1:
               if pbox == boxlist[pos]:
                  g.show("The pattern is stable.")
               else:
                  show_spaceship_speed(1, 0, 0)
            elif pbox == boxlist[pos]:
               g.show("Oscillator detected (period = " + str(period) + ")")
            else:
               deltax = abs(boxlist[pos].x - pbox.x)
               deltay = abs(boxlist[pos].y - pbox.y)
               show_spaceship_speed(period, deltax, deltay)
            return period
         else:
            # look at next matching hash value or insert if no more
            pos += 1
   
   # store hash/gen/pop/box info at same position in various lists
   hashlist.insert(pos, h)
   genlist.insert(pos, int(g.getgen()))
   poplist.insert(pos, int(g.getpop()))
   boxlist.insert(pos, pbox)

   return 0

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

def fit_if_not_visible():
   # fit pattern in viewport if not empty and not completely visible
   r = rect(g.getrect())
   if (not r.empty) and (not r.visible()): g.fit()

# --------------------------------------------------------------------
rand_quad(px,d)
per = 0

while not found:
	while per <= 0:
		per = oscillating()
		g.select([px-ux,px-ux,2*ux,2*ux])
		g.advance(0,1)
		g.setgen("+1")
		#g.update()
	if per >= minp:
		per = 0
		g.select([px-ux,px-ux,2*ux,2*ux])
		g.copy()
		g.new("")
		g.paste(0,0,"or")
		while (per <= 0) and (int(g.getgen()) < maxgen):
			per = oscillating()
			g.run(1)
		if per >= minp:
			found = True
		else:
			rand_quad(px,d)
			per = 0
	else:
		rand_quad(px,d)
		per = 0


#g.show("Oscillator detected (period = " + str(per) + ")")
This program is very simple, and so does not separate sub-patterns from the overall pattern. What might be better is to perform a census on radially symmetric patterns so that ones consisting of disjoint objects (such as the p5 octagon and some blinkers) are not counted as a single high period oscillator. This is the reason for the removal of period 10 in the search results. The same idea can be applied to different symmetry types, such as odd 90-degree radial symmetry, bilateral symmetry, etc. and could be made to save files for results rather than just stop.

Edit: I found some oscillators in other rules with this (probably nothing new). Note, some of these were found with an odd-width symmetry search.

#C A period 8 oscillator in 2x2
x = 10, y = 10, rule = B36/S125
6bo$3bo3bo$b3o3bo$o6b2o3$b2o6bo$2bo3b3o$2bo3bo$3bo!

#C Some oscillators in Day and Night
x = 166, y = 70, rule = B3678/S34678
23bobobo21b2ob2o$22bob3obo19bob3obo73b3o$21bob5obo18b7o70b2o3bo$22b7o
20b5o71bob3o$21b9o18b7o70bob3obo$22b7o19bob3obo72b3obo$21bob5obo19b2ob
2o72bo3b2o$22bob3obo97b3o$23bobobo20$156b2o$155bo2bo$154bob2obo$27b2o
125b6o$23bob3o50bo2bob2o67bob6obo$22b8o46b9o69b6o$20bob9o17bobo2bobo
20b8o66b14o$4o16b10o19b6o17b2ob10o19bob2o41bob12obo$4o17b10o17b8o16b
15o16b3ob2o39bob14obo$4o17b10o18b6o18b14o16bo3bo40bob14obo$4o18b10o17b
6o17b16o16bo3bo40bob12obo$21b9obo16b8o17b14o16b2ob3o41b14o$22b8o19b6o
18b14o17b2obo46b6o$24b3obo19bobo2bobo16b16o64bob6obo$23b2o48b14o67b6o$
73b15o66bob2obo$75b10ob2o67bo2bo$76b8o72b2o$75b9o$75b2obo2bo5$48bob2ob
o$47bobo2bobo$47b8o$48b6o$47b8o$43b2ob10ob2o$42bob14obo$43b16o$42bob
14obo$42bob14obo$43b16o$42bob14obo$43b2ob10ob2o$47b8o$48b6o$47b8o$47bo
bo2bobo$48bob2obo!
-Matthias Merzenich
Sokwe
Moderator
Posts: 3401
Joined: July 9th, 2009, 2:44 pm

Re: Quadsymm Python Script

Post by Sokwe »

I performed some searches on Conway's game of Life to see what I could find. I didn't find any new oscillators, but I was quite pleased to see Gabriel's p138 and some other familiar oscillators appear in the results. I also found a period 96/2 agar, and was wondering if it was new (it's so simple, I would assume not). On that note, does anyone have any large agar collections?

#C Some oscillators
x = 186, y = 41, rule = B3/S23
32b3o3b3o2$30bo4bobo4bo$30bo4bobo4bo$30bo4bobo4bo$32b3o3b3o2$32b3o3b3o
$30bo4bobo4bo$30bo4bobo4bo$30bo4bobo4bo2$32b3o3b3o100bo$141bobo29b3o$
142bobo27bo2bo$142bo2bo26bo2bo$35b2o105bobo27b2o$18bo15bo2bo34b2o18b2o
17b2o28bobo$2o2b2o10bobo15b2obo31b2o3bo18bo16bobo28bo$obo2bo14bo16bob
2o13bo2bo10b3obob2o12b3o2b2o14b2obo4bo14bo$4bo9b2o16b4obobobo11bob2obo
8bo4bob2o10b3obo3bo16bo5b2o12bobo48b3o$bo18b2o9bo9bo12bo2bo9bob2o15bo
4bob2o21bo2bo10bo3bo47bo2bo$o2bobo9bo15bobobob4o13bo2bo15b2obo13bobo
24b3o11b3o51bo$2o2b2o11bobo12b2obo17bob2obo9b2obo4bo11b2obo4bo32b2o3b
2o29b3o15b3o$17bo17bob2o15bo2bo10b2obob3o12bo3bob3o10b3o36b2o3b2o12bo$
35bo2bo30bo3b2o13b2o2b3o12bo2bo37b3o14bo2bo$36b2o32b2o17bo18b2o5bo31bo
3bo14b3o$89b2o18bo4bob2o30bobo$114bobo32bo$114b2o24bo$36bo101bobo36b2o
$35b3o99bobo35bo2bo$33b3ob3o96bo2bo35bo2bo$33bo5bo97bobo35b3o$32b2o5b
2o97bobo$31b2o7b2o98bo$32b2o5b2o$33bo5bo$33b3ob3o$35b3o$36bo!

#C period 96/2 agar
x = 191, y = 225, rule = B3/S23
59$52bo5bo47bo5bo$51b3o3b3o45b3o3b3o$51bob2ob2obo45bob2ob2obo$76b2o9b
2o41b2o9b2o$75b2o11b2o39b2o11b2o$76b2o9b2o41b2o9b2o$77bo9bo43bo9bo2$
77bo9bo43bo9bo$76b2o9b2o41b2o9b2o$75b2o11b2o39b2o11b2o$76b2o9b2o41b2o
9b2o$51bob2ob2obo45bob2ob2obo$51b3o3b3o45b3o3b3o$52bo5bo47bo5bo13$79bo
5bo47bo5bo$78b3o3b3o45b3o3b3o$78bob2ob2obo45bob2ob2obo$49b2o9b2o41b2o
9b2o$48b2o11b2o39b2o11b2o$49b2o9b2o41b2o9b2o$50bo9bo43bo9bo2$50bo9bo
43bo9bo$49b2o9b2o41b2o9b2o$48b2o11b2o39b2o11b2o$49b2o9b2o41b2o9b2o$78b
ob2ob2obo45bob2ob2obo$78b3o3b3o45b3o3b3o$79bo5bo47bo5bo13$52bo5bo47bo
5bo$51b3o3b3o45b3o3b3o$51bob2ob2obo45bob2ob2obo$76b2o9b2o41b2o9b2o$75b
2o11b2o39b2o11b2o$76b2o9b2o41b2o9b2o$77bo9bo43bo9bo2$77bo9bo43bo9bo$
76b2o9b2o41b2o9b2o$75b2o11b2o39b2o11b2o$76b2o9b2o41b2o9b2o$51bob2ob2ob
o45bob2ob2obo$51b3o3b3o45b3o3b3o$52bo5bo47bo5bo13$79bo5bo47bo5bo$78b3o
3b3o45b3o3b3o$78bob2ob2obo45bob2ob2obo$49b2o9b2o41b2o9b2o$48b2o11b2o
39b2o11b2o$49b2o9b2o41b2o9b2o$50bo9bo43bo9bo2$50bo9bo43bo9bo$49b2o9b2o
41b2o9b2o$48b2o11b2o39b2o11b2o$49b2o9b2o41b2o9b2o$78bob2ob2obo45bob2ob
2obo$78b3o3b3o45b3o3b3o$79bo5bo47bo5bo!
-Matthias Merzenich
knightlife
Posts: 566
Joined: May 31st, 2009, 12:08 am

Re: Quadsymm Python Script

Post by knightlife »

Another type of symmetry rotates the pattern 180 degrees.

Bi_symm180:

Code: Select all

# Bi_symm180
# by knightlife -- Sept 2009
# Create 2-way symmetry (rotate 180) using the current selection.
# Current selection becomes the left half of resulting pattern.
# based on invert.py script from golly 2.0

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

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

oldsecs = time()
maxstate = g.numstates() - 1

for row in xrange(r.top, r.top + r.height):
   # if large selection then give some indication of progress
   newsecs = time()
   if newsecs - oldsecs >= 1.0:
      oldsecs = newsecs
      g.update()

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

   for col in xrange(r.left, r.left + r.width):
      g.setcell(r.left + 2*r.width - 1 - (col - r.left), r.top + r.height - 1 - (row - r.top), g.getcell(col, row))

if not r.visible(): g.fitsel()
In less than a minute, found this precursor for a paperclip:

x = 17, y = 7, rule = B3/S23
12bo$b2o2b3o3bo$bobobo2bobo2b2o$bobobo2b2o2bobobo$3b2o2bobo2bobobo$6bo
3b3o2b2o$5bo!

To make the symmetry odd, take out the first "-1" in the g.setcell command.
There are still more types of symmetry, I will combine them into one script when I have more time.
Last edited by knightlife on September 19th, 2009, 5:27 pm, edited 1 time in total.
User avatar
apg
Moderator
Posts: 3007
Joined: June 1st, 2009, 4:32 pm

Re: Quadsymm Python Script

Post by apg »

Please can you rename the script? Bilateral symmetry generally refers to reflectional symmetry.

http://en.wikipedia.org/wiki/Symmetry_i ... l_symmetry

At least, I hope that vertebrates aren't rotationally symmetric where you come from. :)
What do you do with ill crystallographers? Take them to the mono-clinic!
knightlife
Posts: 566
Joined: May 31st, 2009, 12:08 am

Re: Quadsymm Python Script

Post by knightlife »

The script has been renamed.

I've got to stop naming scripts according to vertebrates in my area! :)
Post Reply