advent24-llm / day20 /solution_claude-3-5-sonnet-20241022.py
jerpint's picture
Add solution files
a4da721
raw
history blame
3.67 kB
from collections import deque
from typing import Set, Tuple, List
import heapq
def parse_maze(filename: str) -> Tuple[List[List[str]], Tuple[int, int], Tuple[int, int]]:
maze = []
start = end = None
with open(filename, 'r') as f:
for i, line in enumerate(f):
row = list(line.strip())
if 'S' in row:
start = (i, row.index('S'))
if 'E' in row:
end = (i, row.index('E'))
maze.append(row)
return maze, start, end
def get_normal_distance(maze: List[List[str]], start: Tuple[int, int], end: Tuple[int, int]) -> int:
rows, cols = len(maze), len(maze[0])
visited = set()
queue = [(0, start)]
while queue:
dist, (r, c) = heapq.heappop(queue)
if (r, c) == end:
return dist
if (r, c) in visited:
continue
visited.add((r, c))
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nr, nc = r + dr, c + dc
if (0 <= nr < rows and 0 <= nc < cols and
maze[nr][nc] != '#' and (nr, nc) not in visited):
heapq.heappush(queue, (dist + 1, (nr, nc)))
return float('inf')
def find_cheats(maze: List[List[str]], start: Tuple[int, int], end: Tuple[int, int],
max_cheat_duration: int) -> Set[int]:
rows, cols = len(maze), len(maze[0])
normal_dist = get_normal_distance(maze, start, end)
saved_times = set()
visited = set()
def is_valid(r: int, c: int) -> bool:
return 0 <= r < rows and 0 <= nc < cols
queue = deque([(start, 0, 0, False, set())]) # pos, dist, cheat_duration, is_cheating, path
while queue:
(r, c), dist, cheat_dur, is_cheating, path = queue.popleft()
state = ((r, c), cheat_dur, is_cheating)
if state in visited:
continue
visited.add(state)
if (r, c) == end and not is_cheating:
if dist < normal_dist:
saved_times.add(normal_dist - dist)
continue
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nr, nc = r + dr, c + dc
if not (0 <= nr < rows and 0 <= nc < cols):
continue
new_path = path | {(nr, nc)}
# Normal movement
if not is_cheating and maze[nr][nc] != '#':
queue.append(((nr, nc), dist + 1, cheat_dur, False, new_path))
# Start cheating
if not is_cheating and cheat_dur == 0 and maze[nr][nc] == '#':
queue.append(((nr, nc), dist + 1, 1, True, new_path))
# Continue cheating
if is_cheating and cheat_dur < max_cheat_duration:
if maze[nr][nc] != '#' and (nr, nc) not in path: # Can end cheat
queue.append(((nr, nc), dist + 1, cheat_dur, False, new_path))
queue.append(((nr, nc), dist + 1, cheat_dur + 1, True, new_path))
return saved_times
def solve(filename: str) -> Tuple[int, int]:
maze, start, end = parse_maze(filename)
# Part 1: cheats that save >= 100 picoseconds with max duration of 2
saved_times_p1 = find_cheats(maze, start, end, 2)
part1 = sum(1 for t in saved_times_p1 if t >= 100)
# Part 2: cheats that save >= 100 picoseconds with max duration of 20
saved_times_p2 = find_cheats(maze, start, end, 20)
part2 = sum(1 for t in saved_times_p2 if t >= 100)
return part1, part2
part1, part2 = solve("input.txt")
print(str(part1))
print(str(part2))