Advertisement
Guest User

AoC 2022 Day 14

a guest
Jan 14th, 2023
409
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.79 KB | None | 0 0
  1. # aoc202214.py
  2.  
  3. import pathlib
  4. import re
  5. import sys
  6.  
  7. from typing import NamedTuple
  8.  
  9. class Coordinate(NamedTuple):
  10.     x: int
  11.     y: int
  12.  
  13.  
  14. def parse(puzzle_input: str) -> list[list[Coordinate]]:
  15.     """ Parse input """
  16.     pattern = re.compile(r'\d+,\d+')
  17.     string_paths: list[list[str]] = [
  18.             re.findall(pattern, line) for line in puzzle_input.splitlines()
  19.             ]
  20.     solid_structures: list[list[Coordinate]] = []
  21.     for struct in string_paths:
  22.         coords: list[Coordinate] = []
  23.         for coord in struct:
  24.             x, y = coord.split(',')
  25.             coords.append(Coordinate(int(x), int(y)))
  26.         solid_structures.append(coords)
  27.     return solid_structures
  28.  
  29.  
  30. def missing_values(beginning: int, final: int) -> list[int]:
  31.     """ Return list of ints of missing values between start and end points """
  32.     low: int = min(beginning, final)
  33.     high: int = max(beginning, final)
  34.     return list(range(low, high + 1))
  35.  
  36.  
  37. def get_line(start: Coordinate, end: Coordinate) -> list[Coordinate]:
  38.     """ Get full set of coordinates assuming straight line """
  39.     coords: list[Coordinate] = []
  40.     if start.x == end.x:
  41.         x = start.x
  42.         y_values = missing_values(start.y, end.y)
  43.         for y in y_values:
  44.             coords.append(Coordinate(x, y))
  45.     else:
  46.         y = start.y
  47.         x_values = missing_values(start.x, end.x)
  48.         for x in x_values:
  49.             coords.append(Coordinate(x, y))
  50.     return coords
  51.  
  52.  
  53. def make_grid(paths: list[list[Coordinate]]) -> dict[Coordinate, str]:
  54.     """ Make grid """
  55.     grid: dict[Coordinate, str] = {}
  56.     for path in paths:
  57.         for idx, coord in enumerate(path):
  58.             if idx == len(path) - 1:
  59.                 break
  60.             coords: list[Coordinate] = get_line(coord, path[idx + 1])
  61.             for coord in coords:
  62.                 grid[coord] = "#"
  63.     return grid
  64.  
  65.  
  66. def move_sand(grid: dict[Coordinate, str],
  67.               pos: Coordinate,
  68.               overshoot: int) -> Coordinate:
  69.     while True:
  70.         next_pos = Coordinate(pos.x, pos.y + 1)
  71.         if next_pos in grid:
  72.             left_move = Coordinate(pos.x - 1, pos.y + 1)
  73.             if left_move in grid:
  74.                 right_move = Coordinate(pos.x + 1, pos.y + 1)
  75.                 if right_move in grid:
  76.                     return pos
  77.                 else:
  78.                     return move_sand(grid, right_move, overshoot)
  79.             else:
  80.                 return move_sand(grid, left_move, overshoot)
  81.         elif next_pos.y > overshoot:
  82.             return next_pos
  83.         else:
  84.             pos = next_pos
  85.  
  86.  
  87. def fill_sand(grid: dict[Coordinate, str],
  88.               pos: Coordinate,
  89.               floor: int) -> Coordinate:
  90.     while True:
  91.         next_pos = Coordinate(pos.x, pos.y + 1)
  92.         if next_pos in grid:
  93.             left_move = Coordinate(pos.x - 1, pos.y + 1)
  94.             if left_move in grid:
  95.                 right_move = Coordinate(pos.x + 1, pos.y + 1)
  96.                 if right_move in grid:
  97.                     return pos
  98.                 else:
  99.                     return fill_sand(grid, right_move, floor)
  100.             else:
  101.                 return fill_sand(grid, left_move, floor)
  102.         elif next_pos.y > floor:
  103.             return pos
  104.         else:
  105.             pos = next_pos
  106.  
  107.  
  108. def part1(data: list[list[Coordinate]]) -> int:
  109.     """ Solve part 1 """
  110.     grid: dict[Coordinate, str] = make_grid(data)
  111.     source = Coordinate(500, 0)
  112.     grid[source] = '+'
  113.     units_of_sand: int = 0
  114.     max_y: int = max(y for x, y in grid.keys())
  115.     while True:
  116.         sand: Coordinate = move_sand(grid, source, max_y)
  117.         if sand.y > max_y:
  118.             break
  119.         grid[sand] = 'o'
  120.         units_of_sand += 1
  121.     return units_of_sand
  122.  
  123.  
  124. def part2(data: list[list[Coordinate]]) -> int:
  125.     """ Solve part 2 """
  126.     grid: dict[Coordinate, str] = make_grid(data)
  127.     source = Coordinate(500, 0)
  128.     grid[source] = '+'
  129.     units_of_sand: int = 0
  130.     max_y: int = max(y for x, y in grid.keys()) + 1
  131.     while True:
  132.         sand: Coordinate = fill_sand(grid, source, max_y)
  133.         grid[sand] = 'o'
  134.         units_of_sand += 1
  135.         if sand == source:
  136.             break
  137.     return units_of_sand
  138.  
  139.  
  140. def solve(puzzle_input: str) -> tuple[int, int]:
  141.     """ Solve the puzzle for the given input """
  142.     data: list[list[Coordinate]] = parse(puzzle_input)
  143.     solution1: int = part1(data)  # Correct answer was 873 (with my data)
  144.     solution2: int = part2(data)  # Correct answer was 24813 (with my data)
  145.  
  146.     return solution1, solution2
  147.  
  148.  
  149. if __name__ == "__main__":
  150.     for path in sys.argv[1:]:
  151.         print(f"{path}:")
  152.         puzzle_input = pathlib.Path(path).read_text().strip()
  153.         solutions = solve(puzzle_input)
  154.         print('\n'.join(str(solution) for solution in solutions))
  155.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement