Golly scripts

For scripts to aid with computation or simulation in cellular automata.
AlbertArmStain
Posts: 1331
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: 2938
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: 10737
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: 1331
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: 97
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: 856
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: 60
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: 559
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 = 4, y = 3, rule = B3-cnqy4e5kr6n7c/S2-i3-ay4einrtyz5cejn6cin78
bo$3o$ob2o!

Code: Select all

#14c/85265o
x = 10, y = 4, rule = B2-an3-iqy4iknrtz5aijqy6aei78/S02ck3nqy4eiqrtwy5-ekq6-i78
2bo4bo$3b4o$ob6obo$2b6o!

User avatar
Tawal
Posts: 309
Joined: October 8th, 2023, 7:20 am

Re: Golly scripts

Post by Tawal » February 12th, 2024, 4:55 pm

Hey, these are my first steps with Python.
Please bear with me :P

About this 2 old scripts :
dvgrn wrote:
February 11th, 2024, 10:15 am
Tawal wrote:
February 11th, 2024, 9:17 am
About this 2 scripts :
save-bmp.py
save-image.py

Are they Python3 compatible ?
No. I don't believe any of those scripts have been updated since Golly moved to Python 3.x. An 'xrange' error is unambiguous evidence of a Python 2.x script.

https://python2to3.com/ can probably fix most such errors for you very quickly, but no guarantees. If anyone is willing to run through all of those old scripts and upgrade them to Python 3, I can work to get the old versions replaced.

Otherwise it might be simplest just to remove that page from conwaylife.com -- a GitHub or GitLab repo would probably be a better way to serve up that kind of content these days.
The site python2to3.com works fine :)
So, I've managed a script that produces a number (requested from the user) of image files of the selected pattern according to a generation increment (requested from the user).
The user is also asked where to save the files.

The original idea was to create a GIF but I don't have the knowledge to do that ...
If anyone would like to correct me and help me create the GIF file, they are most welcome.

At least you have all the image files to create a GIF with the software of your choice :)

Code: Select all

# Uses PIL (Python Imaging Library) to save the current selection or pattern
# in a user-specified image file (.png/.bmp/.gif/.tif/.jpg).
# See http://www.pythonware.com/products/pil/ for more info about PIL.
# Author: Andrew Trevorrow (andrew@trevorrow.com), Oct 2009.

import golly as g
import os.path
import webbrowser
import urllib.request, urllib.parse, urllib.error

try:
   from PIL import Image
except:
   g.exit("You need to install PIL (Python Imaging Library).")

### Rapid exit if iznogood ...
if g.empty(): g.exit("There is no pattern.")
# prevent Image.new allocating a huge amount of memory
prect = g.getrect()
srect = g.getselrect()
if len(srect) > 0: prect = srect    # save selection rather than pattern
wd = prect[2]
ht = prect[3]
if wd * ht >= 100000000:
   g.exit("Image area is restricted to < 100 million cells.")


#set initial directory for the save dialog
initdir = ""
savename = g.getdir("data") + "save-image.ini"
try:
   # try to get the directory saved by an earlier run
   f = open(savename, 'r')
   initdir = f.readline()
   f.close()
except:
   # this should only happen the very 1st time
   initdir = g.getdir("data")


# remove any existing extension from layer name and append .png
initfile = g.getname().split('.')[0] + ".png"


# prompt user for the number of images
gens = int( g.getstring("Enter the number of images : ", "", "Make a GIF") )

# prompt user for the step (one image each n genearations)
step = int( g.getstring("Enter the step \n  (one image each n genearations) :", "", "Make a GIF") )


# prompt user for output file (image type depends on extension)
outfile = g.savedialog("Make a GIF - Save image files",
                     "PNG (*.png)|*.png" ,
                     initdir, initfile)


# iteration 'step' by 'step' and get image
for nb_img in range(gens) :
   prect = g.getrect()
   srect = g.getselrect()
   if len(srect) > 0: prect = srect    # save selection rather than pattern
   x = prect[0]
   y = prect[1]
   wd = prect[2]
   ht = prect[3]

   # create RGB image filled initially with state 0 color
   multistate = g.numstates() > 2
   colors = g.getcolors()          # [0,r0,g0,b0, ... N,rN,gN,bN]
   im = Image.new("RGB", (wd,ht), (colors[1],colors[2],colors[3]))

   # get a row of cells at a time to minimize use of Python memory
   cellcount = 0
   for row in range(ht):
      cells = g.getcells( [ x, y + row, wd, 1 ] )
      clen = len(cells)
      if clen > 0:
         inc = 2
         if multistate:
            # cells is multi-state list (clen is odd)
            inc = 3
            if clen % 3 > 0: clen -= 1    # ignore last 0
         for i in range(0, clen, inc):
            if multistate:
               n = cells[i+2] * 4 + 1
               im.putpixel((cells[i]-x,row), (colors[n],colors[n+1],colors[n+2]))
            else:
               im.putpixel((cells[i]-x,row), (colors[5],colors[6],colors[7]))
            cellcount += 1
            if cellcount % 1000 == 0:
               # allow user to abort huge pattern/selection
               g.dokey( g.getkey() )

   if cellcount == 0:
      g.show("Pattern died toot early - Only get " + str(nb_img) + " images.")
      break

   g.show("Creating images...")

   outfile = outfile.split('.')[0]  + "." + str(nb_img) + ".png"
   if len(outfile) > 0:
      im.save(outfile)
      g.run(step)



# remember file's directory for next time
try:
   f = open(savename, 'w')
   f.write(os.path.dirname(outfile))
   f.close()
except:
   g.warn("Unable to save directory to file:\n" + savename)

g.reset()
Edit:
I used a hard coded version to make my avatar.
Alone we go faster … Together we go further …

Avatar's pattern
Possible uses found by Dave Green
Jormungant's explanation and uses

Currently investigating signal collisions … (stand by)

User avatar
Tawal
Posts: 309
Joined: October 8th, 2023, 7:20 am

Re: Golly scripts

Post by Tawal » February 16th, 2024, 2:59 pm

Tawal wrote:
February 12th, 2024, 4:55 pm
...
The original idea was to create a GIF but I don't have the knowledge to do that ...
If anyone would like to correct me and help me create the GIF file, they are most welcome.
...
Well, I've had a bit of a look at the Python language and I've realised what I was kindly asking for :)

Here's a script that creates an animated GIF file from a selected pattern. It's intended for small patterns rather than large ones.
It opens a window asking the user for 5 parameters:
- The duration of the GIF in tenths of a second.
- The number of captures to take from the selection.
- The incremental step of the generations.
- The number of the first capture to be inserted at the beginning of the GIF, creating a start delay.
- The number of the last capture to be added at the end of the GIF, creating an end delay.

On the Python side, the script uses Python 3, of course.
It also uses several libraries:
- PIL = Python Imaging Library
- tkinter
- ttk
- tkfont
I wrote the script on a Linux Debian 12 and it works very well for me.
There may be some modifications to make it work on Windows or MacOSX.

As this is really my very first script in Python, I'm quite happy with it.

Well, enough said, the script :

make-a-gif.py

Code: Select all

# Original Script : save-image..py
      # Uses PIL (Python Imaging Library) to save the current selection or pattern
      # in a user-specified image file (.png/.bmp/.gif/.tif/.jpg).
      # See http://www.pythonware.com/products/pil/ for more info about PIL.
      # Author: Andrew Trevorrow (andrew@trevorrow.com), Oct 2009.

# Modifications : make-a-gif.py
      # Always use PIL (see above).
      # Also use 'tkinter', 'ttk' and 'tkfont'.
      # See https://docs.python.org/fr/3/library/tk.html for more about tkinter, ttk and tkfont.
      # Now the script makes an animated GIF file from the selected pattern
      #+ and its evolving according to a generation step.
      # It has 5 user-provided parameters :
      #     - Duration : the duration of the GIF file in tenths of a second.
      #     - Number of images to capture from the selected pattern.
      #     - Step for the evolving pattern (must be an integer).
      #     - Starting Delay : Number of the first captured image
      #        to insert at the beginning of the GIF file.
      #     - Ending Delay : Number of the last captured image
      #        to append at the end of the GIF file.
      # The script creates a new working layer which is destroyed at the end.
      #+ So it doesn't touch the original pattern layer :)
      # The script stops to capture if the pattern dies. The GIF file is created yet.
      #+ This affects the start and end delays.
# Contributor : Tawal (https://conwaylife.com/forums - user : Tawal), Feb 2024.

import golly as g
import os.path
import urllib.request, urllib.parse, urllib.error

try:
   from PIL import Image
except:
   g.exit("You need to install 'PIL' (Python Imaging Library).")

try:
   import tkinter as tk
   from tkinter import ttk
   from tkinter import font
except:
   g.exit("You need to install 'tkinter' (Tkinter - Python Module.")

# Rapid exit if iznogood ...
srect = g.getselrect()
if len(srect) == 0:
   g.exit("There is no selection.")
x = srect[0]
y = srect[1]
wd = srect[2]
ht = srect[3]
# prevent Image.new allocating a huge amount of memory
if wd * ht >= 100000000:
   g.exit("Image area is restricted to < 100 million cells.")

# for all further normal exit
def clean_exit(msg):
   g.dellayer()
   g.exit(msg)

# get the rule's colors
multistate = g.numstates() > 2
colors = g.getcolors()          # [0,r0,g0,b0, ... N,rN,gN,bN]

# new working layer (deleted at the end of script).
initfile = g.getname().split('.')[0] + ".gif" # remove any existing extension from layer name and append .gif
g.copy()
gif_layer = g.addlayer()
g.setname(initfile)
g.paste(x, y, "or")
g.update()

#set initial directory for the save dialog
initdir = ""
savename = g.getdir("data") + initfile
try:
   # try to get the directory saved by an earlier run
   f = open(savename, 'r')
   initdir = f.readline()
   f.close()
except:
   # this should only happen the very 1st time
   initdir = g.getdir("data")


# Multi-entry window
end_script= False
def button_exit_clicked():
   global end_script
   in_win.destroy()
   end_script = True

def button_OK_clicked():
   global duration, n_im, step, start_delay, end_delay
   n_im = n_imSV.get()
   duration = durationSV.get()
   step = stepSV.get()
   start_delay = start_delaySV.get()
   end_delay = end_delaySV.get()
   in_win.destroy()

def OnValidate(S):
   if S.isdigit():
      return True
   return False

in_win = tk.Tk()
in_win.title("Make a GIF")
in_win.geometry('520x320')
in_win.eval('tk::PlaceWindow . center')
valid_inputs = (in_win.register(OnValidate), '%S')

intro_lbl = ttk.Label(in_win, underline=80, text="Make an animated GIF file from the selected pattern")
intro_f = font.Font(intro_lbl, intro_lbl.cget("font"))
intro_f.configure(underline=True, weight=font.BOLD)
intro_lbl.configure(font=intro_f)

duration_lbl = tk.Label(in_win, text="Duration of GIF   (x0.1s)   :  ")
durationSV = tk.StringVar()
duration_ask = tk.Entry(in_win, textvariable=durationSV, width=6, justify=tk.RIGHT, validate="key", vcmd=valid_inputs)

n_im_lbl = tk.Label(in_win, text="Number of captures to take from pattern   :  ")
n_imSV = tk.StringVar()
n_im_ask = tk.Entry(in_win, textvariable=n_imSV, width=6, justify=tk.RIGHT, validate="key", vcmd=valid_inputs)

step_lbl = tk.Label(in_win, text="Generation Step   (integer)   :  ")
stepSV = tk.StringVar()
step_ask = tk.Entry(in_win, textvariable=stepSV, width=6, justify=tk.RIGHT, validate="key", vcmd=valid_inputs)
step_ask.insert(0, "1")

start_delay_lbl = tk.Label(in_win, text="Number of first same frames   (starting delay)   :  ")
start_delaySV = tk.StringVar()
start_delay_ask = tk.Entry(in_win, textvariable=start_delaySV, width=6, justify=tk.RIGHT, validate="key", vcmd=valid_inputs)
start_delay_ask.insert(0, "0")

end_delay_lbl = tk.Label(in_win, text="Number of last same frames   (ending delay)   :  ")
end_delaySV = tk.StringVar()
end_delay_ask = tk.Entry(in_win, textvariable=end_delaySV, width=6, justify=tk.RIGHT, validate="key", vcmd=valid_inputs)
end_delay_ask.insert(0, "0")

button_exit = tk.Button(in_win, text="Exit", width=8, command=button_exit_clicked)
button_OK = tk.Button(in_win, text='Make GIF', width=8, command=button_OK_clicked)

in_win.columnconfigure(0, weight=1)
in_win.columnconfigure(1, weight=15)
in_win.columnconfigure(2, weight=15)
in_win.columnconfigure(3, weight=15)
in_win.columnconfigure(4, weight=1)
in_win.rowconfigure(0, weight=20)
in_win.rowconfigure(1, weight=2)
in_win.rowconfigure(2, weight=2)
in_win.rowconfigure(3, weight=2)
in_win.rowconfigure(4, weight=2)
in_win.rowconfigure(5, weight=2)
in_win.rowconfigure(6, weight=15)

intro_lbl.grid(column=0, row=0, columnspan=4)

duration_lbl.grid(column=1, row=1, columnspan=2, stick=tk.E)
duration_ask.grid(column=3, row=1, stick=tk.W)

n_im_lbl.grid(column=1, row=2, columnspan=2, stick=tk.E)
n_im_ask.grid(column=3, row=2, stick=tk.W)

step_lbl.grid(column=1, row=3, columnspan=2, stick=tk.E)
step_ask.grid(column=3, row=3, stick=tk.W)

start_delay_lbl.grid(column=1, row=4, columnspan=2, stick=tk.E)
start_delay_ask.grid(column=3, row=4, stick=tk.W)

end_delay_lbl.grid(column=1, row=5, columnspan=2, stick=tk.E)
end_delay_ask.grid(column=3, row=5, stick=tk.W)

button_OK.grid(column=3, row=6, sticky=tk.W)
button_exit.grid(column=1, row=6, sticky=tk.W, padx=50)

duration_ask.focus()
in_win.mainloop()

if end_script:         # exit if exit_button is clicked
   clean_exit('Exited')

# then prompt user for output GIF file.
outfile = g.savedialog("Make a GIF - Save image file",
                     "GIF (*.gif)|*.gif" ,
                     initdir, initfile)
if len(outfile) < 1 :     # exit if empty string or cancel choice
   clean_exit('Exited')

# create images list
g.show("Please wait while creating GIF file ...")
list_im = []

for k in range(int(n_im)):
   # create RGB image filled initially with state 0 color
   im = Image.new("RGB", (wd,ht), (colors[1],colors[2],colors[3]))

   # get a row of cells at a time to minimize use of Python memory
   cellcount = 0
   for row in range(ht):
      cells = g.getcells( [ x, y + row, wd, 1 ] )
      clen = len(cells)
      if clen > 0:
         inc = 2
         if multistate:
            # cells is multi-state list (clen is odd)
            inc = 3
            if clen % 3 > 0: clen -= 1    # ignore last 0
         for i in range(0, clen, inc):
            if multistate:
               n = cells[i+2] * 4 + 1
               im.putpixel((cells[i]-x,row), (colors[n],colors[n+1],colors[n+2]))
            else:
               im.putpixel((cells[i]-x,row), (colors[5],colors[6],colors[7]))
            cellcount += 1
            if cellcount % 1000 == 0:
               # allow user to abort huge pattern/selection
               g.dokey( g.getkey() )
   # stop to capture if pattern dies
   if cellcount == 0:
      g.show("Pattern died too early - Only get " + str(k) + " images.")
      break

   if k == 0:             # insert the frames for starting delay
      for j in range(int(start_delay)):
         list_im.append(im)
   else:
      list_im.append(im)
   g.run(int(step))

if int(end_delay) > 0:         # append the frames for ending delay
   for i in range(int(end_delay)):
         list_im.append(im)

# convert the images list in a GIF file
list_im[0].save(outfile,
               save_all=True, append_images=list_im[1:], optimize=True, duration=int(duration), loop=0)

# remember file's directory for next time
try:
   f = open(savename, 'w')
   f.write(os.path.dirname(outfile))
   f.close()
except:
   g.warn("Unable to remember save location and last used filename for next run !")

# clean and exit
clean_exit('Animated GIF created here : '+ outfile)
And what it looks like :
Image

Edit:
Clean version of script (not a draft :P)
And smaller image.
Alone we go faster … Together we go further …

Avatar's pattern
Possible uses found by Dave Green
Jormungant's explanation and uses

Currently investigating signal collisions … (stand by)

User avatar
otismo
Posts: 1279
Joined: August 18th, 2010, 1:41 pm
Location: Florida
Contact:

Re: Golly scripts

Post by otismo » February 17th, 2024, 12:09 pm

ThankYou Tawal for Sharing !

(also want to note that we have giffer.lua)
"One picture is worth 1000 words; but one thousand words, carefully crafted, can paint an infinite number of pictures."
- autonomic writing
forFUN : http://viropet.com
Art Gallery : http://cgol.art
Video WebSite : http://conway.life

User avatar
May13
Posts: 801
Joined: March 11th, 2021, 8:33 am

Re: Golly scripts

Post by May13 » February 19th, 2024, 9:08 am

Version 0.3 of collector.py

Code: Select all

# collector.py
# Version 0.3
# Written by May13, December 2022 - February 2024

# This script contains modified part of script written by Nathaniel Johnston, June 2009.
# Source: https://conwaylife.com/forums/viewtopic.php?p=465#p465

import golly as g
from math import ceil

fontNumber = 0
space = 2
scale = 10
count = 100
exponential = False
mx = 30
my = 30

filename = g.getstring("Enter file name:","oscillators.txt")

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

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

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

def giveRLE(rl_list):
   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
      
# --------------------------------------------------------------------

def get_RLE(rect):
	clist = list(chunks(g.getcells(rect), 2))
	mcc = [rect[0], rect[1]]
	n = g.getselrect()[1]-rect[1]
	clist = [[x[0]-mcc[0],x[1]-mcc[1]] for x in clist]
	if n>1: return str(n)+'$'+giveRLE(clist)
	if n==1: return '$'+giveRLE(clist)
	return giveRLE(clist)

rule = g.getrule()
ruleHistory = rule + 'History'
font0 = {
'0': 'obobo2$o3bo2$o3bo2$o3bo2$obobo!',
'1': '2bo2$obo2$2bo2$2bo2$obobo!',
'2': 'obobo2$4bo2$obobo2$o2$obobo!',
'3': 'obobo2$4bo2$obobo2$4bo2$obobo!',
'4': 'o3bo2$o3bo2$obobo2$4bo2$4bo!',
'5': 'obobo2$o2$obobo2$4bo2$obobo!',
'6': 'obobo2$o2$obobo2$o3bo2$obobo!',
'7': 'obobo2$4bo2$4bo2$4bo2$4bo!',
'8': 'obobo2$o3bo2$obobo2$o3bo2$obobo!',
'9': 'obobo2$o3bo2$obobo2$4bo2$obobo!'
}
font = dict()
for i in font0.keys():
	font[i] = g.parse(font0[i])
def scan_number(u, v):
	cells = g.getcells([u, v, 5, 9])
	if cells == []: return ''
	for i in font.keys():
		if len(font[i]) != len(cells): continue
		p = font[i][0] - cells[0]
		q = font[i][1] - cells[1]
		for j in range(2, len(font[i]), 2):
			if font[i][j] - cells[j] != p: break
			if font[i][j+1] - cells[j+1] != q: break
		else:
			return i
	return ''
rect = g.getrect()
upos = set()
text = []
if rect==[]: rect=[0,0,0,0]
for u in range(rect[0], rect[0]+rect[2], scale):
	for v in range(rect[1], rect[1]+rect[3], scale):
		cells = g.getcells([u, v, 5, 9])
		n = []
		U = u
		while True:
			c = scan_number(U, v)
			if c == '': break
			n.append(c)
			U += 8
		if len(n) > 0:
			n = ''.join(n)
			text.append((n, u, v))
			upos.add(u)
upos = sorted(upos)
patterns = []
for i in range(len(text)):
	T = text[i][0]
	u1 = text[i][1]
	v1 = text[i][2]
	if upos.index(u1) == len(upos)-1: u2 = rect[0]+rect[2]+int(T)
	else: u2 = upos[upos.index(u1)+1]
	if i == len(text)-1 or text[i+1][1] > u1: v2 = rect[1]+rect[3]+int(T)
	else: v2 = text[i+1][2]
	u1 += len(T)*8-3
	#u1 += len(T)*6+2
	g.select([u1, v1, u2-u1, v2-v1])
	g.update()
	gen0 = g.getcells(g.getselrect())
	g.setrule(ruleHistory)
	g.run(int(T))
	g.shrink(True)
	rectT = g.getselrect()
	g.reset()
	g.setrule(rule)
	g.run(int(T))
	genT = g.getcells(rectT)
	g.reset()
	g.select(rectT)
	g.update()
	if gen0 != genT or rectT == []: g.warn('Invalid period-' + T + ' oscillator!')
	else:
		g.shrink()
		rle = get_RLE(rectT)
		rle = f'#{T} {rectT[2]} {rectT[3]}:\n' + rle + '!\n'
		patterns.append((int(T),rle))
if patterns!=[]:
	try:
		with open(filename) as f: l = f.read().split('!\n')
		if l != ['']:
			if l[-1] == '': del l[-1]
			while l[-1].endswith('!') or l[-1].endswith('\n'): l[-1] = l[-1][:-1]
			for i in l: patterns.append((int(i[1:i.index(' ')]) , i+'!\n'))
	except: pass
	e0 = lambda e: e[0]
	patterns.sort(key=e0)
	duplicates = set()
	for i in range(len(patterns)-1):
		if patterns[i][0] == patterns[i+1][0]: duplicates.add(patterns[i][0])
	if len(duplicates) > 0: g.warn("Duplicate entries: "+str(sorted(duplicates)))
	with open(filename, 'w') as f:
		for i in patterns: f.write(i[1])


g.setrule(rule)
fonts0 = [

[{
"0": "*.*.*$.....$*...*$.....$*...*$.....$*...*$.....$*.*.*!",
"1": "..*..$.....$*.*..$.....$..*..$.....$..*..$.....$*.*.*!",
"2": "*.*.*$.....$....*$.....$*.*.*$.....$*....$.....$*.*.*!",
"3": "*.*.*$.....$....*$.....$*.*.*$.....$....*$.....$*.*.*!",
"4": "*...*$.....$*...*$.....$*.*.*$.....$....*$.....$....*!",
"5": "*.*.*$.....$*....$.....$*.*.*$.....$....*$.....$*.*.*!",
"6": "*.*.*$.....$*....$.....$*.*.*$.....$*...*$.....$*.*.*!",
"7": "*.*.*$.....$....*$.....$....*$.....$....*$.....$....*!",
"8": "*.*.*$.....$*...*$.....$*.*.*$.....$*...*$.....$*.*.*!",
"9": "*.*.*$.....$*...*$.....$*.*.*$.....$....*$.....$*.*.*!",
"M": "*...*$.....$*.*.*$.....$*.*.*$.....$*...*$.....$*...*!",
"N": "*.*..$.....$*...*$.....$*...*$.....$*...*$.....$*...*!",
"c": ".....$.....$.....$.....$..*.*$.....$*....$.....$..*.*!",
"d": "....*$.....$....*$.....$..*.*$.....$*...*$.....$..*.*!",
"o": ".....$.....$.....$.....$..*..$.....$*...*$.....$..*..!",
"+": ".....$.....$..*..$.....$*.*.*$.....$..*..$.....$.....!",
"/": "....*$.....$....*$.....$..*..$.....$*....$.....$*....!",
"(": "....*$.....$..*..$.....$..*..$.....$..*..$.....$....*!",
")": "*....$.....$..*..$.....$..*..$.....$..*..$.....$*....!",
",": ".....$.....$.....$.....$.....$.....$..*..$.....$..*..!",
},5,8,9],

[{
"0": "*..*..*$.......$.......$*.....*$.......$.......$*.....*$.......$.......$*..*..*",
"1": "...*...$.......$*......$...*...$.......$.......$...*...$.......$.......$*..*..*",
"2": "*..*..*$.......$.......$......*$...*...$.......$*......$.......$.......$*..*..*",
"3": "*..*..*$.......$.......$......*$...*...$.......$......*$.......$.......$*..*..*",
"4": "......*$.......$.......$...*..*$.......$.......$*..*..*$.......$.......$......*",
"5": "*..*..*$.......$.......$*......$...*...$.......$......*$.......$.......$*..*..*",
"6": "*..*..*$.......$.......$*......$...*...$.......$*.....*$.......$.......$*..*..*",
"7": "*..*..*$.......$.......$.....*.$.......$.......$....*..$.......$.......$...*...",
"8": "*..*..*$.......$.......$*.....*$...*...$.......$*.....*$.......$.......$*..*..*",
"9": "*..*..*$.......$.......$*.....*$...*...$.......$......*$.......$.......$*..*..*",
"M": "*..*..*$.......$...*...$*.....*$.......$.......$*.....*$.......$.......$*.....*",
"N": "*..*...$.......$.......$*.....*$.......$.......$*.....*$.......$.......$*.....*",
"c": ".......$.......$.......$...*..*$.......$.......$*......$.......$.......$...*..*",
"d": "......*$.......$.......$...*..*$.......$.......$*.....*$.......$.......$...*..*",
"o": ".......$.......$.......$...*...$.......$.......$*.....*$.......$.......$...*...",
"+": ".......$...*...$.......$.......$*..*..*$.......$.......$...*...$.......$.......",
"/": "......*$.......$.......$....*..$.......$.......$..*....$.......$.......$*......",
"(": "....*..$.......$.......$..*....$.......$.......$..*....$.......$.......$....*..",
")": "..*....$.......$.......$....*..$.......$.......$....*..$.......$.......$..*....",
",": ".......$.......$.......$.......$.......$.......$...*...$.......$.......$..*....",
},7,10,10],

]

fonts = fonts0.copy()

for i in range(len(fonts0)):
	for j in fonts0[i][0].keys():
		fonts[i][0][j] = fonts0[i][0][j].replace('*','o').replace('.','b')

font = fonts[fontNumber]
fw = font[1]
ft = font[2]
my = max(my,font[3])
font = font[0]
snap = lambda x: ceil(x/scale)*scale
f = open(filename)
l = [i.split(":\n") for i in f.read().split("#")[1:]]
f.close()
l = [i[0].split(" ") + [i[1]] if i != ["\n"] else [] for i in l]
g.new("Collection")
dx = 0
dy = 0
tx = 0
j = 1
textState = 0
patternState = 0
states=[".","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","pA"]
for i in range(len(l)):
	if l[i]==[]:
		dx += snap(tx+space)
		dy = 0
		tx = 0
		if exponential: count *= 2
		continue
	if len(l[i])==5: patternState = int(l[i][3])
	elif len(l[i])==6:
		textState = int(l[i][3])
		patternState = int(l[i][4])
	p = l[i][0]
	i00 = ""
	i0 = "0"
	if "/" in p: i00=p.split("/")[1]
	else: i00 = p
	for ch in i00:
		if ch in "0123456789": i0 += ch
	i0 = int(i0)
	if count>0 and (ceil(i0/count)!=ceil(j/count) or i0<j):
		dx += snap(tx+space)
		dy = 0
		tx = 0
		if exponential: count *= 2
	fx = 0
	for k in range(len(p)):
		char = font[p[k]]
		if textState > 0: char = char.replace("*",states[textState])
		g.putcells(g.parse(char), dx+fx, dy)
		fx += ft
	fx = snap(max(fx+fw-ft+space, mx))
	if patternState > 0: l[i][-1] = l[i][-1].replace("b",".").replace("o",states[patternState]).replace("*",states[patternState])
	g.putcells(g.parse(l[i][-1]), dx+fx, dy)
	tx = max(tx, fx+int(l[i][1]))
	dy += snap(max(int(l[i][2])+space, my))
	j = i0
g.fit()
New important feature: collector.py v0.3 can scan oscillators from pattern and add them to the database. Example:
  • Open Golly, then make this pattern:

    Code: Select all

    x = 193, y = 61, rule = B3/S23
    2bo7b2o18bobobo3bobobo57bo3bo5bo43bo$10b2o41b3o30b3o63b3o$obo31bo3bo3b
    o57bo3bo3bobo40bo$51bo5bo2b2o18b2o2bo5bo45b2o13b2o$2bo27bobobo3bobobo
    8bo4b2o4bo16bo4b2o4bo9bobobo5bo26bo$51bo11bo14bo11bo46bobo$2bo31bo3bo
    3bo19b2o14b2o24bo5bo27b2o$148bo$obobo25bobobo3bobobo61bo3bobobo34bo$
    56b2o26b2o63bo$56bo28bo61b3o$57b3o22b3o$59bo22bo2$160bo$125b2o24b2o5bo
    bo$126bo24bo7b2o$124bo13b2o12b3o$122b4o15b2o11bo$121bo14b2obo$obobo13b
    2o100bo2b3o10b2o3bob2o$10b3o5b2o101b2o2bo10b2obo3b2o$4bo11b2o105b2o16b
    ob2o$16b2o105bo14b2o$obobo119bo6b3o7b2o26bo$121b3o7bo38b2o$o120bo5b2o
    3bo11bo24b2o$128bo14bobo$obobo120b3o15b2o$125bo2$187bo$168b2o15b3o$
    167bobo14bo$142b2o24bo11bo3b2o5bo$141b2o38bo7b3o$143bo26b2o7b3o6bo$
    173b2o14bo$168b2obo16b2o$168b2o3bob2o10bo2b2o$168b2obo3b2o10b3o2bo$
    173bob2o14bo$158bo11b2o15b4o$158b3o12b2o13bo$152b2o7bo24bo$152bobo5b2o
    24b2o$152bo4$163b3o$163bo$165bo$164bo$173b2o$173bobo$175bo$160b2o13b2o
    $161bo$158b3o$158bo!
    
  • Run collector.py, then enter filename;
  • Then you will see an oscillator collection like this one:

    Code: Select all

    x = 103, y = 151, rule = B3/S23
    2bo27b2o$30b2o$obo2$2bo2$2bo2$obobo22$obobo33b2o$30b3o5b2o$4bo31b2o$
    36b2o$obobo2$o2$obobo22$obobo3bobobo$33b3o30b3o$4bo3bo3bo$31bo5bo2b2o
    18b2o2bo5bo$obobo3bobobo18bo4b2o4bo16bo4b2o4bo$31bo11bo14bo11bo$4bo3bo
    3bo29b2o14b2o2$obobo3bobobo$36b2o26b2o$36bo28bo$37b3o22b3o$39bo22bo18$
    o3bo5bo53bo$62b3o$o3bo3bobo50bo$46b2o13b2o$obobo5bo36bo$47bobo$4bo5bo
    37b2o$58bo$4bo3bobobo44bo$59bo$57b3o4$70bo$35b2o24b2o5bobo$36bo24bo7b
    2o$34bo13b2o12b3o$32b4o15b2o11bo$31bo14b2obo$30bo2b3o10b2o3bob2o$31b2o
    2bo10b2obo3b2o$33b2o16bob2o$33bo14b2o$34bo6b3o7b2o26bo$31b3o7bo38b2o$
    31bo5b2o3bo11bo24b2o$38bo14bobo$35b3o15b2o$35bo2$97bo$78b2o15b3o$77bob
    o14bo$52b2o24bo11bo3b2o5bo$51b2o38bo7b3o$53bo26b2o7b3o6bo$83b2o14bo$
    78b2obo16b2o$78b2o3bob2o10bo2b2o$78b2obo3b2o10b3o2bo$83bob2o14bo$68bo
    11b2o15b4o$68b3o12b2o13bo$62b2o7bo24bo$62bobo5b2o24b2o$62bo4$73b3o$73b
    o$75bo$74bo$83b2o$83bobo$85bo$70b2o13b2o$71bo$68b3o$68bo!
    
Moreover, you can merge input pattern with database. For example, if oscillators.txt already contained pulsar, you would get this:

Code: Select all

x = 103, y = 181, rule = B3/S23
2bo27b2o$30b2o$obo2$2bo2$2bo2$obobo22$obobo33b2o$30b3o5b2o$4bo31b2o$
36b2o$obobo2$o2$obobo22$obobo$33b3o3b3o$4bo$31bo4bobo4bo$obobo26bo4bob
o4bo$31bo4bobo4bo$4bo28b3o3b3o2$obobo28b3o3b3o$31bo4bobo4bo$31bo4bobo
4bo$31bo4bobo4bo2$33b3o3b3o17$obobo3bobobo$33b3o30b3o$4bo3bo3bo$31bo5b
o2b2o18b2o2bo5bo$obobo3bobobo18bo4b2o4bo16bo4b2o4bo$31bo11bo14bo11bo$
4bo3bo3bo29b2o14b2o2$obobo3bobobo$36b2o26b2o$36bo28bo$37b3o22b3o$39bo
22bo18$o3bo5bo53bo$62b3o$o3bo3bobo50bo$46b2o13b2o$obobo5bo36bo$47bobo$
4bo5bo37b2o$58bo$4bo3bobobo44bo$59bo$57b3o4$70bo$35b2o24b2o5bobo$36bo
24bo7b2o$34bo13b2o12b3o$32b4o15b2o11bo$31bo14b2obo$30bo2b3o10b2o3bob2o
$31b2o2bo10b2obo3b2o$33b2o16bob2o$33bo14b2o$34bo6b3o7b2o26bo$31b3o7bo
38b2o$31bo5b2o3bo11bo24b2o$38bo14bobo$35b3o15b2o$35bo2$97bo$78b2o15b3o
$77bobo14bo$52b2o24bo11bo3b2o5bo$51b2o38bo7b3o$53bo26b2o7b3o6bo$83b2o
14bo$78b2obo16b2o$78b2o3bob2o10bo2b2o$78b2obo3b2o10b3o2bo$83bob2o14bo$
68bo11b2o15b4o$68b3o12b2o13bo$62b2o7bo24bo$62bobo5b2o24b2o$62bo4$73b3o
$73bo$75bo$74bo$83b2o$83bobo$85bo$70b2o13b2o$71bo$68b3o$68bo!
Version 0.2: viewtopic.php?p=159028#p159028
To do:
  • Support more fonts
  • Implement faster scanning algorithm
The latest version of hex-gliders.db have 668 gliders from OT hexagonal rules. Let's find more!
My CA (13 rules)
My scripts: new-glider.py v0.2, nbsearch2a.py, collector.py v0.3

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

Re: Golly scripts

Post by dvgrn » April 2nd, 2024, 8:01 pm

There's been some work recently on a project to clean up some of the over-use of the LifeWiki userspace for miscellaneous data storage.

For a set of several hundred "data-dump" userspace pages related to photon partials, wwei47 mentioned that it would be okay to remove them from the LifeWiki if there was a way to do a bulk download.

Bulk downloads are not particularly difficult to do with a Python script, so I've written one that works for this specific problem:

Code: Select all

import golly as g
import urllib.request
from urllib.request import urlopen, Request
import os

outpath = g.getstring("Enter base path for output files, using '/' as folder delimiter","C:/users/{username}/Desktop/out/")
if outpath == "C:/users/{username}/Desktop/out/":
  g.note("Please run this script again and change the sample path to something that works on your system.")
  g.exit()
if not outpath.endswith("/"):
  outpath += "/"
  
pages = """User:Prime Raptor21/Photon partials for 5S/P31
User:Prime Raptor21/Photon partials for 5S/P37
User:Prime Raptor21/Photon partials for 5S/P38
User:Prime Raptor21/Photon partials for 5S/P39
User:Prime Raptor21/Photon partials for 5S/P41
User:Prime Raptor21/Photon partials for 5S/P43
User:Prime Raptor21/Photon partials for 5S/P44
User:Prime Raptor21/Photon partials for 5S/P45
User:Prime Raptor21/Photon partials for 5S/P46
User:Prime Raptor21/Photon partials for 5S/P47
User:Prime Raptor21/Photon partials for 5S/P48
User:Prime Raptor21/Photon partials for 5S/P49
User:Prime Raptor21/Photon partials for 5S/P50
User:Prime Raptor21/Photon partials for 5S/P51
User:Prime Raptor21/Photon partials for 5S/P52
User:Prime Raptor21/Photon partials for 5S/P53
User:Prime Raptor21/Photon partials for 5S/P54
User:Prime Raptor21/Photon partials for 5S/P55
User:Prime Raptor21/Photon partials for 5S/P56
User:Prime Raptor21/Photon partials for 5S/P57
User:Prime Raptor21/Photon partials for 5S/P58
User:Prime Raptor21/Photon partials for 5S/P59
User:Prime Raptor21/Photon partials for 5S/P61
User:Prime Raptor21/Photon partials for 5S/P62
User:Prime Raptor21/Photon partials for 5S/P63
User:Prime Raptor21/Photon partials for 5S/P64
User:Prime Raptor21/Photon partials for 5S/P65
User:Prime Raptor21/Photon partials for 5S/P66
User:Prime Raptor21/Photon partials for 5S/P67
User:Prime Raptor21/Photon partials for 5S/P68
User:Prime Raptor21/Photon partials for 5S/P69
User:Prime Raptor21/Photon partials for 5S/P70
User:Prime Raptor21/Photon partials for 5S/P71
User:Prime Raptor21/Photon partials for 5S/P72
User:Prime Raptor21/Photon partials for 5S/P73
User:Prime Raptor21/Photon partials for 5S/P74
User:Prime Raptor21/Photon partials for 5S/P75
User:Prime Raptor21/Photon partials for 5S/P76
User:Prime Raptor21/Photon partials for 5S/P77
User:Prime Raptor21/Photon partials for 5S/P78
User:Prime Raptor21/Photon partials for 5S/P79
User:Prime Raptor21/Photon partials for 5S/P80
User:Prime Raptor21/Photon partials for 5S/P81
User:Prime Raptor21/Photon partials for 5S/P82
User:Prime Raptor21/Photon partials for 5S/P83
User:Prime Raptor21/Photon partials for 5S/P84
User:Prime Raptor21/Photon partials for 5S/P85
User:Prime Raptor21/Photon partials for 5S/P86
User:Prime Raptor21/Photon partials for 5S/P87
User:Prime Raptor21/Photon partials for 5S/P88
User:Prime Raptor21/Photon partials for 5S/P89
User:Prime Raptor21/Photon partials for 5S/P90
User:Prime Raptor21/Photon partials for 5S/P91
User:Prime Raptor21/Photon partials for 5S/P92
User:Prime Raptor21/Photon partials for 5S/P93
User:Prime Raptor21/Photon partials for 5S/P94
User:Prime Raptor21/Photon partials for 5S/P95
User:Prime Raptor21/Photon partials for 5S/P96
User:Prime Raptor21/Photon partials for 5S/P97
User:Prime Raptor21/Photon partials for 5S/P98
User:Prime Raptor21/Photon partials for 5S/P99
User:Prime Raptor21/Photon partials for 5S/P100
User:Prime Raptor21/Photon partials for 5S/P101
User:Prime Raptor21/Photon partials for 5S/P102
User:Prime Raptor21/Photon partials for 5S/P103
User:Prime Raptor21/Photon partials for 5S/P104
User:Prime Raptor21/Photon partials for 5S/P105
User:Prime Raptor21/Photon partials for 5S/P106
User:Prime Raptor21/Photon partials for 5S/P107
User:Prime Raptor21/Photon partials for 5S/P108
User:Prime Raptor21/Photon partials for 5S/P109
User:Prime Raptor21/Photon partials for 5S/P110
User:Prime Raptor21/Photon partials for 5S/P111
User:Prime Raptor21/Photon partials for 5S/P112
User:Prime Raptor21/Photon partials for 5S/P113
User:Prime Raptor21/Photon partials for 5S/P114
User:Prime Raptor21/Photon partials for 5S/P115
User:Prime Raptor21/Photon partials for 5S/P116
User:Prime Raptor21/Photon partials for 5S/P117
User:Prime Raptor21/Photon partials for 5S/P118
User:Prime Raptor21/Photon partials for 5S/P119
User:Prime Raptor21/Photon partials for 5S/P120
User:Prime Raptor21/Photon partials for 5S/P122
User:Prime Raptor21/Photon partials for 5S/P123
User:Prime Raptor21/Photon partials for 5S/P124
User:Prime Raptor21/Photon partials for 5S/P125
User:Prime Raptor21/Photon partials for 5S/P126
User:Prime Raptor21/Photon partials for 5S/P127
User:Prime Raptor21/Photon partials for 5S/P128
User:Prime Raptor21/Photon partials for 5S/P129
User:Prime Raptor21/Photon partials for 5S/P130
User:Prime Raptor21/Photon partials for 5S/P131
User:Prime Raptor21/Photon partials for 5S/P132
User:Prime Raptor21/Photon partials for 5S/P133
User:Prime Raptor21/Photon partials for 5S/P134
User:Prime Raptor21/Photon partials for 5S/P135
User:Prime Raptor21/Photon partials for 5S/P136
User:Prime Raptor21/Photon partials for 5S/P137
User:Prime Raptor21/Photon partials for 5S/P138
User:Prime Raptor21/Photon partials for 5S/P139
User:Prime Raptor21/Photon partials for 5S/P140
User:Prime Raptor21/Photon partials for 5S/P141
User:Prime Raptor21/Photon partials for 5S/P142
User:Prime Raptor21/Photon partials for 5S/P143
User:Prime Raptor21/Photon partials for 5S/P144
User:Prime Raptor21/Photon partials for 5S/P145
User:Prime Raptor21/Photon partials for 5S/P146
User:Prime Raptor21/Photon partials for 5S/P147
User:Prime Raptor21/Photon partials for 5S/P148
User:Prime Raptor21/Photon partials for 5S/P149
User:Prime Raptor21/Photon partials for 5S/P150
User:Prime Raptor21/Photon partials for 5S/P151
User:Prime Raptor21/Photon partials for 5S/P152
User:Prime Raptor21/Photon partials for 5S/P153
User:Prime Raptor21/Photon partials for 5S/P154
User:Prime Raptor21/Photon partials for 5S/P155
User:Prime Raptor21/Photon partials for 5S/P156
User:Prime Raptor21/Photon partials for 5S/P157
User:Prime Raptor21/Photon partials for 5S/P158
User:Prime Raptor21/Photon partials for 5S/P160
User:Prime Raptor21/Photon partials for 5S/P161
User:Prime Raptor21/Photon partials for 5S/P162
User:Prime Raptor21/Photon partials for 5S/P163
User:Prime Raptor21/Photon partials for 5S/P164
User:Prime Raptor21/Photon partials for 5S/P165
User:Prime Raptor21/Photon partials for 5S/P166
User:Prime Raptor21/Photon partials for 5S/P168
User:Prime Raptor21/Photon partials for 5S/P169
User:Prime Raptor21/Photon partials for 5S/P170
User:Prime Raptor21/Photon partials for 5S/P171
User:Prime Raptor21/Photon partials for 5S/P172
User:Prime Raptor21/Photon partials for 5S/P174
User:Prime Raptor21/Photon partials for 5S/P175
User:Prime Raptor21/Photon partials for 5S/P176
User:Prime Raptor21/Photon partials for 5S/P177
User:Prime Raptor21/Photon partials for 5S/P180
User:Prime Raptor21/Photon partials for 5S/P181
User:Prime Raptor21/Photon partials for 5S/P182
User:Prime Raptor21/Photon partials for 5S/P184
User:Prime Raptor21/Photon partials for 5S/P186
User:Prime Raptor21/Photon partials for 5S/P187
User:Prime Raptor21/Photon partials for 5S/P188
User:Prime Raptor21/Photon partials for 5S/P189
User:Prime Raptor21/Photon partials for 5S/P190
User:Prime Raptor21/Photon partials for 5S/P194
User:Prime Raptor21/Photon partials for 5S/P195
User:Prime Raptor21/Photon partials for 5S/P197
User:Prime Raptor21/Photon partials for 5S/P198
User:Prime Raptor21/Photon partials for 5S/P200
User:Prime Raptor21/Photon partials for 5S/P202
User:Prime Raptor21/Photon partials for 5S/P203
User:Prime Raptor21/Photon partials for 5S/P204
User:Prime Raptor21/Photon partials for 5S/P205
User:Prime Raptor21/Photon partials for 5S/P206
User:Prime Raptor21/Photon partials for 5S/P208
User:Prime Raptor21/Photon partials for 5S/P209
User:Prime Raptor21/Photon partials for 5S/P210
User:Prime Raptor21/Photon partials for 5S/P217
User:Prime Raptor21/Photon partials for 5S/P220
User:Prime Raptor21/Photon partials for 5S/P224
User:Prime Raptor21/Photon partials for 5S/P226
User:Prime Raptor21/Photon partials for 5S/P228
User:Prime Raptor21/Photon partials for 5S/P230
User:Prime Raptor21/Photon partials for 5S/P232
User:Prime Raptor21/Photon partials for 5S/P236
User:Prime Raptor21/Photon partials for 5S/P238
User:Prime Raptor21/Photon partials for 5S/P249
User:Prime Raptor21/Photon partials for 5S/P252
User:Prime Raptor21/Photon partials for 5S/P254
User:Prime Raptor21/Photon partials for 5S/P255
User:Prime Raptor21/Photon partials for 5S/P258
User:Prime Raptor21/Photon partials for 5S/P260
User:Prime Raptor21/Photon partials for 5S/P271
User:Prime Raptor21/Photon partials for 5S/P282
User:Prime Raptor21/Photon partials for 5S/P284
User:Prime Raptor21/Photon partials for 5S/P296
User:Prime Raptor21/Photon partials for 5S/P309
User:Prime Raptor21/Photon partials for 5S/P335
User:Prime Raptor21/Photon partials for 5S/P376
User:Prime Raptor21/Photon partials for 5S/P378
User:Prime Raptor21/Photon partials for 5S/P383
User:Prime Raptor21/Photon partials for 5S/P392
User:Prime Raptor21/Photon partials for 5S/P213
User:Prime Raptor21/Photon partials for 5S/P215
User:Prime Raptor21/Photon partials for 5S/P216
User:Prime Raptor21/Photon partials for 5S/P218
User:Prime Raptor21/Photon partials for 5S/P225
User:Prime Raptor21/Photon partials for 5S/P231
User:Prime Raptor21/Photon partials for 5S/P234
User:Prime Raptor21/Photon partials for 5S/P240
User:Prime Raptor21/Photon partials for 5S/P242
User:Prime Raptor21/Photon partials for 5S/P250
User:Prime Raptor21/Photon partials for 5S/P251
User:Prime Raptor21/Photon partials for 5S/P264
User:Prime Raptor21/Photon partials for 5S/P265
User:Prime Raptor21/Photon partials for 5S/P270
User:Prime Raptor21/Photon partials for 5S/P273
User:Prime Raptor21/Photon partials for 5S/P278
User:Prime Raptor21/Photon partials for 5S/P280
User:Prime Raptor21/Photon partials for 5S/P283
User:Prime Raptor21/Photon partials for 5S/P286
User:Prime Raptor21/Photon partials for 5S/P287
User:Prime Raptor21/Photon partials for 5S/P290
User:Prime Raptor21/Photon partials for 5S/P298
User:Prime Raptor21/Photon partials for 5S/P300
User:Prime Raptor21/Photon partials for 5S/P308
User:Prime Raptor21/Photon partials for 5S/P312
User:Prime Raptor21/Photon partials for 5S/P314
User:Prime Raptor21/Photon partials for 5S/P315
User:Prime Raptor21/Photon partials for 5S/P322
User:Prime Raptor21/Photon partials for 5S/P324
User:Prime Raptor21/Photon partials for 5S/P329
User:Prime Raptor21/Photon partials for 5S/P338
User:Prime Raptor21/Photon partials for 5S/P339
User:Prime Raptor21/Photon partials for 5S/P344
User:Prime Raptor21/Photon partials for 5S/P348
User:Prime Raptor21/Photon partials for 5S/P354
User:Prime Raptor21/Photon partials for 5S/P360
User:Prime Raptor21/Photon partials for 5S/P366
User:Prime Raptor21/Photon partials for 5S/P370
User:Prime Raptor21/Photon partials for 5S/P372
User:Prime Raptor21/Photon partials for 5S/P385
User:Prime Raptor21/Photon partials for 5S/P407
User:Prime Raptor21/Photon partials for 5S/P411
User:Prime Raptor21/Photon partials for 5S/P412
User:Prime Raptor21/Photon partials for 5S/P414
User:Prime Raptor21/Photon partials for 5S/P415
User:Prime Raptor21/Photon partials for 5S/P417
User:Prime Raptor21/Photon partials for 5S/P420
User:Prime Raptor21/Photon partials for 5S/P434
User:Prime Raptor21/Photon partials for 5S/P441
User:Prime Raptor21/Photon partials for 5S/P446
User:Prime Raptor21/Photon partials for 5S/P447
User:Prime Raptor21/Photon partials for 5S/P462
User:Prime Raptor21/Photon partials for 5S/P465
User:Prime Raptor21/Photon partials for 5S/P479
User:Prime Raptor21/Photon partials for 5S/P487
User:Prime Raptor21/Photon partials for 5S/P492
User:Prime Raptor21/Photon partials for 5S/P502
User:Prime Raptor21/Photon partials for 5S/P504
User:Prime Raptor21/Photon partials for 5S/P508
User:Prime Raptor21/Photon partials for 5S/P510
User:Prime Raptor21/Photon partials for 5S/P511
User:Prime Raptor21/Photon partials for 5S/P538
User:Prime Raptor21/Photon partials for 5S/P540
User:Prime Raptor21/Photon partials for 5S/P616
User:Prime Raptor21/Photon partials for 5S/P623
User:Prime Raptor21/Photon partials for 5S/P658
User:Prime Raptor21/Photon partials for 5S/P679
User:Prime Raptor21/Photon partials for 5S/P717
User:Prime Raptor21/Photon partials for 5S/P732
User:Prime Raptor21/Photon partials for 5S/P734
User:Prime Raptor21/Photon partials for 5S/P757
User:Prime Raptor21/Photon partials for 5S/P778
User:Prime Raptor21/Photon partials for 5S/P788
User:Prime Raptor21/Photon partials for 5S/P889
User:Prime Raptor21/Photon partials for 5S/P982
User:Prime Raptor21/Photon partials for 5S/P1006
User:Prime Raptor21/Photon partials for 5S/P1018
User:Prime Raptor21/Photon partials for 5S/P1022
User:Prime Raptor21/Photon partials for 5S/P1436
User:Prime Raptor21/Photon partials for 5S/P1636
User:Prime Raptor21/Photon partials for 5S/The Vault/P31
User:Prime Raptor21/Photon partials for 5S/The Vault/P37
User:Prime Raptor21/Photon partials for 5S/The Vault/P38
User:Prime Raptor21/Photon partials for 5S/The Vault/P39
User:Prime Raptor21/Photon partials for 5S/The Vault/P41
User:Prime Raptor21/Photon partials for 5S/The Vault/P43
User:Prime Raptor21/Photon partials for 5S/The Vault/P44
User:Prime Raptor21/Photon partials for 5S/The Vault/P45
User:Prime Raptor21/Photon partials for 5S/The Vault/P46
User:Prime Raptor21/Photon partials for 5S/The Vault/P47
User:Prime Raptor21/Photon partials for 5S/The Vault/P48
User:Prime Raptor21/Photon partials for 5S/The Vault/P49
User:Prime Raptor21/Photon partials for 5S/The Vault/P50
User:Prime Raptor21/Photon partials for 5S/The Vault/P51
User:Prime Raptor21/Photon partials for 5S/The Vault/P52
User:Prime Raptor21/Photon partials for 5S/The Vault/P53
User:Prime Raptor21/Photon partials for 5S/The Vault/P54
User:Prime Raptor21/Photon partials for 5S/The Vault/P55
User:Prime Raptor21/Photon partials for 5S/The Vault/P56
User:Prime Raptor21/Photon partials for 5S/The Vault/P57
User:Prime Raptor21/Photon partials for 5S/The Vault/P58
User:Prime Raptor21/Photon partials for 5S/The Vault/P59
User:Prime Raptor21/Photon partials for 5S/The Vault/P61
User:Prime Raptor21/Photon partials for 5S/The Vault/P62
User:Prime Raptor21/Photon partials for 5S/The Vault/P63
User:Prime Raptor21/Photon partials for 5S/The Vault/P64
User:Prime Raptor21/Photon partials for 5S/The Vault/P65
User:Prime Raptor21/Photon partials for 5S/The Vault/P66
User:Prime Raptor21/Photon partials for 5S/The Vault/P67
User:Prime Raptor21/Photon partials for 5S/The Vault/P68
User:Prime Raptor21/Photon partials for 5S/The Vault/P69
User:Prime Raptor21/Photon partials for 5S/The Vault/P70
User:Prime Raptor21/Photon partials for 5S/The Vault/P71
User:Prime Raptor21/Photon partials for 5S/The Vault/P72
User:Prime Raptor21/Photon partials for 5S/The Vault/P73
User:Prime Raptor21/Photon partials for 5S/The Vault/P74
User:Prime Raptor21/Photon partials for 5S/The Vault/P75
User:Prime Raptor21/Photon partials for 5S/The Vault/P76
User:Prime Raptor21/Photon partials for 5S/The Vault/P77
User:Prime Raptor21/Photon partials for 5S/The Vault/P78
User:Prime Raptor21/Photon partials for 5S/The Vault/P79
User:Prime Raptor21/Photon partials for 5S/The Vault/P80
User:Prime Raptor21/Photon partials for 5S/The Vault/P81
User:Prime Raptor21/Photon partials for 5S/The Vault/P82
User:Prime Raptor21/Photon partials for 5S/The Vault/P83
User:Prime Raptor21/Photon partials for 5S/The Vault/P84
User:Prime Raptor21/Photon partials for 5S/The Vault/P85
User:Prime Raptor21/Photon partials for 5S/The Vault/P86
User:Prime Raptor21/Photon partials for 5S/The Vault/P87
User:Prime Raptor21/Photon partials for 5S/The Vault/P88
User:Prime Raptor21/Photon partials for 5S/The Vault/P89
User:Prime Raptor21/Photon partials for 5S/The Vault/P90
User:Prime Raptor21/Photon partials for 5S/The Vault/P91
User:Prime Raptor21/Photon partials for 5S/The Vault/P92
User:Prime Raptor21/Photon partials for 5S/The Vault/P93
User:Prime Raptor21/Photon partials for 5S/The Vault/P94
User:Prime Raptor21/Photon partials for 5S/The Vault/P95
User:Prime Raptor21/Photon partials for 5S/The Vault/P96
User:Prime Raptor21/Photon partials for 5S/The Vault/P97
User:Prime Raptor21/Photon partials for 5S/The Vault/P98
User:Prime Raptor21/Photon partials for 5S/The Vault/P99
User:Prime Raptor21/Photon partials for 5S/The Vault/P100
User:Prime Raptor21/Photon partials for 5S/The Vault/P101
User:Prime Raptor21/Photon partials for 5S/The Vault/P102
User:Prime Raptor21/Photon partials for 5S/The Vault/P103
User:Prime Raptor21/Photon partials for 5S/The Vault/P104
User:Prime Raptor21/Photon partials for 5S/The Vault/P105
User:Prime Raptor21/Photon partials for 5S/The Vault/P106
User:Prime Raptor21/Photon partials for 5S/The Vault/P107
User:Prime Raptor21/Photon partials for 5S/The Vault/P108
User:Prime Raptor21/Photon partials for 5S/The Vault/P109
User:Prime Raptor21/Photon partials for 5S/The Vault/P110
User:Prime Raptor21/Photon partials for 5S/The Vault/P111
User:Prime Raptor21/Photon partials for 5S/The Vault/P112
User:Prime Raptor21/Photon partials for 5S/The Vault/P113
User:Prime Raptor21/Photon partials for 5S/The Vault/P114
User:Prime Raptor21/Photon partials for 5S/The Vault/P115
User:Prime Raptor21/Photon partials for 5S/The Vault/P116
User:Prime Raptor21/Photon partials for 5S/The Vault/P117
User:Prime Raptor21/Photon partials for 5S/The Vault/P118
User:Prime Raptor21/Photon partials for 5S/The Vault/P119
User:Prime Raptor21/Photon partials for 5S/The Vault/P120
User:Prime Raptor21/Photon partials for 5S/The Vault/P121
User:Prime Raptor21/Photon partials for 5S/The Vault/P122
User:Prime Raptor21/Photon partials for 5S/The Vault/P123
User:Prime Raptor21/Photon partials for 5S/The Vault/P124
User:Prime Raptor21/Photon partials for 5S/The Vault/P125
User:Prime Raptor21/Photon partials for 5S/The Vault/P126
User:Prime Raptor21/Photon partials for 5S/The Vault/P127
User:Prime Raptor21/Photon partials for 5S/The Vault/P128
User:Prime Raptor21/Photon partials for 5S/The Vault/P129
User:Prime Raptor21/Photon partials for 5S/The Vault/P130
User:Prime Raptor21/Photon partials for 5S/The Vault/P131
User:Prime Raptor21/Photon partials for 5S/The Vault/P132
User:Prime Raptor21/Photon partials for 5S/The Vault/P133
User:Prime Raptor21/Photon partials for 5S/The Vault/P134
User:Prime Raptor21/Photon partials for 5S/The Vault/P135
User:Prime Raptor21/Photon partials for 5S/The Vault/P136
User:Prime Raptor21/Photon partials for 5S/The Vault/P137
User:Prime Raptor21/Photon partials for 5S/The Vault/P138
User:Prime Raptor21/Photon partials for 5S/The Vault/P139
User:Prime Raptor21/Photon partials for 5S/The Vault/P140
User:Prime Raptor21/Photon partials for 5S/The Vault/P141
User:Prime Raptor21/Photon partials for 5S/The Vault/P142
User:Prime Raptor21/Photon partials for 5S/The Vault/P143
User:Prime Raptor21/Photon partials for 5S/The Vault/P144
User:Prime Raptor21/Photon partials for 5S/The Vault/P145
User:Prime Raptor21/Photon partials for 5S/The Vault/P146
User:Prime Raptor21/Photon partials for 5S/The Vault/P147
User:Prime Raptor21/Photon partials for 5S/The Vault/P148
User:Prime Raptor21/Photon partials for 5S/The Vault/P149
User:Prime Raptor21/Photon partials for 5S/The Vault/P150
User:Prime Raptor21/Photon partials for 5S/The Vault/P151
User:Prime Raptor21/Photon partials for 5S/The Vault/P152
User:Prime Raptor21/Photon partials for 5S/The Vault/P153
User:Prime Raptor21/Photon partials for 5S/The Vault/P154
User:Prime Raptor21/Photon partials for 5S/The Vault/P155
User:Prime Raptor21/Photon partials for 5S/The Vault/P156
User:Prime Raptor21/Photon partials for 5S/The Vault/P157
User:Prime Raptor21/Photon partials for 5S/The Vault/P158
User:Prime Raptor21/Photon partials for 5S/The Vault/P159
User:Prime Raptor21/Photon partials for 5S/The Vault/P160
User:Prime Raptor21/Photon partials for 5S/The Vault/P161
User:Prime Raptor21/Photon partials for 5S/The Vault/P162
User:Prime Raptor21/Photon partials for 5S/The Vault/P163
User:Prime Raptor21/Photon partials for 5S/The Vault/P164
User:Prime Raptor21/Photon partials for 5S/The Vault/P165
User:Prime Raptor21/Photon partials for 5S/The Vault/P166
User:Prime Raptor21/Photon partials for 5S/The Vault/P167
User:Prime Raptor21/Photon partials for 5S/The Vault/P168
User:Prime Raptor21/Photon partials for 5S/The Vault/P169
User:Prime Raptor21/Photon partials for 5S/The Vault/P170
User:Prime Raptor21/Photon partials for 5S/The Vault/P171
User:Prime Raptor21/Photon partials for 5S/The Vault/P172
User:Prime Raptor21/Photon partials for 5S/The Vault/P173
User:Prime Raptor21/Photon partials for 5S/The Vault/P174
User:Prime Raptor21/Photon partials for 5S/The Vault/P175
User:Prime Raptor21/Photon partials for 5S/The Vault/P176
User:Prime Raptor21/Photon partials for 5S/The Vault/P177
User:Prime Raptor21/Photon partials for 5S/The Vault/P178
User:Prime Raptor21/Photon partials for 5S/The Vault/P179
User:Prime Raptor21/Photon partials for 5S/The Vault/P180
User:Prime Raptor21/Photon partials for 5S/The Vault/P181
User:Prime Raptor21/Photon partials for 5S/The Vault/P182
User:Prime Raptor21/Photon partials for 5S/The Vault/P183
User:Prime Raptor21/Photon partials for 5S/The Vault/P184
User:Prime Raptor21/Photon partials for 5S/The Vault/P185
User:Prime Raptor21/Photon partials for 5S/The Vault/P186
User:Prime Raptor21/Photon partials for 5S/The Vault/P187
User:Prime Raptor21/Photon partials for 5S/The Vault/P188
User:Prime Raptor21/Photon partials for 5S/The Vault/P189
User:Prime Raptor21/Photon partials for 5S/The Vault/P190
User:Prime Raptor21/Photon partials for 5S/The Vault/P191
User:Prime Raptor21/Photon partials for 5S/The Vault/P192
User:Prime Raptor21/Photon partials for 5S/The Vault/P193
User:Prime Raptor21/Photon partials for 5S/The Vault/P194
User:Prime Raptor21/Photon partials for 5S/The Vault/P196
User:Prime Raptor21/Photon partials for 5S/The Vault/P197
User:Prime Raptor21/Photon partials for 5S/The Vault/P198
User:Prime Raptor21/Photon partials for 5S/The Vault/P199
User:Prime Raptor21/Photon partials for 5S/The Vault/P200
User:Prime Raptor21/Photon partials for 5S/The Vault/P201
User:Prime Raptor21/Photon partials for 5S/The Vault/P202
User:Prime Raptor21/Photon partials for 5S/The Vault/P203
User:Prime Raptor21/Photon partials for 5S/The Vault/P204
User:Prime Raptor21/Photon partials for 5S/The Vault/P205
User:Prime Raptor21/Photon partials for 5S/The Vault/P206
User:Prime Raptor21/Photon partials for 5S/The Vault/P207
User:Prime Raptor21/Photon partials for 5S/The Vault/P208
User:Prime Raptor21/Photon partials for 5S/The Vault/P209
User:Prime Raptor21/Photon partials for 5S/The Vault/P210
User:Prime Raptor21/Photon partials for 5S/The Vault/P212
User:Prime Raptor21/Photon partials for 5S/The Vault/P213
User:Prime Raptor21/Photon partials for 5S/The Vault/P214
User:Prime Raptor21/Photon partials for 5S/The Vault/P215
User:Prime Raptor21/Photon partials for 5S/The Vault/P216
User:Prime Raptor21/Photon partials for 5S/The Vault/P217
User:Prime Raptor21/Photon partials for 5S/The Vault/P218
User:Prime Raptor21/Photon partials for 5S/The Vault/P219
User:Prime Raptor21/Photon partials for 5S/The Vault/P220
User:Prime Raptor21/Photon partials for 5S/The Vault/P221
User:Prime Raptor21/Photon partials for 5S/The Vault/P222
User:Prime Raptor21/Photon partials for 5S/The Vault/P223
User:Prime Raptor21/Photon partials for 5S/The Vault/P225
User:Prime Raptor21/Photon partials for 5S/The Vault/P228
User:Prime Raptor21/Photon partials for 5S/The Vault/P230
User:Prime Raptor21/Photon partials for 5S/The Vault/P231
User:Prime Raptor21/Photon partials for 5S/The Vault/P233
User:Prime Raptor21/Photon partials for 5S/The Vault/P234
User:Prime Raptor21/Photon partials for 5S/The Vault/P236
User:Prime Raptor21/Photon partials for 5S/The Vault/P238
User:Prime Raptor21/Photon partials for 5S/The Vault/P239
User:Prime Raptor21/Photon partials for 5S/The Vault/P240
User:Prime Raptor21/Photon partials for 5S/The Vault/P241
User:Prime Raptor21/Photon partials for 5S/The Vault/P247
User:Prime Raptor21/Photon partials for 5S/The Vault/P248
User:Prime Raptor21/Photon partials for 5S/The Vault/P252
User:Prime Raptor21/Photon partials for 5S/The Vault/P254
User:Prime Raptor21/Photon partials for 5S/The Vault/P255
User:Prime Raptor21/Photon partials for 5S/The Vault/P259
User:Prime Raptor21/Photon partials for 5S/The Vault/P260
User:Prime Raptor21/Photon partials for 5S/The Vault/P262
User:Prime Raptor21/Photon partials for 5S/The Vault/P264
User:Prime Raptor21/Photon partials for 5S/The Vault/P266
User:Prime Raptor21/Photon partials for 5S/The Vault/P268
User:Prime Raptor21/Photon partials for 5S/The Vault/P270
User:Prime Raptor21/Photon partials for 5S/The Vault/P272
User:Prime Raptor21/Photon partials for 5S/The Vault/P273
User:Prime Raptor21/Photon partials for 5S/The Vault/P274
User:Prime Raptor21/Photon partials for 5S/The Vault/P275
User:Prime Raptor21/Photon partials for 5S/The Vault/P276
User:Prime Raptor21/Photon partials for 5S/The Vault/P278
User:Prime Raptor21/Photon partials for 5S/The Vault/P279
User:Prime Raptor21/Photon partials for 5S/The Vault/P280
User:Prime Raptor21/Photon partials for 5S/The Vault/P282
User:Prime Raptor21/Photon partials for 5S/The Vault/P283
User:Prime Raptor21/Photon partials for 5S/The Vault/P285
User:Prime Raptor21/Photon partials for 5S/The Vault/P286
User:Prime Raptor21/Photon partials for 5S/The Vault/P287
User:Prime Raptor21/Photon partials for 5S/The Vault/P288
User:Prime Raptor21/Photon partials for 5S/The Vault/P294
User:Prime Raptor21/Photon partials for 5S/The Vault/P295
User:Prime Raptor21/Photon partials for 5S/The Vault/P296
User:Prime Raptor21/Photon partials for 5S/The Vault/P297
User:Prime Raptor21/Photon partials for 5S/The Vault/P300
User:Prime Raptor21/Photon partials for 5S/The Vault/P304
User:Prime Raptor21/Photon partials for 5S/The Vault/P308
User:Prime Raptor21/Photon partials for 5S/The Vault/P310
User:Prime Raptor21/Photon partials for 5S/The Vault/P312
User:Prime Raptor21/Photon partials for 5S/The Vault/P315
User:Prime Raptor21/Photon partials for 5S/The Vault/P318
User:Prime Raptor21/Photon partials for 5S/The Vault/P320
User:Prime Raptor21/Photon partials for 5S/The Vault/P322
User:Prime Raptor21/Photon partials for 5S/The Vault/P330
User:Prime Raptor21/Photon partials for 5S/The Vault/P339
User:Prime Raptor21/Photon partials for 5S/The Vault/P341
User:Prime Raptor21/Photon partials for 5S/The Vault/P346
User:Prime Raptor21/Photon partials for 5S/The Vault/P352
User:Prime Raptor21/Photon partials for 5S/The Vault/P360
User:Prime Raptor21/Photon partials for 5S/The Vault/P363
User:Prime Raptor21/Photon partials for 5S/The Vault/P364
User:Prime Raptor21/Photon partials for 5S/The Vault/P369
User:Prime Raptor21/Photon partials for 5S/The Vault/P372
User:Prime Raptor21/Photon partials for 5S/The Vault/P376
User:Prime Raptor21/Photon partials for 5S/The Vault/P381
User:Prime Raptor21/Photon partials for 5S/The Vault/P382
User:Prime Raptor21/Photon partials for 5S/The Vault/P383
User:Prime Raptor21/Photon partials for 5S/The Vault/P392
User:Prime Raptor21/Photon partials for 5S/The Vault/P394
User:Prime Raptor21/Photon partials for 5S/The Vault/P399
User:Prime Raptor21/Photon partials for 5S/The Vault/P403
User:Prime Raptor21/Photon partials for 5S/The Vault/P413
User:Prime Raptor21/Photon partials for 5S/The Vault/P414
User:Prime Raptor21/Photon partials for 5S/The Vault/P420
User:Prime Raptor21/Photon partials for 5S/The Vault/P426
User:Prime Raptor21/Photon partials for 5S/The Vault/P428
User:Prime Raptor21/Photon partials for 5S/The Vault/P434
User:Prime Raptor21/Photon partials for 5S/The Vault/P435
User:Prime Raptor21/Photon partials for 5S/The Vault/P447
User:Prime Raptor21/Photon partials for 5S/The Vault/P448
User:Prime Raptor21/Photon partials for 5S/The Vault/P456
User:Prime Raptor21/Photon partials for 5S/The Vault/P459
User:Prime Raptor21/Photon partials for 5S/The Vault/P462
User:Prime Raptor21/Photon partials for 5S/The Vault/P464
User:Prime Raptor21/Photon partials for 5S/The Vault/P465
User:Prime Raptor21/Photon partials for 5S/The Vault/P467
User:Prime Raptor21/Photon partials for 5S/The Vault/P476
User:Prime Raptor21/Photon partials for 5S/The Vault/P480
User:Prime Raptor21/Photon partials for 5S/The Vault/P482
User:Prime Raptor21/Photon partials for 5S/The Vault/P483
User:Prime Raptor21/Photon partials for 5S/The Vault/P485
User:Prime Raptor21/Photon partials for 5S/The Vault/P489
User:Prime Raptor21/Photon partials for 5S/The Vault/P501
User:Prime Raptor21/Photon partials for 5S/The Vault/P504
User:Prime Raptor21/Photon partials for 5S/The Vault/P506
User:Prime Raptor21/Photon partials for 5S/The Vault/P508
User:Prime Raptor21/Photon partials for 5S/The Vault/P510
User:Prime Raptor21/Photon partials for 5S/The Vault/P511
User:Prime Raptor21/Photon partials for 5S/The Vault/P514
User:Prime Raptor21/Photon partials for 5S/The Vault/P536
User:Prime Raptor21/Photon partials for 5S/The Vault/P543
User:Prime Raptor21/Photon partials for 5S/The Vault/P546
User:Prime Raptor21/Photon partials for 5S/The Vault/P549
User:Prime Raptor21/Photon partials for 5S/The Vault/P558
User:Prime Raptor21/Photon partials for 5S/The Vault/P570
User:Prime Raptor21/Photon partials for 5S/The Vault/P572
User:Prime Raptor21/Photon partials for 5S/The Vault/P574
User:Prime Raptor21/Photon partials for 5S/The Vault/P583
User:Prime Raptor21/Photon partials for 5S/The Vault/P585
User:Prime Raptor21/Photon partials for 5S/The Vault/P608
User:Prime Raptor21/Photon partials for 5S/The Vault/P619
User:Prime Raptor21/Photon partials for 5S/The Vault/P620
User:Prime Raptor21/Photon partials for 5S/The Vault/P630
User:Prime Raptor21/Photon partials for 5S/The Vault/P635
User:Prime Raptor21/Photon partials for 5S/The Vault/P646
User:Prime Raptor21/Photon partials for 5S/The Vault/P651
User:Prime Raptor21/Photon partials for 5S/The Vault/P682
User:Prime Raptor21/Photon partials for 5S/The Vault/P684
User:Prime Raptor21/Photon partials for 5S/The Vault/P694
User:Prime Raptor21/Photon partials for 5S/The Vault/P699
User:Prime Raptor21/Photon partials for 5S/The Vault/P708
User:Prime Raptor21/Photon partials for 5S/The Vault/P732
User:Prime Raptor21/Photon partials for 5S/The Vault/P762
User:Prime Raptor21/Photon partials for 5S/The Vault/P780
User:Prime Raptor21/Photon partials for 5S/The Vault/P819
User:Prime Raptor21/Photon partials for 5S/The Vault/P826
User:Prime Raptor21/Photon partials for 5S/The Vault/P840
User:Prime Raptor21/Photon partials for 5S/The Vault/P868
User:Prime Raptor21/Photon partials for 5S/The Vault/P889
User:Prime Raptor21/Photon partials for 5S/The Vault/P930
User:Prime Raptor21/Photon partials for 5S/The Vault/P961
User:Prime Raptor21/Photon partials for 5S/The Vault/P990
User:Prime Raptor21/Photon partials for 5S/The Vault/P1013
User:Prime Raptor21/Photon partials for 5S/The Vault/P1016
User:Prime Raptor21/Photon partials for 5S/The Vault/P1020
User:Prime Raptor21/Photon partials for 5S/The Vault/P1022
User:Prime Raptor21/Photon partials for 5S/The Vault/P1023
User:Prime Raptor21/Photon partials for 5S/The Vault/P1073
User:Prime Raptor21/Photon partials for 5S/The Vault/P1085
User:Prime Raptor21/Photon partials for 5S/The Vault/P1094
User:Prime Raptor21/Photon partials for 5S/The Vault/P1113
User:Prime Raptor21/Photon partials for 5S/The Vault/P1146
User:Prime Raptor21/Photon partials for 5S/The Vault/P1190
User:Prime Raptor21/Photon partials for 5S/The Vault/P1207
User:Prime Raptor21/Photon partials for 5S/The Vault/P1250
User:Prime Raptor21/Photon partials for 5S/The Vault/P1365
User:Prime Raptor21/Photon partials for 5S/The Vault/P1454
User:Prime Raptor21/Photon partials for 5S/The Vault/P1524
User:Prime Raptor21/Photon partials for 5S/The Vault/P1533
User:Prime Raptor21/Photon partials for 5S/The Vault/P1663
User:Prime Raptor21/Photon partials for 5S/The Vault/P1676
User:Prime Raptor21/Photon partials for 5S/The Vault/P1714
User:Prime Raptor21/Photon partials for 5S/The Vault/P1771
User:Prime Raptor21/Photon partials for 5S/The Vault/P1778
User:Prime Raptor21/Photon partials for 5S/The Vault/P1785
User:Prime Raptor21/Photon partials for 5S/The Vault/P1860
User:Prime Raptor21/Photon partials for 5S/The Vault/P1880
User:Prime Raptor21/Photon partials for 5S/The Vault/P1905
User:Prime Raptor21/Photon partials for 5S/The Vault/P1953
User:Prime Raptor21/Photon partials for 5S/The Vault/P2044
User:Prime Raptor21/Photon partials for 5S/The Vault/P2046
User:Prime Raptor21/Photon partials for 5S/The Vault/P2047
User:Prime Raptor21/Photon partials for 5S/The Vault/P2239
User:Prime Raptor21/Photon partials for 5S/The Vault/P2467
User:Prime Raptor21/Photon partials for 5S/The Vault/P2504
User:Prime Raptor21/Photon partials for 5S/The Vault/P2643
User:Prime Raptor21/Photon partials for 5S/The Vault/P2644
User:Prime Raptor21/Photon partials for 5S/The Vault/P3298
User:Prime Raptor21/Photon partials for 5S/The Vault/P3810
User:Prime Raptor21/Photon partials for 5S/The Vault/P3937
User:Prime Raptor21/Photon partials for 5S/The Vault/P4094
User:Prime Raptor21/Photon partials for 5S/The Vault/P4095"""

