72 lines
2.2 KiB
Python
72 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
from dataclasses import dataclass
|
|
from typing import TypeAlias
|
|
|
|
from .aoc import Aoc2, AocParseLines, AocSameImplementation
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Line:
|
|
springs: str
|
|
groups: tuple[int, ...]
|
|
|
|
@classmethod
|
|
def from_str(cls, inpt: str, multi: int) -> Line:
|
|
springs, groups_str = inpt.split(" ")
|
|
springs = "?".join([springs] * multi)
|
|
groups = tuple(int(d) for d in groups_str.split(",")) * multi
|
|
|
|
return Line("." + springs + ".", groups)
|
|
|
|
|
|
P1: TypeAlias = list[Line]
|
|
P2: TypeAlias = list[Line]
|
|
|
|
|
|
class Day12(AocSameImplementation[P1], AocParseLines[Line, Line], Aoc2[P1, P2]):
|
|
def parseline1(self, inpt: str) -> Line:
|
|
return Line.from_str(inpt, 1)
|
|
|
|
def parseline2(self, inpt: str) -> Line:
|
|
return Line.from_str(inpt, 5)
|
|
|
|
@staticmethod
|
|
def fits(line: str, startpos: int, length: int) -> bool:
|
|
starter = line[startpos - 1]
|
|
segment = line[startpos : startpos + length]
|
|
stopper = line[startpos + length]
|
|
return "#" not in (starter, stopper) and "." not in segment
|
|
|
|
def part(self, inpt: P1) -> int:
|
|
accum = 0
|
|
for line in inpt:
|
|
ends = {0: 1}
|
|
for i, group in enumerate(line.groups):
|
|
next_ends: dict[int, int] = defaultdict(lambda: 0)
|
|
for end, amount in ends.items():
|
|
for startvalue in range(end + 1, len(line.springs) - group):
|
|
if not (
|
|
"#" in line.springs[end:startvalue]
|
|
or (
|
|
i == len(line.groups) - 1
|
|
and "#" in line.springs[startvalue + group :]
|
|
)
|
|
or not Day12.fits(line.springs, startvalue, group)
|
|
):
|
|
next_ends[startvalue + group] += amount
|
|
|
|
ends = next_ends
|
|
accum += sum(ends.values())
|
|
|
|
return accum
|
|
|
|
|
|
def main() -> None:
|
|
aoc = Day12(2023, 12, example_code_nr1=1, example_code_nr2=1)
|
|
aoc.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|