Files
Advent_of_code/src/Year_2022/Day12.py
2023-08-12 10:46:04 +02:00

141 lines
4.6 KiB
Python

# --- Day 12: Hill Climbing Algorithm ---
# You try contacting the Elves using your handheld device, but the river you're
# following must be too low to get a decent signal.
# You ask the device for a heightmap of the surrounding area (your puzzle
# input). The heightmap shows the local area from above broken into a grid;
# the elevation of each square of the grid is given by a single lowercase
# letter, where a is the lowest elevation, b is the next-lowest, and so on up
# to the highest elevation, z.
# Also included on the heightmap are marks for your current position (S) and
# the location that should get the best signal (E). Your current position (S)
# has elevation a, and the location that should get the best signal (E) has
# elevation z.
# You'd like to reach E, but to save energy, you should do it in as few steps
# as possible. During each step, you can move exactly one square up, down,
# left, or right. To avoid needing to get out your climbing gear, the elevation
# of the destination square can be at most one higher than the elevation of
# your current square; that is, if your current elevation is m, you could step
# to elevation n, but not to elevation o. (This also means that the elevation
# of the destination square can be much lower than the elevation of your
# current square.)
# For example:
# Sabqponm
# abcryxxl
# accszExk
# acctuvwj
# abdefghi
# Here, you start in the top-left corner; your goal is near the middle. You
# could start by moving down or right, but eventually you'll need to head
# toward the e at the bottom. From there, you can spiral around to the goal:
# v..v<<<<
# >v.vv<<^
# .>vv>E^^
# ..v>>>^^
# ..>>>>>^
# In the above diagram, the symbols indicate whether the path exits each
# square moving up (^), down (v), left (<), or right (>). The location that
# should get the best signal is still E, and . marks unvisited squares.
# This path reaches the goal in 31 steps, the fewest possible.
# What is the fewest steps required to move from your current position to the
# location that should get the best signal?
with open("/home/xfeluser/AoC_2022/P12.txt") as f:
diagram = {
(x, y): ord(e)
for y, line in enumerate(f.read().strip().split("\n"))
for x, e in enumerate(line.strip())
}
start = [k for k in diagram if diagram[k] == ord("S")][0]
end = [k for k in diagram if diagram[k] == ord("E")][0]
# Use expected value for starting/ending point
diagram[start] = ord("a")
diagram[end] = ord("z")
queue = [(start, 0)]
pos_to_steps = {}
while queue:
cur, steps = queue.pop()
if cur not in pos_to_steps or steps < pos_to_steps[cur]:
pos_to_steps[cur] = steps
for offset in ((1, 0), (-1, 0), (0, 1), (0, -1)):
new_pos = cur[0] + offset[0], cur[1] + offset[1]
if new_pos in diagram and diagram[new_pos] - diagram[cur] <= 1:
queue.append((new_pos, steps + 1))
print(pos_to_steps[end])
# --- Part Two ---
# As you walk up the hill, you suspect that the Elves will want to turn this
# into a hiking trail. The beginning isn't very scenic, though; perhaps you can
# find a better starting point.
# To maximize exercise while hiking, the trail should start as low as possible:
# elevation a. The goal is still the square marked E. However, the trail should
# still be direct, taking the fewest steps to reach its goal. So, you'll need
# to find the shortest path from any square at elevation a to the square marked
# E.
# Again consider the example from above:
# Sabqponm
# abcryxxl
# accszExk
# acctuvwj
# abdefghi
# Now, there are six choices for starting position (five marked a, plus the
# square marked S that counts as being at elevation a). If you start at the
# bottom-left square, you can reach the goal most quickly:
# ...v<<<<
# ...vv<<^
# ...v>E^^
# .>v>>>^^
# >^>>>>>^
# This path reaches the goal in only 29 steps, the fewest possible.
# What is the fewest steps required to move starting from any square with
# elevation a to the location that should get the best signal?
starts = []
pos_to_steps = {}
for start in [k for k in diagram if diagram[k] == ord("a")]:
queue = [(start, 0)]
while queue:
cur, steps = queue.pop()
if cur not in pos_to_steps or steps < pos_to_steps[cur]:
pos_to_steps[cur] = steps
for offset in ((1, 0), (-1, 0), (0, 1), (0, -1)):
new_pos = cur[0] + offset[0], cur[1] + offset[1]
if new_pos in diagram and diagram[new_pos] - diagram[cur] <= 1:
queue.append((new_pos, steps + 1))
if end in pos_to_steps:
starts.append(pos_to_steps[end])
print(min(starts))