pageslist = pages.split("\n")
count = len(pageslist)
for article in pageslist:
  url = ("https://conwaylife.com/wiki/" + article).replace(" ","_")
  outfname = article.replace("User:Prime Raptor21/Photon partials for 5S/","").replace("/","_") + ".txt"
  response = urllib.request.urlopen(url)
  html = str(response.read())
  i1 = html.index("<pre>")
  i2 = html.index("</pre>")
  
  with open(outpath + outfname,"w") as f:
    output = html[i1+5:i2].replace("\\n","\n").strip()
    f.write(output)
  g.show(str(count) + " : " + str(len(output)))
  count -= 1
g.show("Done.")
The details that are unique to this specific problem are

1) the list of articles at the top of the script,
2) the fact that each automatically-generated article started with "<pre>" and ended with "</pre>", so there's a little bit of code at the end that extracts just the text between "<pre>" and "</pre>" and sends that to a file.

Something very similar could be done for other LifeWiki data extraction problems, with minor changes to the code.

Not as big as I thought...
It turns out that when you compress the script's output files, they only come to 1.2 megabytes. Here's the archive:
614_photon_partial_files.zip
Zipped-up extracted photon partial files
(1.19 MiB) Downloaded 5 times

iddi01
Posts: 146
Joined: January 24th, 2024, 5:14 am
Location: B3-n/S1e2-a3-e4e

Re: Golly scripts

Post by iddi01 » April 13th, 2024, 9:48 am

Couldn't find such a script using forum search (seriously? it's so simple!), so i'm posting my random MAP rule generator here:

Code: Select all

import random
li = [chr(n) for n in range(65, 91)] + [chr(n) for n in range(97, 123)] + [chr(n) for n in range(48, 58)] + ['+', '/']
def randomap():
    mapstr = 'MAP'
    for n in range(86):
        mapstr += li[random.randrange(random.randrange(random.randrange(64)+1)+1)]
    return mapstr

if __name__ == "__main__":
    print(randomap())
else:
    import golly
    golly.setrule(randomap())
It can also be run in Shell.

Some interesting outputs:

Code: Select all

x = 2, y = 4, rule = MAPAFVBDLCEASEEDEBAeEIDUAABSNNCHALALPACLMAHCGSDHFBAGAJZMKAKbCDXBDIHMVFJCAwGACOKCBAEBDGFDA
o2$bo$o!

Code: Select all

x = 89, y = 12, rule = MAPAcABBaGeAMAKABBPFNCDAAITEBEAPAAMCYFCsLPFS9EMBYCEDCeCDBNXSAgAHJAABHTAABRIACBGZBAFdAABEA
2$4bo$3bobo$71bo$3bo55b29o$3bo51b4o12bo$3bo43b8o16bo$3bo41b2o24bo$3bo
67bo$3bo67bo$3bo!

