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)