Script request thread

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

Re: Script request thread

Post by dvgrn » March 7th, 2018, 7:42 pm

77topaz wrote:How about a script that takes a single switch engine and then tries every possible combination of that switch engine plus N objects from a certain list of small stable objects (e.g. block, blinker, beehive, etc.) and reports when it finds something interesting like a stable puffer?]
This sounds like a minor modification of A for awesome's script for finding the 2-engine Cordership, mixed with the exhaustive constellation enumeration script that I used to check for switch-engine seeds a while back:

Code: Select all

# build-constellation-v13b.py
# Still a work in progress -- it should really work with more than two blinkers,
#    and write to a file instead of messing with the clipboard for output,
#    and probably output RLE instead of cell lists

import golly as g
from time import sleep
from glife.text import make_text

MAXOBJ=2
xsize,ysize=6,9

def getinp(s):
###########################
  temp=g.getcell(x,y)
  g.setcell(x,y,5)
  g.show(s+"Ptr:"+str(ptr)+" x:" +str(x)+" y:"+str(y))
  g.fit()
  g.update()
  g.setcell(x,y,temp)
  # return
  k=g.getevent()
  count=0
  while k=="":
    sleep(.01)
    k=g.getevent()
    count+=1
  if k=="key q none": g.exit()
  return
###########################

patdict={}
# for i in range(2,xsize+1):
#   for j in range(2,ysize+1):
#     patdict[str([i,j])]=[]
objs=["2o$2o!","2o$obo$bo!","b2o$obo$bo!","bo$obo$b2o!","bo$obo$2o!", # block and boat
      "b2o$o2bo$b2o!","bo$obo$obo$bo!","bo$obo$bo!","b2o$o2bo$o2bo$b2o!", # beehive, tub, pond
      "2o$obo$b2o!", "b2o$obo$2o!", "b2o$o2bo$obo$bo!", # ship, loaf1
      "b2o$o2bo$bobo$2bo!","2bo$bobo$o2bo$b2o!","bo$obo$o2bo$b2o!", # other loaves
      "2o$bo$bobo$2b2o!","3bo$b3o$o$2o!","2o$obo$2bo$2b2o!","2b2o$3bo$3o$o!", # one chirality of eater
      "2o$o$b3o$3bo!","2b2o$bobo$bo$2o!","o$3o$3bo$2b2o!","2b2o$2bo$obo$2o!", # the other chirality of eater
      "2o$obo$bobo$2bo!","2b2o$bobo$obo$bo!","bo$obo$bobo$2b2o!","2bo$bobo$obo$2o!", # longboats
      "bo$3o$bo!"]  # and last but not least, the weird case -- blinkers
objlist=[]
for i in range(len(objs)):
  # normalize so that the first ON cell in the list is always (0,0)
  templist=g.parse(objs[i])
  objlist+=[g.transform(templist,-templist[0],-templist[1])]
numobjs=len(objlist)
zonelist=[]
for item in objlist:
  g.setrule("B12345678/S012345678")
  neighbors=g.evolve(item,1)
  g.setrule("B2345678/S012345678")
  zone=g.evolve(neighbors,1)
  zonelist+=[zone]    # includes cells for object also

g.setrule("LifeHistory")
nearlist=[[i,j] for i in range(-1,xsize+1) for j in range(-1,ysize+1) if i<0 or i>=xsize or j<0 or j>=ysize]
count,x,y,ptr,filledlist,searchlist=0,0,0,0,[],[]
while y==0 or len(searchlist)>0:
  overlap=([x,y] in nearlist)
  # place current object
  #############################################
  # TODO:  if there's an overlap, set ptr to max value, then handle all cases with same code at the bottom
  if overlap==0:
    # g.show(str(ptr))
    obj=g.transform(objlist[ptr],x,y)
    objpairs=[[obj[i],obj[i+1]] for i in range(0,len(obj),2)]
    for item in objpairs:
      if item in nearlist:
        overlap=1
        break
    if overlap==0:
      zone=g.transform(zonelist[ptr],x,y)
      zonepairs=[[zone[i],zone[i+1]] for i in range(0,len(zone),2)]
      for item in zonepairs:
        if item in filledlist:
          overlap=2
          break
      if overlap==0:
        g.new("TestPage")
        newptr,newx,newy=ptr+1,x,y
        if newptr>=len(objlist): newptr,newx=0,newx+1
        if newx>=xsize-1: newptr,newx,newy=0,0,y+1
        searchlist.append([newx,newy,newptr,nearlist[:],filledlist[:]]) # add next state to search to the stack
        nearlist+=zonepairs
        filledlist+=objpairs
        for i,j in nearlist: g.setcell(i,j,2)
        minx, maxx, miny, maxy = 999,-999,999,-999
        for i,j in filledlist:
          g.setcell(i,j,1)
          if i<minx:minx=i
          elif i>maxx:maxx=i
          if j<miny: miny=j
          elif j>maxy: maxy=j          
        # Take a snapshot of the current successful placement
        # [e.g., successful if two objects placed, etc.]
        if minx==0 and miny==0:
          keystr=str(maxx+1)+"x"+str(maxy+1)
          if keystr not in patdict: patdict[keystr]=[]
          if filledlist not in patdict[keystr]: patdict[keystr]+=[filledlist[:]]
        # Now continue searching from where we are
        count+=1
        if count%100==0:
          g.show(str(count))
          g.update()        
        ptr,x=0,x+4
      else:  # the neighbor zone for this object overlaps some already placed object
        ptr+=1
        if ptr>=len(objlist): ptr,x=0,x+1
    else: # the object itself overlaps some already placed object's neighborhood
      ptr+=1
      if ptr>=len(objlist): ptr,x=0,x+1
  else:
    ptr,x=0,x+1
  # getinp("Overlap type " + str(overlap) + ".  Lenlist="+str(len(searchlist))+".")
  if x>=xsize-1: ptr,x,y=0,0,y+1
  if y>=ysize-1 or len(searchlist)>=MAXOBJ:
    # we've reached the end of the frame.
    # Time to clear the last placed object, go back to that x,y
    if searchlist==[]:
      g.new("TestPage")
      g.exit("Search is complete.")
    x,y,ptr,nearlist,filledlist=searchlist.pop()

# no point in looking at configurations with no object on the first line
g.new("TestPage")
outstr="Total count = " + str(count)
t=make_text(outstr,"mono")
g.putcells(t,0,-20)
s=""
for item in sorted(patdict):
  y=0
  t = make_text(item + " ("+str(len(patdict[item]))+")","mono")
  outstr+="\n" + item + " ("+str(len(patdict[item]))+")"
  g.putcells(t,x-6,y)
  y+=30
  for pat in patdict[item]:
    blinkercenters = []
    for i,j in pat:
      if [i-1,j] in pat and [i+1,j] in pat and [i,j-1] in pat and [i,j+1] in pat:
        blinkercenters+=[[i,j]]
    if len(blinkercenters)>0:
      if len(blinkercenters)==1:
        i,j = blinkercenters[0]
        patphase1=[coord for coord in pat if coord not in [[i-1,j],[i+1,j]]]
        patphase2=[coord for coord in pat if coord not in [[i,j-1],[i,j+1]]]
        s+=str(patphase1)+"\n"+str(patphase2)+"\n"
        for cell in patphase1:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
        for cell in patphase2:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
      elif len(blinkercenters)==2:
        i,j = blinkercenters[0]
        m,n = blinkercenters[1]
        patphase1=[coord for coord in pat if coord not in [[i-1,j],[i+1,j],[m-1,n],[m+1,n]]]
        patphase2=[coord for coord in pat if coord not in [[i,j-1],[i,j+1],[m-1,n],[m+1,n]]]
        patphase3=[coord for coord in pat if coord not in [[i-1,j],[i+1,j],[m,n-1],[m,n+1]]]
        patphase4=[coord for coord in pat if coord not in [[i,j-1],[i,j+1],[m,n-1],[m,n+1]]]
        s+=str(patphase1)+"\n"+str(patphase2)+"\n"+str(patphase3)+"\n"+str(patphase4)+"\n"
        for cell in patphase1:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
        for cell in patphase2:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
        for cell in patphase3:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
        for cell in patphase4:
          g.setcell(cell[0]+x,cell[1]+y,1)
        y+=20
      else:
        g.note("The general case with more than two blinkers is not yet handled in this code.  Skipping this configuration.")
    else:
      s+=str(pat)+"\n"
      for cell in pat:
        g.setcell(cell[0]+x,cell[1]+y,1)
      y+=20
  x+=100
g.note("Search is complete.")
g.setclipstr(s)
It seems as if a fairly high percentage of what would be tested by this hypothetical script has already been tested by Catagolue / apsearch by this time. But no doubt there's some unsearched space out there somewhere.

User avatar
77topaz
Posts: 1496
Joined: January 12th, 2018, 9:19 pm

Re: Script request thread

Post by 77topaz » March 7th, 2018, 8:06 pm

Well, with a high enough N it should break new territory. Anyway, it's the only feasible way I can think of to search for a potential one-engine Cordership.

Also, I don't quite know enough Python to be able to edit those scripts at the moment (though I am learning mathematical Python for a class at my university).

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

Re: Script request thread

Post by dvgrn » March 7th, 2018, 10:50 pm

77topaz wrote:Well, with a high enough N it should break new territory. Anyway, it's the only feasible way I can think of to search for a potential one-engine Cordership.
Really there's probably no need to enumerate still lifes -- though that would be the easy part: you could choose your N and bounding box and write all the possibilities to a file, using the enumeration script, then read the file in with a second script and test for stable puffers using A for Awesome's method.

However, is there anything really special about still lifes? If there's a one-engine Cordership out there, or even a third stable orbit for a switch engine to go along with the block-laying and glider-producing varieties, then necessarily it will have to appear eventually if you enumerate all possible patterns of cells in a region around and behind the switch engine.

So you *could* just start at the beginning and try everything -- number the cells near the switch engine that you want to try searching, and just start producing patterns by counting in binary. You'll only get to just so many cells before you run out of time and CPU power, but at least you'll be able to report confidently that no arrangements of N cells in such-and-such configuration evolve into a new switch-engine puffer (or ship). Still life arrangements are a really small subset of the possible configurations that could evolve into a New Switch Engine Thing, and they're not even the most likely subset.

If the smallest single-engine Cordership is some huge artificial thing, though, like the diagonal c/12 Caterloopillar support that Calcyman suggested a while back, then it's not going to be found with an exhaustive search. Based on Catagolue results so far, it does seem fairly likely that there really are no new switch-engine orbits within reach of any reasonable search. (As usual, I will be very happy to be proved wrong.)
Also, I don't quite know enough Python to be able to edit those scripts at the moment (though I am learning mathematical Python for a class at my university).
I think that everyone I know of who is good at solving problems with Python, got that way not by taking classes, but just by having problems they really wanted to solve, and jumping in and solving them. The Internet, and particularly StackOverflow these days, is an impressively good resource for learning programming languages. Almost every Python mystery ever encountered seems to be documented in a StackOverflow question.

User avatar
77topaz
Posts: 1496
Joined: January 12th, 2018, 9:19 pm

Re: Script request thread

Post by 77topaz » March 7th, 2018, 11:18 pm

I didn't specify still lifes, I said stable objects, and my mini-list of examples included the blinker. :P That's just a way of avoiding simulating umpteen one-cell sparks that instantly vanish anyway, or preblocks that immediately turn into blocks.

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

Re: Script request thread

Post by dvgrn » March 8th, 2018, 9:52 am

77topaz wrote:I didn't specify still lifes, I said stable objects, and my mini-list of examples included the blinker. :P That's just a way of avoiding simulating umpteen one-cell sparks that instantly vanish anyway, or preblocks that immediately turn into blocks.
Blinkers are somewhat supported by the current script, but it's a very ugly hack that only works for up to two blinkers. More custom code would be needed to add other common p2 stuff like beacons and toads.

For a one-switch-engine search, another limitation of the script is that it only builds constellations of well-separated objects, no pseudo-objects like bi-blocks -- so that the constellations will be constructible by slow gliders, which you don't care about. And with a big enough area you might want to include p3 and higher-period objects, and that's pretty far beyond the scope of what the enumeration script was intended to handle.

On the other hand, it's going to be pretty easy to enumerate a large enough search space that you won't get to the end of testing it for several centuries. So maybe it doesn't matter which search space you never get to the end of, as long as you don't run out of new cases to test.

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

Re: Script request thread

Post by dvgrn » March 9th, 2018, 11:28 pm

Extrementhusiast wrote:What's the most efficient way to store all cells in one layer corresponding to cells in another layer into a cell list? (Or, in other words, is there a way to perform a modified AND-paste where a (possibly specific) nonzero cell state in the pattern being pasted keeps the cell underneath, whereas a state 0 cell in such turns the cell underneath to state 0?)
... is there a third method that beats them both?
Sorry, didn't see this when it was originally posted. Can you separate out the state-0 cells and the nonzero cells into two separate cell lists? If you could, then you could do what you want pretty quickly in two steps -- a regular AND paste with the non-zero cells, followed by a copy-mode paste of the zero cells. Here's a list of state-0 cells blanking out part of a pattern:

Code: Select all

import golly as g

pat1 = g.parse("9o$9o$9o$9o$9o$9o$9o$9o$9o!")
pat2 = g.parse(".A$2.A$3A!")

for i in range(0, len(pat2), 3):
  pat2[i+2]=0

g.new("Test")
g.putcells(pat1, -3, -3)
g.putcells(pat2,0,0,1,0,0,1,"copy")
As Scorbie suggests, though, it's probably a good idea to let a rule table do as much of the work as possible. If the "corresponding layer" contained cells with states, say, 8 and 16 instead of 1 and 2, then a single XOR paste would give you a result that you could then clean up however you wanted with a custom rule.

User avatar
2718281828
Posts: 738
Joined: August 8th, 2017, 5:38 pm

Re: Script request thread

Post by 2718281828 » March 12th, 2018, 7:20 am

hkoenig wrote:I took the 3-Glider collisions data and created a database from it.

Each record contains fields for the header, the rle (including the bounds), a modified apgcode (no prefix) the locations, directions and phases of each glider, the number of generations until stability (up to 200), an object count, and the final census (including location).
Thanks for dealing with my messy 3g collisions. I am still working on my spaceship collision script.

If I got it correctly you determined the 'number of generations until stability'. A python script which returns me such a number would be great. Is there something to use from apgsearch? I would also need something which gives me the period of a stable pattern, so e.g. 384 for a glider-producing switch engine.

User avatar
2718281828
Posts: 738
Joined: August 8th, 2017, 5:38 pm

Re: Script request thread

Post by 2718281828 » March 19th, 2018, 11:59 pm

I would need a python function that returns me the RLE-string of a pattern, e.g. in this example

Code: Select all

patR = g.parse("b2o$2o$bo!")
g.run(5)
patternOfInterest = g.select(g.getrect())
just "2bo$2ob2o$o2bo$b2o!". I know I can save the pattern as a file, and then read the string from that file, but I think this is not really efficient as I will deal with many small patterns.

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

Re: Script request thread

Post by wildmyron » March 20th, 2018, 12:37 am

2718281828 wrote:I would need a python function that returns me the RLE-string of a pattern, e.g. in this example

Code: Select all

patR = g.parse("b2o$2o$bo!")
g.run(5)
patternOfInterest = g.select(g.getrect())
just "2bo$2ob2o$o2bo$b2o!". I know I can save the pattern as a file, and then read the string from that file, but I think this is not really efficient as I will deal with many small patterns.
Here is my version of giveRLE which includes a modification by dvgrn and a change I made to sorting for performance reasons (performance only tested with small patterns, the Golly method may be faster for large patterns). Input is a Golly cell list, output is the rle encoded string without header.

Code: Select all

# Python function to convert a cell list to RLE
# Author: Nathaniel Johnston (nathaniel@nathanieljohnston.com), June 2009.
#           DMG: Refactored slightly so that the function input is a simple cell list.
#                No error checking added.
#                TBD:  check for multistate rule, show appropriate warning.
#           AJP: Replace g.evolve(clist,0) with Python sort
# --------------------------------------------------------------------
def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i+n]

def giveRLE(clist):
    # clist_chunks = list (chunks (g.evolve(clist,0), 2))
    clist_chunks = list(chunks(clist, 2))
    clist_chunks.sort(key=lambda l:(l[1], l[0]))
    mcc = min(clist_chunks)
    rl_list = [[x[0]-mcc[0],x[1]-mcc[1]] for x in clist_chunks]
    rle_res = ""
    rle_len = 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+"!"
# --------------------------------------------------------------------
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

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

User avatar
jimmyChen2013
Posts: 184
Joined: December 11th, 2017, 3:28 am

Re: Script request thread

Post by jimmyChen2013 » March 20th, 2018, 10:17 pm

Need a pop-plot script but for all states in other CAs. custom color based on the state's color would be nice, but I'm ok without it
This would be really helpful

Code: Select all

x = 8, y = 13, rule = B3aeiqr4-aijn5c6cei7/S2cn3-ajr4ceiqt5eijkq6-a7c8
2bo$b3o$5o$b5o$2b5o$3b5o$2b5o$b5o$5o$4o$3o$2o$o!

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

Re: Script request thread

Post by Andrew » March 21st, 2018, 5:59 am

2718281828 wrote:I would need a python function that returns me the RLE-string of a pattern
If the pattern is in a selection then it's very easy:

Code: Select all

def selection_to_rle():
    oldclip = g.getclipstr()
    g.copy()
    rledata = g.getclipstr()
    g.setclipstr(oldclip)
    return rledata
Use Glu to explore CA rules on non-periodic tilings: DominoLife and HatLife

User avatar
77topaz
Posts: 1496
Joined: January 12th, 2018, 9:19 pm

Re: Script request thread

Post by 77topaz » March 30th, 2018, 1:20 am

Are there any ntgfind/ntzfind-like search programs that run in Lua or Python?

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » March 31st, 2018, 10:32 pm

I was playing with the meta-meta-blinker pattern using OTCA metapixels earlier, but obviously trying to generate meta-patterns that large within Golly causes RAM issues pretty quickly. Are there any scripts out there for generating huge meta-patterns outside Golly?

While we're at it, are there scripts either inside or outside Golly to generate meta-patterns using other unit cells, like the Deep Cell or W110 unit cell?

Edit: After some more forum searches, I found helpful scripts for W110 unit cells and David Bell's unit cells here, thank you Naszvadi: http://conwaylife.com/forums/viewtopic.php?f=9&t=2604

Still looking for a script for Deep Cells, and OTCA or P1 scripts that don't crash Golly on large patterns, if anyone is able to help :)
Last edited by thorsilver on April 1st, 2018, 12:50 am, edited 1 time in total.

User avatar
77topaz
Posts: 1496
Joined: January 12th, 2018, 9:19 pm

Re: Script request thread

Post by 77topaz » March 31st, 2018, 10:50 pm

Golly does have a built-in script for what you're describing, metafier.lua/metafier.py, in the Scripts folder.

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » March 31st, 2018, 11:21 pm

Maybe I'm missing something, but don't those scripts only tile the OTCA metapixels, not Deep Cells or any of the other unit cell types? I'm wondering if there are scripts doing the same thing but for other unit cells (edit: just Deep Cells now).

Also those scripts run within Golly, and several people in other threads I found mentioned that due to the way Golly encodes the meta-patterns RAM usage blows up really quickly, so it's best to generate them outside Golly -- but I can't find a script that allows me to do this.

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

Re: Script request thread

Post by dvgrn » April 1st, 2018, 8:48 am

thorsilver wrote:Also those scripts run within Golly, and several people in other threads I found mentioned that due to the way Golly encodes the meta-patterns RAM usage blows up really quickly, so it's best to generate them outside Golly -- but I can't find a script that allows me to do this.
So far I believe there have been only two or three pieces of code written to do this kind of thing. The first is a Perl script written by Brice Due a long time ago (2006):
In 2010, dvgrn wrote:Brice wrote a Perl script to generate all the metapatterns in Golly's current collection (and they're probably much more compact than what metafier.py puts out -- if I were starting over today, I'd definitely write out .mc files directly instead of pasting into Golly). Brice's script can be found at

http://cranemtn.com/life/weblog/tileotcamp9pl.zip

(link is in http://b3s23life.blogspot.com/2006/09/b ... pixel.html ).

The biggest problem with meta^n-pixel patterns was always that they look like an undifferentiated ON block at any useful scale. To fix that we'd have to improve Golly to aggregate its population counts somehow for larger tiles -- maybe some kind of population percentage instead of a single bit showing ON-cell presence or absence for each tile? Then you'd probably have to adjust the mapping of percentage to color for every different zoom level and every different-density pattern, though.

... My, how time flies when you're having fun. I should probably really write a new Python version that writes .mc files directly, starting with Golly's LifeHistory rule instead of MCell's HistoricalLife -- and I might actually do it, too, but I'll be busy building Geminoid spaceships for a while yet. Maybe next year --
The second is some sample code calcyman wrote calling his GoL API library to make the meta-meta-blinker, which I was able to find in another old email:
In December 2016, calcyman wrote:I've been testing some new functionality of my GoL API, and wrote an example program to generate a meta-meta-blinker (not least because this was proposed on LifeCA a few years ago, and was apparently beyond Golly's capabilities back then):

Code: Select all

    apg::lifetree<uint32_t, 2> lt(1500);

    apg::pattern offcell(&lt, "metacell-off.mc");
    apg::pattern oncell(&lt, "metacell-on.mc");

    offcell = offcell.centre();
    oncell = oncell.centre();

    apg::pattern blinker(&lt, lt.rectangle(3, 1), "b3s23History");
    blinker = blinker[8];

    apg::pattern metablinker = blinker.metafy(oncell, offcell);
    metablinker = metablinker["b3s23History"][262144];

    apg::pattern mmblinker = metablinker.metafy(oncell, offcell);
    lt.write_macrocell(std::cout, mmblinker.gethnode());

    return 0;
This takes about 2 seconds to produce the attached pattern file on my laptop. I still have quite a bit of work before the metafy() interface is polished (ideally it will support varargs for multi-state patterns). Behind the scenes, metafy() chops each of the ON- and OFF-metacells into four quadrants -- because OTCAmetapixels overlap slightly -- and makes four separate calls to tensor() for tiling non-overlapping patterns. Then, the four resulting tilings are combined (using shifts and Boolean OR) to give the complete metafied pattern.

The example code above uses metafy() twice, once for the blinker and once for the metablinker. It's able to produce the meta-meta-blinker, and Golly is capable of running it at a step size of 8^5 at one step per second (equating to a period of roughly one day).

...These metapatterns are easily manipulable provided they're kept as compressed hashed quadtrees rather than as flat RLE files.
Then I think KZhang of the Quest for Tetris project ended up having to write something similar, to finish putting together the Tetris simulator in a reasonable amount of time.

There are some notes in one of the metacell threads about the next steps someone would have to take to complete an efficient Golly Python megafier script. It won't be terribly difficult, just a bit tedious.

It will be much easier to write a macrocell metafier script for the Quest-for-Tetris Hashlife-friendly metacell, once that gets completed -- at least, I can try to design it so that there aren't any pieces that hang over the 512x512 area.

User avatar
thorsilver
Posts: 13
Joined: March 31st, 2018, 9:43 pm
Location: Glasgow, Scotland

Re: Script request thread

Post by thorsilver » April 1st, 2018, 4:27 pm

dvgrn wrote: So far I believe there have been only two or three pieces of code written to do this kind of thing. The first is a Perl script written by Brice Due a long time ago (2006):
This is brilliant, thank you! :) Just to road-test the Perl script, I used it to metafy a 16x16 Deep Cell test pattern. It worked perfectly, generated a 1.1MB mc file with 1.46 trillion cells! It ran just fine, although of course there's the problem you mentioned in that old post that the pattern becomes an undifferentiated square of colour beyond a certain zoom level. Is there any way to tweak Golly to reduce that, or is that still in the works?
dvgrn wrote: The second is some sample code calcyman wrote calling his GoL API library to make the meta-meta-blinker, which I was able to find in another old email:
Honestly I've not really got a clue what's going on in that code snippet, or how to use it, but I'd be curious if you have any more info on it :)
dvgrn wrote: Then I think KZhang of the Quest for Tetris project ended up having to write something similar, to finish putting together the Tetris simulator in a reasonable amount of time.
Yes, I have that script as well and plan to study it for some ideas. It's made to metafy VarLife patterns specifically, I think, so it will take some tweaking to make it usable more generally. But it did generate the sizable Tetris pattern without a hitch.
dvgrn wrote: There are some notes in one of the metacell threads about the next steps someone would have to take to complete an efficient Golly Python megafier script. It won't be terribly difficult, just a bit tedious.

It will be much easier to write a macrocell metafier script for the Quest-for-Tetris Hashlife-friendly metacell, once that gets completed -- at least, I can try to design it so that there aren't any pieces that hang over the 512x512 area.
Well that sounds exciting! How's that metacell coming along? I'd love to help but I'm just getting back into GoL after not playing with it since high school... catching up now, it's incredible to see what's been accomplished! The metacells are particularly fascinating for me. If there's anything a newbie could do to help, even just stress-testing patterns or tedious little experiments, please let me know!

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

Re: Script request thread

Post by dvgrn » April 2nd, 2018, 5:24 pm

thorsilver wrote:Well that sounds exciting! How's that metacell coming along? I'd love to help but I'm just getting back into GoL after not playing with it since high school... catching up now, it's incredible to see what's been accomplished! The metacells are particularly fascinating for me. If there's anything a newbie could do to help, even just stress-testing patterns or tedious little experiments, please let me know!
The metacell is "all done" except for the painful annoying wiring part -- have to somehow pack eight outputs in eight different directions, and the equivalent inputs from neighboring metacells, into the remaining space in the latest 512x512 blueprint. Or give up on that and move up to 1024x1024, but it still looks doable at 512x512.

But speaking of "tedious little experiments", there is definitely no shortage of such things waiting to be done. Just for example, it would be all kinds of wonderful if someone would do a fresh survey of known Herschel-to-glider converters, and make a more complete collection of H-to-Gs according to their classification. I can explain in more detail if anyone wants to try tackling the rest of that project.