Code: Select all

x = 5, y = 4, rule = MAPHCMAKDSDRGbLJAAABfMBBBHPBMSgCRACTEECABICCQERTBZqQDGACLDAlCBDIFRNALNBHAGACfAQVAFCBjBJHA
$4bo$2bo$bo!

Code: Select all

x = 19, y = 5, rule = MAPNAJAFBGOABEAHEAFSAUmMgCAeaCJXGCOlEoGFRMAJEKDEZCHEAFDBACAFFLYOiGIFPUFAUBXTQOOEJesKIAOBA
2o14b2o$2o14bo$2o14bo$2o14b2o$bo12bo2bo!

Code: Select all

x = 5, y = 36, rule = MAPBCSABBtAHCEIATFERACAPQCDAWCIBMDEARFGAIABGACQdEEKCPCAACBACEAGODCEEAcACBAPHfnBAAkiLDKACQ
5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$
5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o$5o!
Wiki: User:iddi01

I'm making a poll. please contribute.

First gun i constructed:

Code: Select all

x = 69, y = 69, rule = B3-n/S1e2-a3-e4e
2$32b3o$32bobo$32bobo$32b3o27$63b4o$b4o58bo2bo$bo2bo23bo4b2o28b4o$b4o
21bobo$28bo21$35bo$34b3o6$33b3o$33bobo$33bobo$33b3o!

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

Re: Golly scripts

Post by dvgrn » April 14th, 2024, 12:35 pm

iddi01 wrote:
April 13th, 2024, 9:48 am
Couldn't find such a script using forum search (seriously? it's so simple!), so i'm posting my random MAP rule generator...
I've moved a response by b-engine out to the "Miscellaneous Discoveries in Other Cellular Automata" thread, just because this "Golly scripts" thread needs to be about Golly scripts, not extended reports of things that a particular script produces.

However, one part of that post was really more relevant to this thread:
b-engine wrote:
April 14th, 2024, 12:23 am
I've also found out that most MAP rules it generates mostly contains B1e or B1c in some directions. Can you make a variant that don't spit B1 rules?

iddi01
Posts: 146
Joined: January 24th, 2024, 5:14 am
Location: B3-n/S1e2-a3-e4e

Re: Golly scripts

Post by iddi01 » April 19th, 2024, 8:39 am

b-engine wrote:
April 14th, 2024, 12:23 am
I've also found out that most MAP rules it generates mostly contains B1e or B1c in some directions. Can you make a variant that don't spit B1 rules?
After i finally figured out how to toggle individual transitions in MAP rules, i made this:

Code: Select all

import random,base64
def randomap():
    bits = bytearray(64)
    for n in range(512):
        if (not n in [1,2,4,8,32,64,128,256]) and random.randrange(3) == 0:
            bits[n // 8] |= (0x80 >> (n % 8))
    return "MAP" + base64.b64encode(bits).decode('ascii').strip("=")

if __name__ == "__main__":
    print(randomap())
else:
    import golly
    golly.setrule(randomap())
Note: some of the code are borrowed from hex2map.py .
Example output:

Code: Select all

x = 25, y = 18, rule = MAPAgFqoA2HKpoWxCggISgoOB48gW5KhE25JJJskOJRAC8QJPjghpkELA4MSiR3bAUGgPQxEGXAcMiCBX1+IOJg+A
$11bo$11bo$11bo$2bo8bo$2b5o4bo$7b8o$11bo$11bo$11bo$11bo11bo$11bo$11bo$
11bo$11bo$12bo$12bo!
Here's a slightly different version which also disallows b2a:

Code: Select all

import random,base64
def randomap():
    bits = bytearray(64)
    for n in range(512):
        if (not n in [0,1,2,4,8,32,64,128,256,3,6,9,36,72,192,288,384]) and random.randrange(2) == 0:
            bits[n // 8] |= (0x80 >> (n % 8))
    return "MAP" + base64.b64encode(bits).decode('ascii').strip("=")

if __name__ == "__main__":
    print(randomap())
else:
    import golly
    golly.setrule(randomap())
Example outputs:

Code: Select all

x = 25, y = 30, rule = MAPADXh4ANyV1U3d7FJ5Vq1ITlqxjGmqPA0WOez8kU5i2MXpd6IVq2P7X5ymu7X7wJsRKYer8LonpouLE6d2+Fo3g
2$12bo$11bo7bo$11bo7bo$10bo8bo$10bo8bobo$9bo9bo$9bo9bo$8bo10bo$8bo10bo
$7bo11bo$7bo11bo$6bo12bo$6bo12bo$5bo13bo$5bo13bo$20bo$21bo$22bo6$2bo$
3bo$b3o!

Code: Select all

x = 67, y = 52, rule = MAPBALlxwV5TgUrFlPhJl3EXyGXwtb6euRbQ1YRoZtQLWp/DlEpTfDYupbir8dtJZdsHnhnnhNksAfSfOZLVblmCA
2$10b3o$11bo21$50b2o$50bo$47bo2bo$47bobo$46bo2bo$46bo2bo$46bobo$12bo
33bobo$12b2o32bobo$45bo2b7o$3bo41b4o5bo$2b3o6bobo30bo2bo$12bo30b2o2bo$
46bo15b3o$46bo4b11o$36b15o$26b10o10bo$46bo$45bo$45bo$45bo$45bo$45bo$
45bo$44b2o!
Next i'll probably try to make a script for defining custom MAP rules, which is my ultimate goal when i started creating these scripts.
Wiki: User:iddi01

I'm making a poll. please contribute.

First gun i constructed:

Code: Select all

x = 69, y = 69, rule = B3-n/S1e2-a3-e4e
2$32b3o$32bobo$32bobo$32b3o27$63b4o$b4o58bo2bo$bo2bo23bo4b2o28b4o$b4o
21bobo$28bo21$35bo$34b3o6$33b3o$33bobo$33bobo$33b3o!

iddi01
Posts: 146
Joined: January 24th, 2024, 5:14 am
Location: B3-n/S1e2-a3-e4e

Re: Golly scripts

Post by iddi01 » April 21st, 2024, 6:49 am

iddi01 wrote:
April 19th, 2024, 8:39 am
Next i'll probably try to make a script for defining custom MAP rules, which is my ultimate goal when i started creating these scripts.
I invented a notation for MAP rules, which is used in the following script for defining custom MAP rules:

Code: Select all

import base64
letter_to_number = {
    'z': 0,
    'u': 128,
    'd': 2,
    'l': 32,
    'r': 8,
    'h': 40,
    'v': 130,
    'w': 42,
    'c': 162,
    'q': 138,
    'n': 168,
    'f': 160,
    'g': 136,
    'i': 10,
    'j': 34,
    'a': 170,
    'Z': 0,
    'U': 256,
    'D': 1,
    'L': 4,
    'R': 64,
    'H': 68,
    'V': 257,
    'W': 69,
    'C': 261,
    'Q': 321,
    'N': 324,
    'F': 260,
    'G': 320,
    'I': 65,
    'J': 5,
    'A': 325}
    
def customap(rulestring):
    bits = bytearray(64)
    n = 0
    n_base = 0
    for char in rulestring:
        if char in ['b', 'B']:
            n = n_base = 0
            char_ = False
        elif char in ['/', ',']:
            if char_:
                bits[n // 8] |= (0x80 >> (n % 8))
                n = n_base
            char_ = False
        elif char in ['/', 's', 'S']:
            n = n_base = 16
        else:
            char_ = True
            n += letter_to_number[char]
    if char_:
        bits[n // 8] |= (0x80 >> (n % 8))
        n = n_base
    return "MAP" + base64.b64encode(bits).decode('ascii').strip("=")

if __name__ == "__main__":
    rstr = input('Type a rulestring: ')
    print(customap(rstr))
else:
    import golly
    rstr = golly.getstring("Type a rulestring, using the notation documented at conwaylife.com/wiki/User:iddi01/Anisotropic_rule_notation: ")
    golly.setrule(customap(rstr))
    try:
        f = open(golly.getdir('rules') + "mapstrings.txt", 'r+')
        f.read()
    except FileNotFoundError:
        f = open(golly.getdir('rules') + "mapstrings.txt", 'w')
    f.write(rstr + " = " + customap(rstr) + '\n')
    f.close()
Note: this script currently does not correct non-canonical rulestrings. Make sure you type the rulestring strictly in the format BxX,xX/SxX,xX or you might get a weird unintended rule.

Whenever you set a rule using the script, the rulestring you typed and the corresponding MAP string is saved in mapstrings.txt in Golly's rule folder. That way, you can reuse a rule without having to type the rulestring again.

Example of what it generates:
A rule which emulates W110 at the upper and lower edge of a pattern and is chaotic within it (BzR,uZ,uU,uR,G,zL,dZ,dL,dD,J/S):

Code: Select all

x = 1, y = 1, rule = MAPPgAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAA
o!
Also, any suggestions on improving the notation?
Wiki: User:iddi01

I'm making a poll. please contribute.

First gun i constructed:

Code: Select all

x = 69, y = 69, rule = B3-n/S1e2-a3-e4e
2$32b3o$32bobo$32bobo$32b3o27$63b4o$b4o58bo2bo$bo2bo23bo4b2o28b4o$b4o
21bobo$28bo21$35bo$34b3o6$33b3o$33bobo$33bobo$33b3o!

User avatar
confocaloid
Posts: 3138
Joined: February 8th, 2022, 3:15 pm

Re: Golly scripts

Post by confocaloid » April 21st, 2024, 6:51 am

Prior art overview: viewtopic.php?f=11&t=6470
iddi01 wrote:
April 21st, 2024, 6:49 am
I invented a notation for MAP rules, which is used in the following script for defining custom MAP rules:
127:1 B3/S234c User:Confocal/R (isotropic CA, incomplete)
Unlikely events happen.
My silence does not imply agreement, nor indifference. If I disagreed with something in the past, then please do not construe my silence as something that could change that.

Post Reply