Advertisement
hhoppe

Advent of code 2024 day 21

Dec 21st, 2024 (edited)
328
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.57 KB | None | 0 0
  1. def day21(s, *, part2=False):
  2.   yx_of_digit = dict(
  3.       [('7', (0, 0)), ('8', (0, 1)), ('9', (0, 2)), ('4', (1, 0)), ('5', (1, 1)), ('6', (1, 2))]
  4.       + [('1', (2, 0)), ('2', (2, 1)), ('3', (2, 2)), ('0', (3, 1)), ('A', (3, 2))]
  5.   )
  6.   yx_of_dir = dict([('^', (0, 1)), ('A', (0, 2)), ('<', (1, 0)), ('v', (1, 1)), ('>', (1, 2))])
  7.  
  8.   # Length of the shortest final sequence to move from (y, x) to (y2, x2) on the digit keypad
  9.   # given that the previous direction code (from the last directional keypad) was `last_dir_ch`.
  10.   def digit_cost(y, x, y2, x2, last_dir_ch='A'):
  11.     if (y, x) == (y2, x2):
  12.       return dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir['A'])
  13.     min_cost = 10**18
  14.     if y != y2 and not (x == 0 and y2 == 3):  # Move up or down.
  15.       y1, ch = (y + 1, 'v') if y2 > y else (y - 1, '^')
  16.       cost1 = dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir[ch])
  17.       min_cost = min(min_cost, cost1 + digit_cost(y1, x, y2, x2, ch))
  18.     if x != x2 and not (y == 3 and x2 == 0):  # Move left or right.
  19.       x1, ch = (x + 1, '>') if x2 > x else (x - 1, '<')
  20.       cost1 = dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir[ch])
  21.       min_cost = min(min_cost, cost1 + digit_cost(y, x1, y2, x2, ch))
  22.     return min_cost
  23.  
  24.   # Length of the shortest final sequence to move from (y, x) to (y2, x2) on the n_dir'th
  25.   # directional keypad given that the previous direction code (from the prior directional keypad
  26.   # n_dir - 1, if n_dir > 0) was `last_dir_ch`.
  27.   @functools.cache
  28.   def dir_cost(y, x, y2, x2, n_dir=25 if part2 else 2, last_dir_ch='A'):
  29.     if n_dir == 0:
  30.       return 1  # On the zero'th directional keypad, all codes cost exactly 1.
  31.     if (y, x) == (y2, x2):
  32.       return dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir['A'], n_dir - 1)  # Move to 'A'.
  33.     min_cost = 10**18
  34.     if y != y2 and not (x == 0 and y2 == 0):  # Move up or down.
  35.       y1, ch = (y + 1, 'v') if y2 > y else (y - 1, '^')
  36.       cost1 = dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir[ch], n_dir - 1)
  37.       min_cost = min(min_cost, cost1 + dir_cost(y1, x, y2, x2, n_dir, ch))
  38.     if x != x2 and not (y == 0 and x2 == 0):  # Move left or right.
  39.       x1, ch = (x + 1, '>') if x2 > x else (x - 1, '<')
  40.       cost1 = dir_cost(*yx_of_dir[last_dir_ch], *yx_of_dir[ch], n_dir - 1)
  41.       min_cost = min(min_cost, cost1 + dir_cost(y, x1, y2, x2, n_dir, ch))
  42.     return min_cost
  43.  
  44.   def code_cost(code):
  45.     yxs = [yx_of_digit[ch] for ch in 'A' + code]
  46.     return sum(digit_cost(*yx, *yx2) for yx, yx2 in itertools.pairwise(yxs))
  47.  
  48.   return sum(code_cost(code) * int(code.rstrip('A')) for code in s.splitlines())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement