Advertisement
JonathanGupton

Advent of Code 2022 - Day 14 - Python

Dec 14th, 2022 (edited)
317
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.92 KB | None | 0 0
  1. from __future__ import annotations
  2. from enum import Enum
  3. from dataclasses import dataclass
  4.  
  5.  
  6. class T(Enum):
  7.     Rock = "#"
  8.     Sand = "+"
  9.     Air = "."
  10.  
  11.  
  12. @dataclass(unsafe_hash=True, frozen=True)
  13. class Coordinate:
  14.     x: int
  15.     y: int
  16.  
  17.  
  18. class Sand:
  19.     def __init__(self, coordinate: Coordinate, cave: Cave) -> None:
  20.         self.coordinate = coordinate
  21.         self.cave = cave
  22.         self.stopped = False
  23.  
  24.     def drop_infinite(self) -> Coordinate:
  25.         while not self.stopped:
  26.             next_coordinate = Coordinate(self.coordinate.x, self.coordinate.y + 1)
  27.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  28.                 self.coordinate = next_coordinate
  29.                 if self.coordinate.y == self.cave.y_max:
  30.                     self.stopped = True
  31.                 continue
  32.  
  33.             next_coordinate = Coordinate(self.coordinate.x - 1, self.coordinate.y + 1)
  34.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  35.                 self.coordinate = next_coordinate
  36.                 if self.coordinate.y == self.cave.y_max:
  37.                     self.stopped = True
  38.                 continue
  39.  
  40.             next_coordinate = Coordinate(self.coordinate.x + 1, self.coordinate.y + 1)
  41.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  42.                 self.coordinate = next_coordinate
  43.                 if self.coordinate.y == self.cave.y_max:
  44.                     self.stopped = True
  45.                 continue
  46.  
  47.             self.stopped = True
  48.  
  49.         return self.coordinate
  50.  
  51.  
  52.     def drop_to_floor(self) -> Coordinate:
  53.         while not self.stopped:
  54.             next_coordinate = Coordinate(self.coordinate.x, self.coordinate.y + 1)
  55.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  56.                 self.coordinate = next_coordinate
  57.                 if self.coordinate.y == self.cave.y_max - 1:
  58.                     break
  59.                 continue
  60.  
  61.             next_coordinate = Coordinate(self.coordinate.x - 1, self.coordinate.y + 1)
  62.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  63.                 self.coordinate = next_coordinate
  64.                 if self.coordinate.y == self.cave.y_max - 1:
  65.                     break
  66.                 continue
  67.  
  68.             next_coordinate = Coordinate(self.coordinate.x + 1, self.coordinate.y + 1)
  69.             if self.cave.rock_coordinates.get(next_coordinate, T.Air) == T.Air:
  70.                 self.coordinate = next_coordinate
  71.                 if self.coordinate.y == self.cave.y_max -1:
  72.                     break
  73.                 continue
  74.  
  75.             self.stopped = True
  76.  
  77.         return self.coordinate
  78.  
  79. class Cave:
  80.     def __init__(self):
  81.         self.origin = Coordinate(500, 0)  # sand starts here
  82.         self.rock_coordinates: dict[Coordinate, T] = {}
  83.         self.x_min = 0
  84.         self.x_max = 0
  85.         self.y_min = 0
  86.         self.y_max = 0
  87.         self.rock_paths = []
  88.         self.sand_count = 0
  89.  
  90.     @classmethod
  91.     def from_file(cls, filepath) -> Cave:
  92.         c = Cave()
  93.         with open(filepath, "r") as f:
  94.             for line in f.readlines():
  95.                 c.add_rock_path_from_str(line)
  96.         c.set_bounds()
  97.         return c
  98.  
  99.     def set_bounds(self):
  100.         for coordinate in self.rock_coordinates:
  101.             self.y_max = max(coordinate.y, self.y_max)
  102.             self.x_min = min(coordinate.x, self.x_min)
  103.             self.x_max = max(coordinate.x, self.x_max)
  104.  
  105.     def add_rock_path_from_str(self, input_line: str) -> None:
  106.         path: list[Coordinate] = []
  107.         for pair in input_line.split(" -> "):
  108.             path.append(Coordinate(*map(int, pair.split(","))))
  109.  
  110.         for i, coordinate in enumerate(path[1:], 1):
  111.             self.add_rock_path(path[i - 1], coordinate)
  112.  
  113.     def add_rock_path(self, start: Coordinate, end: Coordinate):
  114.         self.rock_paths.append((start, end))
  115.         if start.x == end.x:
  116.             x = start.x
  117.             min_y, max_y = min(start.y, end.y), max(start.y, end.y)
  118.             self.draw_vertical_rock(min_y, max_y, x)
  119.         else:
  120.             y = start.y
  121.             min_x, max_x = min(start.x, end.x), max(start.x, end.x)
  122.             self.draw_horizontal_rock(min_x, max_x, y)
  123.  
  124.     def draw_vertical_rock(self, start, stop, x):
  125.         for y in range(start, stop + 1):
  126.             self.rock_coordinates[Coordinate(x=x, y=y)] = T.Rock
  127.  
  128.     def draw_horizontal_rock(self, start, stop, y):
  129.         for x in range(start, stop + 1):
  130.             self.rock_coordinates[Coordinate(x=x, y=y)] = T.Rock
  131.  
  132.     def draw_sand(self, coordinate) -> None:
  133.         self.rock_coordinates[coordinate] = T.Sand
  134.  
  135.     def simulate_sand(self, print_=False) -> int:
  136.         while True:
  137.             sand = Sand(self.origin, self)
  138.             coordinate = sand.drop_infinite()
  139.             if coordinate.y >= self.y_max:
  140.                 break
  141.             else:
  142.                 self.draw_sand(coordinate)
  143.             self.sand_count += 1
  144.             if print_:
  145.                 print()
  146.                 print(f"{self.sand_count}")
  147.                 print(f"{self}")
  148.  
  149.         return self.sand_count
  150.  
  151.     def simulate_infinite_floor(self, print_=False) -> int:
  152.         while True:
  153.             sand = Sand(self.origin, self)
  154.             coordinate = sand.drop_to_floor()
  155.             self.sand_count += 1
  156.             if coordinate == self.origin:
  157.                 break
  158.             else:
  159.                 self.draw_sand(coordinate)
  160.             if print_:
  161.                 print()
  162.                 print(f"{self.sand_count}")
  163.                 print(f"{self}")
  164.         return self.sand_count
  165.  
  166.  
  167. def part_one(filepath) -> int:
  168.     c = Cave.from_file(filepath)
  169.     sand_count = c.simulate_sand()
  170.     return sand_count
  171.  
  172.  
  173. def part_two(filepath) -> int:
  174.     c = Cave.from_file(filepath)
  175.     c.y_max += 2
  176.     sand_count = c.simulate_infinite_floor()
  177.     return sand_count
  178.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement