Advertisement
JonathanGupton

Advent of Code 2022 - Day 15 - Python

Dec 15th, 2022 (edited)
420
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.39 KB | None | 0 0
  1. from bisect import insort
  2. from itertools import chain
  3. import re
  4. from typing import Generator, Optional
  5.  
  6. Coordinate = tuple[int, int]
  7. Beacon = Coordinate
  8. Sensor = Coordinate
  9. Distance = int
  10.  
  11.  
  12. def parse_input(filepath) -> Generator[tuple[Sensor, Beacon], None, None]:
  13.     with open(filepath, "r") as f:
  14.         for line in f.readlines():
  15.             x1, y1, x2, y2 = map(int, re.findall(r"-*\d+", line))
  16.             yield (x1, y1), (x2, y2)
  17.  
  18.  
  19. def manhattan_distance(x1, y1, x2, y2) -> Distance:
  20.     return abs(x2 - x1) + abs(y2 - y1)
  21.  
  22.  
  23. def get_range_at_row(sensor, beacon, row) -> Optional[tuple[int, int]]:
  24.     distance = manhattan_distance(*sensor, *beacon)
  25.     if abs(sensor[1] - row) > distance:
  26.         return None
  27.     else:
  28.         delta_h = abs(distance - abs(sensor[1] - row))
  29.         left, right = sensor[0] - delta_h, sensor[0] + delta_h
  30.         return left, right
  31.  
  32.  
  33. def merge_intervals(ranges: list[tuple[int, int]]) -> list[tuple[int, int]]:
  34.     index = 0
  35.     for i in range(1, len(ranges)):
  36.         if ranges[index][1] >= ranges[i][0]:
  37.             ranges[index][1] = max(ranges[index][1], ranges[i][1])
  38.         else:
  39.             index = index + 1
  40.             ranges[index] = ranges[i]
  41.     return ranges[0 : index + 1]
  42.  
  43.  
  44. def find_positions_wo_beacon_by_row(beacon_data: list[tuple[Sensor, Beacon]], row: int) -> list[tuple[int, int]]:
  45.     range_at_row = []
  46.     for (sensor, beacon) in beacon_data:
  47.         if rng := get_range_at_row(sensor, beacon, row):
  48.             insort(range_at_row, list(rng))
  49.     merged = merge_intervals(range_at_row)
  50.     return merged
  51.  
  52.  
  53. def find_isolated_beacon(beacon_data, search_space) -> tuple[int, int]:
  54.     for i in range(search_space + 1):
  55.         val = find_positions_wo_beacon_by_row(beacon_data, i)
  56.         if len(val) > 1:
  57.             x = (val[1][0] + val[0][1])//2
  58.             return x, i
  59.  
  60.  
  61. def part_one(filepath, row) -> int:
  62.     beacon_info = [(sensor, beacon) for sensor, beacon in parse_input(filepath)]
  63.     merged = find_positions_wo_beacon_by_row(beacon_info, row)
  64.     return len(list(chain.from_iterable([range(l, r) for l, r in merged])))
  65.  
  66.  
  67. def part_two(filepath, search_space) -> int:
  68.     multiplier = 4000000
  69.     beacon_info = [(sensor, beacon) for sensor, beacon in parse_input(filepath)]
  70.     x, y = find_isolated_beacon(beacon_info, search_space)
  71.     tuning_frequency = multiplier * x + y
  72.     return tuning_frequency
  73.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement