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