Skip to content

Instantly share code, notes, and snippets.

@DUznanski
Created August 26, 2025 14:07
Show Gist options
  • Save DUznanski/2ca99ae7315a261494076e888db0e6ac to your computer and use it in GitHub Desktop.
Save DUznanski/2ca99ae7315a261494076e888db0e6ac to your computer and use it in GitHub Desktop.
Rolling Block and Phasic Dial solvers
from itertools import product
from math import lcm
sizes = (8,8,8)
initial_positions = (6,5,4)
buttons = [
(1,1,1),
(0,1,1),
(1,0,1),
]
def solve_phasic_dial(sizes, initial_positions, buttons):
target = [(s-i) % s for s,i in zip(sizes,initial_positions)]
max_presses = lcm(*sizes)
press_piles = []
for button in buttons:
pile = []
for k in range(max_presses):
press_result = tuple((i * k) % s for i, s in zip(button, sizes))
if k != 0 and max(press_result) == 0: break
pile.append(press_result)
press_piles.append(pile)
best_count = max_presses * len(sizes)
best_result = None
for p in sorted(product(*(range(len(pile)) for pile in press_piles)), key = sum):
pile_selection = [pile[k] for pile, k in zip(press_piles, p)]
pile_sum = [sum(i) for i in zip(*pile_selection)]
fixed_sum = [i % s for i, s in zip(pile_sum, sizes)]
if fixed_sum == target:
return p
print(solve_phasic_dial(sizes, initial_positions, buttons))
board = """ xxxx
xxxx
xxxx
xxxx
xxxxxxx
xxxxx x
xxxx x
xxxxxxxxx
xxxxxxxxx
"""
start = (4,5,2,1,2)
finish = (7,8,2,1,2)
tiles = set()
fragile = set()
for l, line in enumerate(board.splitlines()):
for c, char in enumerate(line):
if char == 'x':
tiles.add((c,l))
elif char == 'f':
fragile.add((c,l))
fragile = frozenset(fragile)
def go_east(pos):
return (pos[0] + pos[2], pos[1], pos[4], pos[3], pos[2])
def go_west(pos):
return (pos[0] - pos[4], pos[1], pos[4], pos[3], pos[2])
def go_south(pos):
return (pos[0], pos[1] + pos[3], pos[2], pos[4], pos[3])
def go_north(pos):
return (pos[0], pos[1] - pos[4], pos[2], pos[4], pos[3])
def is_legal(pos,fragile,tiles):
for x in range(pos[0], pos[0] + pos[2]):
for y in range(pos[1], pos[1] + pos[3]):
if (x, y) not in tiles and (x, y) not in fragile:
return False
return True
def delete_fragile(pos,fragile):
coverage = frozenset((x,y) for x in range(pos[0], pos[0] + pos[2]) for y in range(pos[1], pos[1] + pos[3]))
return fragile - coverage
def is_solved(pos,fragile,finish):
return not fragile and (finish is None or pos == finish)
directions = [
('n', go_north),
('s', go_south),
('e', go_east),
('w', go_west)
]
fragile = delete_fragile(start, fragile)
itinerary = [(start, fragile)]
known_paths = {(start, fragile): ''}
while itinerary:
pos, fragile = itinerary.pop(0)
path = known_paths[pos, fragile]
for d, move in directions:
new_pos = move(pos)
new_fragile = delete_fragile(new_pos, fragile)
if is_legal(new_pos,fragile,tiles) and (new_pos, new_fragile) not in known_paths:
known_paths[(new_pos, new_fragile)] = path + d
itinerary.append((new_pos, new_fragile))
for (pos, fragile), path in known_paths.items():
if is_solved(pos,fragile,finish):
print(path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment