Page 15 of 15

Re: Golly scripts

Posted: September 3rd, 2026, 3:54 pm
by rabbit
hotdogPi wrote: September 3rd, 2026, 3:33 pm Hopefully this won't 403, because for some inexplicable reason, trying to post this unzipped gives me a 403 error on both the forum and the wiki.
The issue appears to be the presence of the string "sleep(#)" within the script's source, where # is replaced with a number like 123 or 0.4. Attempting to post or preview with that in the body causes a 403.

Re: Golly scripts

Posted: September 3rd, 2026, 4:13 pm
by NNlk05
rabbit wrote: September 3rd, 2026, 3:54 pm The issue appears to be the presence of the string "sleep(#)" within the script's source, where # is replaced with a number like 123 or 0.4. Attempting to post or preview with that in the body causes a 403.
Can comfirm

Re: Golly scripts

Posted: September 3rd, 2026, 4:25 pm
by hotdogPi
There were four instances, but they were all commented out anyway, so here is the code.

Code: Select all

import golly as g
import time

'''Initializing'''
g.select([0,0,1,1])
#g.autoupdate(1)
g.new("example")
sparker = g.parse('''
14b3o$14bobo$6b2o6b3o$5bobo6b3o$2o3bo8b3o$2o3bobo6b3o$6b2o6bobo$14b3o
5$10bo$9bobo$10b2o!
''')
g.putcells(sparker, 0, 0)
sparker_pop = int(g.getpop())
sparker_box = g.getrect()
g.new("example")
type_ = 'r'
if type_ == 'hf':
    target_pattern = g.parse('2b3o$bo3bo$o5bo$o5bo$o5bo$bo3bo$2b3o!')
elif type_ == 'tl':
    target_pattern = g.parse('2bo$bobo$o3bo$bobo$2bo!')
elif type_ == 'pi':
    target_pattern = g.parse('b3o$o3bo$2ob2o!')
elif type_ == 'r':
    target_pattern = g.parse('2bo$2ob2o$o2bo$b2o!')
elif type_ == 'c':
    target_pattern = g.parse('b3o$bobo$o2bo$2o!')
rotation = 2
g.putcells(target_pattern, 0, 0)
for i in range(rotation):
    g.select(g.getrect())
    g.rotate(1)
    if i == 3:
        g.flip(0)
target_pop = int(g.getpop())
target_box = g.getrect()
if type_ == 'pi' and rotation == 0:
    target_box[3] += 1
hash1 = g.hash(target_box)
g.select(target_box)
g.flip(0)
g.flip(1) #180 degree rotation
target_pattern = g.getcells(target_box)
period = 45
g.new("example")
g.putcells(sparker, 0, 0)
minpop = int(g.getpop())
initial_box = g.getrect()
stator_cells = set(zip(g.getcells(g.getrect())[::2],g.getcells(g.getrect())[1::2]))
for i in range(period):
    g.run(1)
    minpop = min(minpop, int(g.getpop()))
    for j in (0,1):
        initial_box[j] = min(initial_box[j],g.getrect()[j])
    for j in (2,3):
        initial_box[j] = max(initial_box[j],g.getrect()[j])
    stator_cells = set.intersection(stator_cells,set(zip(g.getcells(g.getrect())[::2],g.getcells(g.getrect())[1::2])))
    g.show(str(len(stator_cells)))
    #g.update()
    #time.REDACTED(0.1)
