Golly scripts

For scripts to aid with computation or simulation in cellular automata.
AlbertArmStain
Posts: 1077
Joined: January 28th, 2022, 7:18 pm
Location: Planet Z

Re: Golly scripts

Post by AlbertArmStain » April 7th, 2023, 2:23 pm

I’ve requested a kind of script for golly similar to octohash, but completely independent. It was mentioned here, but I really don’t know where to start. As mentioned before, it will be able to take an unstable object/component and find a way to make a 1g seed for it without using splitters.
calcyman wrote:
April 2nd, 2023, 8:37 am
dvgrn wrote:
March 31st, 2023, 5:48 pm
However, it now seems unlikely that any synchronized perpendicular gliders will be needed at all, let alone clock-inserter recipes. The script will probably be able to build a spacefiller seed using a much smaller toolkit of turners and splitters. So now we're just working out exactly what needs to be in the script's toolkit.
Consider the following list of eight OTTs, arranged in increasing order of estimated cost. They're all slmake-constructible in all orientations:

Code: Select all

# [[ ZOOM 2 THEME Book WIDTH 800 HEIGHT 480 ]]
x = 366, y = 35, rule = B3/S23
352bo$353b2o$351b2o$353bo4$359bo$359b3o$362bo$361b2o$204bo46bo99bo$
203bobo44bobo97bobo11b2o$107b2o95bobo44b2o96bobo12b2o$51b2o53bobo49b2o
45b2o48b2o92b2o$52bo52bobo50b2o50b2o42bobo$52bobo51bo102bo2bo41b2o103b
2o$6bo46b2o154bo2bo93bo40b2o10bobo$5bobo155b2o45b2o93bobo39bobo10bo$6b
2o155b2o141b2o40bobo$349bo3$313b2o$3o47b3o47b3o47b3o47b3o47b3o47b3o9bo
bo35b3o$2bo49bo49bo49bo49bo49bo49bo8bobo38bo$bo49bo49bo49bo49bo49bo49b
o10bo38bo5$308b2o$307bo2bo$308bo2bo$309b2o!
I think that it's reasonable to do something like the following pseudocode:

Code: Select all

for each salvo:
    while salvo is nonempty:
        for each of the eight OTTs:
            for each glider in the salvo:
                if this glider can be built by that OTT (in either orientation) without interfering with the rest of the salvo:
                    subtract it from the salvo and jump to the 'while' statement.
This is using the same 'backward' approach as slmake, constructing the gliders in the salvo in reverse order. It's guaranteed to be successful, because the last OTT is just a single-glider version of the clock inserter, and we know that there's always a glider at the front of the salvo that can be inserted in this manner. By greedily choosing the first OTT in the list that works at each stage, it should be very rare (maybe so rare as to never be needed in the spacefiller synthesis!) that it falls back on the last OTT, but even that isn't terribly expensive*.

* the adjustable splitter is 3sL, so if we use the clock inserter then the cost for that glider is 9sL, just over twice the 4sL cost for a glider that's built using the cheapest mechanism (or 3x the absolute minimum if we can use a 2sL non-adjustable splitter).
Has anyone tried making a script at this request?

User avatar
calcyman
Moderator
Posts: 2904
Joined: June 1st, 2009, 4:32 pm

Re: Golly scripts

Post by calcyman » April 7th, 2023, 2:29 pm

AlbertArmStain wrote:
April 7th, 2023, 2:23 pm
I’ve requested a kind of script for golly similar to octohash, but completely independent. It was mentioned here, but I really don’t know where to start. As mentioned before, it will be able to take an unstable object/component and find a way to make a 1g seed for it without using splitters.
calcyman wrote:
April 2nd, 2023, 8:37 am
(quote elided)
Has anyone tried making a script at this request?
Why did you quote an entire post of mine from a different thread about something completely irrelevant to your script request?
What do you do with ill crystallographers? Take them to the mono-clinic!

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

Re: Golly scripts

Post by dvgrn » April 7th, 2023, 3:01 pm

AlbertArmStain wrote:
April 7th, 2023, 2:23 pm
Has anyone tried making a script at this request?
No.

The link you gave above outlined exactly how to make the script -- most of the code already exists, though admittedly it's my code so it's fairly terrible -- but it also lists a series of objections to the idea of bothering to make such a script.

Here would probably be a good place to explain how you expect the script to accomplish the "not limited to 3 objects" part of the description, without taking months or years for each individual search. As far as I can tell, that's what your idea amounts to: basically "octohash without a database".

User avatar
SuperSupermario24
Posts: 121
Joined: July 22nd, 2014, 12:59 pm
Location: Within the infinite expanses of the Life universe

Re: Golly scripts

Post by SuperSupermario24 » April 15th, 2023, 6:03 am

So I was looking through some of my Golly stuff and I realized I never actually posted this "random fill" script I made. So, uh, here it is:

Code: Select all

--[[
random-fill.lua
by SSM24, last updated 2020-11-16

This script fills the selection randomly, with density and states 
determined by the user.

This script is partially based off of the random-fill.py script by 
Andrew Trevorrow from the Golly Scripts Database, but contains the 
following additional features:

 - non-contiguous state ranges

 - ability to save the default input for each rule individually 
   rather than one for every single rule

 - non-integer density percentage values

 - state multipliers, allowing certain states to be more likely than 
   others

Additionally, it is significantly faster than the Python version, 
although Golly's native random fill is still faster.

Usage information:

 - Inputs are of the form "density, <states and ranges>".

 - Values are delimited by commas. Ranges are of the form min-max. 
   Multipliers are given with a colon (:) or lowercase x.

 - If no states are provided, every live state in the rule will be 
   used.

 - Spaces are optional. (They are immediately stripped from the input 
   if they are present.)

Example inputs:
50               - 50% density using every live state in the rule
20, 1            - 20% density using only state 1
100, 1-20        - 100% density using states 1 through 20
0.1, 1, 4-7, 10  - 0.1% density using states 1, 4 through 7, and 10
50, 1:20, 2      - 50% density with a 20x multiplier on state 1
100, 1-4x10, 5   - 100% density with a 10x multiplier on 1 through 4
]]

--------------------------------------------------------------------------------

local g = golly()
local gp = require "gplus"

-- if true, save default inputs
local saveinputs = true

-- set to true if you want to benchmark the random fill process
-- might only work properly on Windows?
local benchmark = false

--------------------------------------------------------------------------------

-- plain replace function because lua doesn't have one for some reason
function string.replace(s, orig, new)
    -- escape magic characters
    orig = orig:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
    -- perform the subsitution
    return s:gsub(orig, new)
end

--------------------------------------------------------------------------------

-- get the selection rectangle
local r = gp.rect(g.getselrect())
if r.empty then
    g.exit("There is no selection.")
end

local maxlive = g.numstates() - 1

local rule = g.getrule()

local prompt = [[
Enter density, followed by the states that should be included.

Separate ranges or individual values with commas. 
Use format min-max for ranges.
Leave states blank to use every state.]]

-- default is just "50" for 2-state rules
local default = "50"

-- if rule has more than 2 states, add a range
if g.numstates() > 2 then
    default = default..",".."1-"..maxlive
end

local inipath = g.getdir("data").."random-fill-lua.ini"

local rulefound = false

-- get previous used values for rule if they exist
if saveinputs then
    file = io.open(inipath, "r")
    if file then
        local line = ""
        -- check every line until rule is found or end of file is reached
        while line ~= nil do
            -- if rule is found, grab input and break from loop
            if line:find(rule) then
                local _,result = gp.split(line, "=")
                default = result
                rulefound = true
                break
            end
            line = file:read()
        end
        file:close()
    end
end

--------------------------------------------------------------------------------

-- get user input and put it in a table
local input = g.getstring(prompt, default, "Randomly fill selection")

-- if blank, silently exit
if input == "" then g.exit() end

-- save input to ini file
if saveinputs then

    -- if rule was found earlier, overwrite its entry
    if rulefound then
    
        -- function so that pcall() can be used to handle write failure
        local function update()
        
            -- copy file to string
            file = io.open(inipath, "r")
            local contents = file:read("a")
            file:close()
            
            -- replace old line with new line
            contents = contents:replace(rule.."="..default, rule.."="..input)
            
            -- overwrite file with new contents
            file = io.open(inipath, "w+")
            file:write(contents)
            file:close()
        end
        
        -- show message if file could not be written to
        if not pcall(update) then
            g.warn("Could not save input to ini file.")
        end
    
    -- if rule was not found, add a new entry for it 
    -- this will create the file if it doesn't exist
    else
    
        local function update()
            file = io.open(inipath, "a")
            file:write(rule.."="..input.."\n")
            file:close()
        end
        
        if not pcall(update) then
            g.warn("Could not save input to ini file.")
        end
    end
end

 -- strip all spaces from the result
input = input:gsub(" ", "")

-- take the input, split it, and put it in a table
local results = table.pack(gp.split(input, ","))

--------------------------------------------------------------------------------

-- get density and then remove it from table
local density = tonumber(results[1])
table.remove(results, 1)

-- check if density is valid
if density == nil then
    g.exit("Density must be a number.")
elseif density < 0 or density > 100 then
    g.exit("Density must be in the range 0-100.")
end

-- put density in range 0.0 - 1.0
-- math.random() is faster than math.random(100)
density = density / 100

--------------------------------------------------------------------------------

-- use a range containing all states if states were blank
if #results == 0 then
    table.insert(results, "1-"..maxlive)
end

-- collect all states into a table
local states = {}

-- for every entry in the table
for i = 1, #results do
    
    -- get result
    local result = results[i]
    
    -- replace any lowercase Xs with a colon for parsing
    result = result:replace("x", ":")

    -- get the state/range and the multiplier
    local str, mult = gp.split(result, ":")
    
    -- if weight not provided, default to 1
    mult = mult or 1
    
    -- add to states table the number of times given by multiplier
    for _ = 1, mult do
    
        -- if it's a range, add every state within it
        if str:find("%d%-%d") ~= nil then
            
            -- get min and max values
            local minval, maxval = gp.split(str, "-")
            minval = math.tointeger(minval)
            maxval = math.tointeger(maxval)
            
            -- make sure both are valid integers
            if minval == nil or maxval == nil then
                g.exit("\""..str.."\" is not a valid state or range.")
            end
            
            -- make sure both are within range
            if minval < 0 or minval > maxlive 
            or maxval < 0 or maxval > maxlive then
                g.exit("Values must be in range 0-"..maxlive..".")
            end
            
            -- reverse if min > max for some reason
            local inc = 1
            if minval > maxval then inc = -1 end
            
            -- add states between min and max to table
            for j = minval, maxval, inc do
                table.insert(states, j)
            end
        
        -- if it's an individual value, add that value
        elseif math.tointeger(str) ~= nil then
        
            local num = math.tointeger(str)
            
            -- make sure value is within range
            if num < 0 or num > maxlive then
                g.exit("Values must be in range 0-"..maxlive..".")
            end
            
            -- add to states table
            table.insert(states, num)
        
        -- if any value is invalid, exit
        else
            g.exit("\""..str.."\" is not a valid state or range.")
        end
    end
end

--------------------------------------------------------------------------------

--[[
-- debugging, lists all states in the states table
local str = ""
for i = 1, #states do
    str = str..tostring(states[i]).." "
end
g.show(str)
]]

local numstates = #states
local currstate = 0

local oldsecs = os.time()

-- function to update the view if enough time has passed
function checktime()
    local newsecs = os.time()
    if newsecs - oldsecs >= 1 then
        oldsecs = newsecs
        g.update()
    end
end

local starttime = os.clock()

-- the following is the generation of the random fill
--
-- it's done like this to minimize the amount of math.random() calls
-- in case of special conditions

-- if only one state, don't generate currstate every time
if numstates == 1 then
    currstate = states[1]
    -- if density is 100%, just fill the selection
    if density == 1.0 then
        for y = r.top, r.bottom do
            for x = r.left, r.right do
                g.setcell(x, y, currstate)
            end
            checktime()
        end
    -- otherwise, randomly fill it
    else
        for y = r.top, r.bottom do
            for x = r.left, r.right do
                if math.random() < density then
                    g.setcell(x, y, currstate)
                else
                    g.setcell(x, y, 0)
                end
            end
            checktime()
        end
    end
-- if more than one state, generate currstate every time
else
    -- if density is 100%, completely fill it
    if density == 1.0 then
        for y = r.top, r.bottom do
            for x = r.left, r.right do
                currstate = states[math.random(numstates)]
                g.setcell(x, y, currstate)
            end
            checktime()
        end
    -- otherwise, randomly fill it
    else
        for y = r.top, r.bottom do
            for x = r.left, r.right do
                if math.random() < density then
                    currstate = states[math.random(numstates)]
                    g.setcell(x, y, currstate)
                else
                    g.setcell(x, y, 0)
                end
            end
            checktime()
        end
    end
end

-- end of random fill generation

local endtime = os.clock()

if benchmark then
    local timetaken = string.format("%.3f", endtime-starttime)
    g.show("Took "..timetaken.." seconds.")
end
The comment at the top should hopefully contain all the info you need. As you can see I made this quite some time ago (it says updated 2020 but I think most of this was actually made in 2018). I think I was holding off on posting it until I implemented some additional features, but then I apparently never did and I don't even remember what they would've been at this point.

I don't plan on maintaining this or adding any additional features at this point, though of course anyone's free to add stuff on their own :p

Code: Select all

bobo2b3o2b2o2bo3bobo$obobobo3bo2bobo3bobo$obobob2o2bo2bobo3bobo$o3bobo3bo2bobobobo$o3bob3o2b2o3bobo2bo!

AlbertArmStain
Posts: 1077
Joined: January 28th, 2022, 7:18 pm
Location: Planet Z

Re: Golly scripts

Post by AlbertArmStain » May 1st, 2023, 7:14 am

calcyman wrote:
April 7th, 2023, 2:29 pm
Why did you quote an entire post of mine from a different thread about something completely irrelevant to your script request?
I forgot that it pings you, they were intended to be separated from each other, but it look like I’m still talking about the top on. I’m supposed to be talking about making a script for both ideas

User avatar
b3s23love
Posts: 93
Joined: May 24th, 2023, 6:30 am
Location: The (Life?) Universe

Re: Golly scripts

Post by b3s23love » September 22nd, 2023, 12:53 pm

This is a script I got from EvinZL in a PM that may help with setting up LLS searches.

Code: Select all

import golly as g

if len(g.getselrect()) == 0: g.exit("No selection")
sr = g.getselrect()
generations = int(g.getstring("How many generations back?"))

cell_arr = []
for y in range(sr[1], sr[1]+sr[3]):
    row = []
    for x in range(sr[0], sr[0]+sr[2]):
        row.append(g.getcell(x, y))
    cell_arr.append(row)


search_file = ""
def writecells(l):
    global search_file
    for _ in range(sr[2]+2):
        search_file += "0 "
    search_file += "\n"
    for i in cell_arr:
        search_file += "0 "
        for j in i:
            search_file += l[j] + " "
        search_file += "0\n"
    for _ in range(sr[2]+2):
        search_file += "0 "
    search_file += "\n\n"

writecells("0**00")
for _ in range(generations-1):
    writecells("0****")
writecells("01010")

g.setclipstr(search_file)
How to use it:
EvinZL wrote:
September 21st, 2023, 6:59 pm
Create the search in LifeHistory, with 1 and 3=cells that are on in the final state, others=cells that must be off in the final state, 1 and 2=cells that can be on in the starting state and the intermediate states, and 3, 4=cells that can be on in the intermediate states but not the starting state. All other cells must be off in all states. Select the search, then run it, and it will paste the LLS input to clipboard.
EvinZL wrote:
September 22nd, 2023, 12:16 pm
State 0: must be off in all generations
State 1: must be on in final generation, can be on in starting and intermediate generations
State 2: must be off in final generation, can be on in starting and intermediate generations
State 3: must be on in final generation, can be on in intermediate but NOT starting generation
State 4: must be off in final generation, can be on in intermediate but NOT starting generation
EvinZL used this script to generate the diehard-related LLS files.

User avatar
EvinZL
Posts: 796
Joined: November 8th, 2018, 4:15 pm
Location: A tungsten pool travelling towards the sun
Contact:

Re: Golly scripts

Post by EvinZL » October 13th, 2023, 7:45 pm

This is a script to download rule files from LifeWiki to Golly's rules folder

Code: Select all

import golly as g
import urllib.request
rulename = g.getstring("Rule name:", "StateInvestigator", "Get rule")
try:
    req = urllib.request.urlopen(f"https://conwaylife.com/w/index.php?title=Rule:{rulename}&action=raw")
except:
    g.exit("Rule not found")
rulefile = req.read()
path = g.getdir("rules") + rulename + ".rule"
with open(path, "wb") as f:
    f.write(rulefile)
g.setrule(rulename)
g.exit(f"Saved rule {rulename}")

User avatar
johamwit
Posts: 41
Joined: September 12th, 2021, 6:58 am

Re: Golly scripts

Post by johamwit » November 19th, 2023, 3:05 pm

EvinZL wrote:
October 13th, 2023, 7:45 pm
This is a script to download rule files from LifeWiki to Golly's rules folder

