Day10
This commit is contained in:
parent
4e47570718
commit
e013b76439
1 changed files with 146 additions and 0 deletions
146
aoc/day10.py
Normal file
146
aoc/day10.py
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TypeAlias
|
||||||
|
|
||||||
|
from .aoc import Aoc2, AocSameParser
|
||||||
|
|
||||||
|
Position: TypeAlias = tuple[int, int]
|
||||||
|
Graph: TypeAlias = dict[Position, list[Position]]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PipeDiagram:
|
||||||
|
raw_input: list[str]
|
||||||
|
start_pos: Position
|
||||||
|
start_shape: str
|
||||||
|
reachable_nodes: list[Position]
|
||||||
|
|
||||||
|
|
||||||
|
class Day10(AocSameParser[PipeDiagram], Aoc2[PipeDiagram, PipeDiagram]):
|
||||||
|
@staticmethod
|
||||||
|
def position_plus(a: Position, b: Position) -> Position:
|
||||||
|
return (a[0] + b[0], a[1] + b[1])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_start(inpt: list[str]) -> Position:
|
||||||
|
for y, line in enumerate(inpt):
|
||||||
|
try:
|
||||||
|
return line.index("S"), y
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
raise ValueError("S not found")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def connects(shape: str) -> list[Position]:
|
||||||
|
match shape:
|
||||||
|
case "|":
|
||||||
|
return [(0, 1), (0, -1)]
|
||||||
|
case "-":
|
||||||
|
return [(1, 0), (-1, 0)]
|
||||||
|
case "F":
|
||||||
|
return [(0, 1), (1, 0)]
|
||||||
|
case "L":
|
||||||
|
return [(0, -1), (1, 0)]
|
||||||
|
case "J":
|
||||||
|
return [(0, -1), (-1, 0)]
|
||||||
|
case "7":
|
||||||
|
return [(0, 1), (-1, 0)]
|
||||||
|
case _:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def shape_of_start(pos: Position, inpt: list[str]) -> str:
|
||||||
|
possibilities = [
|
||||||
|
inpt[npos[1]][npos[0]]
|
||||||
|
for npos in (
|
||||||
|
Day10.position_plus(pos, next)
|
||||||
|
for next in ((-1, 0), (1, 0), (0, 1), (0, -1))
|
||||||
|
)
|
||||||
|
] # Left, Right, Down, Up
|
||||||
|
|
||||||
|
possibles_shapes = {"|", "-", "F", "L", "J", "7"}
|
||||||
|
if possibilities[0] not in "FL-":
|
||||||
|
possibles_shapes = possibles_shapes.difference({"-", "7", "J"})
|
||||||
|
if possibilities[1] not in "J7-":
|
||||||
|
possibles_shapes = possibles_shapes.difference({"-", "F", "L"})
|
||||||
|
if possibilities[2] not in "J|L":
|
||||||
|
possibles_shapes = possibles_shapes.difference({"|", "F", "7"})
|
||||||
|
if possibilities[3] not in "7|F":
|
||||||
|
possibles_shapes = possibles_shapes.difference({"|", "J", "L"})
|
||||||
|
|
||||||
|
if len(possibles_shapes) != 1:
|
||||||
|
raise RuntimeError("Malformed Pipes")
|
||||||
|
|
||||||
|
return list(possibles_shapes)[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reach(
|
||||||
|
start_pos: Position, shape_of_start: str, inpt: list[str]
|
||||||
|
) -> list[Position]:
|
||||||
|
reach = [start_pos]
|
||||||
|
|
||||||
|
last_pos = start_pos
|
||||||
|
current_pos = Day10.position_plus(start_pos, Day10.connects(shape_of_start)[0])
|
||||||
|
while current_pos != start_pos:
|
||||||
|
reach.append(current_pos)
|
||||||
|
shape = inpt[current_pos[1]][current_pos[0]]
|
||||||
|
next_pos = list(
|
||||||
|
filter(
|
||||||
|
lambda p: p != last_pos,
|
||||||
|
(
|
||||||
|
Day10.position_plus(current_pos, p)
|
||||||
|
for p in Day10.connects(shape)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
last_pos = current_pos
|
||||||
|
current_pos = next_pos[0]
|
||||||
|
|
||||||
|
return reach
|
||||||
|
|
||||||
|
def parseinput(self, inpt: str) -> PipeDiagram:
|
||||||
|
lines = inpt.splitlines()
|
||||||
|
start_pos = Day10.find_start(lines)
|
||||||
|
start_shape = Day10.shape_of_start(start_pos, lines)
|
||||||
|
positions = Day10.reach(start_pos, start_shape, inpt.splitlines())
|
||||||
|
return PipeDiagram(lines, start_pos, start_shape, positions)
|
||||||
|
|
||||||
|
def part1(self, inpt: PipeDiagram) -> int:
|
||||||
|
return len(inpt.reachable_nodes) // 2
|
||||||
|
|
||||||
|
def part2(self, inpt: PipeDiagram) -> int:
|
||||||
|
r = set(inpt.reachable_nodes)
|
||||||
|
inside = 0
|
||||||
|
for y, line in enumerate(inpt.raw_input):
|
||||||
|
last_char = None
|
||||||
|
accum = 0
|
||||||
|
for x, char in enumerate(line):
|
||||||
|
if (x, y) not in r:
|
||||||
|
if accum % 2 != 0:
|
||||||
|
inside += 1
|
||||||
|
continue
|
||||||
|
if char == "|":
|
||||||
|
accum += 1
|
||||||
|
elif char == "S":
|
||||||
|
char = inpt.start_shape
|
||||||
|
|
||||||
|
if char not in "FJ7L":
|
||||||
|
continue
|
||||||
|
if last_char is None:
|
||||||
|
last_char = char
|
||||||
|
else:
|
||||||
|
match last_char, char:
|
||||||
|
case "F", "J":
|
||||||
|
accum += 1
|
||||||
|
case "L", "7":
|
||||||
|
accum += 1
|
||||||
|
last_char = char
|
||||||
|
return inside
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
day10 = Day10(2023, 10, example_code_nr1=6, example_code_nr2=-10)
|
||||||
|
day10.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Add table
Reference in a new issue