width = g.getrect()[2]
height = g.getrect()[3]
#single_cell = g.parse('o!')
start_x, end_x, start_y, end_y = -15, 20, -9, 0
for y in range(start_y, end_y+1):
    for x in range(start_x, end_x+1):
        #if y == -18 and x < 4:
        #    continue
        recent_pops = list(range(100,109)) #9 same population in a row: end
        recent_min_x = list(range(100,108)) #checking for escaping gliders
        current_gen = None
        g.new("example")
        '''Putting patterns in place'''
        g.putcells(sparker, 0, 0)
        #g.putcells(target_pattern, x, y)
        g.run(0)
        if g.empty():
            continue
        found = []
        for generation in range(period):
            g.new("example")
            g.putcells(sparker, 0, 0)
            g.run(generation)
            sparker_pop = int(g.getpop())
            g.putcells(target_pattern, x, y)
            if int(g.getpop()) != sparker_pop + target_pop:
                continue
            original = g.getcells(g.getrect())
            hash_list = []
            broken = False
            if generation == 0:
                g.show('%s, %s' % (x, y))
                g.update()
            for generation1 in range(200):
                if broken:
                    break
                g.run(1)
                if generation1 % 15 == 0 and not g.getcell(14,0): #temporary
                    break
                if generation1 % 12 == 0 or generation1 % period == 0:  # checks for same pattern
                    hash_list.append(g.hash(g.getrect()))
                    if hash_list[-1] in hash_list[:-1]:
                        break
                max_x = None
                if max_x is None:
                    top_rect = g.getrect()
                else:
                    g.select([-50,-50,50+max_x,100]) # maximum x to check
                    g.shrink()
                    top_rect = g.getselrect()
                g.select(top_rect)
                # g.update()
                # time.REDACTED(.1)
                top_rect[1] = top_rect[1]
                top_rect[3] = target_box[3]
                g.select(top_rect)
                #g.update()
                #time.REDACTED(.1)
                g.shrink()
                top_rect = g.getselrect()
                top_rect[1] = top_rect[1]
                top_rect[2] = target_box[2]
                top_rect[3] = target_box[3]
                #g.update()
                #time.REDACTED(0.2)
                for cell in stator_cells:
                    if not g.getcell(cell[0],cell[1]):
                        broken = True
                        break
                '''if generation1 % period == 1 or g.hash(top_rect) == hash1:
                    temp = g.getcells(g.getrect())
                    g.select(sparker_box)
                    g.clear(1)
                    #g.warn('%s, %s, %s, %s' % (generation, generation1, int(g.getpop()), sparker_pop))
                    #g.update()
                    if generation1 % period == 1 and int(g.getpop()) < sparker_pop:
                        break
                    if int(g.getpop()) < minpop:
                        g.warn('%s, %s' % (int(g.getpop()), minpop))
                    g.new("example")
                    g.putcells(temp, 0, 0)'''
                if g.hash(top_rect) == hash1:
                    total_gens = generation+generation1
                    #if total_gens < 3:
                    #    continue
                    if len(g.getcells(sparker_box))//2 < minpop: #sparker region has missing cells
                        #g.warn(str([len(g.getcells(sparker_box)) // 2, minpop]))
                        continue
                    if int(g.getpop()) < minpop + target_pop: #total has too few cells
                        continue
                    if generation1 in (10,13) and type_ == 'tl': #common 11 and 14 pushes for traffic lights
                        continue
                    if generation1 == 77 and type_ == 'hf':  # common 78 push for honey farms
                            continue
                    if generation1 in (10,61) and type_ == 'lom':  # common 11 and 62 pushes for lumps of muck
                        continue
                    if generation1 == 25 and type_ == 'r' and rotation == 0:  # p52 shuttle (26-tick push)
                        continue
                    if generation1 == 41 and type_ == 'r' and rotation == 1:  # p84 shuttle (42-tick push)
                            continue
                    if generation1 == 122 and type_ == 'c' and rotation == 4:  # p246 shuttle (123-tick push)
                        continue
                    if generation1 == 87 and type_ == 'pi' and rotation == 1:  # p246 shuttle (123-tick push)
                        continue
                    if generation+generation1 in found:
                        continue
                    #if int(g.getpop()) == 178:
                    #    continue
                    #if min(recent_pops1[-min(10,total_gens):]) == max(recent_pops1[-min(10,total_gens):]):
                    #    continue
                    #g.putcells(current_gen_a, 100, 0)
                    #g.new("example")
                    g.putcells(original, -100, 0)
                    #g.getcells(g.getrect())
                    g.update()
                    g.warn('Found: %s, %s, %s' % (y, x, generation1+1))
                    #generation_snapshots = [''] * 200
                    found.append(generation+generation1)
                    break

Re: Golly scripts

Posted: September 20th, 2026, 3:43 pm
by TheWayOfTheCon
May13 wrote: August 26th, 2022, 2:06 am From B34q/S23-k thread:
I found it using modified version of nbsearch2.
My modification (I call it nbsearch2a) does not affect performance, but makes searching easier and supports bounded grids:
Branching off of this, I present (if we're all fine with the nomenclature) nbsearch2b.

I only added a few things to this version, though, those being
  • An option to dump the results pattern in a file, making long-term searches more viable
  • Gutter symmetries, specifically for D2_+1, D4_+1 along one axis, D4_+1 along both axes, D4_+2, and D8_1. You can also just type "Gut" to search all of them
  • A bit more user-friendliness

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=100         #the highest oscillator period to detect in the search
result_spacing=150
stab_step=8            #how many generations to simulate a soup before detecting periodicity (2^stab_step)
x=10                   #size of the soup (actually larger in most symmetries besides C1 )
main_fill=50           #density of the soup
count_update=1000      #how frequently the soup count will be updated
file_dump=False        #creates rle files containing the results

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")
g.note("You can further specify your search by looking through the .py file and changing the parameters near the top.", False)

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, D2_+1 gutter, D4_+1 gutter, D4_+1 two gutter, D8_+1 gutter, Gut

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=="Rot":
        symm=["C1", "C2_1", "C2_2", "C2_4", "C4_1", "C4_4"][random.randrange(6)]

    if s=="Gut":
        symm=["D2_+1 gutter", "D4_+1 gutter", "D4_+1 two gutter", "D4_+2 gutter", "D8_+1 gutter"][random.randrange(5)]
   
    # 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)

    if symm=="D2_+1 gutter" or symm=="D4_+1 gutter" or symm=="D4_+1 two gutter" or symm=="D4_+2 gutter":
        g.putcells(cell_list,x0,-y0-2,axx,axy,-ayx,-ayy,mode)

    if symm=="D4_+1 gutter":
        g.putcells(cell_list,x0,-y0,-axx,-axy,ayx,ayy,mode)
        g.putcells(cell_list,-x0,-y0-2,-axx,-axy,ayx,-ayy,mode)

    if symm=="D4_+1 two gutter":
        g.putcells(cell_list,x0-2,-y0,-axx,-axy,ayx,ayy,mode)
        g.putcells(cell_list,-x0-2,-y0-2,-axx,-axy,ayx,-ayy,mode)

    if symm=="D4_+2 gutter":
        g.putcells(cell_list,x0-1,-y0,-axx,-axy,ayx,ayy,mode)
        g.putcells(cell_list,-x0-1,-y0-2,-axx,-axy,ayx,-ayy,mode)

    if symm=="D8_1 gutter":
        g.putcells(cell_list,-x0-2,-y0-2,-axx,-axy,-ayx,-ayy,mode)
        g.putcells(cell_list,x0,y0-2,axx,axy,-ayx,-ayy,mode)
        g.putcells(cell_list,x0-2,y0,-axx,-axy,ayx,ayy,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>-1:
                        oc[test]+=1
                        g.setlayer(results_layer)
                        g.putcells(osc, result_spacing*test, result_spacing*oc[test])
                        g.setname('OscResults (%d)' % results)
                        if file_dump:
                            g.save('OscResults (%d).rle' % results, 'rle')
                        g.fit()
                        g.update()
                        g.setlayer(test_layer)
                        g.setname('RandOsc (%d)' % results)
                    else:
                        return True
            else:
                results+=1
                if results_layer>-1:
                    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()


If I feel like improving this version more, here's what to expect:
  • Period verify to filter out non interacting LCMs
  • A few more weird symmetries
  • Optimizations if I can find them

Re: Golly scripts

Posted: September 22nd, 2026, 7:50 am
by qqd
Rudyj wrote: September 20th, 2026, 2:16 pm Greetings everyone!

I would like to invite the community to join a fun challenge that I call searching for "Devote Parents" (Щирі Батьки by ukrainian).

For a given pattern P1, a Devote Parent is a strict phoenix configuration P0 (where no live cell survives to the next generation, i.e., every cell dies by underpopulation or overcrowding) that gives birth to pattern P1 in exactly one generation, while none of the parent's cells survive into P1.
...
Here's a script that generates and displays devote parents of the barge family (which means there are infinitely many still lives with devote parents and all barges are "faithful"):

Code: Select all

local g = golly()
g.new("Devote Parents for Barges")

local function makeDevoteParentForLine(lengthOfLine)
    -- For the sake of demonstration it will be quicker to write for generating an RLE than to write for generating a cell array directly
    local length = lengthOfLine + 2 -- We need this length for most of the generation
    local RLE = ""
    for i = 2, length do
        local rowStart = (i % 3 == 0) and "2o" or "2b"
        RLE = RLE..rowStart..i.."o$"
    end
    if length % 3 == 0 then
        RLE = RLE.."2b"..length.."o$"
    elseif length % 3 == 1 then
        RLE = RLE.."3b"..(length - 1).."o$"
    else
        RLE = RLE.."2o"..length.."o$"
    end
    local lastRows = string.rep("b", (length - 1) % 3)..string.rep("bob", (length - 1) // 3 + 1)
    RLE = RLE..lastRows.."$"
    if length % 3 == 1 then lastRows = lastRows:gsub("o", "b", 1) end
    RLE = RLE..lastRows.."!"
    return g.parse(RLE)
end

local function makeDevoteParentForBarge(lengthOfBarge)
    local dpLine1 = makeDevoteParentForLine(lengthOfBarge)
    local dpLine2 = g.transform(dpLine1, lengthOfBarge + 8, lengthOfBarge - 2, -1, 0, 0, -1)
    return g.join(dpLine1, dpLine2)
end

local k = 2
while true do
    g.select{-2, -2, 1, 1}
    g.clear(1) -- Doing this so that you can zoom in and out without resetting the zoom (which would occur using g.new)
    g.select{}
    g.putcells(makeDevoteParentForBarge(k))
    g.update()
    g["sleep"](500) -- Currently using g . sleep directly in a post causes a 403 forbidden error (It appears the security system has been updated)
    g.run(1)
    g.update()
    g["sleep"](500)
    k = k + 1
end
I believe with a couple of modifications this can be extended to other still life families based on stable diagonal lines.

Re: Golly scripts

Posted: September 26th, 2026, 8:57 am
by qqd
(Apologies for double posting, but this is an unrelated script.)
There is a script called create-custom-ltl.lua in Golly that allows you to create a custom Larger than Life neighborhood. However, no such Lua script exists for weighted neighborhoods, so here is one for that as well:

Code: Select all

-- create-weighted-ltl.lua
-- A script that creates a weighted Larger than Life rule using the current selection
-- By qqd (conwaylife.com forum user) based on Chris Rowett's create-custom-ltl script located in Scripts > Lua in Golly
-- Instructions:
--[[
1. Run this script WITHOUT making a selection first so that the layer colors will be set in a way that 
corresponds to the weights each cell state has (this step can be omitted but that is not recommended)
2. Draw your weighted Larger than Life neighborhood, ensuring it fits in a square with odd dimensions
For all states their corresponding weights are:
0, 1, ... 127, -127, ... -1 for states from 0 to 255 for the -127 to 127 range
0, 1, ... 7, -7 ,... -1 for states from 0 to 15 for to -7 to 7 range
0 is black, positive weights range in a dark blue-reddish purple-brown-yellow gradient, 
and negative weights range in a maroon-bluish purple-blue-cyan gradient
Darker the color, lesser the magnitude of the weight
3. Select your desired neighborhood, ensuring that the selection is square with odd dimensions from 3 to 99
4. Run this script again to convert your selection to a weighted Larger than Life neighborhood
--]]

local g = golly()

local selrect = g.getselrect()
local minlen = 3
local maxlen = 99

if selrect[1] then -- Selection exists

    if selrect[3] ~= selrect[4] then
        g.exit("Selection must be square")
    elseif selrect[3] & 1 == 0 then
        g.exit("Square selection must have odd dimensions")
    elseif selrect[3] < minlen or selrect[3] > maxlen then
        g.exit("Square selection must have dimensions from "..minlen.." to "..maxlen)
    end
    local width = selrect[3]
    local maxweight = (g.numstates() > 15) and 127 or 7
    local array = g.getcells(selrect)
    local weightmap = {}
    for i = 1, width*width do
        weightmap[i] = (maxweight == 127) and "00" or "0"
    end
    local arraylen = #array
    if arraylen & 1 == 1 then
        for i = 1, arraylen - 3, 3 do
            local x, y, state = array[i], array[i+1], array[i+2]
            local weight = (state + maxweight) % (2*maxweight + 1) - maxweight
            local hex = string.format("%0"..((maxweight == 127) and "2" or "1").."X", ((weight < 0) and (maxweight + 1) or 0)|math.abs(weight))
            weightmap[1 + (x-selrect[1]) + width*(y-selrect[2])] = hex
        end
    else
        for i = 1, arraylen - 2, 2 do
            local x, y = array[i], array[i+1]
            weightmap[1 + (x-selrect[1]) + width*(y-selrect[2])] = "1"
        end
    end
    local rule = "R"..(width//2)..",C0,S1,B1,NW"..table.concat(weightmap)
    g.note("Created rule with weighted Larger than Life neighborhood:\n\n"..rule,false)
    g.setrule(rule)
    g.setcolors{}
    
else -- No selection
    g.error("") -- Beep without updating status bar
    local ans = g.query("Create Weighted Larger than Life neighborhood", 
    [[There is no square selection to create a neighborhood.

Select your range of weights to convert the rule and layer colors to create a 
weighted neighborhood, else press 'Cancel'.]],
    "From -7 to 7", "From -127 to 127", "Cancel")
    if ans == "Cancel" then g.exit("Script aborted.") end
    g.new("")
    local colpositive = {255, 210, 0}
    local colnegative = {0, 210, 255}
    local editcols = {0,0,0,0}
    local colstate = {}
    local maxweight = (ans == "From -127 to 127") and 127 or 7
    local max = math.max
    local min = math.min
    local floor = math.floor
    local minbright = 6
    local maxbright = 255
    local invert = {false, false, false} -- Stores whether the 3 color channels should start increasing in value instead of decreasing
    -- Function to calculate the next color (its not exactly very intuitive but
    -- this is used to ensure at least a bare minimum level of distinguishibility for the -127 to 127 range)
    local function nextcol(basecol, channel)
        local newcol
        if invert[channel] then
            newcol =  min(maxbright, colstate[channel] + 0.5)
        else
            newcol =  max(minbright, colstate[channel] - max(2, (basecol[channel] // (maxweight+1) )))
        end
        if colstate[channel] <= minbright then
            invert[(channel - 1) % 3 + 1] = true
        end
        colstate[channel] = newcol
    end
    invert = {false, false, false}
    for k = 1, 3 do colstate[k] = colnegative[k] end
    
    for i = maxweight + 1, 2*maxweight do
        editcols[4*i+1] = i
        for channel = 1, 3 do
            editcols[4*i+channel+1] = floor(colstate[channel])
            nextcol(colnegative, channel)
        end
    end
    invert = {false, false, false}
    for k = 1, 3 do colstate[k] = colpositive[k] end
    
    for i = maxweight, 1, -1 do
        editcols[4*i+1] = i
        local invert = 0 -- Bitwise storage of whether the 3 channels should start increasing instead of decreasing
        for channel = 1, 3 do
            editcols[4*i+channel+1] = floor(colstate[channel])
            nextcol(colpositive, channel)
        end
    end
    
    
    g.setrule("//"..(2*maxweight + 1)) -- Switch to either //15 or //255
    g.setcolors(editcols)
    
end