Advertisement
kupuguy

Advent Of Code 2024 Day 21

Dec 21st, 2024
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.64 KB | Source Code | 0 0
  1. from pathlib import Path
  2. from functools import cache
  3.  
  4.  
  5. def parse(input: str) -> list[tuple[str, int]]:
  6.     return [(line, int(line[:-1], 10)) for line in input.splitlines()]
  7.  
  8.  
  9. NUMERIC = {
  10.     "7": (0, 0),
  11.     "8": (0, 1),
  12.     "9": (0, 2),
  13.     "4": (1, 0),
  14.     "5": (1, 1),
  15.     "6": (1, 2),
  16.     "1": (2, 0),
  17.     "2": (2, 1),
  18.     "3": (2, 2),
  19.     "0": (3, 1),
  20.     "A": (3, 2),
  21. }
  22. DIRECTIONS = {"^": (0, 1), "A": (0, 2), "<": (1, 0), "v": (1, 1), ">": (1, 2)}
  23.  
  24.  
  25. @cache
  26. def numeric_pad(current: str, want: str) -> list[str]:
  27.     """Returns a list of the shortest routes between the two keys."""
  28.     cur_y, cur_x = NUMERIC[current]
  29.     want_y, want_x = NUMERIC[want]
  30.     routes = []
  31.     moves = [("", cur_x, cur_y)]
  32.     while moves:
  33.         m = moves
  34.         moves = []
  35.         for d, x, y in m:
  36.             if want_x < x:
  37.                 if y != 3 or x != 1:
  38.                     moves.append((d + "<", x - 1, y))
  39.             elif want_x > x:
  40.                 moves.append((d + ">", x + 1, y))
  41.             if want_y < y:
  42.                 moves.append((d + "^", x, y - 1))
  43.             elif want_y > y:
  44.                 if y != 2 or x != 0:
  45.                     moves.append((d + "v", x, y + 1))
  46.             elif want_x == x and want_y == y:
  47.                 routes.append(d)
  48.     return [r + "A" for r in routes if r]
  49.  
  50.  
  51. @cache
  52. def direction_pad(current: str, want: str) -> list[str]:
  53.     """Returns a list of the shortest routes between the two keys."""
  54.     cur_y, cur_x = DIRECTIONS[current]
  55.     want_y, want_x = DIRECTIONS[want]
  56.     routes = []
  57.     moves = [("", cur_x, cur_y)]
  58.     while moves:
  59.         m = moves
  60.         moves = []
  61.         for d, x, y in m:
  62.             if want_x < x:
  63.                 if y != 0 or x != 1:
  64.                     moves.append((d + "<", x - 1, y))
  65.             elif want_x > x:
  66.                 moves.append((d + ">", x + 1, y))
  67.             if want_y < y:
  68.                 if x != 0:
  69.                     moves.append((d + "^", x, y - 1))
  70.             elif want_y > y:
  71.                 moves.append((d + "v", x, y + 1))
  72.             elif want_x == x and want_y == y:
  73.                 routes.append(d)
  74.     return [r + "A" for r in routes]
  75.  
  76.  
  77. @cache
  78. def cost_for_direction_pair(p2a: str, p2b: str) -> int:
  79.     cur_y, cur_x = DIRECTIONS[p2a]
  80.     want_y, want_x = DIRECTIONS[p2b]
  81.     return abs(cur_y - want_y) + abs(cur_x - want_x) + 1
  82.  
  83.  
  84. @cache
  85. def cost_forpadseq(seq: str, npads: int) -> int:
  86.     res = sum(padcost(p2a, p2b, npads) for p2a, p2b in zip("A" + seq, seq))
  87.     return res
  88.  
  89.  
  90. def padcost(c0: str, c1: str, npads: int) -> int:
  91.     if npads == 1:
  92.         return cost_for_direction_pair(c0, c1)
  93.     return min(cost_forpadseq(seq, npads - 1) for seq in direction_pad(c0, c1))
  94.  
  95.  
  96. @cache
  97. def humancost(seq: str, npads: int = 3) -> int:
  98.     res = sum(padcost(p2a, p2b) for p2a, p2b in zip("A" + seq, seq))
  99.     return res
  100.  
  101.  
  102. @cache
  103. def humanpad(c0: str, c1: str, npads: int) -> int:
  104.     costs = []
  105.     for pad1_route in numeric_pad(c0, c1):
  106.         costs.append(cost_forpadseq(pad1_route, npads - 1))
  107.     return min(costs)
  108.  
  109.  
  110. def enter_code2(code: str, npads: int) -> int:
  111.     cost = sum(humanpad(c0, c1, npads) for c0, c1 in zip("A" + code[:-1], code))
  112.     return cost
  113.  
  114.  
  115. def part2(codes: list[tuple[str, int]], npads: int = 3) -> int:
  116.     return sum(value * enter_code2(code, npads) for code, value in codes)
  117.  
  118.  
  119. TEST = parse(Path("input/day21-test.txt").read_text())
  120. assert part2(TEST, 3) == 126384
  121. INPUT = parse(Path("input/day21.txt").read_text())
  122. part1_total = part2(INPUT, 3)
  123. print(f"{part1_total=:,}")  # 164,960
  124.  
  125. part2_total = part2(INPUT, 26)
  126. print(f"{part2_total=:,}")  # 205,620,604,017,764
  127.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement