Passed
Push — main ( 73cdde...636602 )
by Yohann
01:03
created

main.Point.is_touching()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
from __future__ import annotations
2
3
import pathlib
4
from typing import Literal, cast, Set, Tuple
5
6
7
class Point:
8
9
    def __init__(self, x, y):
10
        self.previous = None
11
        self.x = x
12
        self.y = y
13
14
    def is_touching(self, other: Point) -> bool:
15
        return (
16
            self.x == other.x and self.y == other.y
17
            or (
18
                abs(self.x - other.x) < 2
19
                and abs(self.y - other.y) < 2
20
            )
21
        )
22
23
    def move(self, x: int, y: int):
24
        self.previous = Point(self.x, self.y)
25
        self.x += x
26
        self.y += y
27
28
    def copy(self, other: Point):
29
        self.previous = Point(self.x, self.y)
30
        self.x = other.x
31
        self.y = other.y
32
33
34
class Rope:
35
36
    def __init__(self, knots: int):
37
        if knots < 2:
38
            raise ValueError('Rope must have at least 2 knots')
39
40
        self.knots = [Point(0, 0) for _ in range(knots)]
41
        self.prints: Set[Tuple[int, int]] = set()
42
43
    @property
44
    def head(self) -> Point:
45
        return self.knots[0]
46
47
    @property
48
    def tail(self) -> Point:
49
        return self.knots[-1]
50
51
    def move_head(self, direction: Literal['L', 'R', 'U', 'D']):
52
        dx: int = (direction == 'L') - (direction == 'R')
53
        dy: int = (direction == 'U') - (direction == 'D')
54
55
        self.head.move(dx, dy)
56
        self.propagate_movement()
57
58
        self.prints.add((self.tail.x, self.tail.y))
59
60
    def propagate_movement(self):
61
        for head, tail in zip(self.knots, self.knots[1:]):
62
            if not tail.is_touching(head):
63
                tail.copy(head.previous)
64
65
    def solve(self, lines) -> int:
66
        for line in lines:
67
            move = cast(Literal['L', 'R', 'U', 'D'], line[0])
68
            value = int(line[1:])
69
70
            for i in range(value):
71
                self.move_head(move)
72
73
        return len(self.prints)
74
75
76
def main():
77
    content = pathlib.Path('./input.txt').read_text()
78
    lines = content.splitlines()
79
80
    rope = Rope(2)
81
    print("Part 1:", rope.solve(lines))
82
83
84
if __name__ == '__main__':
85
    main()
86