aoc2022/d24/run.py

55 lines
2.0 KiB
Python
Raw Permalink Normal View History

2022-12-29 11:08:35 +00:00
import sys
from collections import deque, namedtuple
MOVES = [(0, 1), (0, -1), (1, 0), (-1, 0), (0, 0)]
Blizzards = namedtuple('Blizzards', ['rows', 'cols', 'left', 'right', 'up', 'down'])
def is_free(blizz, r, c, t):
return not any((
(r, (c-t) % blizz.cols) in blizz.right,
(r, (c+t) % blizz.cols) in blizz.left,
((r-t) % blizz.rows, c) in blizz.down,
((r+t) % blizz.rows, c) in blizz.up
))
def parse(data):
lines = [l[1:-1] for l in data.splitlines()[1:-1]] # Cut off walls
rows, cols = len(lines), len(lines[0])
left = frozenset((r, c) for r in range(rows) for c in range(cols) if lines[r][c] == '<')
right = frozenset((r, c) for r in range(rows) for c in range(cols) if lines[r][c] == '>')
up = frozenset((r, c) for r in range(rows) for c in range(cols) if lines[r][c] == '^')
down = frozenset((r, c) for r in range(rows) for c in range(cols) if lines[r][c] == 'v')
return Blizzards(rows, cols, left, right, up, down)
def shortest_path(blizz, start, end, start_time):
visited = set()
q = deque()
while True:
# When do we enter the board? We can enter at any time after the start_time
while not q:
start_time += 1 # Increment first, because it takes one timestep to enter the board
if is_free(blizz, start[0], start[1], start_time):
q.append((start[0], start[1], start_time))
r, c, t = q.popleft()
if (r, c, t) in visited:
continue
visited.add((r, c, t))
if (r, c) == end:
return t + 1
for dr, dc in MOVES:
nr, nc = r + dr, c + dc
if 0 <= nr < blizz.rows and 0 <= nc < blizz.cols and is_free(blizz, nr, nc, t+1):
q.append((nr, nc, t+1))
if __name__ == '__main__':
b = parse(sys.stdin.read())
start = (0, 0)
end = (b.rows-1, b.cols-1)
t1 = shortest_path(b, start, end, 0)
t2 = shortest_path(b, end, start, t1)
t3 = shortest_path(b, start, end, t2)
print(t1)
print(t3)