Advertisement
Guest User

AoC 2022 Day 9

a guest
Dec 19th, 2022
588
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.97 KB | None | 0 0
  1. # aoc202209.py
  2.  
  3. import pathlib
  4. import sys
  5.  
  6. from typing import TypeAlias
  7.  
  8. # Type aliases
  9. Coordinate: TypeAlias = tuple[int, int]
  10. Instruction: TypeAlias = tuple[str, int]
  11.  
  12.  
  13. def parse(puzzle_input: str) -> list[Instruction]:
  14.     """Parse input"""
  15.     return [(line[0], int(line[2:])) for line in puzzle_input.splitlines()]
  16.  
  17.  
  18. def move_head(curr_pos: Coordinate, direction: Instruction) -> list[Coordinate]:
  19.     """ Move head knot based on instruction given """
  20.     positions = []
  21.     x, y = curr_pos
  22.     d, a = direction
  23.     for n in range(a):
  24.         match d:
  25.             case "R":
  26.                 x += 1
  27.             case "U":
  28.                 y += 1
  29.             case "L":
  30.                 x -= 1
  31.             case "D":
  32.                 y -= 1
  33.         positions.append((x, y))
  34.     return positions
  35.  
  36.  
  37. def neighbours(pos: Coordinate) -> list[Coordinate]:
  38.     """Return coords of neighbours (including diagonals and pos)"""
  39.     x0, y0 = pos
  40.     # candidates = [
  41.     return [
  42.         (x0, y0),
  43.         (x0 - 1, y0),
  44.         (x0 + 1, y0),
  45.         (x0, y0 - 1),
  46.         (x0, y0 + 1),
  47.         (x0 - 1, y0 - 1),
  48.         (x0 + 1, y0 + 1),
  49.         (x0 - 1, y0 + 1),
  50.         (x0 + 1, y0 - 1),
  51.     ]
  52.  
  53.  
  54. def part1(data: list[Instruction]) -> int:
  55.     """Solve part 1"""
  56.     grid: dict[Coordinate, int] = {}
  57.     head_pos: Coordinate = (0, 0)
  58.     tail_pos: Coordinate = (0, 0)
  59.     # Set initial position of tail in grid
  60.     grid.setdefault(tail_pos, 1)
  61.     for instruct in data:
  62.         grid.setdefault(head_pos, 0)
  63.         moves = move_head(head_pos, instruct)
  64.         for move in moves:
  65.             grid.setdefault(move, 0)
  66.             prev_move: Coordinate = head_pos
  67.             head_pos = move
  68.             if tail_pos not in neighbours(head_pos):
  69.                 tail_pos = prev_move
  70.                 grid[tail_pos] = 1
  71.     return sum(v for v in grid.values() if v == 1)
  72.  
  73.  
  74. def move_knot(head: Coordinate, knot: Coordinate) -> Coordinate:
  75.     """ Move knot releative to "head" """
  76.     hr, hc = head
  77.     kr, kc = knot
  78.     if hr == kr:
  79.         kc = kc - 1 if hc < kc else kc + 1
  80.     elif hc == kc:
  81.         kr = kr - 1 if hr < kr else kr + 1
  82.     else:
  83.         # Deal with diagonal movement
  84.         if hc > kc and hr > kr:
  85.             kr += 1
  86.             kc += 1
  87.         elif hc < kc and hr < kr:
  88.             kr -= 1
  89.             kc -= 1
  90.         elif hc > kc and hr < kr:
  91.             kr -= 1
  92.             kc += 1
  93.         elif hc < kc and hr > kr:
  94.             kr += 1
  95.             kc -= 1
  96.     return (kr, kc)
  97.  
  98.  
  99. def update_knots(
  100.     knots: dict[int, Coordinate], head: Coordinate, grid: dict[Coordinate, int]
  101. ) -> None:
  102.     """ Update position of all knots """
  103.     previous: Coordinate = head
  104.     tail: int = len(knots) - 1
  105.     for n in range(len(knots)):
  106.         if knots[n] not in neighbours(previous):
  107.             knots[n] = move_knot(previous, knots[n])
  108.         previous = knots[n]
  109.         if n == tail:
  110.             grid.setdefault(knots[n], 1)
  111.  
  112.  
  113. def part2(data: list[Instruction]) -> int:
  114.     """Solve part 2"""
  115.     grid: dict[Coordinate, int] = {}
  116.     head_pos: Coordinate = (0, 0)
  117.     knot_pos: dict[int, Coordinate] = {n: (0, 0) for n in range(9)}
  118.     for instruct in data:
  119.         moves: list[Coordinate] = move_head(head_pos, instruct)
  120.         for move in moves:
  121.             head_pos = move
  122.             update_knots(knot_pos, head_pos, grid)
  123.     return len(grid)
  124.  
  125.  
  126. def solve(puzzle_input: str) -> tuple[int, int]:
  127.     """Solve the puzzle for the given input"""
  128.     data: list[Instruction] = parse(puzzle_input)
  129.     solution1: int = part1(data)  # Correct answer was 6486 (with my data)
  130.     solution2: int = part2(data)  # Correct answer was 2678 (with my data)
  131.  
  132.     return solution1, solution2
  133.  
  134.  
  135. if __name__ == "__main__":
  136.     for path in sys.argv[1:]:
  137.         print(f"{path}:")
  138.         puzzle_input = pathlib.Path(path).read_text().strip()
  139.         solutions = solve(puzzle_input)
  140.         print("\n".join(str(solution) for solution in solutions))
  141.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement