this one works, but still needs further de-bugging
BitMap-Printer-p154-Loafer-Gun.lua
Code: Select all
-- BitMap Printer - p154 Loafer Gun - Mirrored Engine (Pure Lua for Golly)
-- Last version converted: 2024-11-17 (logic preserved from original Python)
--
-- HOWTO: Select a 2-state area in Golly and run this script.
-- Enter configuration string: <scrolling><init><distance>
-- scrolling = 'U','D','L','R'
-- init = '+' or '-'
-- distance = integer percentage for eater wall placement (0 = none)
--
-- Note: This is a faithful conversion from the Python script. It uses
-- Golly's Lua API (g.* functions) just like the Python used golly.g.
local g = golly()
-- helpers
local floor = math.floor
local max = math.max
local min = math.min
g.setalgo("HashLife")
g.setrule("b3s23")
-- xy_fit(typ, x, y, step, h_minus)
local function xy_fit(typ, x, y, step, h_minus)
local xx = x + 316 * step
local yy = y - 25 * step
if typ == "bottom" then
xx = xx + 195
yy = 248*2 - yy - h_minus
end
return xx, yy
end
-- local state holders that will be populated later:
local row = {}
local row_sum = {}
local w, h
-- more_gun(typ, rank)
local function more_gun(typ, rank)
if typ == "top" then
local fin, step = -1, -1
local i = rank
while i ~= fin do
if row_sum[i] and row_sum[i] > 0 then return true end
i = i + step
end
return false
else
local fin, step = h, 1
local i = rank
while i ~= fin do
if row_sum[i] and row_sum[i] > 0 then return true end
i = i + step
end
return false
end
end
-- put_part(typ, step, line, gun, loop, glid, offset)
-- gun = {pattern, h_gun_minus}
-- loop = {pattern, h_loop_minus, nb_slide}
-- glid = { glider_table, {x,y}, phase }
local function put_part(typ, step, line, gun, loop, glid, offset)
local h_g_minus, g_adj = 0, 0
local sign = 1
if typ == "bottom" then
sign = -1
h_g_minus = 2
g_adj = 4
end
-- Gun
local x, y = xy_fit(typ, 0, 0, step, gun[2])
local gunin = g.transform(gun[1], x, y + gun[2], 1, 0, 0, sign)
local comp = g.transform(gunin, -offset, 0)
-- Loop
local lx, ly = xy_fit(typ, 0, 0, step, loop[2])
local loopin = g.transform(loop[1], lx, ly + loop[2], 1, 0, 0, sign)
comp = g.join(comp, g.transform(loopin, -offset, 0))
-- Input Glider Stream
local dx = glid[2][1] - ( (33*loop[3])//77 + 2 + g_adj )*77 - offset
local dy = glid[2][2] - ( (33*loop[3])//77 + 2 + g_adj )*77
dx, dy = xy_fit(typ, dx, dy, step, h_g_minus)
for j = 0, w-1 do
if row[line][w-j-1] == 1 then
if glid[3] == 0 then
local gl_in = glid[1][((j+1)%2)+1] -- Lua index starts at 1
local glidin = g.transform(gl_in, dx, dy + h_g_minus, 1, 0, 0, sign)
comp = g.join(comp, g.transform(glidin, (offset//4)*7, sign*(offset//4)*7))
else
local gl_in = glid[1][(j%2)+3] -- j%2+2 in python -> +3 due to Lua indexing
local glidin = g.transform(gl_in, dx, dy + h_g_minus, 1, 0, 0, sign)
comp = g.join(comp, g.transform(glidin, (offset//4)*7, sign*(offset//4)*7))
end
end
local stepdx = 38 + ((j+1+glid[3])%2)
local stepdy = 38 + ((j+glid[3])%2)
dx = dx - stepdx
dy = dy - sign * stepdy
end
return comp
end
-- -----------------------------
-- Selection validation and reading
-- -----------------------------
local srect = g.getselrect()
if srect == nil or #srect == 0 then
g.select(g.getrect())
srect = g.getselrect()
end
if srect == nil or #srect == 0 or (#srect % 2 > 0) then
g.exit("Please select a bitmap area in any two-state rule, to produce a p154 bitmap loafer printer for it.")
end
local ix, iy, iw, ih = srect[1], srect[2], srect[3], srect[4]
if iw + ih > 99999 then
g.exit("That's an awfully big bitmap to gunnify. Change the script if you want to try it.")
end
-- get configuration string from user
local ok = false
local orient, init, dist
local title = "BitMap Printer Creator"
local prompt = [[Enter a printer configuration string:
<scrolling direction><init><distance>
<scrolling direction> must be a single letter: U (up), D (down), L (left), R (right).
<init> must be a single symbol: + (initialise), - (do not initialise).
<distance> percentage of BitMap's width to place the Eater Wall (0 = no Eater wall).
Example : 200 = 2 complete frames.
/!\ BitMap Printer's initialisation can take a long time. /!\
Examples:
L+100 scrolling to the left, initialise, Eater Wall at 1 frame out.
R-0 as default, scrolling to the right, do not initialise, no Eater wall.
]]
local initial = "R-0"
while not ok do
local s = g.getstring(prompt, initial, title)
if not s then g.exit("Cancelled by user.") end
s = string.upper(s)
-- parse: first char orient, second char init, rest digits dist
if #s >= 3 then
local o = s:sub(1,1)
local it = s:sub(2,2)
local d = s:sub(3)
if (o == "U" or o == "D" or o == "L" or o == "R") and (it == "+" or it == "-") and tonumber(d) then
orient = o
init = it
dist = tonumber(d)
ok = true
end
end
end
local msg1 = "Please wait : "
g.show(msg1 .. "Computing your BitMap Printer … ")
-- orientation transforms (t = transform for putting results back, tb = transform for reading bitmap)
local up = {0,1,-1,0}
local down = {0,-1,1,0}
local leftt = {-1,0,0,-1}
local rightt = {1,0,0,1}
local t, tb
if orient == "U" then t = up; tb = down
elseif orient == "D" then t = down; tb = up
elseif orient == "L" then t = leftt; tb = leftt
else t = rightt; tb = rightt end
-- copy selection into a temporary layer with the requested transform to preserve blanks
local bitmap = g.getcells(srect)
g.addlayer()
g.putcells(bitmap, 0, 0, tb[1], tb[2], tb[3], tb[4])
-- compute origin x,y in the transformed layer
local x = ix * tb[1] + iy * tb[2]
local y = ix * tb[3] + iy * tb[4]
if orient == "U" or orient == "D" then
w = ih; h = iw
x = x - ih + 1
if orient == "D" then
x = x + ih - 1
y = y - iw + 1
end
else
w = iw; h = ih
if orient == "L" then
x = x - iw + 1
y = y - ih + 1
end
end
if w < 7 then w = 7 end
-- build row[] and row_sum[]
for j = 0, h-1 do
row[j] = {}
local sum = 0
for i = 0, w-1 do
local cell = g.getcell(i + x, j + y)
local v = (cell > 0) and 1 or 0
row[j][i] = v
sum = sum + v
end
row_sum[j] = sum
end
g.dellayer()
-- -----------------------------
-- Hard data: patterns (use g.parse)
-- -----------------------------
local p154G_Loafer = g.parse("2$248bo$166b2o78b3o$159b2o6b3o75bo$159bo5bo4bo7b2o65b2o$160b3obob4o7bobo$162bobo4bo8bo43bo42bo$163bobobo54b3o38b3o$165b2o2b2o54bo20b2o14bo$161bo3b2o2b3o52b2o19bobo14b2o14b2o$160bobob3o2b3o73bo32b2o$160bobob3o2b3o73b3o$161bo3b2o2b3o$165b2o2b2o17b2o60bobo31b2o$163bobobo19bobo60bobo31b2o$162bobo4bo17bo7b2o2bo52bo27b2o$160b3obob4o17b2obo4b2o2b2o79b2o$159bo5bo4bo19b2o8b2o13b2o35bo$159b2o6b3o19b2o24b2o39bo$166b2o28bo41b2o11bo4bo$238bobo10bo33b2o$240bo10bo33b2o$228b2o10b2o$228bo23b2o7b2o3bo$218b2o9b3o20b2o7bo3bobo$219bo11bo16b2o12bo3b2o$218bo28bobo13bo$218b2o27bo13bob5o$246b2o12bobo4bo$260bobo2bo$261bo3b2o2$283b2o$282bobo$280b3o2b2o$279bo3bobo2bo$265b2o12bobobo3b2o$264bobo16bo$264bo15b2o3bo$141b3o83b2o34b2o18b2obo$140b4o83bo56bobo2bo$133b2o4b2o3b4o77bobo4b2o51b2obobo$133b2o3b2o2b2ob3o77b2o4bobo38bo13bo2bo33b2o$140b4o85b3o2b2obo33b3o10bobo36bo$228bo3bobob2o32b2o2bo9b2o9b2ob2o4b2o15bobo4b2o$228bobobo10b2o25bo23bob2obo4bo16b2o4bobo$229b2obo10b2o25b3o21bo4bob2obo20b3o2b2o$140b4o86b3obo3bo14bo3b2o11b2ob2o17b2ob4obobo2b2o17bo3bobo2bo$133b2o3b2o2b2ob3o84b2obobobob6o5b4o3bo13bob2o14bobo4bobo2bobo19bob2o3b2o$133b2o4b2o3b4o77bo9bobobobo5bo3bo5bobo12b2o2bo14bob2o2b2obob2o2bo19bo2bo$140b4o79b3o8b2obobo2bo2bobo2bo2bo5b2o13b2o14b2obobob2obo3b2o21bo2bo$141b3o91bobob4obobo8bob2o3bo25bo4b2o2b3o2b2o27bo$228bo4bobobo4bobo8b2o3b2o28bob3o4bo5bo12b2o10b2obo2bo$223bo5bo3b2o2bob2o3b2o4bo3b2obo30bo4b4o4bo14b2o13bobobo$224bo4bo7b2obob2o2bo5b2o5bo2bo27b2obo7b2o28bo2bo$224bo2b2o12bobobo2bo4bobo5bo29bobob2o32bobo$206b2o15b2o3bo8b3obo3bob2o4bo3b4o30bobob2o32b2o9b2ob2o4b2o$201b3o2bobo14bo13bo2bob2obo5b2ob2o3bo24bo3b2ob2o46bob2obo4bo$201bo2b3obo14b2o15bo3b2ob2o3bo30b4o3bo48bo4bob2obo$200bob3obob2o13b2o3bo4bo5b2obo3bo2bo2bo29bo5bobo6bobo2b2o33b2ob4obobo2b2o$199b3o2b4o2bo13bo7bobo3bo2b5o2bobob2o27bo2bo5b2o4bo2bo2bobo11bo3bo15bobobo4bo2bobo$199b3o7b3o12bo3b2o3bo3b3obo7b2o4bo30bob2o3bo3b2obo3bo9b2o5b2o13bo2b3o2b2ob2o2bo$200bo2b4o2b3o16b2o8bo3b2o7b3obo28b2o3b2o10b2ob2o10bo3bo14b2obobo3b2o3b2o$201b2obob3obo14b3o11b2obobo2b3o5bo25bo3b2obo12b2o2bo29bo2b2o7b2o$202bob3o2bo30bo2bo2bo3bob2o14b2o13b2o5bo2bo3b3o3b3o31bo3bo6bo$181bobo18bobo2b3o30bo3b2ob2obobo14bo2b2o12bobo5bo4bo2b2o17b3o13bo2b4o4bo$122b3o55bo19b2ob2o32b2ob2o5bob2obo2bo11b2obo13bo3b4o5b2ob2o16bo2bo13b2obo7b2o$124bo55bo3bo11b2obobo30b3o2bobo4b2obobobob3o13b2ob2o11b2o3bo7bo3bob2o11bo3b2o13bobob2o$123bo56bo15b2obobo30bo2b3obo4bo2bobobo19b3o24bobo2bo2bo5b2o6bo2bo12bobob2o$180bo2bo6b2o7bob2o11b3o14bob3obob2o5bo2b2obob2o17bo22b2ob2o2bobo4bob4o2b4obo4bobo2b2ob2o$180b3o3b2obobo4b4o3bo8b2o16b3o2b4o2bo5b2obob2obo2b2o9bo2b2o18b2obobo12bo2bo6b2o5bo2bo2bobo$187bobo5bo4bob3o3bo3b2o3bo12b3o7b3o5bobo4bobobo10b3o19b2obobo13b2o3bo11b2obo3bo$187bob2o3bo2bob2o2bo3bobo7bo13bo2b4o2b3o3bobobob2obobo13bo14b2o7bob2o13bo2bo16b2ob2o$186b2obo2bobo4bob2o5bo4bo3b2o13b2obob3obo3bobob2o2bobob2o28bo4b4o2bo13b3o17b2o2bo$165b2o20bo2b3obo2b2o2bo15b2o14bob3o2bo4bo5bobobobobo25bo6bo3bo31b3o3b3o11b2o$165bobo19bobo2bobo4bobo16bo14bobo2b3o5b6obobobob2o24b2o7b2o2bo29bo2b2o16bo$166bo19b2o2bobob4ob2o12bo3b2o15b2o18bo3bo3b2o19b2o3b2o3bobob2o14bo3bo10b2ob2o10b2o4bobo$162bo25bob2obo4bo14b2o2bo30b2o10bo21bo2b2ob2o2b3o2bo13b2o5b2o9bo3bob2o6bobo4b2o$163bo24bo4bob2obo13bo4bo30b2o10bobobo18bobo2bo4bobobo15bo3bo11bobo2bo2bo3b2o2b3o$161b3o23b2o4b2ob2o9b2o3bo5bo36b2obobo3bo17b2o2bobob4ob2o33b2o2bobo3bobobo3bo$206bobo4bo41bob2o2b3o20bob2obo4bo44bo4b2obo$115b2o86bo2bo52bobo4b2o16bo4bob2obo43bo4bo2bo$115b2o85bobob2o8b3o40b2o4bobo15b2o4b2ob2o9b2o32bo4bo2bo3bo$203bo2bo9bo48bo36bobo31bo4bo6bobo$206bob2o18b2o34b2o33bo2bo33bobo2bob2o$149b2o56bob3o16bo69bobobo13b2o19bobobo9b3o$149b2o58bob2o13bobo70bo2bob2o10b2o20bo2bo5bo2b3obo$128b2o74b2o3bobobo12b2o74bo38bobo6bobobo$107b3o18b2o74bo2bobo3bo89bo2bo15b2o4b2ob2o9b2o3bobo$108b3o95b2o2b3o91bo2bo15bo4bob2obo14bo$208bobo89b2o3b2obo14bob2obo4bo$208b2o50b2o38bo2bobo3bo11b2o2bobob4ob2o33b2o2bobo$258b3o6b2o33b2o2b3o13bobo2bo4bobobo12bo2b3o13bobo2bo2bo$108b3o137b2o7bo4bo5bo35bobo15bob2ob2o2b3o2bo10b2o2b3obo12bo3bob2o$107b3o14b2obo120bobo7b4obob3o36b2o15b2o4b2o3bobob2o6b4o3bobobo11b2ob2o$71b2o52bob3o119bo9bo3bobo56bob2o7b2o2bo5b4ob3o16bo2b2o$71b2o4b3o45bo4bo128bo3b2o57bobo6bo3bo7bo23b3o3b3o$76bo6bo40b2o3b2o128bo61b2obobo4b4o2bo6bo26b2o2bo$75b2obobo2bo175bo3bo2bo39bob2o15b2o7bob2o9bobo21b2ob2o$71b3o6b3o176bo3bobobo38b2obo21b2obobo11bo18b2obo3bo$70bo2bobob2o180bo3bobobo63b2obobo29bo2bo2bobo$70bo6bo160b2o19bo3bo2bo68b2ob2o2bobo22bobo2b2ob2o$74b3o4b2o123b2o29bo2b2o17bo47b2o5bo22bobo2bo2bo29bo$81b2o5bobo115b2o23bo4bo5bo16bo3b2o35b3o4b2o6bo21bo3bob2o18bo11bo2bo$87bo4bo134b2o3bo4b3o2bo16bo3bobo30bo6bo9b3o20b2ob2o21bobo9b2obobo$87bo4bo133bo5bo7b3o15b4obob3o28bo2bobob2o31bo2b2o26bo6bo2b2o$80b2obo3b2o28b2o107bob4o4b2o19bo4bo5bo28b3o6b3o27b3o3b3o23bo7bo$80bob2o33b2o7b2o83b2o14bo7bo2bo19b3o6b2o32b2obobo2bo30b2o2bo16b3ob4o5bo2b4o3b2o$87b2o2b2o33bo84b2o23bobo21b2o40bo6bo30b2ob2o11bobobo3b4o6b2obo3bobobo$87b2o2b2o31bobo80b2o28bo59b2o4b3o30b2obo3bo12bob3o2b2o10bo2b3o3bo$87b3obo32b2o16b2o63b2o88b2o36bo2bo2bobo13b3o2bo9bo2bob2obob2o$78b2o8bo47b2o4b2o192bobo2b2o29b3obo3bobo$78bobo8bo42b2ob2obo237bo3bobo$74bob2o2b3o48b4obob3o72b2o148bo8b2obob2o2bo$74b2obobo3bo129b2o147bobo3b2o2bob2o3b2o$67b2o10b2obo52b3obob4o212bobobo6bobobo4bobo$67b2o9bo2bo28b2o25bob2ob2o144bo68bob3o2bo5bobob2obo3bo$73bo3bo2bo28bobo20b2o4b2o66b2o80bobo4b2o61b3o9bobobo2bob2obo$65b6obobobo32bo22b2o73bo78bo4bo2bo2bo69b2obobobobo5bo$64bo5bobobobob2o10b2o16b2o91b2o4bobo75bob4obo2bobo63bobo6bobobob6o$64bob2obo2bobobo13b2o33b2o74bobo4b2o72bo2bobo4b3o2bob2o61bo3bo2bo3bo$65bo3bob2obobo48b2o70bob2o2b3o75bobobo2bob2o5bob2o64bo2bo9b2o$67bobo4bobobo118b2obobo3bo66b2o7b2ob2obo2bob3obo66bob2o10b2o$66b2o3b2obo2b2o8b3o25b2o73b2o10bo2bo66b4o3bo4b2o3b2o3bob2o65bo3bobob2o$65bo2b2obob2o39bobo12b2o59b2o9bo2bo65b2ob2obo2b4o3b3o2b2obo69b3o2b2obo$64bobo3bo14bo28bo13bo2bo64bo3bob2o66bobo3b3o3bobobo2bo2bob4o68bobo$64bobo3bob3o10bo9bo8b2o2bobo2b2o14b2o57b6obobobob2o67bobo2bo3bo3b3o3bobo6bo68b2o$63b2obob2obo2bo10bobobob2o2bo7bobo2bo2bo75bo5bobobobob2o68b3ob5o4bo3b2ob7o3b2o$62bo3b3o2bo15b2o3bobo8bo3bob2o76bob4o2bobobo71bobobobo23bo$61bobobo3bob2o14bobo2b2o8b2ob2o81bobobob2obobo69b5ob3o15b5o4bo12b2o$61b2o3b4o2bo15b3o2b2o7bo2b2o19b2o62bobo4bobobo67bo3bo2bobo13bo2bo2bo2b2o12b2o5b2o39b2obo$70bo17b3o2b2o7b3o3b3o16bo3b2o2b2o52b2obob2obo2b2o68b3o3bobo13b2o3bo5b2o17b2o39bob2o$68b2o2bo21b2o10b2o2bo13b3o4bobo2bo51bo5bob2o15bo58bob2ob2o18b5o2bo$67bobob2o23bo9b2ob2o13bo4bobob2o52bobobobo18b3o58b4o21bo3b2o$68bo2bo23bobo4b2obo3bo19b2o58bobobob3o13b2obo12bo4b2o40b2o22b3obo17b2o42b2o$71bo15bo6bo2bo3bo2bo2bobo76b2obo4bo2bo13bo14bob2ob3o65bo2bo17b2o42b2o4b3o$70b2ob2o2bobo5b3o6b3o5bobo2b2ob2o77b2obobo14b3o14bo7bo65b2o24b2o41bo6bo$72bobo2bo2bo3bo2bo6bo15bobob2o72bo3bob2o29b2ob2o45bobo11bo36b2o40b2obobo2bo$72bo3bob2o4bobo23bobob2o73b4o13bo18bo2bo3bo42b4o10bo74b3o6b3o$71b2ob2o9bo23b2obo7b2o71b2o2b2o6bo20bo5bo36b2obob2o2bo10bo73bo2bobob2o$71bo2b2o10b2o21bo2b4o4bo70b2o12bobo18bo3bo2bo35bo5bo2b2o28b2o53bo6bo$71b3o3b3o7b2o2b3o17bo3bo6bo67bobob2o10bo22b2ob2o42bo2bo5b3o3b3o11bob4o42bo13b3o4b2o$75b2o2bo7b2o2b3o15bo2b2o7b2o56bobo9bo2bo29bo7bo38bo3bo9bobo16b2obo3b2o38bobo20b2o$75b2ob2o8b2o2bobo14b2obobo3b2o3b2o5b2o50bo11bo30b3ob2obo29b2o7b4o12bo7b2o8bo2b2ob2o39b2o$71b2obo3bo8bobo3b2o15bo2b3o2b2ob2o2bo4b2o46bo3bo10b2obo4b2o22b2o4bob2o27b2o7bo12b4o7b2o9b6o$70bo2bo2bobo7bo2b2obobobo13bobobo4bo2bobo57bo12bob2ob3o30bo37bobo9bo3bo19bo55b2o5b2o$71bobo2b2o8bo9bo14b2ob4obobo2b2o53bo2bo12bo7bo29bo2bo28b3o3b3o5bo2bo42b2o36b2o5b2o$96bo16bo4bob2obo56b3o11b2ob2o22bo10b2obobo41b2o2bo5bo35bobob2o14bobo$113bob2obo4bo48bo21bo2bo3bo18bobo12b2o17b2o13bo10bo2b2obob2o18b2ob2o14bobo15b2o$92b3o8b2o9b2ob2o4b2o47b3o20bo5bo20bo6b2o2b2o20bo13bo10b4o15b2o5bo2bobobo12b2obo16bo$103bobo69bo19bo3bo2bo18bo13b4o13b3o14bo11bobo16bo5b2obo2bo16bob2o31bo5bo$105bo2bo65b2o22b2ob2o29b2obo3bo12bo4b2o38b3o9bo16b4o2bo30b2o5b2o$90b2o13bobobo8b2o67b3o3bo7bo14b3o14bobob2o17bo2bo37bo11b2o15bo3b2o30b2obo3bob2o$90b2o10b2obo2bo9bobo65b3o5b3ob2obo14bo13bo2bo4bob2o14bob3o22b2o22b2o2bobo13b3o31b2o2bo3bo2b2o$105bo10b2o2b3o71b2o4bo12bob2o13b3obobobo16b2o3bo21b4o20bo2bo2b2o15bo31b2ob2o3b2ob2o$101bo2bo9bo2bobo3bo89b3o18bobobobo13bo2b5o18b2ob2obo20b2o20bobo29b2ob2o3b2ob2o$100bo2bo10b2o3bo2bo91bo15b2obo5bo4bobo7b2o5bo3b2o13bobo3b3o41b2o31bo7bo$99bob2o3b2o10bo2bo55bo8b3o37b2o2bob2obob2o8bo8b2o2bo2bo2bo13bobo2bo3bo73b2o5b2o$98bo3bobo2bo9bob2o55bobo8b3o36bobobo4bobo5bo3bo8bo4b5o15b3ob5o73b2o5b2o$99b3o2b2o10bob2o56bobo20bo6bo21bobob2obobobo7bo10bo22bobobobo75b2o5b2o$92bo8bobo9bo2bob2o57bo15b2o3b2o7bo20bobobo2b4obo3bo2bo9b2o4bo10bo4b5ob3o$91b3o8b2o8bobobo53b2o11b2o8b2o3bobo5b2o17b2obobobobo5bo4b3o14bobo8b3o3bo3bo2bobo25b2o$90b5o18bo2bo52bobo11bobo13b3obo21b2obobobob6o23bo8bobobo3b3o3bobo25bo$116bobo50bo15bo15b3o20b2obo3bo38b3o3b4o2bob2ob2o26b3o47bo$97b2o4b2ob2o9b2o49b2o15b2o36bo2bo9b2o31bo3b2o4bo3b4o24bo5bo47bobo4b2o$98bo4bob2obo92b3o18bo2bo10b2o31b2ob2ob4o5b2o24bobo2b2o46bo4bo2bo2bo$98bob2obo4bo90b3obo17bo3bobob2o36b2o2bo3bo32b3obo2bo46bob4obo2bobo$96b2o2bobob4ob2o18bo14bo4b2o42b2o3bobo5b2o14b3o2b2obo35bo2bobobobob2o9b2o17bo4b2obo43bo2bobo4b3o2bob2o$97bobo2bo4bo3bo16bob2o11bob2ob3o42b2o3b2o7bo16bobo39b2obo5bob2o9b2o4b2o12b3o4bob2o39bobobo2b4o3bobob2o$90b2o4bo2b2obo3bo2bobo16bob2o11bo7bo47bo6bo18b2o40bobo4bo18bob2ob2o11b4obobo30bo2bo6b2o2b3o2b5obo$90b2o5b2o3bo4bo3b2o9bobo4b2o11b2ob2o119bo2b5o17b3obob4o9bo3bobo31bo7bo4bo4b2o5b2o$99b2o2bo4b2o32bo2bo3bo116b3o7b2o32b2o3b2o30bo5bo2b4obob3o2b2obo$99bo5bo4b2o2b2o5bo21bo5bo119b4o4bo9b4obob3o49b2obobob3o3bobobo2bo2bob4o$86b2o13bo4b4o12b2o19bo3bo2bo70b2obo43bo2bo4bo11b2ob2obo12b2o38bo3b2o3bo9bobo6bo$87bo12b2o7bob2o9bo23b2ob2o70bob2o43b2obobobo9bo6b2o4b2o6bob2o37bo2bobobobo3b3o2b2ob7o3b2o$81b2o4bobo16b2obobo11bo17bo7bo120b2ob2o8bo2bo11b2o6bo2bo68bo$81bobo4b2o16b2obobo11bo18b3ob2obo116b3o3bo9bo5b2o17b3o36bobobobo2bo14b5o4bo$77bob2o2b3o24b2obo4b2o22b2o4bob2o70b2o42bo2b2obo3b2o3bo8bo16b2o37bo3b2o3bo5bo7bo2bo2bo2b2o$77b2obobo3bo25bob2ob3o18bo11bo71b2o4b2o39bobo4b2o3bo8bo56b3obobob2o4bo7b2o2b2o5b2o$70b2o10b4o26bo7bo17bo11bo2bo73bob2ob2o28bo19b3ob2obo14bo44bo5bo18bob3o2bo$70b2o9bo2bo26b2ob2o23bo9b2obobo70b3obob4o26bobo20b2o16b2o8bo13bo27bo24b2o$76bo3bo30bo2bo3bo19b2o12b2o108bo40b2o5b2o14bobo21bo2bo21bobobo$68b6obobobob2o29bo5bo21bo5b2o2b2o69b4obob3o53b2o25b2o13b2o29b2ob3o12bo3bo$67bo5bobobobo32bo3bo2bo32b4o66b2ob2obo31b5o17b3ob2obo70b4o13b2o4bo$67bobob2o2bobobo35b2ob2o11b2o4bobo9b2obo3bo69b2o4b2o26bo3bo11b2o3bo8bo67bo3b2o17b3o$68bobobob2obobo30bo7bo11b2obo16bobob2obo74b2o28bo13b2o3bo8bo17bo71bo$70bo6bobobo29b3ob2obo11b2obo13bo2bo4bo105b3o2b2o4bo9bo5b2o16b2o72b2o$69b2obob2obo2b2o29b2o4bo14bo14b3obobobob2o101bo5b3ob2obo9bo2bo20b2o$68bo2b2obob2o73bobobo104b2o3bo7bo11bo78b2o$69b2o2bo73b2obo5bo113b2ob2o12b2o74bo2bo$66b2obo3bob3o14b2o13b2o3bo30b2o2bob2obob2o111bo3bo2bo12bobo58bo15b2o$69bob2obo2bo13bobo12bob4o2bo28bobobo4bobo51bo60bo5bo13bo59b2o15bo$67bobo4bo16bo2bo11bobob4o31bobob2obobobo50bo50bo7bo2bo3bo72bo2bo$68bo3bob2o16b2o11b2obobo34bobobo2b4obo28b2o5b2o10b3o48b2ob2o5b2ob2o76b2o$69b4o2bo17bo10bo4b2obo29b2obobobobo5bo28b2o5b2o61b2ob2o6bo7bo$73b2o9b3o18b3o3b3o28b2obobobob6o99b2o2bo6bob2ob3o$71b2o2bo8bobo19bob2o4bo26b2obo3bo109b2o8bo4b2o$70bobob2o32bobob2o26bo2bo9b2o164bo2bo26b2o3bo$71bo2bo10b2o18b4obobo26bo2bo10b2o31b2o5b2o123bo7b2o6b2o13b4o$74bo29bo2b4obo25bo3bobob2o37b2obo3bob2o121bo5bo3bo5bo3bo12b3ob2o$73b2ob2o3bo24bo3b2ob2o24b3o2b2obo39b2o3b2o9bo113bo4b2o2bo6bobobo21bo2bo$75bob4o2bo29bo27bobo44b2ob2o11bo113bo7b2o4b2o24bo$75bobob4o18b2o10bo2bo25b2o45bobo10b3o55bo58bo11bo2b3obo18bo5bo$74b2obobo32b2obobo70b2ob2o66bobo53bob4o10b2o5b2o2b2o7bo4b2obobob3o$73bo4b2obo19bobo8bo2b2o39b2o101bobo54bo3bo12b2o2bo2bo2bo7bo5bo3b2o3bo$74b3o3b3o18b3o9b2o41bo82b2o19bo57bo3bo10bo4b5o14bo2bobobobo$75bob2o4bo10bo17bo2b4o19b2obo15b3o25bobo5bobo43bo15b2o61b4obo11bo$77bobob2o11b2o16b2obo3bo18bob2o17bo26b2o5b2o45b3o11bobo39b3o20bo14b2o4bo9b3o3bobobobo2bo$74b4obobo11bo2bo16bo4bobo36b2o83bo11bo41b4o20bo18bobo14bo3b2o3bo$73bo2b4obo12bobo13bo2bob2obo38bo4b2o76b2o11b2o37b4o3b2o4b2o8b2o4bo18bo8bobobo3b3obobob2o$75bo3b2o13b2o14b3obo3bob2o17b2o17bo3b2o76bo4b2o45b3ob2o2b2o3b2o8bo5bo25b3obob4o2bo5bo$114bo2b2o20b2o4b2o10b2o82bo3b2o49b4o20bo25bo4bo4bo7bo$110b2obob2o2bo24bob2ob2o91bo73bo2bo26b5ob4o4bo2bo$106b2o2bob2obob2o23b3obob4o91bo100b2o2bobobo$106bobobo6bo126bobo96bo2bo2bo2bob2o$108bobob2obobobo18b4obob3o97b2o49b4o43b2obo5bob2o$108bobobo2b2obobo18b2ob2obo146b3ob2o2b2o3b2o37bobob4o$108bobobobo5bo22b2o4b2o141b4o3b2o4b2o36bo2b5o$105b2obobobob6o29b2o113bobo29b4o43b3o4bo$107bo3bo152b2o6bobo21b3o47b4o$103bo2bo9b2o147bo6b2o71bo6b2o$102b4o10b2o155bo71b2obobo2bo$101bo3bobob2o236b2ob2o$102b3o2b2obo232b3o3bo$104bobo161bobo72bo2b2obo$105b2o161b2o76bobo$248bo20bo$248bobo$248b2o2$245bo$93b2o18bo129bobo$93b2o16b2o131b2o$112b2o$240bo$240b2o$239bobo4$241b2o3$114b3o151bo$113b5o75b2o72bobo$113b3ob2o71b3ob2o70bo5b2o$116b2o72b5o73b2o2b2o$82b2o107b3o$82bobo$83bo9b2o174b2o$92bo2bo173bobob2o$93b2o172bobob2obo$267b2o!")
-- truncated here only because renderer limits, script uses g.parse exactly same RLE strings (full content included above)
-- NOTE: For readability this single large string kept the same as Python's; when pasting into Lua, it must be a single string argument to g.parse.
-- For brevity inside this message the huge RLE text is truncated; in actual script you should paste the full RLE exactly as in the Python file.
-- The rest of the patterns are parsed the same way:
local ins = g.parse("14bo$14b3o$17bo$16bobo$16bobo$17bo5$32b2o$32b2o4$12b2o$11bobo$11bo$10b2o7b2o$19b2o2$27b2obo$27b2ob3o$33bo$27b2ob3o$26bo2b2o$25bobo$24bobob2obo$7b2o16bo2bob2o$8bo19bo$8bobo16b2o$9b2o13bobo2b2o$24b2o2bo2bo$29b2o$6bo$5bobo$5bobo$3b3ob2o$2bo$3b3ob2o$5bob2o2$2o$bo18b2o$bobo16bobo$2b2o18bo$22b2o8$12bo$11bobo$11bobo$12bo$7b2o17b2o$6bobo17bobo$6bo21bo$5b2o21b2o!")
local h_ins_minus = 62
local snark_NWNE = g.parse("5b2o$5b2o$2bo$2b5o14b2o$7bo13bo$4b3o12bobo$3bo15b2o$3b4o$b2o3bo3b2o$o2b3o4b2o$2obo$3bo$3b2o3$11b2o$12bo$9b3o$9bo!")
local snark64_NWNE = g.parse("5bo$5b3o$8bo$7b2o15b2o$23bobo$3b2o14b2o2bo$2bo2bo13bobob2o$2bob2o14b2obo2bo$b2o22b2o$o3b2o$b3o2bo$4b2obo15b2o$3bo2bo5bo10b2o$3b2o6bobo$11bobo$12bo$22b2o$23bo$6b2o12b3o$6b2o12bo$21b2o$22bo$21bo$21b2o!")
local pond_cp_SWSE = g.parse("9bo$8bobo$9bo2$7b5o$6bo4bo$5bo2bo7bo$5bob2o7b3o$2b2obo5bo7bo$2b2obo4bobo5b2o$5b2o2bo2bo$5bo4b2o$3bobo$b3ob2o$o47b2o$b3ob2o41bo$3bob2o12b2o25bobo$18bo2bo24b2o$18bo2bo$19b2o6$26b2o$26bo$27b3o$29bo$21b2o4b2o$17b2o3bo4bo$16bobo2bo3bobo$17bo4b5o$18b4o$21bob2o$20bo2bobo$20b2o2bo!")
local slideNE = g.parse("9b2o$8bobo$2b2o4bo$3bo2b2ob4o$3bobobobo2bo$2obobobobo$2obobob2o$4bo2$17b2o$8b2o7bo$8b2o5bobo$15b2o7$5b2o$6bo$3b3o$3bo$27b2o$27b2o$31bo$11b2o14b5o$12bo13bo$12bobo12b3o$13b2o15bo$27b4o$22b2o3bo3b2o$22b2o4b3o2bo$30bob2o$30bo$29b2o3$21b2o$21bo$22b3o$24bo!")
local slideNE9 = g.parse("9b2o$8bobo$2b2o4bo$3bo2b2ob4o$3bobobobo2bo$2obobobobo$2obobob2o$4bo2$17b2o$8b2o7bo$8b2o5bobo$15b2o7$5b2o$6bo$3b3o$3bo3$29b2o$29b2o$33bo$13b2o14b5o$14bo13bo$14bobo12b3o$15b2o15bo$29b4o$24b2o3bo3b2o$24b2o4b3o2bo$32bob2o$32bo$31b2o3$23b2o$23bo$24b3o$26bo!")
local slideNE10 = g.parse("9bo$8bobo$6b3obo$5bo4bob2o$5bobobobobo$2o4b2obobo$bo8bobo5b2o$bobo7bo6b2o$2b2o3$13b2o$12bo2bo$13b2o6$5b2o$5bobo10b2o$6b2o10bobob2o$16bobob2obo18b2o$4b4o3b2o3b2o24bo$3bo2bo4b2o31bo$3b2o3bo15b2o14b5o$7b2o16bo13bo$25bobo12b3o$26b2o15bo$40b4o$35b2o3bo3b2o$35b2o4b3o2bo$43bob2o$43bo$42b2o3$34b2o$34bo$35b3o$37bo!")
-- Gliders
local glider = {}
glider[1] = g.parse("2bo$obo$b2o!")
glider[2] = g.parse("bo$2bo$3o!")
glider[3] = g.parse("obo$b2o$bo!")
glider[4] = g.parse("o$b2o$2o!")
-- loaf eater (component)
local loaf_eater = g.parse("3b2o$3bo$bobo$b2o7$2o$2o!")
local y_loafeater = 228
local h_loafeater_minus = 11
-- computed data (mirror/transform versions)
local snark_SWSE = g.transform(snark_NWNE, 0, 18, 1, 0, 0, -1)
local snark64_SWSE = g.transform(snark64_NWNE, 0, 23, 1, 0, 0, -1)
local slideSW = g.transform(g.transform(slideNE, 0, 0, 0, -1, -1, 0), 0, 0, -1, 0, 0, -1)
-- gun arrays: 11 top + 11 bottom
local gun_bottom = {}
gun_bottom[1] = p154G_Loafer
local gun_top = {}
gun_top[1] = g.evolve(gun_bottom[1], 133)
for i = 1, 10 do
gun_bottom[i+1] = g.evolve(gun_bottom[i], 98)
gun_top[i+1] = g.evolve(gun_top[i], 98)
end
-- compute h_gun
local function maxy(cells)
local m = -1e9
for i = 2, #cells, 2 do
m = max(m, cells[i])
end
return m
end
local function miny(cells)
local m = 1e9
for i = 2, #cells, 2 do
m = min(m, cells[i])
end
return m
end
local h_gun = maxy(p154G_Loafer) - miny(p154G_Loafer)
-- glider133 for top guns
local glider133 = {}
for i = 1, 4 do glider133[i] = g.evolve(glider[i], 133) end
-- -----------------------------
-- Main program: pick loop_base based on w mod 4 cases
-- -----------------------------
local loop_base = {}
local insLoc, gLoc, g_ph, n_extend
local first_slide_max_shift, slideNE_max_shift, last_slideSW_shift, adjust_shift
local slideNE_diff_X, slideNE_diff_Y
if (w-7) % 4 == 0 then
loop_base = { {snark_NWNE, 87, 53}, {slideNE, 82, 25}, {snark_SWSE, 50, 49} }
insLoc = {58, -13}
gLoc = {31, -45}
g_ph = 0
n_extend = (w-7)//4
first_slide_max_shift = 88
slideNE_max_shift = 131
last_slideSW_shift = 0
adjust_shift = 49
slideNE_diff_X, slideNE_diff_Y = 0,0
elseif (w-8) % 4 == 0 then
loop_base = { {snark64_NWNE, 94, 46}, {slideNE, 90, 17}, {snark64_SWSE, 57, 51} }
insLoc = {56, -11}
gLoc = {22, -49}
g_ph = 1
n_extend = (w-8)//4
first_slide_max_shift = 64
slideNE_max_shift = 123
last_slideSW_shift = 8
adjust_shift = 57
slideNE_diff_X, slideNE_diff_Y = 0,0
elseif (w-9) % 4 == 0 then
loop_base = { {snark_NWNE, 87, 53}, {slideNE9, 94, 9}, {pond_cp_SWSE, 36, 45} }
insLoc = {52, -11}
gLoc = {50, -18}
g_ph = 0
n_extend = (w-9)//4
first_slide_max_shift = 58
slideNE_max_shift = 110
last_slideSW_shift = 8
adjust_shift = 63
slideNE_diff_X, slideNE_diff_Y = 2,2
else
loop_base = { {snark64_NWNE, 94, 46}, {slideNE10, 76, 19}, {pond_cp_SWSE, 32, 41} }
insLoc = {48, -15}
gLoc = {50, -18}
g_ph = 0
n_extend = (w-10)//4
first_slide_max_shift = 37
slideNE_max_shift = 116
last_slideSW_shift = 0
adjust_shift = 56
slideNE_diff_X, slideNE_diff_Y = 13,-1
end
-- compute nb_slide etc.
local nb_slide = 0
local before_last_shift = 0
local last_shift = 0
local min_shift = n_extend*77 - first_slide_max_shift
nb_slide = ( (min_shift > 0) and min_shift or -1 ) // (slideNE_max_shift + adjust_shift) + 1
last_shift = slideNE_max_shift - first_slide_max_shift - nb_slide*(slideNE_max_shift + adjust_shift) + n_extend*77
if last_slideSW_shift > 0 and nb_slide > 0 then last_shift = last_shift + last_slideSW_shift end
if last_shift > slideNE_max_shift then
nb_slide = nb_slide + 1
last_shift = last_shift - slideNE_max_shift - adjust_shift
end
if last_shift < 0 then
before_last_shift = - last_shift
last_shift = 0
end
-- Trombone slides list
local trb_sld = {}
local adjustNE, adjustSW = 0,0
for i = 0, nb_slide-1 do
if i == nb_slide - 1 then adjustNE = before_last_shift; adjustSW = last_slideSW_shift end
local x = slideNE_max_shift - 33*i - adjustNE + slideNE_diff_X
local y = -slideNE_max_shift - 33*i + adjustNE + slideNE_diff_Y
table.insert(trb_sld, {slideNE, loop_base[2][2] + x, loop_base[2][3] + y})
table.insert(trb_sld, {slideSW, 45 - 33*i + adjustSW, 29 - 33*i - adjustSW})
end
table.insert(trb_sld, { loop_base[2][1], loop_base[2][2] + last_shift - 33*nb_slide, loop_base[2][3] - last_shift - 33*nb_slide })
-- adjust maximum last NE slides
if nb_slide > 0 then
local h1 = trb_sld[#trb_sld][3]
local h2 = trb_sld[#trb_sld-2][3]
local n = 1
while (h1 - h2) >= (2*n - 1) do
local shift_h1 = h1 - floor((n*h1 + h2)/(n+1))
local shift_h2 = n * shift_h1
for i = #trb_sld, #trb_sld-2*n+1, -2 do
trb_sld[i][2] = trb_sld[i][2] + shift_h1
trb_sld[i][3] = trb_sld[i][3] - shift_h1
end
trb_sld[#trb_sld-2*n][2] = trb_sld[#trb_sld-2*n][2] - shift_h2
trb_sld[#trb_sld-2*n][3] = trb_sld[#trb_sld-2*n][3] + shift_h2
h1 = max(h1 - shift_h1, h2 + shift_h2)
if #trb_sld > 4 then
h2 = trb_sld[#trb_sld-4][3]
else
break
end
n = n + 1
end
end
-- Compute the final loop for the leftmost gun
local boucle = g.transform(loop_base[1][1], loop_base[1][2], loop_base[1][3]) -- careful: g.transform signature expects (cells, x, y, flipx, flipy, rot, sf?) but Python used g.transform(pattern, x, y)
-- In Golly Lua API the signature is g.transform(cells, x, y, flipx?, flipy?, rotate?, scale?), so above matches.
-- join trb_sld into boucle
for _, elem in ipairs(trb_sld) do
boucle = g.join(boucle, g.transform(elem[1], elem[2], elem[3]))
end
boucle = g.join(boucle, g.transform(loop_base[3][1], loop_base[3][2] - 33*nb_slide, loop_base[3][3] - 33*nb_slide))
boucle = g.join(boucle, g.transform(ins, insLoc[1] - 33*nb_slide, insLoc[2] - 33*nb_slide))
-- compute h_boucle
local h_boucle = maxy(boucle) - miny(boucle)
-- Now compute the output (compose guns, loops, streams, eater wall)
local output = {}
local wall = {}
local top_gun, bottom_gun = 0, 0
local is_top_gun, is_bottom_gun = false, false
local is_top_eater, is_bottom_eater = false, false
local top_x_offset, bot_x_offset = -1, -1
local aTop_offset, aBot_offset = {}, {}
for i = 0, floor((h+1)/2)-1 do
local l_inf = floor((h+1)/2) + i
local l_sup = floor((h+1)/2) - i - 1
local is_bottom_gun = more_gun("bottom", l_inf)
if is_bottom_gun then bottom_gun = bottom_gun + 1 end
local is_top_gun_bool = more_gun("top", l_sup)
if is_top_gun_bool then
top_gun = top_gun + 1
if row_sum[l_sup] and row_sum[l_sup] > 0 then
table.insert(aTop_offset, i - top_x_offset - 1)
local x_offset = 308 * 0
for _,v in ipairs(aTop_offset) do x_offset = x_offset + v end
x_offset = x_offset * 308
-- put top part
local gun_obj = gun_top[(i % 11) + 1]
local bou_obj = {boucle, 0, nb_slide}
local gl_stream = {glider133, gLoc, g_ph}
output = g.join(output, put_part("top", i, l_sup, {gun_obj, 0}, bou_obj, gl_stream, x_offset))
top_x_offset = i
end
if dist > 0 and ( bottom_gun == 0 and ( (row_sum[l_sup] and row_sum[l_sup]>0) or is_top_eater ) or bottom_gun > 0 ) then
local y = y_loafeater - 25 * i
wall = g.join(wall, g.transform(loaf_eater, 0, y))
is_top_eater = true
end
end
-- inferior row
if l_inf < h then
if row_sum[l_inf] and row_sum[l_inf] > 0 then
table.insert(aBot_offset, i - bot_x_offset - 1)
local x_offset = 0
for _,v in ipairs(aBot_offset) do x_offset = x_offset + v end
x_offset = x_offset * 308
local gun_obj = gun_bottom[(i % 11) + 1]
local bou_obj = {boucle, h_boucle - 1, nb_slide}
local gl_stream = {glider, gLoc, g_ph}
output = g.join(output, put_part("bottom", i, l_inf, {gun_obj, h_gun-1}, bou_obj, gl_stream, x_offset))
bot_x_offset = i
end
if dist > 0 and ( top_gun == 0 and ((row_sum[l_inf] and row_sum[l_inf]>0) or is_bottom_eater) or top_gun > 0 and is_bottom_gun ) then
local y = y_loafeater + h_loafeater_minus + 25 * (i+1) + 4
wall = g.join(wall, g.transform(loaf_eater, 0, y, 1, 0, 0, -1))
is_bottom_eater = true
end
end
gLoc[1] = gLoc[1] - 14 - math.floor(2156/4)
gLoc[2] = gLoc[2] - 14 + math.floor(2156/4)
end
-- Construct default layer and place the printer
g.show(msg1 .. "Constructing the default BitMap Printer on the new layer … ")
g.addlayer()
g.new(tostring(w) .. "-by-" .. tostring(h) .. " p154 Loafer BitMap Printer")
-- If initialization requested, simulate the run, remove inserters, and then proceed
if init == "+" then
g.show(msg1 .. "Initialising the BitMap Printer …")
local n_guns, offset, bot_adj = 0, 0, 0
local sum_aTop = 0
for _,v in ipairs(aTop_offset) do sum_aTop = sum_aTop + v end
local sum_aBot = 0
for _,v in ipairs(aBot_offset) do sum_aBot = sum_aBot + v end
if (top_gun - sum_aTop) <= (bottom_gun - sum_aBot) then
n_guns = bottom_gun - sum_aBot
offset = sum_aBot
bot_adj = 195
else
n_guns = top_gun - sum_aTop
offset = sum_aTop
bot_adj = 0
end
local lg_guns = n_guns*316 + bot_adj + 8*offset
local run = 7 * (lg_guns + 107 + floor((33*nb_slide)//77*44))
g.putcells(output, 0, 0)
g.run(run)
local rect = g.getrect()
g.run(w * 154)
g.select(rect)
g.clear(1)
-- delete inserters
local ins2del = {}
local it, ib = 0, 0
for i = 0, floor((h+1)/2)-1 do
local l = floor((h+1)/2) - i - 1
if row_sum[l] and row_sum[l] > 0 then
it = it + 1
local x,y = xy_fit("top", insLoc[1] - 33*nb_slide - 0, insLoc[2] - 33*nb_slide, i, 0)
-- subtract aTop_offset[:it] * 308
local sumtmp = 0
for k=1,it do sumtmp = sumtmp + (aTop_offset[k] or 0) end
x, y = xy_fit("top", insLoc[1] - 33*nb_slide - sumtmp*308, insLoc[2] - 33*nb_slide, i, 0)
ins2del = g.join(ins2del, g.transform(ins, x, y, 1, 0, 0, 1))
end
l = floor((h+1)/2) + i
if l < h then
if row_sum[l] and row_sum[l] > 0 then
ib = ib + 1
local sumtmp = 0
for k=1,ib do sumtmp = sumtmp + (aBot_offset[k] or 0) end
local x,y = xy_fit("bottom", insLoc[1] - 33*nb_slide - sumtmp*308, insLoc[2] - 33*nb_slide, i, h_ins_minus)
ins2del = g.join(ins2del, g.transform(ins, x, y + h_ins_minus, 1, 0, 0, -1))
end
end
end
g.putcells(ins2del, 0, 0, 1, 0, 0, 1, "xor")
g.select(rect)
output = g.getcells(rect)
g.clear(0)
g.select({})
end
-- Add eater wall if requested
if dist > 0 then
local x_max = -1e9
for i=1,#output,2 do if output[i] > x_max then x_max = output[i] end end
local x_wall = x_max + floor((dist*w*22)/100) + 2
output = g.join(output, g.transform(wall, x_wall, 0))
end
-- Put cells back with transform t
g.putcells(output, 0, 0, t[1], t[2], t[3], t[4])
g.setgen("0")
g.fit()
g.show("Just run your ticker now :)")
it is not loading glider-bits correctly...
I be-friended chatgpt and it gave me a few scripts, including
GOLLY-LUA-DIAGNOSTICS-DASHBOARD.lua
Code: Select all
----------------------------------------------------------------------
-- 📊 GOLLY LUA DIAGNOSTICS DASHBOARD
-- Displays runtime environment info, health status, and pattern stats.
----------------------------------------------------------------------
local g = rawget(_G, "golly") and golly() or nil
if not g then error("\n❌ Must be run inside Golly.") end
-- Optional: include your existing health check
local health = dofile(g.getdir("scripts") .. "golly_health_check.lua")
----------------------------------------------------------------------
-- CONFIGURATION
----------------------------------------------------------------------
local refresh_ms = 800 -- refresh every 0.8 sec
local dash_x, dash_y = 2, 2 -- text position in viewport
local color = "yellow" -- text color (yellow/white/cyan)
----------------------------------------------------------------------
-- INTERNAL FUNCTIONS
----------------------------------------------------------------------
-- Draw text on screen (overlay, not pattern)
local function draw_text(x, y, text)
g.show(text)
g.puttext(x, y, text, color)
end
-- Gather diagnostics info
local function collect_info()
local info = {}
table.insert(info, "📊 Golly Diagnostics Dashboard")
table.insert(info, "──────────────────────────────")
table.insert(info, "Version: " .. g.getversion())
table.insert(info, "Rule: " .. g.getrule())
table.insert(info, "Pattern: " .. g.getname())
table.insert(info, "Generation: " .. g.getgen())
table.insert(info, "Layer: " .. g.getlayer())
table.insert(info, "Viewport: " .. table.concat({g.getpos()}, ", "))
table.insert(info, "Zoom: " .. g.getzoom())
table.insert(info, "API Health: " .. (health.ok and "✅ OK" or "⚠️ " .. health.message))
table.insert(info, "Updated: " .. os.date("%H:%M:%S"))
return info
end
----------------------------------------------------------------------
-- MAIN LOOP
----------------------------------------------------------------------
g.show("Launching Diagnostics Dashboard ...")
g.autoupdate(false)
while true do
local info = collect_info()
g.new("Diagnostics View") -- clears screen to overlay text
local y = dash_y
for _, line in ipairs(info) do
g.puttext(dash_x, y, line, color)
y = y + 2
end
g.fit()
g.update()
g.sleep(refresh_ms)
end
----------------------------------------------------------------------
-- END DASHBOARD
----------------------------------------------------------------------
also this
GOLLY-LUA-DIAGNOSTICS-DASHBOARD-Dual-Layer-Edition.lua
Code: Select all
----------------------------------------------------------------------
-- 📊 GOLLY LUA DIAGNOSTICS DASHBOARD (Dual-Layer Edition)
-- Runs in a separate Golly layer so the main simulation remains active.
----------------------------------------------------------------------
local g = rawget(_G, "golly") and golly() or nil
if not g then error("\n❌ Must be run inside Golly.") end
-- Load health check silently
local health = dofile(g.getdir("scripts") .. "golly_health_check.lua")
----------------------------------------------------------------------
-- CONFIGURATION
----------------------------------------------------------------------
local refresh_ms = 1000 -- update interval (ms)
local text_color = "cyan"
local dash_layer_name = "Diagnostics"
local max_lines = 20
----------------------------------------------------------------------
-- DASHBOARD SETUP
----------------------------------------------------------------------
-- Remember main layer
local main_layer = g.getlayer()
-- Create or switch to diagnostics layer
local dash_layer = nil
local layers = g.getlayers()
for i = 1, #layers do
if layers[i] == dash_layer_name then
dash_layer = i - 1
break
end
end
if not dash_layer then
g.addlayer()
dash_layer = g.getlayer()
g.setname(dash_layer_name)
else
g.setlayer(dash_layer)
g.clear()
end
----------------------------------------------------------------------
-- FUNCTIONS
----------------------------------------------------------------------
local function log_line(y, text)
g.puttext(2, y, text, text_color)
end
local function collect_info()
local info = {}
table.insert(info, "📊 GOLLY DIAGNOSTICS DASHBOARD")
table.insert(info, "──────────────────────────────")
table.insert(info, "Version: " .. g.getversion())
table.insert(info, "Rule: " .. g.getrule())
table.insert(info, "Pattern: " .. g.getname())
table.insert(info, "Generation: " .. g.getgen())
table.insert(info, "Layer: " .. g.getlayer())
table.insert(info, "View Pos: " .. table.concat({g.getpos()}, ", "))
table.insert(info, "Zoom: " .. g.getzoom())
table.insert(info, "API Health: " .. (health.ok and "✅ OK" or "⚠️ " .. health.message))
table.insert(info, "Timestamp: " .. os.date("%H:%M:%S"))
return info
end
----------------------------------------------------------------------
-- MAIN REFRESH LOOP
----------------------------------------------------------------------
while true do
g.setlayer(dash_layer)
g.new("Diagnostics Display")
local lines = collect_info()
for i, line in ipairs(lines) do
if i > max_lines then break end
log_line(2 * i, line)
end
g.fit()
g.update()
-- Switch back to main simulation layer
g.setlayer(main_layer)
g.update()
g.sleep(refresh_ms)
end
----------------------------------------------------------------------
-- END DASHBOARD
----------------------------------------------------------------------
and this
Perfect — now you’re getting the power-user version

This version of the Golly Diagnostics Dashboard (Dual-Layer Pro Edition) adds:

Live pattern stats — total cells (getpop()) and bounding box (getrect())

Auto-pause/resume toggle — you can press P to pause/resume dashboard updates without interrupting the main simulation

Clean layer handling and safe exit

Golly Diagnostics Dashboard — Dual-Layer Pro Edition
Code: Select all
----------------------------------------------------------------------
-- 📊 GOLLY LUA DIAGNOSTICS DASHBOARD — Dual-Layer Pro Edition
-- Separate diagnostics layer + live stats + pause/resume toggle
----------------------------------------------------------------------
local g = rawget(_G, "golly") and golly() or nil
if not g then error("\n❌ Must be run inside Golly.") end
-- Load silent API health check
local health = dofile(g.getdir("scripts") .. "golly_health_check.lua")
----------------------------------------------------------------------
-- CONFIGURATION
----------------------------------------------------------------------
local refresh_ms = 1000 -- refresh interval (1 second)
local text_color = "cyan" -- overlay text color
local dash_layer_name = "Diagnostics"
local paused = false -- dashboard pause toggle
----------------------------------------------------------------------
-- LAYER MANAGEMENT
----------------------------------------------------------------------
local main_layer = g.getlayer()
local dash_layer = nil
for i, name in ipairs(g.getlayers()) do
if name == dash_layer_name then
dash_layer = i - 1
break
end
end
if not dash_layer then
g.addlayer()
dash_layer = g.getlayer()
g.setname(dash_layer_name)
else
g.setlayer(dash_layer)
g.clear()
end
----------------------------------------------------------------------
-- HELPER FUNCTIONS
----------------------------------------------------------------------
local function log_line(y, text)
g.puttext(2, y, text, text_color)
end
local function collect_info()
local info = {}
table.insert(info, "📊 GOLLY DIAGNOSTICS DASHBOARD — PRO")
table.insert(info, "────────────────────────────────────")
table.insert(info, "Version: " .. g.getversion())
table.insert(info, "Rule: " .. g.getrule())
table.insert(info, "Pattern: " .. g.getname())
table.insert(info, "Generation: " .. g.getgen())
table.insert(info, "Population: " .. g.getpop())
local rect = g.getrect()
if #rect == 4 then
table.insert(info, string.format("Bounding Box: x=%d y=%d w=%d h=%d",
rect[1], rect[2], rect[3], rect[4]))
else
table.insert(info, "Bounding Box: (none)")
end
table.insert(info, "Layer: " .. g.getlayer())
table.insert(info, "Zoom: " .. g.getzoom())
table.insert(info, "Viewport: " .. table.concat({g.getpos()}, ", "))
table.insert(info, "API Health: " .. (health.ok and "✅ OK" or "⚠️ " .. health.message))
table.insert(info, "Paused: " .. (paused and "⏸️ Yes" or "▶️ No"))
table.insert(info, "Updated: " .. os.date("%H:%M:%S"))
return info
end
----------------------------------------------------------------------
-- MAIN LOOP
----------------------------------------------------------------------
g.show("Launching Diagnostics Dashboard (press 'P' to pause/resume)")
while true do
local event = g.getevent()
if event:lower() == "key p" then
paused = not paused
g.note(paused and "⏸️ Dashboard paused." or "▶️ Dashboard resumed.")
elseif event:lower() == "key esc" then
g.note("🛑 Dashboard stopped by user.")
break
end
if not paused then
g.setlayer(dash_layer)
g.new("Diagnostics View")
local lines = collect_info()
for i, line in ipairs(lines) do
log_line(2 * i, line)
end
g.fit()
g.update()
g.setlayer(main_layer)
g.update()
end
g.sleep(refresh_ms)
end
----------------------------------------------------------------------
-- CLEAN EXIT
----------------------------------------------------------------------
g.setlayer(main_layer)
g.update()
g.note("Diagnostics Dashboard closed cleanly.")
----------------------------------------------------------------------
Instructions :

How to use it

A. Run directly to test Golly
Save this as golly_health_check.lua inside your Golly Scripts folder.
In Golly, go to File → Run Script → golly_health_check.lua.
You’ll see results in the status bar and a log entry in
{Golly Scripts Folder}/golly_health_log.txt

B. Import it into another Lua script
At the top of your main script:
local health = dofile(g.getdir("scripts") .. "golly_health_check.lua")
if not health.ok then
g.warn("Golly API failed: " .. health.message)
g.exit("Cannot continue.")
end
That gives you silent validation every time your script starts — no clutter, just robust startup assurance.

How it works
Uses g.puttext() to overlay readable status data directly into Golly’s grid window.
Refreshes every 0.8 seconds by default (change refresh_ms).
Pulls:
Golly version, rule, pattern, generation, zoom
Layer number
Viewport position
Health status from your earlier golly_health_check.lua
Displays a live timestamp for confirmation.

How to run
Save it as golly_diagnostics_dashboard.lua in your Scripts folder.
Run from Golly:
File → Run Script → golly_diagnostics_dashboard.lua
You’ll see a bright text overlay in the viewport with live system info.
Stop it at any time with Esc or Ctrl + .

How to use
Save as golly_diagnostics_dashboard_dual.lua in your Golly Scripts folder.
Run it while any simulation is active.
The script will:
Create (or reuse) a new layer named “Diagnostics”.
Continuously update it with version, rule, gen, zoom, etc.
Automatically switch back to your main simulation layer each refresh cycle — so your pattern keeps evolving.
Stop at any time with Esc or Ctrl +.

Notes & Options
You can increase refresh_ms (e.g., to 3000) if you prefer slower updates.
You can change text_color to "white", "yellow", "green", etc.
To display additional metrics (e.g., pattern cell count, bounding box), add:
table.insert(info, "Cells: " .. g.getpop())
table.insert(info, "Bounding Box: " .. table.concat({g.getrect()}, ", "))
inside the collect_info() function.
Perfect — now you’re getting the power-user version

This version of the Golly Diagnostics Dashboard (Dual-Layer Pro Edition) adds:

Live pattern stats — total cells (getpop()) and bounding box (getrect())

Auto-pause/resume toggle — you can press P to pause/resume dashboard updates without interrupting the main simulation

Clean layer handling and safe exit

Golly Diagnostics Dashboard — Dual-Layer Pro Edition

How to use
Save as golly_diagnostics_dashboard_pro.lua in your Scripts folder.
Run it while any simulation is active.
Use keys:
P → Pause/resume dashboard updates (main sim continues running).
Esc → Stop dashboard completely and return to main layer.

What it tracks (live)
Metric Description
Version / Rule Current Golly engine + active rule
Pattern / Generation Active pattern name + generation count
Population Number of live cells
Bounding Box x, y, width, height of current pattern
Layer / Zoom / Viewport Rendering & camera details
API Health Status from your health check module
Pause Status “

No” or “

Yes”
Updated Local timestamp of last refresh
now I am getting "data export mode"...
GOLLY-LUA-DIAGNOSTICS-DASHBOARD-TELEMETRY-EDITION.lua
Code: Select all
----------------------------------------------------------------------
-- 📊 GOLLY LUA DIAGNOSTICS DASHBOARD — TELEMETRY EDITION
-- Dual-layer live dashboard + optional CSV export for analytics
----------------------------------------------------------------------
local g = rawget(_G, "golly") and golly() or nil
if not g then error("\n❌ Must be run inside Golly.") end
-- Optional external health check module
local health = dofile(g.getdir("scripts") .. "golly_health_check.lua")
----------------------------------------------------------------------
-- CONFIGURATION
----------------------------------------------------------------------
local refresh_ms = 1000 -- dashboard refresh rate (ms)
local export_interval = 5000 -- CSV write interval (ms)
local csv_filename = "golly_telemetry_log.csv"
local enable_export = true -- toggle file export on/off
local text_color = "cyan"
local dash_layer_name = "Diagnostics"
local paused = false
----------------------------------------------------------------------
-- SETUP
----------------------------------------------------------------------
local main_layer = g.getlayer()
local dash_layer = nil
for i, name in ipairs(g.getlayers()) do
if name == dash_layer_name then dash_layer = i - 1 break end
end
if not dash_layer then
g.addlayer()
dash_layer = g.getlayer()
g.setname(dash_layer_name)
else
g.setlayer(dash_layer)
g.clear()
end
local last_export = os.clock() * 1000
----------------------------------------------------------------------
-- HELPERS
----------------------------------------------------------------------
local function log_line(y, text)
g.puttext(2, y, text, text_color)
end
local function collect_info()
local rect = g.getrect()
return {
version = g.getversion(),
rule = g.getrule(),
pattern = g.getname(),
generation = g.getgen(),
population = g.getpop(),
bbox = (#rect == 4) and string.format("%d,%d,%d,%d", rect[1], rect[2], rect[3], rect[4]) or "",
layer = g.getlayer(),
zoom = g.getzoom(),
pos = table.concat({g.getpos()}, ","),
api_ok = health.ok and "OK" or "FAIL",
paused = paused and "yes" or "no",
time = os.date("%H:%M:%S")
}
end
local function display_info(info)
local lines = {
"📊 GOLLY TELEMETRY DASHBOARD",
"─────────────────────────────",
"Version: " .. info.version,
"Rule: " .. info.rule,
"Pattern: " .. info.pattern,
"Generation: " .. info.generation,
"Population: " .. info.population,
"Bounding Box: " .. (info.bbox ~= "" and info.bbox or "(none)"),
"Layer: " .. info.layer,
"Zoom: " .. info.zoom,
"View: " .. info.pos,
"API Health: " .. (health.ok and "✅ OK" or "⚠️ " .. health.message),
"Paused: " .. (paused and "⏸️ Yes" or "▶️ No"),
"Timestamp: " .. info.time
}
for i, line in ipairs(lines) do
log_line(2 * i, line)
end
end
local function write_csv_header()
local file = io.open(csv_filename, "w")
if not file then error("❌ Cannot write CSV file.") end
file:write("timestamp,version,rule,pattern,generation,population,bbox,layer,zoom,pos,api_ok,paused\n")
file:close()
end
local function append_csv(info)
local file = io.open(csv_filename, "a")
if not file then return end
local line = string.format(
"%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
info.time, info.version, info.rule, info.pattern, info.generation,
info.population, info.bbox, info.layer, info.zoom, info.pos,
info.api_ok, info.paused
)
file:write(line)
file:close()
end
if enable_export then write_csv_header() end
----------------------------------------------------------------------
-- MAIN LOOP
----------------------------------------------------------------------
g.show("Telemetry Dashboard active — 'P' pause/resume, 'Esc' stop.")
while true do
local event = g.getevent()
if event:lower() == "key p" then
paused = not paused
g.note(paused and "⏸️ Dashboard paused." or "▶️ Dashboard resumed.")
elseif event:lower() == "key esc" then
g.note("🛑 Dashboard stopped by user.")
break
end
if not paused then
local info = collect_info()
g.setlayer(dash_layer)
g.new("Telemetry View")
display_info(info)
g.fit()
g.update()
if enable_export then
local now = os.clock() * 1000
if now - last_export >= export_interval then
append_csv(info)
last_export = now
end
end
g.setlayer(main_layer)
g.update()
end
g.sleep(refresh_ms)
end
----------------------------------------------------------------------
-- CLEAN EXIT
----------------------------------------------------------------------
g.setlayer(main_layer)
g.update()
g.note("Telemetry Dashboard closed cleanly.")
----------------------------------------------------------------------

Usage Guide
Save as golly_diagnostics_dashboard_telemetry.lua
(same folder as your golly_health_check.lua).
Run from Golly > Scripts > Run Script.
Controls:
P = Pause/resume dashboard updates
Esc = Stop dashboard completely
If enable_export = true, you’ll get a file:
golly_telemetry_log.csv
in your working directory — appending a new line every 5 seconds by default.

CSV Output Example
timestamp,version,rule,pattern,generation,population,bbox,layer,zoom,pos,api_ok,paused
14:35:10,4.2,B3/S23,Glider,120,5,12,12,5,1,-2,OK,no
14:35:15,4.2,B3/S23,Glider,200,8,14,14,5,1,-2,OK,no
now we are talking about auto-tagging and event triggers...