Code: Select all

import golly as g
import urllib.request
...
No urllib module in lua :(
Oh my God! -- it's full of search space!

User avatar
NimbleRogue
Posts: 444
Joined: January 11th, 2021, 11:48 pm

Re: Golly scripts

Post by NimbleRogue » November 27th, 2023, 1:43 am

May13 wrote:
August 26th, 2022, 2:06 am

My modification (I call it nbsearch2a) does not affect performance, but makes searching easier and supports bounded grids:
carsoncheng asked me to put my slight modification for ship searching here
All I did was add this if statement if g.getcells(g.getrect())!=testcells: to check if the thing is a ship, and it can be easily commented out when needed, and a simple symmetry Ship for only symmetries that support ships

Code: Select all

import golly as g
import math
import random
from timeit import default_timer as timer
from glife.text import make_text
from glife import getminbox, rect

autoRAB=True #automatically detect rule, algorithm and border
group_by_period=True
auto_exclude=50 #maximal number of oscillators of specific period (if group_by_period)
show_periods=True #print list of periods (if group_by_period)
auto_stop=True #stop if no more oscillators can be added (if group_by_period)
max_period=200
result_spacing=250
stab_step=8
x=15
main_fill=75
count_update=1000

if autoRAB:
    rule=g.getrule()
    algo=g.getalgo()
    if ":" in rule:
        rule,bound=rule.split(":")
        bound=":"+bound
    else: bound=""
else:
    rule=g.getstring("Rule:","B3/S23")
    algo=g.getstring("Algorithm:","QuickLife")
    bound=g.getstring("Bound:","T32,32")
if not (bound=="" or bound.startswith(":")): bound=":"+bound
s=g.getstring("Symmetry","All")

symm=0
if s!="All":
    symm=s

#symm can be either C1, C2_1, C2_2, C2_4, D2_x, D2_+1, D2_+2, C4_1, C4_4, D4_x1, D4_x4, D4_+1, D4_+2, D4_+4, D8_1, D8_4, All, Rot

max_period+=1
oc=[0]*max_period

if rule=="GlideLife":
    exclude_periods=[1,2,4,6,12,16]
if rule=="olife":
    exclude_periods=[1,2,3,4,5,6,9,10,12,15,18,20,26,30,35]
if rule=="B3/S23":
    exclude_periods=[1,2,3,6,8,4,5,10,15,30,14]
if rule in ["tlife","B3/S2-i34q"]:
    exclude_periods=[1,2,4,5,160]
if rule=="salad":
    exclude_periods=[1,2,4]
if rule=="B2inkce_S12":
    exclude_periods=[1,2,4]
if rule=="B3678/S235678":
    exclude_periods=[1,2,3,4,6,12,8]
if rule=="MoveIt":
    exclude_periods=[1,2,3,4,6,8,12,16,24,32,48]
if rule=="B35678/S357":
    exclude_periods=[1,2,6,4,3,8,12,35,5,14,24,13,10,15]
if rule=="B2-aS12":
    exclude_periods=[1,14,6,3,2,4,7,26,42,9,28,12,78,16,48,236,24,84,182,13,156,5,130,21,10,208,234,15,11,70,8,19]
if rule=="B2i35r_S023-a4i":
    exclude_periods=[1,2,4]
if rule=="B2in3S02-in3-n":
    exclude_periods=[1,4,5,8,7,6,20,12,28,2,9,16,36,10,42,72,14,56,3,40,63,30,140,45,35]
if rule=="B37/S2-i34q":
    exclude_periods=[1,2,5,4,3,20]
if rule=="B3/S235e":
    exclude_periods=[1,2,15,3,5,10,8,30,4,6,14,40]
if rule=="randomnn":
    exclude_periods=[20,4,14,7,28,140,84]
if rule=="Rotator":
    exclude_periods=[12,40,120,4,60,10,20,24,8]
if rule=="B2ein3/S13":
    exclude_periods=[1,2,4,6,5,10]
if rule=="PuffLife":
    exclude_periods=[1,2,4,8,3,6,16,12]
if rule=="B3-k/S2-i3-k4cen":
    exclude_periods=[1,2,5,4,3]
if rule=="B34ek5ak/S2-c34iz5y":
    exclude_periods=[1,2,4,6]
if rule=="B35/S2":
    exclude_periods=[2,4,1,3]
if rule=="B3568/S256":
    exclude_periods=[1,2,3,4,6,12,5,10,15,20,14,42]
if rule=="B356/S234i":
    exclude_periods=[1,2,4,6,12]
if rule=="b2ce3aiys12aei3r":
    exclude_periods=[1,2,4,6,7,8,10,12,14,15,24,26,28,29,30,58,42,62,94,126,138,170,186,202,234,266]
if rule=="B3-cnry4-acery5i/S23-a4-jknqr5y8":
    exclude_periods=[1,2,36,92,4,28,12,18,8]
if rule=="B3/S23-a4eiktz":
    exclude_periods=[1,2,4,5,10,78,7,14,9,36,3,6]
if rule=="B34e5e/S2-in3-y5jk":
    exclude_periods=[1,2,4,10,13,26,6,3]
if rule=="B2e3-r5i8/S23-a4kz7c":
    exclude_periods=[1,2,7,14,4]
if rule=="B3/S23-e4k":
    exclude_periods=[1,2,4,5,6,10,98,294,14,22,12]
if rule=="B34aq5c/S135":
    exclude_periods=[1,2,4,3,6,13,26,8,12,52,39]
if rule=="B2-a3/S1c23-ainr4cekn":
    exclude_periods=[1,2,4,12,31,6,62,8,5,10,124,28,20]
if rule=="B2-a3-in/S23":
    exclude_periods=[1,2,6,4,44,12,3,16,9,18,132,5,8,36,220,20,30,22,60]
if rule=="B2-a3-in/S235c":
    exclude_periods=[1,2,4,3,6,44,12,10,20,132,16,60,5,58,8]

exclude_periods=[]

def all_periods(q):
    if not show_periods: return ""
    gmin = gmax = 0
    text = "["
    for i in q+[0]:
        if i==0: text += str(gmin) + ("-"+str(gmax)+"]" if gmax>gmin else "]")
        elif gmin==0: gmin = gmax = i
        elif i-gmax==1: gmax = i
        else:
            text += str(gmin) + ("-"+str(gmax)+"," if gmax>gmin else ",")
            gmin = gmax = i
    return text

def osc_test():
    global exclude_periods
    if g.empty():
        return False
    testcells=g.getcells(g.getrect())
    testpop=g.getpop() # String representation
    testhash=g.hash(g.getrect())
    for i in range(1,max_period):
        g.run(1)
        if g.empty():
            return False
        if g.getpop()==testpop and g.hash(g.getrect())==testhash:
            if g.getcells(g.getrect())!=testcells:
                if i not in exclude_periods:
                    if auto_exclude==1: exclude_periods+=[i]
                    return i
                return 0
    return 0


def put_symm(cell_list,x0=0,y0=0,axx=1,axy=0,ayx=0,ayy=1,mode="or"):
    global symm
   
    if s=="All":
        symm=["C1", "C2_1", "C2_2", "C2_4", "D2_x", "D2_+1", "D2_+2", "C4_1", "C4_4", "D4_x1", "D4_x4", "D4_+1", "D4_+2", "D4_+4", "D8_1", "D8_4"][random.randrange(16)]
    if s=="Ship":
        symm=["C1", "D2_x","D2_+1", "D2_+2"][random.randrange(4)]
    if s=="Rot":
        symm=["C1", "C2_1", "C2_2", "C2_4", "C4_1", "C4_4"][random.randrange(6)]
   
    # g.putcells(cell_list,x0,y0,axx,axy,ayx,ayy,mode)
    if symm=="C2_1" or symm=="C4_1" or symm=="D4_+1" or symm=="D8_1" or symm=="D4_x1":
        g.putcells(cell_list,-x0,-y0,-axx,-axy,-ayx,-ayy,mode)
   
    if symm=="C4_1" or symm=="D8_1":
        g.putcells(cell_list,y0,-x0,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0,x0,-ayx,-ayy,axx,axy,mode)
   
    if symm=="C2_2" or symm=="D4_+2":
        g.putcells(cell_list,-x0-1,-y0,-axx,-axy,-ayx,-ayy,mode)
   
    if symm=="C2_4" or symm=="C4_4" or symm=="D4_+4" or symm=="D8_4" or symm=="D4_x4":
        g.putcells(cell_list,-x0-1,-y0-1,-axx,-axy,-ayx,-ayy,mode)
   
    if symm=="D2_+1" or symm=="D8_1" or symm=="D4_+1":
        g.putcells(cell_list,-x0,y0,-axx,-axy,ayx,ayy,mode)
   
    if symm=="D4_+1" or symm=="D8_1" or symm=="D4_+2":
        g.putcells(cell_list,x0,-y0,axx,axy,-ayx,-ayy,mode)
   
    if symm=="D2_+2" or symm=="D4_+2" or symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,-x0-1,y0,-axx,-axy,ayx,ayy,mode)
   
    if symm=="D4_+4" or symm=="D8_4":
        g.putcells(cell_list,x0,-y0-1,axx,axy,-ayx,-ayy,mode)
   
    if symm=="C4_4" or symm=="D8_4":
        g.putcells(cell_list,y0,-x0-1,ayx,ayy,-axx,-axy,mode)
        g.putcells(cell_list,-y0-1,x0,-ayx,-ayy,axx,axy,mode)
   
    if symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)
   
    if symm=="D2_x" or symm=="D8_1" or symm=="D8_4" or symm=="D4_x1" or symm=="D4_x4":
        g.putcells(cell_list,y0,x0,ayx,ayy,axx,axy,mode)
   
    if symm=="D4_x1" or symm=="D8_1":
        g.putcells(cell_list,-y0,-x0,-ayx,-ayy,-axx,-axy,mode)
   
    if symm=="D4_x4" or symm=="D8_4":
        g.putcells(cell_list,-y0-1,-x0-1,-ayx,-ayy,-axx,-axy,mode)

def clear_layer():
    r = g.getrect()
    if r:
        g.select(r)
        g.clear(0)
    return

def main():
    global oc
    g.new("RandOsc")
    g.setrule(rule+bound)
    g.setalgo(algo)
    g.setbase(2)
    
    test_layer=g.getlayer()
    if g.numlayers()<g.maxlayers():
        results_layer=g.addlayer()
        g.setname('OscResults')
        g.setrule(rule)
        for i in range(max_period-1):
                t = make_text(str(i+1), "mono")
                t.put((i+1)*result_spacing,0)
        g.setlayer(test_layer)
    else:
        resultslayer=-1
    results=0
   
    count=0
    prevcount=0
    t_start=timer()
    t_prev=t_start
    while True:
        clear_layer()
       
        g.select([0,0,x,x])
        g.randfill(main_fill)
        cell_list=g.getcells([0,0,x,x])
        # g.clear(0)
        put_symm(cell_list)
        
        g.setstep(stab_step)
        g.step()
       	test=osc_test()
        if test>0:
            osc = g.getcells(g.getrect())
            if group_by_period:
                if oc[test]==0: results+=1
                if oc[test]<auto_exclude:
                    if results_layer>=0:
                        oc[test]+=1
                        g.setlayer(results_layer)
                        g.putcells(osc, result_spacing*test, result_spacing*oc[test])
                        g.setname('OscResults (%d)' % results)
                        g.fit()
                        g.update()
                        g.setlayer(test_layer)
                        g.setname('RandOsc (%d)' % results)
                    else:
                        return True
            else:
                results+=1
                if results_layer>=0:
                    g.setlayer(results_layer)
                    g.putcells(osc, result_spacing*results, 0)
                    g.setname('OscResults (%d)' % results)
                    g.fit()
                    g.update()
                    g.setlayer(test_layer)
                    g.setname('RandOsc (%d)' % results)
                else:
                    return True
       
        count+=1
        if count%count_update==0:
            t_end=timer()
            g.show("%d results found after %d soups tested (%d/sec current, %d/sec overall)" % (results, count, (count-prevcount)/(t_end-t_prev), (count)/(t_end-t_start))+" "+all_periods([i for i in range(max_period) if oc[i]>0]))
            g.select([])
            g.update()
            g.new("")
            g.setbase(2)
            t_prev=t_end
            prevcount=count
            if auto_stop and auto_exclude>0:
                if sum(oc)==(max_period-1-len([i for i in exclude_periods if i<max_period]))*auto_exclude: break
main()

Code: Select all

x = 7, y = 8, rule = B2i3aeikq4aciqtz5e/S2en3ijq4aiqrw5-cn6aei7e8
2b3o$b2ob2o$2obob2o$obo$2obo$b2ob2o$2b4o$4bo!

Code: Select all

x = 5, y = 6, rule = B2in3-ekny4ckqtz5ekry6in7c/S2aek3-ace4ceiyz5n6n7
2bo$b3o$bobo$bob2o$2bo$obo!

Post Reply