Or another example -- somebody should really finish developing the universal toolkit that would allow any arbitrary glider salvo to be converted into a 1-glider seed for that salvo. Here again we have all the pieces, just need to get them organized so that a compiler script can be written. It doesn't have to be the best possible solution, since it's pretty much impossible to find that anyway -- just any automated solution-finder would be fun to have around.

(And then optionally you could turn four salvos into a Life integer, I suppose, with a few more extensions to the same toolkit.)

Let me know if you want me to go on and on with more ideas like these. As other folks around here can tell you, I'm good at writing too much and scaring people off -- so I'm trying to limit myself to the standard five paragraphs here.

EDIT: For the metacell project specifically, I've put together a collection of the remaining mechanisms needed to complete the circuitry. It's really mostly just a matter of doing a lot of fidgety experimental adjustments now, to get the metacell working. (We can try to find space for some kind of metapixel display later.)

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » April 18th, 2018, 11:23 am

Don't know where to put this, but can this script be optimized?

Code: Select all

--dani, 2018
--this script finds numbers such that the sum of digits of n and n^2 is equal
--(i.e. 176418^2=31123310724, 1+7+6+4+1+8 = 3+1+1+2+3+3+1+0+7+2+4 = 27)
--there are infinitely many numbers, like the series 1, 10, 100, 1000, ...
--find these numbers at http://oeis.org/A058369

x = 0 --number first being tested, gets increased
count = 1000 --amount of results

while count ~= 0 do
    y = x ^ 2 --square x
    cx = 0 --count of x
    cy = 0 --count of square x
    for i = 1,string.len(x) do
        cx = cx + tonumber(string.sub(x,i,i))
    end
    for i = 1,string.len(y) do
        cy = cy + tonumber(string.sub(y,i,i))
    end
    if cx == cy then
        count = count - 1
        print(x .. " is applicable. (" .. count .. " left, " .. cx .. "==" .. cy .. ")")
    end
    x = x + 1
end

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

Re: Script request thread

Post by dvgrn » April 18th, 2018, 12:12 pm

danny wrote:Don't know where to put this, but can this script be optimized?
Sure! Just for starters, here's a math trick that seems to speed things up by about a factor of three.

String manipulation is a lot slower than integer math, so it's worth doing a quick test on each number and its square before you start working through digit by digit. There's no point in doing those sums if the mod-9 residues are different:

Code: Select all

--dani, 2018
--this script finds numbers such that the sum of digits of n and n^2 is equal
--(i.e. 176418^2=31123310724, 1+7+6+4+1+8 = 3+1+1+2+3+3+1+0+7+2+4 = 27)
--there are infinitely many numbers, like the series 1, 10, 100, 1000, ...
--find these numbers at http://oeis.org/A058369

x = 0 --number first being tested, gets increased
count = 1000 --amount of results

while count ~= 0 do
    y = x ^ 2 --square x
    if x%9 == y%9 then 
      cx = 0 --count of x
      cy = 0 --count of square x
      for i = 1,string.len(x) do
          cx = cx + tonumber(string.sub(x,i,i))
      end
      for i = 1,string.len(y) do
          cy = cy + tonumber(string.sub(y,i,i))
      end
      if cx == cy then
          count = count - 1
          print(x .. " is applicable. (" .. count .. " left, " .. cx .. "==" .. cy .. ")")
      end
    end
    x = x + 1
end
After that, I don't know what the next likely target might be. Maybe do the digit sum all numerically somehow? Maybe calculate cx based on the previous value instead of doing the sum every time? Not sure either of those will be much faster, but no doubt there's something clever I haven't though of.

I have a question of my own, though: why does this code work fine on repl.it, but it errors when equivalent code is run in Golly? It looks like Golly's version of Lua is returning its squared values as floats, so there's an annoying ".0" at the end.

Given that the code is just doing integer multiplication, why is Golly (Lua version 5.3.4) returning a float, where repl.it (Lua version 5.1) does the reasonable thing and sends back an integer?

EDIT: Aha -- Lua 5.3 introduced an explicit integer subtype, and adds a ".0" to numbers with a float subtype. And exponentiation always results in a float -- see, e.g., page 19 of this presentation.

So for Lua 5.3, either of the following would patch things up:

Code: Select all

    y = x * x --square x

Code: Select all

        for i = 1,string.len(math.floor(y)) do

User avatar
Macbi
Posts: 903
Joined: March 29th, 2009, 4:58 am

