Advertisement
kupuguy

Advent of Code 2024 Day 20

Dec 20th, 2024
165
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.88 KB | Source Code | 0 0
  1. from pathlib import Path
  2. from collections import deque, defaultdict
  3.  
  4.  
  5. def parse(input: str) -> tuple[complex, complex, set[complex]]:
  6.     start, end = 0j, 0j
  7.     track: set[complex] = set()
  8.     for y, row in enumerate(input.splitlines()):
  9.         for x, c in enumerate(row):
  10.             p = complex(x, y)
  11.             if c == "S":
  12.                 start = p
  13.             elif c == "E":
  14.                 end = p
  15.             if c != "#":
  16.                 track.add(p)
  17.     return start, end, track
  18.  
  19.  
  20. def costs(start: complex, track: set[complex]) -> dict[complex, int]:
  21.     work: deque[tuple[complex, int]] = deque([(start, 0)])
  22.     result: dict[complex, int] = {start: 0}
  23.     while work:
  24.         point, c = work.popleft()
  25.         c += 1
  26.         for p in (point + 1, point - 1, point + 1j, point - 1j):
  27.             if p in track and p not in result:
  28.                 result[p] = c
  29.                 work.append((p, c))
  30.     return result
  31.  
  32.  
  33. def part1(saving: int, start: complex, end: complex, track: set[complex]) -> int:
  34.     forward = costs(start, track)
  35.     backward = costs(end, track)
  36.     cheats: dict[int, int] = defaultdict(int)
  37.     fair_route = forward[end]
  38.     for p in forward:
  39.         for d in (1, -1, 1j, -1j):
  40.             if p + 2 * d in backward and p + d not in track:
  41.                 cheatcost = forward[p] + 2 + backward[p + 2 * d]
  42.                 if cheatcost < fair_route:
  43.                     cheats[fair_route - cheatcost] += 1
  44.     return sum(cheats[s] for s in cheats if s >= saving)
  45.  
  46.  
  47. TEST = parse(Path("input/day20-test.txt").read_text())
  48. assert part1(38, *TEST) == 3
  49.  
  50. INPUT = parse(Path("input/day20.txt").read_text())
  51. part1_total = part1(100, *INPUT)
  52. print(f"{part1_total=:,}")  # 1,404
  53.  
  54.  
  55. def cheat_points(distance: int) -> dict[complex, int]:
  56.     result: dict[complex, int] = {}
  57.     for y in range(-distance, distance + 1):
  58.         for x in range(-distance, distance + 1):
  59.             if 2 <= abs(x) + abs(y) <= distance:
  60.                 result[complex(x, y)] = abs(x) + abs(y)
  61.     return result
  62.  
  63.  
  64. def part2(
  65.     saving: int, distance: int, start: complex, end: complex, track: set[complex]
  66. ) -> int:
  67.     forward = costs(start, track)
  68.     backward = costs(end, track)
  69.     cheats: dict[int, int] = defaultdict(int)
  70.     fair_route = forward[end]
  71.     possible_cheats: list[complex] = cheat_points(distance)
  72.     for p in [q for q in forward if forward[q] < fair_route - saving]:
  73.         for d in possible_cheats:
  74.             if p + d in backward:
  75.                 cheatcost = forward[p] + possible_cheats[d] + backward[p + d]
  76.                 if cheatcost < fair_route:
  77.                     cheats[fair_route - cheatcost] += 1
  78.  
  79.     return sum(cheats[s] for s in cheats if s >= saving)
  80.  
  81.  
  82. # print(f"{part2(50, 20, *TEST)}")
  83. print("Part 1 works for part 2?", part2(100, 2, *INPUT))
  84. part2_total = part2(100, 20, *INPUT)
  85. print(f"{part2_total=:,}")  # 1,010,981
  86.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement