Guest User

AoC 2022 - Day 12

a guest
Dec 12th, 2022
489
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.76 KB | Source Code | 0 0
  1. """
  2. Advent of Code 2022 Day 12
  3. """
  4. import sys
  5.  
  6. from dataclasses import dataclass, field
  7. from itertools import product
  8. from typing import Iterable
  9.  
  10. from advent_tools import get_daily_input
  11.  
  12. DAY = 12
  13.  
  14. DEBUG = sys.argv[1] == "debug" if len(sys.argv) > 1 else False
  15.  
  16. DEBUG_DATA = """
  17. Sabqponm
  18. abcryxxl
  19. accszExk
  20. acctuvwj
  21. abdefghi
  22. """
  23.  
  24. if DEBUG:
  25.     def get_daily_input(_):
  26.         for line in DEBUG_DATA.strip().split("\n"):
  27.             yield line.strip("\n")
  28.  
  29.  
  30. @dataclass
  31. class Coordinate:
  32.     x: int
  33.     y: int
  34.  
  35.     def distance_to(self, other: "Coordinate") -> int:
  36.         return abs(self.x - other.x) + abs(self.y - other.y)
  37.  
  38.  
  39. @dataclass
  40. class MapPoint:
  41.     height: int
  42.     visited: bool = False
  43.     cost_to_reach: int = None
  44.     previous_point: Coordinate = None
  45.  
  46.  
  47. @dataclass
  48. class HeightMap:
  49.     start: Coordinate = None
  50.     end: Coordinate = None
  51.     points: list[list[MapPoint]] = field(default_factory=list)
  52.  
  53.     def reachable_points(self, origin: Coordinate) -> Iterable[Coordinate]:
  54.  
  55.         possible_moves = [
  56.             Coordinate(x, y) for (x, y) in [
  57.                 (origin.x - 1, origin.y), (origin.x + 1, origin.y),
  58.                 (origin.x, origin.y - 1), (origin.x, origin.y + 1)
  59.             ] if 0 <= x < len(self.points[0]) and 0 <= y < len(self.points)
  60.         ]
  61.         max_height = self.points[origin.y][origin.x].height + 1
  62.  
  63.         for point in possible_moves:
  64.             if self.point_at(point).height <= max_height:
  65.                 yield point
  66.  
  67.     def point_at(self, coordinate: Coordinate):
  68.         return self.points[coordinate.y][coordinate.x]
  69.  
  70.     def walk(self, queue: list[tuple[int, Coordinate, Coordinate]] = None) -> None:
  71.         if not queue:
  72.             queue = [(0, self.start, self.start)]
  73.         cycles = 0
  74.         while queue:
  75.             cycles += 1
  76.             curr_cost, curr_point, prev_point = queue.pop(0)
  77.             if not self.point_at(curr_point).visited:
  78.                 self.point_at(curr_point).cost_to_reach = curr_cost
  79.                 self.point_at(curr_point).visited = True
  80.                 self.point_at(curr_point).previous_point = prev_point
  81.                 if curr_point == self.end:
  82.                     break
  83.                 else:
  84.                     curr_cost += 1
  85.                     for next_point in self.reachable_points(curr_point):
  86.                         queue.append((curr_cost, next_point, curr_point))
  87.                     queue.sort(key=lambda l: l[0])
  88.         print(f"Completed in {cycles} cycles")
  89.  
  90.  
  91. def load_map() -> HeightMap:
  92.     height_map = HeightMap()
  93.     for row in get_daily_input(DAY):
  94.         height_map.points.append([])
  95.         for point in row:
  96.             if point == "S":
  97.                 height_map.start = Coordinate(
  98.                     x=len(height_map.points[-1]),
  99.                     y=len(height_map.points) - 1
  100.                 )
  101.                 point = "a"
  102.             elif point == "E":
  103.                 height_map.end = Coordinate(
  104.                     x=len(height_map.points[-1]),
  105.                     y=len(height_map.points) - 1
  106.                 )
  107.                 point = "z"
  108.             height_map.points[-1].append(MapPoint(height=ord(point) - ord("a")))
  109.     return height_map
  110.  
  111.  
  112. def part_1() -> int:
  113.     m = load_map()
  114.     m.walk()
  115.     return m.point_at(m.end).cost_to_reach
  116.  
  117.  
  118. def part_2() -> int:
  119.     m = load_map()
  120.     queue = []
  121.     for (x, y) in product(range(len(m.points[0])), range(len(m.points))):
  122.         coord = Coordinate(x, y)
  123.         if m.point_at(coord).height == 0:
  124.             queue.append((0, coord, coord))
  125.     m.walk(queue=queue)
  126.     return m.point_at(m.end).cost_to_reach
  127.  
  128.  
  129. def main():
  130.     print(f"Part 1: {part_1()}")
  131.     print(f"Part 2: {part_2()}")
  132.  
  133.  
  134. if __name__ == "__main__":
  135.     main()
  136.  
Add Comment
Please, Sign In to add comment