summaryrefslogtreecommitdiffstats
path: root/10.py
blob: a0249096a851a88940edaf0255e6aa5b9a3d778b (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
114
115
116
117
118
import re
from collections.abc import Iterator
from dataclasses import dataclass
from sys import stdin
from typing import Self


@dataclass(frozen=True, slots=True, order=True)
class Point:
    x: int
    y: int

    def __sub__(self, other: Self) -> Self:
        return self.__class__(self.x - other.x, self.y - other.y)

    def __add__(self, other: Self) -> Self:
        return self.__class__(self.x + other.x, self.y + other.y)

    def __neg__(self) -> Self:
        return self.__class__(-self.x, -self.y)


def next_pos(current: Point, prev: Point, pipe: str) -> Point:
    mapping = {
        Point(0, 1): {  # from north \/
            "|": Point(0, 1),  # to south \/
            "L": Point(1, 0),  # to east ->
            "J": Point(-1, 0),  # to west <-
        },
        Point(-1, 0): {  # from east <-
            "-": Point(-1, 0),  # to west <-
            "F": Point(0, 1),  # to south \/
            "L": Point(0, -1),  # to north /\
        },
        Point(0, -1): {  # from south /\
            "|": Point(0, -1),  # to north /\
            "F": Point(1, 0),  # to east ->
            "7": Point(-1, 0),  # to west <-
        },
        Point(1, 0): {  # from west ->
            "-": Point(1, 0),  # to east ->
            "7": Point(0, 1),  # to south \/
            "J": Point(0, -1),  # to north /\
        },
    }
    return current + mapping[current - prev][pipe]


def neighbours(point: Point) -> Iterator[Point]:
    for d in (Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)):
        yield point + d


inp = [line.rstrip() for line in stdin]

start = next(
    Point(x, y) for y, l in enumerate(inp) for x, c in enumerate(l) if c == "S"
)

prev = start
if inp[start.y - 1][start.x] in {"|", "F", "7"}:
    current = start + Point(0, -1)
elif inp[start.y][start.x + 1] in {"-", "J", "7"}:
    current = start + Point(1, 0)
elif inp[start.y + 1][start.x] in {"|", "J", "L"}:
    current = start + Point(0, 1)
elif inp[start.y][start.x - 1] in {"-", "L", "F"}:
    current = start + Point(-1, 0)
else:
    raise ValueError

first_after_start = current
last_before_start = current

bounds: set[Point] = set((start,))
length = 1
while inp[current.y][current.x] != "S":
    bounds.add(current)
    current, prev = next_pos(current, prev, inp[current.y][current.x]), current
    last_before_start = prev
    length += 1

print(length // 2)

dirs = list(sorted((start - first_after_start, start - last_before_start)))

start_replacement = {
    "S": {
        (Point(-1, 0), Point(0, -1)): "F",  # ES
        (Point(-1, 0), Point(0, 1)): "L",  # EN
        (Point(-1, 0), Point(1, 0)): "-",  # EW
        (Point(0, -1), Point(0, 1)): "|",  # SN
        (Point(0, -1), Point(1, 0)): "7",  # SW
        (Point(0, 1), Point(1, 0)): "J",  # NW
    }[(dirs[0], dirs[1])]
}


inp2 = [
    "".join(
        start_replacement.get(c, c) if Point(x, y) in bounds else "."
        for x, c in enumerate(line)
    )
    for y, line in enumerate(inp)
]

for pattern, repl in (("L-*J", ""), ("L-*7", "|"), ("F-*J", "|"), ("F-*7", "")):
    inp2 = [re.sub(pattern, repl, l) for l in inp2]

inner = 0
for line in inp2:
    outside = True
    for c in line:
        if c == "|":
            outside = not outside
        elif c == "." and not outside:
            inner += 1
print(inner)