And as every year: a path finding puzzle.
Python Solution
def read_file(file: TextIO) -> tuple[list[list[int]], Pos, Pos]:
"""Read a grid from the `file`."""
lines = [line.rstrip() for line in file if line]
def char_to_height(char: str) -> int:
if char == 'S':
return 0
if char == 'E':
return 25
return ord(char) - ord('a')
grid = [[char_to_height(c) for c in line] for line in lines]
for pos, char in iter_grid_val(lines):
if char == 'S':
start = pos
elif char == 'E':
end = pos
return grid, start, end
def min_path_length(
start: Pos,
next_states: Callable[[Pos], Iterable[Pos]],
is_end: Callable[[Pos], bool]
) -> int:
"""Calculate the minimal steps to an end."""
visited = {start}
current_positions = [start]
count = 1
while current_positions:
next_positions = []
for pos in current_positions:
for neighbor in next_states(pos):
if neighbor in visited:
continue
if is_end(neighbor):
return count
next_positions.append(neighbor)
visited.add(neighbor)
current_positions = next_positions
count += 1
return -1
def part1(file: TextIO) -> int:
"""Solve the first part of the puzzle."""
grid, start, end = read_file(file)
def next_states(pos):
height_pos = grid[pos.y][pos.x]
for neighbor, neighbor_height in neighbors4_val(pos, grid):
if height_pos + 1 >= neighbor_height:
yield neighbor
return min_path_length(start, next_states, lambda pos: pos == end)
def part2(file: TextIO) -> int:
"""Solve the second part of the puzzle."""
grid, _, start = read_file(file)
def next_states(pos):
height_pos = grid[pos.y][pos.x]
for neighbor, neighbor_height in neighbors4_val(pos, grid):
if height_pos <= neighbor_height + 1:
yield neighbor
return min_path_length(
start, next_states, lambda pos: grid[pos.y][pos.x] == 0)
@IsaacG Were you prepared and achieved some small rank?