summaryrefslogtreecommitdiffstats
path: root/day9/__init__.py
blob: a2d6d82fb75bed01d71f488ccabdb1d1874b3061 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# -*- coding: utf-8 -*-
from abc import ABC
from copy import copy
from typing import Tuple, Iterator, List, Set

from aoc import BaseAssignment
from aoc.datastructures import Coordinate


class Assignment(BaseAssignment, ABC):
    rope_length = NotImplemented

    def parse_line(self, item: str) -> Tuple[str, int]:
        direction, amount = item.split(" ")
        return direction, int(amount)

    def move(self, direction: str, amount: int):
        pass

    @staticmethod
    def next_head(head: Coordinate, direction: str) -> Coordinate:
        match direction:
            case "R":
                return head + Coordinate(1, 0)
            case "L":
                return head + Coordinate(-1, 0)
            case "U":
                return head + Coordinate(0, 1)
            case "D":
                return head + Coordinate(0, -1)

    @staticmethod
    def next_knot(head: Coordinate, tail: Coordinate) -> Coordinate:
        delta = head - tail

        if abs(delta.x) < 2 and abs(delta.y) < 2:
            return tail
        elif abs(delta.x) > 1 and delta.y == 0:
            return tail + Coordinate(
                delta.x - delta.polarity_x,
                0,
            )
        elif abs(delta.x) > 1 and abs(delta.y) == 1:
            return tail + Coordinate(delta.x - delta.polarity_x, delta.y)
        elif abs(delta.y) > 1 and delta.x == 0:
            return tail + Coordinate(
                0,
                delta.y - delta.polarity_y,
            )
        elif abs(delta.y) > 1 and abs(delta.x) == 1:
            return tail + Coordinate(
                delta.x,
                delta.y - delta.polarity_y,
            )
        elif abs(delta.x) > 1 and abs(delta.y) > 1:
            return tail + Coordinate(
                delta.x - delta.polarity_x, delta.y - delta.polarity_y
            )

    def tail_positions(self, input: Iterator[str], length: int) -> Iterator[Coordinate]:
        knots = [Coordinate(0, 0) for _ in range(length)]

        for line in input:
            direction, amount = self.parse_line(line)

            for _ in range(amount):
                for index in range(length):
                    if index == 0:
                        knots[0] = self.next_head(knots[0], direction)
                        continue

                    knots[index] = self.next_knot(knots[index - 1], knots[index])

                yield knots[length - 1]

    def unique_tail_positions(
        self, input: Iterator[str], length: int
    ) -> Set[Coordinate]:
        unique_tail_positions = set()

        for position in self.tail_positions(input, length):
            unique_tail_positions.add(copy(position))

        return unique_tail_positions

    def visualize(self, width: range, height: range, positions: Set[Coordinate]):
        rows = []
        for y in height:
            items = []
            for x in width:
                if x == 0 and y == 0:
                    items.append("s")
                elif Coordinate(x, y) in positions:
                    items.append("#")
                else:
                    items.append(".")
            rows.append(items)

        return "\n".join(["".join(row) for row in reversed(rows)])

    def run(self, input: Iterator) -> int:
        unique_positions = self.unique_tail_positions(input, length=self.rope_length)
        return len(unique_positions)


class AssignmentOne(Assignment):
    example_result = 13
    rope_length = 2


class AssignmentTwo(Assignment):
    example_result = 36
    rope_length = 10