I made some very minimal changes to the script to get it to do something like what you want: all salvos are p1 and a recipe is output if the final pattern has 9 cells and seems to contain a glider moving SE or NW.
Note that it is quite possible that the final pattern could be tub + glider and the script doesn't care if the block has returned to its original position or not. Also it only allows a glider to be produced at the final step. For example, it won't find recipes where a glider is produced at step 3 and then the remaining debris is converted back into a block during later steps.
It seems like there are plenty of 5 glider solutions under these restrictions. (EDIT: Ooops, no it only starts churning out solutions that contain 6 gliders, and for reasons mentioned above it misses dvgrn's 5 glider solution in the other thread). Finally, here is the code:
Code: Select all
import golly as g
from hashlib import sha256
from itertools import chain
#arbitrary numbers
MAX_GENERATIONS = 256
MAX_POPULATION = 40
MAX_GLIDERS = 6
#NE glider
GLIDER = g.parse('3o$2bo$bo!')
#put any ad-hoc patterns that you want to bombard with slow gliders here.
TARGET_PATTERNS = []#('known_splitter', 'bo$obo$b2o$5bo$4bobo$5bobo$6b2o!')]
#put simple targets here, along with rotational symmetry
SIMPLE_TARGETS = [
('block', '2o$2o!', 4),
# ('blinker', '3o$!', 4),
# ('tub', 'bo$obo$bo!', 4),
# ('boat', 'b2o$obo$bo!', 1),
# ('hive', 'b2o$o2bo$b2o!', 2),
# ('ship', 'b2o$obo$2o!', 2),
# ('loaf', 'b2o$o2bo$bobo$2bo!', 1),
# ('lboat', '2b2o$bobo$obo$bo!', 1),
# ('pond', 'b2o$o2bo$o2bo$b2o!', 4),
# ('tlight', '4bo$4bo$4bo2$3o3b3o2$4bo$4bo$4bo!', 4),
# ('hfarm', '6bo$5bobo$5bobo$6bo2$b2o7b2o$o2bo5bo2bo$b2o7b2o2$6bo$5bobo$5bobo$6bo!', 4),
]
def get_pattern_variants(cells, symmetry):
variants = []
for t in range(0, 4, symmetry):
variants.append(cells)
cells = g.transform(cells, 0, 0, 0, -1, 1, 0)
return variants
TARGETS = []
for name, pattern in TARGET_PATTERNS:
cells = g.parse(pattern)
p = len(cells) / 2
TARGETS.append((name, cells, p))
for name, pattern, sym in SIMPLE_TARGETS:
cells = g.parse(pattern)
variants = get_pattern_variants(cells, sym)
for i, v in enumerate(variants):
p = len(v) / 2
TARGETS.append((name+str(i), v, p))
def patterns_identical(cells1, cells2):
if len(cells1) != len(cells2):
return False
if sum(cells1) != sum(cells2):
return False
return sorted(zip(cells1[::2], cells1[1::2])) == sorted(zip(cells2[::2], cells2[1::2]))
def get_pattern_period(cells):
temp_cells = cells
for p in range(0, 2):
temp_cells = g.evolve(temp_cells, 1)
if patterns_identical(cells, temp_cells):
return p+1
return None
def get_shooting_range(cells):
min_d1 = max_d1 = cells[0] + cells[1]
min_d2 = cells[0] - cells[1]
for i in range(2, len(cells), 2):
min_d1 = min(min_d1, cells[i] + cells[i+1])
max_d1 = max(max_d1, cells[i] + cells[i+1])
min_d2 = min(min_d2, cells[i] - cells[i+1])
min_lane = min_d1 - 6
max_lane = max_d1 + 3
shift = 6 - min_d2 // 2
return min_lane, max_lane, shift
def get_pattern_to_try(cells, lane, parity, offset=50):
glider = g.transform(GLIDER, lane - lane // 2 - offset, lane // 2 + offset)
if parity % 2:
glider = g.evolve(glider, 1)
return list(chain(cells, glider))
offset = 0
def display_solution(start, lanes, debug, last_cells):
global offset
cells = [c for n, c, _ in TARGETS if n == start][0]
i = 100
for lane in lanes:
lane_num, parity = lane
cells = get_pattern_to_try(cells, lane_num, parity, i)
i += 100
g.putcells(cells, 0, offset)
for i, p in enumerate(debug):
g.putcells(p, 100 + 100 * i, offset)
g.putcells(last_cells, 100 + 100 * len(debug), offset)
g.fit()
g.update()
g.show(' '.join(chain([str(start), str(len(lanes))], [str(lane) for lane in lanes])))
offset += 400
randoms = []
for i in range(4096):
randoms.append(int(sha256(str(i)).hexdigest()[:16], 16))
def to_hashable(cells):
if not cells:
return 0
minx = min(cells[::2])
miny = min(cells[1::2])
hash = 0
for i in range(0, len(cells), 2):
hash ^= randoms[64 * (cells[i] & 63) + (cells[i+1] & 63)]
return hash
def deltas(cells):
return len(cells), sum(cells[::2]), sum(cells[1::2])
def bombard_final(start, lanes, cells, period, debug, flipx, flipy):
cells = g.transform(cells, 0, 0, flipx, 0, 0, flipy)
min_lane, max_lane, shift = get_shooting_range(cells)
for lane_num in range(min_lane, max_lane + 1):
for parity in range(period):
lane = (lane_num, parity)
start_cells = get_pattern_to_try(cells, lane[0], lane[1], shift)
new_cells = g.evolve(start_cells, MAX_GENERATIONS)
if len(new_cells) == 18:
n1, dx1, dy1 = deltas(new_cells)
n2, dx2, dy2 = deltas(g.evolve(new_cells, 4))
if n1 != n2:
continue
dx = dx2-dx1
dy = dy2-dy1
if (dx, dy) == (5, 5) or (dx, dy) == (-5, -5):
#Success??
#flip back for display purposes
start_cells = g.transform(start_cells, 0, 0, flipx, 0, 0, flipy)
new_cells = g.transform(new_cells, 0, 0, flipx, 0, 0, flipy)
#add
debug.append(start_cells)
lanes.append(lane)
#display
display_solution(start, lanes, debug, new_cells)
#remove
lanes.pop()
debug.pop()
g.new('')
new_queue = []
for name, cells, _ in TARGETS:
period = get_pattern_period(cells)
new_queue.append( (name, [], cells, period, []) )
seen = set()
for n in range(MAX_GLIDERS):
queue = new_queue
new_queue = []
count = 0
for start, lanes, last, period, debug in queue:
g.show(str((n+1,count,len(queue))))
count += 1
min_lane, max_lane, shift = get_shooting_range(last)
for lane_num in range(min_lane, max_lane + 1):
for parity in range(period):
lane = (lane_num, parity)
start_cells = get_pattern_to_try(last, lane[0], lane[1], shift)
new_cells = g.evolve(start_cells, MAX_GENERATIONS)
if not new_cells or len(new_cells) > 2 * MAX_POPULATION:
continue
new_period = get_pattern_period(new_cells)
if new_period != 1:
continue
new_hashable = to_hashable(new_cells)
if new_hashable in seen:
continue
seen.add(new_hashable)
if new_period > 1:
seen.add(to_hashable(g.evolve(new_cells, 1)))
new_lanes = lanes + [lane]
new_debug = debug + [start_cells]
bombard_final(start, new_lanes, new_cells, new_period, new_debug, 1, 1)
# bombard_final(start, new_lanes, new_cells, new_period, new_debug, 1, -1)
# bombard_final(start, new_lanes, new_cells, new_period, new_debug, -1, -1)
if n + 1 < MAX_GLIDERS:
new_queue.append( (start, new_lanes, new_cells, new_period, new_debug) )