aoc2023/aoc/day12.py
2023-12-13 21:33:06 +01:00

152 lines
5.1 KiB
Python

from __future__ import annotations
from typing import TypeAlias
from dataclasses import dataclass
from itertools import groupby
from collections import deque
from .aoc import Aoc2, AocParseLines
@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)
@classmethod
def from_str2(cls, inpt: str) -> Line:
springs, groups_str = inpt.split(" ")
springs = "?".join([springs] * 5)
groups = [int(d) for d in groups_str.split(",")]
groups = groups + groups + groups + groups + groups
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_str2(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 ("." not in segment)
def part1(self, inpt: P1) -> int:
accum = 0
# inpt = inpt[:1]
# print(inpt)
for line in inpt:
belegungen = [[0]]
for i, group in enumerate(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)=}")
# try:
# first_hash = line.springs[last_end:].index("#") + 1
# # print("First hash", first_hash)
# except ValueError:
# first_hash = len(line.springs) - 1
for startvalue in range(last_end + 1, len(line.springs)):
# print(startvalue)
if "#" in line.springs[last_end:startvalue]:
continue
fits = self.fits(line.springs, startvalue, group)
if i == len(line.groups) - 1:
if "#" in line.springs[startvalue + group :]:
continue
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(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]]}"
# )
for belegung in belegungen:
last_belegung = belegung[-1]
if "#" in line.springs[last_belegung:]:
print(line.springs, line.groups)
if len(set((tuple(x) for x in belegungen))) != len(belegungen):
print("X")
# print(line.springs, line.groups)
for belegung in belegungen:
self.check_belegung(line, belegung)
accum += len(belegungen)
# print(len(belegungen))
# print(belegungen)
# print(belegungen)
print(accum)
return accum
def check_belegung(self, line, belegung):
if "#" in [line.springs[x] for x in belegung]:
print(f"Wrong # 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=}")
last_end = 0
bstr = "."
for ends, lengths in zip(belegung[1:], line.groups):
beginning = ends - lengths
bstr += "." * (beginning - len(bstr))
bstr += "#" * lengths
if (line.springs[beginning - 1] == "#") or line.springs[ends] == "#":
print("X")
if beginning - last_end < 1:
print("XXX")
last_end = ends
# print(bstr)
def part2(self, inpt: P2) -> int:
for line in inpt:
print(line)
return self.part1(inpt)
def main() -> None:
aoc = Day12(2023, 12, example_code_nr1=1, example_code_nr2=1)
aoc.run_example_2()
if __name__ == "__main__":
main()