Advertisement
Guest User

AoC 2022 - Day 14

a guest
Dec 14th, 2022
204
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.97 KB | None | 0 0
  1. """
  2. Advent of Code 2022 Day 14
  3. """
  4. import sys
  5.  
  6. from dataclasses import dataclass, field
  7.  
  8. from advent_tools import get_daily_input
  9.  
  10. DAY = 14
  11.  
  12. DEBUG = sys.argv[1] == "debug" if len(sys.argv) > 1 else False
  13. VISUALIZE = sys.argv[1] == "visualize" if len(sys.argv) > 1 else False
  14.  
  15. DEBUG_DATA = """
  16. 498,4 -> 498,6 -> 496,6
  17. 503,4 -> 502,4 -> 502,9 -> 494,9
  18. """
  19.  
  20. if DEBUG:
  21.     def get_daily_input(_):
  22.         for line in DEBUG_DATA.strip().split("\n"):
  23.             yield line.strip("\n")
  24.  
  25.  
  26. @dataclass
  27. class Coordinate:
  28.     x: int = 0
  29.     y: int = 0
  30.  
  31.     def __add__(self, other):
  32.         return Coordinate(self.x + other.x, self.y + other.y)
  33.  
  34.     def __sub__(self, other) -> "Coordinate":
  35.         return Coordinate(self.x - other.x, self.y - other.y)
  36.  
  37.  
  38. @dataclass
  39. class Line:
  40.     points: list[Coordinate] = field(default_factory=list)
  41.  
  42.     @property
  43.     def num_points(self) -> int:
  44.         return len(self.points)
  45.  
  46.     @property
  47.     def point_pairs(self) -> list[tuple[Coordinate, ...]]:
  48.         return [tuple(self.points[i:i + 2]) for i in range(self.num_points - 1)]
  49.  
  50.     def set_origin(self, top_left_corner: Coordinate) -> "Line":
  51.         return Line([p - top_left_corner for p in self.points])
  52.  
  53.     @classmethod
  54.     def line_from_text(cls, text: str) -> "Line":
  55.         line = Line()
  56.         for point in text.split(" -> "):
  57.             x, y = [int(n) for n in point.split(",")]
  58.             line.points.append(Coordinate(x, y))
  59.         return line
  60.  
  61.  
  62. class CaveMap:
  63.     def __init__(self, lines: list[Line], sand_origin: Coordinate) -> None:
  64.         top_left_corner = Coordinate(min([p.x for i in lines for p in i.points]), 0)
  65.         adjusted_lines = [i.set_origin(top_left_corner) for i in lines]
  66.  
  67.         self.sand_origin = sand_origin - top_left_corner
  68.         self.width = max([p.x for i in adjusted_lines for p in i.points]) + 1
  69.         self.height = max([p.y for i in adjusted_lines for p in i.points]) + 1
  70.         self.map: list[list[str]] = []
  71.  
  72.         for _ in range(self.height):
  73.             self.map.append(["."] * self.width)
  74.  
  75.         for line in adjusted_lines:
  76.             self.draw_line(line)
  77.  
  78.     def __repr__(self) -> str:
  79.         return "\n".join(["".join(r) for r in self.map])
  80.  
  81.     def draw_line(self, line: Line) -> None:
  82.         for start, end in line.point_pairs:
  83.             increment = 1 if start.x < end.x or start.y < end.y else -1
  84.             if start.x != end.x:
  85.                 for i in range(start.x, end.x + increment, increment):
  86.                     self.map[start.y][i] = "#"
  87.             else:
  88.                 for i in range(start.y, end.y + increment,  increment):
  89.                     self.map[i][start.x] = "#"
  90.  
  91.     def at(self, coordinate: Coordinate) -> str:
  92.         return self.map[coordinate.y][coordinate.x]
  93.  
  94.     def vacant(self, coordinate: Coordinate) -> bool:
  95.         return self.at(coordinate) == "."
  96.  
  97.     def drop_sand(self, location: Coordinate = None) -> bool:
  98.         location = location if location else self.sand_origin
  99.         if location.y < self.height - 1 and 0 < location.x < self.width:
  100.             if self.vacant(location + Coordinate(0, 1)):
  101.                 return self.drop_sand(location + Coordinate(0, 1))
  102.             elif self.vacant(location + Coordinate(-1, 1)):
  103.                 return self.drop_sand(location + Coordinate(-1, 1))
  104.             elif self.vacant(location + Coordinate(1, 1)):
  105.                 return self.drop_sand(location + Coordinate(1, 1))
  106.             else:
  107.                 self.map[location.y][location.x] = "o"
  108.                 return True
  109.         return False
  110.  
  111.     def fill_cave(self) -> int:
  112.         count = 1
  113.         self.map[self.sand_origin.y][self.sand_origin.x] = "o"
  114.         for y in range(self.height - 1):
  115.             for x in range(self.width):
  116.                 if self.map[y][x] == "o":
  117.                     for x_next in [x - 1, x, x + 1]:
  118.                         if self.map[y + 1][x_next] == ".":
  119.                             self.map[y + 1][x_next] = "o"
  120.                             count += 1
  121.         return count
  122.  
  123.  
  124. def part_1() -> int:
  125.     lines = [Line.line_from_text(i) for i in get_daily_input(DAY)]
  126.     cave_map = CaveMap(lines=lines, sand_origin=Coordinate(500, 0))
  127.     grains = 0
  128.     while cave_map.at(cave_map.sand_origin) == "." and cave_map.drop_sand():
  129.         grains += 1
  130.  
  131.     if DEBUG or VISUALIZE:
  132.         print("\n", cave_map)
  133.  
  134.     return grains
  135.  
  136.  
  137. def part_2() -> int:
  138.     lines = [Line.line_from_text(i) for i in get_daily_input(DAY)]
  139.     largest_y = max([p.y for i in lines for p in i.points]) + 2
  140.     lines.append(Line([Coordinate(500 - largest_y, largest_y),
  141.                        Coordinate(500 + largest_y, largest_y)]))
  142.     cave_map = CaveMap(lines=lines, sand_origin=Coordinate(500, 0))
  143.     grains = cave_map.fill_cave()
  144.  
  145.     if DEBUG or VISUALIZE:
  146.         print("\n", cave_map)
  147.  
  148.     return grains
  149.  
  150.  
  151. def main():
  152.     print(f"Part 1: {part_1()}")
  153.     print(f"Part 2: {part_2()}")
  154.  
  155.  
  156. if __name__ == "__main__":
  157.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement