from __future__ import annotations from typing import TypeAlias from dataclasses import dataclass from itertools import groupby, takewhile, compress from collections import deque from .aoc import Aoc2, AocParseLines """ ?###.??????? 3,2,1 1 2 3 ? 0 0 0 # 0 0 0 # 1 0 0 # 2 0 0 . 2 0 0 ? 2 0 0 ? 2 3 0 ? 2 4 0 ? 2 5 2 ? 2 6 3 """ @dataclass class Line: springs: str spring_groups: deque[tuple[int, str]] groups: list[int] @classmethod def from_str(cls, inpt: str) -> Line: springs, groups_str = inpt.split(" ") groups = [int(d) for d in groups_str.split(",")] springs_group = deque((len(list(d)), i) for i, d in groupby(springs)) return Line("." + springs + ".", springs_group, groups) P1: TypeAlias = list[Line] P2: TypeAlias = list[Line] class Day12(AocParseLines[Line, Line], Aoc2[P1, P2]): def parseline1(self, inpt: str) -> Line: return Line.from_str(inpt) def parseline2(self, inpt: str) -> Line: return Line.from_str(inpt) @staticmethod def fits(line: str, startpos: int, length: int) -> bool: if startpos + length >= len(line): return False starter = line[startpos - 1] segment = line[startpos : startpos + length] stopper = line[startpos + length] return ( starter != "#" and stopper != "#" and (len(segment) >= length) and ("." not in segment) ) def part1(self, inpt: P1) -> int: accum = 0 for line in inpt: belegungen = [[0]] for group in line.groups: # print(f"{belegungen=}") next_belegungen = [] for belegung in belegungen: last_end = belegung[-1] # print(f"{last_start=}, {last_end=}, {group=}, {len(line.springs)=}") for startvalue in range(last_end + 1, len(line.springs)): fits = self.fits(line.springs, startvalue, group) if fits: # print(f"Y {i=}, {startvalue=}, {group=}") next_belegungen.append(belegung + [startvalue + group]) # else: # print(f"N {i=}, {startvalue=}, {group=}") belegungen = next_belegungen # print(line.springs[1:-1], line.groups, len(belegungen)) # if len(belegungen) == 1: # print( # f"{line.springs=}, {line.groups=}, {[line.springs[x] for x in belegungen[0]]}" # ) if len(set((tuple(x) for x in belegungen))) != len(belegungen): print("X") for belegung in belegungen: self.check_belegung(line, belegung) accum += len(belegungen) # print(belegungen) # print(belegungen) return accum def check_belegung(self, line, belegung): if "#" in [line.springs[x] for x in belegung]: print(f"Wron # in {line=}") if len(belegung) != 1 + len(line.groups): print(f"Groups Wrong in {line=}") for ends, lengths in zip(belegung[1:], line.groups): b = line.springs[ends - lengths : ends] if "." in b: print("{line=}") def part2(self, inpt: P2) -> int: return 0 def main() -> None: aoc = Day12(2023, 12, example_code_nr1=1, example_code_nr2=1) aoc.run() if __name__ == "__main__": main()