EDIT Jul. 21: The script is now working. With brutal force method not dichotomy.
I asked if there is an interest to compress Single Lane Channel (i.e. reduce the separation between Gliders).
dvgrn answered :
viewtopic.php?p=214575#p214575
So, I made a Golly Python 3 script to do that.
And it runs not too slow on my mind.
How to do :
The Single Lane Channel must be NW directed and first Glider must be :
Code:
Select all
x = 3, y = 3, rule = B3/S23
b2o$2o$2bo!
The Seed must be outside the total reaction's envelop.
Then select the Seed and run the script.
You will be prompted to enter the compression expected (in ticks).
That's all.
Here is the script :
Code: Select all
# Compress Single Lane Channel - Golly Python3 Script
#
# Restrictions;
# - Single Lane Channel must be directed to NW.
# - First Glider must to be b2o$2o$2bo!
# - First Glider must to be outside the total reaction's enveloppe.
# - 2 states universe only (i.e, rule = B3/S23).
#
# How To:
# - On a new layer, put your Single Lane Channel with the Seed(s) as the stream is directed to NW.
# - Select the Seed(s) without selecting any cell of any Glider.
# - Run the script.
# This may take a while belong the length of Single Lane Channel.
# The script indicates its progression (i.e., Current processing Glider, Total Compression in ticks).
#
# Author : Tawal 2025-07-21
### Imports
import golly as g
import re
# NW Gliders binaries decimal
GLIDERS = [ # Table : XXX parity index - What is XXX ? It's not 'colour' neither 'phase'
g.parse("b2o$2o$2bo!"), # 0 0 0
g.parse("3o$o$bo!"), # 0 1 1
g.parse("bo$2o$obo!"), # 1 0 2
g.parse("2o$obo$o!") # 1 1 3
]
### Functions
# clist functions
def clist2rect(clist):
x, y = min(clist[::2]), min(clist[1::2])
w, h = max(clist[::2])-x+1, max(clist[1::2])-y+1
return [x, y, w ,h]
def canon_clist(clist):
out = []
x, y, w, h = clist2rect(clist)
for i in range(0, len(clist), 2):
out += [clist[i]-x, clist[i+1]-y]
return out
# Gliders functions
def mv_glid(glid, ticks): # Call new_glid() and make change on layer
ng = new_glid(glid, ticks)
g.select([glid[1], glid[2], 3, 3])
g.clear(0)
g.putcells(GLIDERS[ng[0]], ng[1], ng[2])
return ng
def new_glid(glid, ticks): # Can be simplified - Surely !
ind0, x0, y0 = glid
ind = (ind0 + ticks)%4
st = 1 if ticks>0 else -1
shift = (st*ticks)//4 * st # Minimal diagonal shift
adj_shift = 1 if (-st*ticks)%4==1 else 0 # Adjust the shifting
ph = (ind - ind%2)//2 # Called 'ph' for 'phase' but what it is ? See GLIDERS definitions
dph = ph - (ind0 - ind0%2)//2
dx = st*( dph*((dph-st)//2) + adj_shift*(1-abs(dph)) )
dy = st*( dph*((dph+st)//2) + adj_shift*(1-abs(dph)) )
x = x0 - (shift + dx)
y = y0 - (shift + dy)
return ind, x, y # ind in GLIDERS, x, y
def find_glids(): # Special Glider finder for Single Lane only
coords = []
n = 0
jump = 0
x, y, w, h = g.getrect()
for dy in range(h-2):
if jump:
jump -= 1
continue
pat = g.getcells([x+dy, y+dy, 4, 4])
if pat:
px, py, *_ = clist2rect(pat)
pat = canon_clist(pat)
if pat in GLIDERS:
if not (n or pat==GLIDERS[0]):
return 'BAD_FIRST'
ind = GLIDERS.index(pat)
coords += [ (ind, px, py) ]
jump = 4
n += 1
g.show(f'{n} Gliders found')
else:
jump = 2
return coords
# Stream Compression functions
def stream_chunk(stream, start, n): # Generator: G_start-n+1, G_start-n+2 …, G_start , Ind_G_start-n+1
l = len(stream)
for i in range(start, l, 1):
out = [ stream[k] for k in range(i-n+1, i+1, 1) ]
out += [i-n+1]
yield out
def is_ok(g1, g2, g3, ticks):
# First evolve
r = g.getrect()
run = max(r[2], r[3])*4
p = g.getcells(r)
ash = g.evolve(p, run)
# Move Gliders and evolve
ng1 = mv_glid(g1, ticks)
ng2 = mv_glid(g2, ticks)
ng3 = mv_glid(g3, ticks)
p = g.getcells(r)
ash1 = g.evolve(p, run-ticks)
# Restore Gliders on layer
ng3 = mv_glid(ng3, -ticks)
ng2 = mv_glid(ng2, -ticks)
ng1 = mv_glid(ng1, -ticks)
if ash==ash1:
return True
return False
### Main
if __name__=='builtins':
r_seed = g.getselrect()
if not r_seed:
g.exit('Pas de sélection ! Exit.')
else:
g.shrink()
r_seed = g.getselrect()
seed = g.getcells(r_seed)
if not seed:
g.exit('Sélection vide ! Exit.')
g.clear(0) # Clear the seed to well scan on diagonal for Gliders.
# Total comparisons =~ h_stream//2 - nb_Gliders
# User choice : Minimal distance expected between 2 Gliders (ticks)
valid = None
reg = re.compile(r'(?P<dist>^\d+$)')
title = 'Single Lane Channel Compressor'
prompt = '''If you CANCEL this box, reset the pattern to retrieve the seed.
Press 'f' to update progression's viewing.
Distance expected between 2 Gliders (in ticks) :'''
while not valid:
s = g.getstring(prompt, "61", title)
valid = reg.match(s)
dist = int(valid.group('dist'))
dist = max(dist, 14)
# Locate Gliders
g.show('Searching for NW Gliders …')
stream = find_glids()
g.putcells(seed) # Restore seed.
if stream=='BAD_FIRST':
g.note('First Glider is not b2o$2o$2bo!')
g.exit(' ')
seps = [0] # Indexes shifted by 1 ==> seps[1] = Tg1 - Tg0
chunks = stream_chunk(stream, 1, 2) # g0, g1, ind0 … gN-1, gN, indN-1 … (i.e. N = len(stream) = nb Gliders)
for g1, g2, _ in chunks:
seps += [(g2[1] - g1[1])*4 - (g2[0]-g1[0])] # i.e. (X_g1 - X_g0)*4 - (ind_g1 - ind_g0)
smin_in = min(seps[1:])
smax_in = max(seps)
# Start Compression
start = g.getcells([r_seed[0], r_seed[1], stream[2][1]-r_seed[0]+3, stream[2][2]-r_seed[1]+3 ]) # Seed + 3 Gliders
l1 = g.addlayer()
g.new('SLC Compression Working Layer')
g.putcells(start)
nb_g = len(stream)
ratio = nb_g//10 # Ratio to update the layer (user visualisation ~ all 10% reached)
n_gc = 0 # Nb of Gliders compressed/advanced
N = 0
tot = 0 # Total Compression in ticks
gens = 0
chunks = stream_chunk(stream, 3, 3) # g1, g2, g3, ind1 … gN-2, gN-1, gN, indN-2 …
for g1, g2, g3, ind in chunks:
nu = (ind//ratio+1)*ratio
nu = f' at Glider: {nu}' if nu<=nb_g else ': Finish'
g.show(f'Processing Glider: {ind+1}/{nb_g} - Compression: {tot} ticks \t\t\t Next update{nu}')
g1 = new_glid(g1, tot+gens) # Put a Glider more and adjust the coordinates/type of triplet
g2 = new_glid(g2, tot+gens)
g.putcells(GLIDERS[g3[0]], g3[1], g3[2])
g3 = mv_glid(g3, tot+gens)
if ind%ratio==(ratio-1) or ind==1: # Update layer for user ~ all 10% reached
g.select([g1[1], g1[2], 3, 3]) # Seeing the processing Glider at this time
g.fit()
g.update()
for i in range(seps[ind]-dist, 0, -1): # Compress 1rst Glider of triplet (followed by the two last ones)
if is_ok(g1, g2, g3, i):
N = i
break
tot += N
n_gc += 1 if N else 0
g1 = mv_glid(g1, N) # Gliders on layer
g2 = mv_glid(g2, N)
g3 = mv_glid(g3, N)
stream[ind] = new_glid(g1, -gens) # 1rst Glider stored with modifications
step = seps[ind] - N # Run the pattern for a step
g.run(step)
gens += step
N = 0
#Compress 2 last Gliders.
g2 = new_glid(g2, step)
g3 = new_glid(g3, step)
for i in range(seps[-2]-dist, 0, -1):
if is_ok(g2, g3, g3, i):
N = i
break
tot += N
stream[-2] = new_glid(stream[-2], tot)
n_gc += 1 if N else 0
g2 = mv_glid(g2, N)
g3 = mv_glid(g3, N)
N = 0
for i in range(seps[-1]-dist, 0, -1):
if is_ok(g3, g3, g3, i):
N = i
break
tot += N
stream[-1] = new_glid(stream[-1], tot)
n_gc += 1 if N else 0
# Output result
g.new(f'SLC_Compressed_p{dist}_{tot}')
g.putcells(seed)
seps_out = []
chunks = stream_chunk(stream, 1, 2)
for g1, g2, _ in chunks:
g.putcells(GLIDERS[g1[0]], g1[1], g1[2])
seps_out += [(g2[1] - g1[1])*4 - (g2[0]-g1[0])]
g.putcells(GLIDERS[g2[0]], g2[1], g2[2])
smin_out = min(seps_out)
smax_out = max(seps_out)
g.fit()
g.show(f'Compression: {tot}\
- Gliders compressed: {n_gc}/{nb_g}\
- Min-Max: old: {smin_in}-{smax_in}, new: {smin_out}-{smax_out}')
How it works ?
Passing the details how the Gliders are found and stored,
I put the Seed and the 3 first Gliders of the stream on a new layer.
Then (in a loop), I put a Glider more.
So we have : The Seed and 4 Gliders.
Then I launch a recursive dichotomy to find the best compression between the very first Glider and the second. (edit: not in use, change by brutal force method)
Then I move the triplet of Gliders (3 last ones) on the layer (and store the right data of the Glider which have been compressed).
Then I run the pattern for a step (i.e the new separation between 1rst and 2nd Glider)
Etc …
Edit: Why I use a triplet of Gliders ?
First, I tried with only one Glider (+ the very first).
But it arrives sometimes that the next Glider doesn't pass correctly.
So, I tried with 2 Gliders. It was better but not perfect => It arrives sometimes that the last Glider is just clean destroyed without changing the reaction.
So, I use 3 Gliders. The 2 last ones are used to check if the reaction is right.
EDIT Jul. 21:
Dichotomy is not a good method: e.g., testing an advanced Glider at the middle can not work, but may be it can work with higher compression.
So the best method I found is to scan all possibilities from the maximum compression to zero.
It means that the script runs slower.
Doing that, I found another special case:
- A glider can not to be advanced (i.e. keeping its original separation belong its previous)
- Then the following Glider can be advanced
- Once the following Glider is advanced, it is possible to advance the Glider which can't be before (i.e. second pass)
Example for illustration:
Compressing the SLC SnarkMaker for Spiral Growth.
At a time, I get this stage where the white Glider (and its 2 following Gliders in yellow) can't be advanced to reach 61 ticks or more:
Code:
Select all
x = 314, y = 236, rule = LifeHistory
19.2A$20.A$18.A$18.5A15.A$23.A13.A.A$20.3A14.A.A$19.A18.A$19.4A$17.2A
3.A3.2A5.2A$16.A2.3A4.2A4.A2.A11.3A$16.2A.A13.2A$19.A25.A$19.2A24.A$
45.A$49.2A$48.A.A$48.2A34$74.A$74.A6.2A$74.A6.2A2.2A$85.2A4$93.3A$75.
2A2.2A$75.2A2.2A2$71.A$70.A.A7.2A$2A69.A7.3A$2A$79.A$73.3A3.2A$79.A7$
98.A$97.2A$97.A.A36$136.A$135.2A$135.A.A34$170.3A$170.A$171.A9$193.4D
$193.2D$193.D.D$193.D2.D$186.2A9.D13.3D3.3D23.D17.D2.3D2.5D8.3D3.3D9.
3D4.D3.D$185.2A11.D11.D3.D.D3.D7.D5.D8.D16.D2.D3.D5.D7.D3.D.D3.D7.D6.
2D4.D$187.A11.D10.D3.D.D3.D5.5D7.4D.D2.D3.4D5.D7.D4.D2.5D.D3.D.D3.D7.
D7.D5.D$200.D10.4D2.3D8.D5.D2.D5.D.D3.D9.D5.2D4.D10.4D2.3D2.5D.4D4.D
5.D$201.D12.D.D3.D7.D5.D2.D5.3D4.3D6.D7.D2.D4.5D5.D.D3.D7.D3.D3.D5.D$
202.D11.D.D3.D7.D5.D2.D5.D2.D6.D6.D2.D3.D.D15.D.D3.D7.D3.D3.D4.D$203.
D7.3D3.3D9.2D3.2D2.4D.D3.D.4D8.D2.3D2.D12.3D3.3D9.3D3.3D2.D$204.D$
205.D$206.D$207.D$208.D$209.D$210.D$211.D$212.D$213.D2.D$214.D.D$215.
2D$213.4D4$242.3D3.2D11.D24.D3.3D3.3D3.3D3.3D$211.C11.D17.D3.D3.D4.D
6.D23.D.D.D3.D.D3.D.D3.D.D$210.2C10.D2.5D.5D5.D7.D8.4D2.3D2.D.2D6.4D
3.D6.D.D2.2D5.D.D$210.C.C8.D19.D2.2D3.D4.D2.D3.D.D3.D.2D2.D5.D3.D8.D
2.D.D.D4.D2.4D$222.D2.5D.5D5.D3.D3.D4.D2.D3.D.5D.D9.D3.D7.D3.2D2.D3.D
3.D3.D$223.D17.D3.D3.D4.D2.D3.D.D5.D9.D3.D6.D4.D3.D2.D4.D3.D$242.3D4.
2D3.2D2.4D2.4D.D9.D3.D5.5D2.3D2.5D2.3D2$202.4D$202.2D$202.D.D$202.D2.
D$206.D$207.D$208.D$209.D$210.D$163.3D3.3D23.D15.D$162.D3.D.D3.D7.D5.
D8.D16.D$162.D3.D.D2.2D5.5D7.4D.D2.D3.4D7.D$163.4D.D.D.D7.D5.D2.D5.D.
D3.D12.D$166.D.2D2.D7.D5.D2.D5.3D4.3D10.D$166.D.D3.D7.D5.D2.D5.D2.D6.
D10.D$163.3D3.3D9.2D3.2D2.4D.D3.D.4D12.D$218.D14.2E$219.D12.2E$220.D
13.E$221.D$222.D2.D$223.D.D$224.2D$222.4D16$255.3E$255.E$256.E!
Glider n°2026 have a space of 37 ticks to reach p61. But no value between 37 and 0 works (i.e. the Glider can not be advanced)
A stage later, the following Glider can be advanced normally (edit: I didn't show the third Glider for this 2027th Glider advance)
Code:
Select all
x = 314, y = 228, rule = LifeHistory
19.2A$20.A$18.A$18.5A15.A$23.A13.A.A$20.3A14.A.A$19.A18.A$19.4A$17.2A
3.A3.2A5.2A$16.A2.3A4.2A4.A2.A11.3A$16.2A.A13.2A$19.A25.A$19.2A24.A$
45.A$49.2A$48.A.A$48.2A34$74.A$74.A6.2A$74.A6.2A2.2A$85.2A4$93.3A$75.
2A2.2A$75.2A2.2A2$71.A$70.A.A7.2A$2A69.A7.3A$2A$79.A$73.3A3.2A$79.A7$
98.A$97.2A$97.A.A36$136.A$135.2A$135.A.A34$170.3A$170.A$171.A9$193.4D
$193.2D$193.D.D$193.D2.D$186.2A9.D13.3D3.3D23.D17.D2.3D2.5D8.3D3.3D9.
3D4.D3.D$185.2A11.D11.D3.D.D3.D7.D5.D8.D16.D2.D3.D5.D7.D3.D.D3.D7.D6.
2D4.D$187.A11.D10.D3.D.D3.D5.5D7.4D.D2.D3.4D5.D7.D4.D2.5D.D3.D.D3.D7.
D7.D5.D$200.D10.4D2.3D8.D5.D2.D5.D.D3.D9.D5.2D4.D10.4D2.3D2.5D.4D4.D
5.D$201.D12.D.D3.D7.D5.D2.D5.3D4.3D6.D7.D2.D4.5D5.D.D3.D7.D3.D3.D5.D$
202.D11.D.D3.D7.D5.D2.D5.D2.D6.D6.D2.D3.D.D15.D.D3.D7.D3.D3.D4.D$203.
D7.3D3.3D9.2D3.2D2.4D.D3.D.4D8.D2.3D2.D12.3D3.3D9.3D3.3D2.D$204.D$
205.D$206.D$207.D$208.D$209.D$210.D$211.D$212.D$213.D2.D$214.D.D$215.
2D$213.4D4$242.3D3.2D11.D24.D3.3D3.3D3.3D3.3D$211.C11.D17.D3.D3.D4.D
6.D23.D.D.D3.D.D3.D.D3.D.D$210.2C10.D2.5D.5D5.D7.D8.4D2.3D2.D.2D6.4D
3.D6.D.D2.2D5.D.D$210.C.C8.D19.D2.2D3.D4.D2.D3.D.D3.D.2D2.D5.D3.D8.D
2.D.D.D4.D2.4D$222.D2.5D.5D5.D3.D3.D4.D2.D3.D.5D.D9.D3.D7.D3.2D2.D3.D
3.D3.D$223.D17.D3.D3.D4.D2.D3.D.D5.D9.D3.D6.D4.D3.D2.D4.D3.D$242.3D4.
2D3.2D2.4D2.4D.D9.D3.D5.5D2.3D2.5D2.3D$204.4D$204.2D$204.D.D$204.D2.D
$208.D$209.D$210.D$161.3D4.D24.D17.D$160.D6.2D9.D5.D8.D18.D$160.D7.D
7.5D7.4D.D2.D3.4D9.D$160.4D4.D9.D5.D2.D5.D.D3.D14.D10.3E$160.D3.D3.D
9.D5.D2.D5.3D4.3D12.D2.D6.E$160.D3.D3.D9.D5.D2.D5.D2.D6.D12.D.D7.E$
161.3D3.3D9.2D3.2D2.4D.D3.D.4D14.2D$215.4D18$249.E$248.2E$248.E.E!
Once Glider n°2027 is advanced, the Glider n°2026 (+following Gliders) can be advanced without changing the final reaction :
Code:
Select all
x = 303, y = 219, rule = LifeHistory
19.2A$20.A$18.A$18.5A15.A$23.A13.A.A$20.3A14.A.A$19.A18.A$19.4A$17.2A
3.A3.2A5.2A$16.A2.3A4.2A4.A2.A11.3A$16.2A.A13.2A$19.A25.A$19.2A24.A$
45.A$49.2A$48.A.A$48.2A34$74.A$74.A6.2A$74.A6.2A2.2A$85.2A4$93.3A$75.
2A2.2A$75.2A2.2A2$71.A$70.A.A7.2A$2A69.A7.3A$2A$79.A$73.3A3.2A$79.A7$
98.A$97.2A$97.A.A36$136.A$135.2A$135.A.A34$170.3A$170.A$171.A9$193.4D
11.3D4.D24.D$193.2D12.D6.2D9.D5.D8.D$193.D.D11.D7.D7.5D7.4D.D2.D3.4D$
193.D2.D10.4D4.D9.D5.D2.D5.D.D3.D$186.2A9.D9.D3.D3.D9.D5.D2.D5.3D4.3D
$185.2A11.D8.D3.D3.D9.D5.D2.D5.D2.D6.D$187.A11.D8.3D3.3D9.2D3.2D2.4D.
D3.D.4D$200.D$201.D$202.D$203.D$204.D2.D$205.D.D$206.2D$204.4D4$233.
3D3.2D11.D24.D3.3D3.3D3.3D3.3D$201.2C11.D17.D3.D3.D4.D6.D23.D.D.D3.D.
D3.D.D3.D.D$201.C.C9.D2.5D.5D5.D7.D8.4D2.3D2.D.2D6.4D3.D6.D.D2.2D5.D.
D$201.C10.D19.D2.2D3.D4.D2.D3.D.D3.D.2D2.D5.D3.D8.D2.D.D.D4.D2.4D$
213.D2.5D.5D5.D3.D3.D4.D2.D3.D.5D.D9.D3.D7.D3.2D2.D3.D3.D3.D$214.D17.
D3.D3.D4.D2.D3.D.D5.D9.D3.D6.D4.D3.D2.D4.D3.D$233.3D4.2D3.2D2.4D2.4D.
D9.D3.D5.5D2.3D2.5D2.3D$195.4D$195.2D$195.D.D$195.D2.D$199.D$200.D$
201.D$152.3D4.D24.D17.D$151.D6.2D9.D5.D8.D18.D$151.D7.D7.5D7.4D.D2.D
3.4D9.D12.E$151.4D4.D9.D5.D2.D5.D.D3.D14.D10.2E$151.D3.D3.D9.D5.D2.D
5.3D4.3D12.D2.D6.E.E$151.D3.D3.D9.D5.D2.D5.D2.D6.D12.D.D$152.3D3.3D9.
2D3.2D2.4D.D3.D.4D14.2D$206.4D18$239.2E$239.E.E$239.E!
So a second pass (or more) is needed to ensure to reach the maximum compression.
In fact, the second pass finds a new right timing between some Gliders that change the reaction but not the result.