Re: Script request thread

Post by Macbi » April 18th, 2018, 1:27 pm

A number is congruent to its own square mod 9 iff it's 0 or 1 mod 9. So instead of

Code: Select all

y = x ^ 2 --square x
if x%9 == y%9 then
    ...
you can do

Code: Select all

if x%9 in [0, 1] then --or whatever the lua version of that is
    y = x ^ 2 --square x
    ...
This saves having to square x every loop.

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » April 18th, 2018, 1:51 pm

Thank you both very much! is there any way to make it stop erroring out at 10^7? (set x to 10^7-1 and watch)

I also don't really know to do copyright stuff, but I guess it's free use since it's nothing too complex *shrug*

BCNU,
danny

EDIT: Oh, @Macbi, I used this for your suggestion:

Code: Select all

if x % 9 < 2 then

User avatar
Macbi
Posts: 903
Joined: March 29th, 2009, 4:58 am

Re: Script request thread

Post by Macbi » April 18th, 2018, 2:04 pm

danny wrote:EDIT: Oh, @Macbi, I used this for your suggestion:

Code: Select all

if x % 9 < 2 then
Yeah, checking size is definitely a better way to do it than by checking inclusion in a list.

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

Re: Script request thread

Post by dvgrn » April 18th, 2018, 2:44 pm

danny wrote:Thank you both very much! is there any way to make it stop erroring out at 10^7? (set x to 10^7-1 and watch)...
There's always a way. I have no idea what the best bignum library is for Lua -- lbc, maybe? Not sure if you can get that working easily on repl.it.

Basically you're running into an automatic conversion to scientific notation for squared numbers 10^14 and bigger Here's what string.sub() is returning for the value of y in the key range:

Code: Select all

99999900000025	
99999920000016	
99999940000009	
99999960000004	
99999980000001	
9999999 is applicable. (9 left, 63==63)	
1e+14
Adding up the digits isn't going to work too well on the string "1e+14". And Lua only keeps 14 digits after the decimal point, so you wouldn't get an accurate digit sum for squares much above that point anyway.

Multiplying each digit separately will be slower, but not terribly tricky especially for x up to 10^13 or so. Then you'd calculate the square by adding each product, one digit at a time, to a Lua table (with the start index appropriately shifted for each product). Probably there are lots of sneaky shortcuts, but I'm not clever enough to think of them offhand.

You could check if the number is over 10^7, and calculate the sum differently if so. But checking that on every cycle will waste some time -- maybe it's better to just run a different script if the count is going to hit 10^7.

dani
Posts: 1222
Joined: October 27th, 2017, 3:43 pm

Re: Script request thread

Post by dani » April 21st, 2018, 4:41 pm

Thank you, guys! I've been steadily getting used to Python instead. Once I made the switch, it was easier to compute numbers, as Python handles them very well. I wrote my first script, which checks if said number is one of these, although obviously programmatically.

Code: Select all

# powerz.py
# dani, 2018
# checks what number x gives when you multiply its digits, then compares it against what it gives when you multiply its square's digits. if they are equal, it prints and writes to results.txt (check that file for my search progress! <3)
# also special thanks to @schimatt for pretty much all of this, and helping me learn python in general c:
# this was my first real python script
with open("powerz_results.txt", "r") as res:
  x = int((list(res)[-1])) + 1
  num_res = sum(1 for line in open("powerz_results.txt"))
  print("last result:", x - 1,"is applicable. (#" + str(num_res) + ")")
# x = 0
while True:
  y = x ** 2
  cx = 1
  cy = 1
  if str(x).find("0") == -1 and str(y).find("0") == -1:
    for i in range(0,len(str(x))):
      cx = cx * int(str(x)[i:i + 1])

    for i in range(0,len(str(y))):
      cy = cy * int(str(y)[i:i + 1])

    if cx == cy:
      num_res += 1
      print(str(x) + " is applicable. (#" + str(num_res) + ", " + str(cx) + "==" + str(cy) + ")")
      with open("powerz_results.txt", "a") as res:
        res.write("\n" + str(x))
  x += 1
Currently I'm on the 548th of these numbers, and plan to compute at least 10000 over the next few months. I have this community to thank for bringing me over to the Python side, otherwise I would have just stuck to Lua and been banging my head on the desk in frustration.

You can try it out with the powerz_results.txt already set up at repl, but I'm wondering if there's anything I could optimize besides the 'if str(x).find("0") == -1 and str(y).find("0") == -1:' line. Thanks again!

Post Reply