This commit is contained in:
Christoph Stahl 2023-12-14 01:55:54 +01:00
parent ef470926c0
commit d606451b8c
2 changed files with 35 additions and 119 deletions

View file

@ -108,6 +108,8 @@ class Aoc2(ABC, Generic[T1, T2]):
return str(self.part2(self.parseinput2(inpt))) return str(self.part2(self.parseinput2(inpt)))
def run(self) -> None: def run(self) -> None:
print(f"Running Day {self._day:02d}")
s_time = default_timer()
print("Example:\t", end="") print("Example:\t", end="")
print(f"{self.run_example_1()}\t", end="") print(f"{self.run_example_1()}\t", end="")
print(f"{self.run_example_2()}") print(f"{self.run_example_2()}")
@ -115,6 +117,8 @@ class Aoc2(ABC, Generic[T1, T2]):
print("Real input:\t", end="") print("Real input:\t", end="")
print(f"{self.run_1()}\t", end="") print(f"{self.run_1()}\t", end="")
print(f"{self.run_2()}") print(f"{self.run_2()}")
e_time = default_timer()
print(f"Time: {e_time-s_time}")
class Aoc(Generic[T1], Aoc2[T1, T1]): class Aoc(Generic[T1], Aoc2[T1, T1]):
@ -188,11 +192,7 @@ def run_latest() -> None:
if file.name.endswith(".py") and file.name.startswith("day") if file.name.endswith(".py") and file.name.startswith("day")
] ]
day = days[-1] day = days[-1]
print(f"Running {day.__name__}")
s_time = default_timer()
day.main() day.main()
e_time = default_timer()
print(f"Time: {e_time-s_time}")
def new() -> None: def new() -> None:
@ -251,11 +251,7 @@ def main() -> None:
] ]
b_time = default_timer() b_time = default_timer()
for day in days: for day in days:
print(f"Running {day.__name__}")
s_time = default_timer()
day.main() day.main()
e_time = default_timer()
print(f"Time: {e_time-s_time}")
print() print()
f_time = default_timer() f_time = default_timer()
print(f"Overall time: {f_time - b_time}") print(f"Overall time: {f_time - b_time}")

View file

@ -1,151 +1,71 @@
from __future__ import annotations from __future__ import annotations
from typing import TypeAlias
from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
from itertools import groupby from typing import TypeAlias
from collections import deque
from .aoc import Aoc2, AocParseLines from .aoc import Aoc2, AocParseLines, AocSameImplementation
@dataclass @dataclass(frozen=True)
class Line: class Line:
springs: str springs: str
spring_groups: deque[tuple[int, str]] groups: tuple[int, ...]
groups: list[int]
@classmethod @classmethod
def from_str(cls, inpt: str) -> Line: def from_str(cls, inpt: str, multi: int) -> Line:
springs, groups_str = inpt.split(" ") springs, groups_str = inpt.split(" ")
groups = [int(d) for d in groups_str.split(",")] springs = "?".join([springs] * multi)
groups = tuple(int(d) for d in groups_str.split(",")) * multi
springs_group = deque((len(list(d)), i) for i, d in groupby(springs)) return Line("." + springs + ".", groups)
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] P1: TypeAlias = list[Line]
P2: TypeAlias = list[Line] P2: TypeAlias = list[Line]
class Day12(AocParseLines[Line, Line], Aoc2[P1, P2]): class Day12(AocSameImplementation[P1], AocParseLines[Line, Line], Aoc2[P1, P2]):
def parseline1(self, inpt: str) -> Line: def parseline1(self, inpt: str) -> Line:
return Line.from_str(inpt) return Line.from_str(inpt, 1)
def parseline2(self, inpt: str) -> Line: def parseline2(self, inpt: str) -> Line:
return Line.from_str2(inpt) return Line.from_str(inpt, 5)
@staticmethod @staticmethod
def fits(line: str, startpos: int, length: int) -> bool: def fits(line: str, startpos: int, length: int) -> bool:
if startpos + length >= len(line):
return False
starter = line[startpos - 1] starter = line[startpos - 1]
segment = line[startpos : startpos + length] segment = line[startpos : startpos + length]
stopper = line[startpos + length] stopper = line[startpos + length]
return starter != "#" and stopper != "#" and ("." not in segment) return "#" not in (starter, stopper) and "." not in segment
def part1(self, inpt: P1) -> int: def part(self, inpt: P1) -> int:
accum = 0 accum = 0
# inpt = inpt[:1]
# print(inpt)
for line in inpt: for line in inpt:
belegungen = [[0]] ends = {0: 1}
for i, group in enumerate(line.groups): for i, group in enumerate(line.groups):
# print(f"{belegungen=}") next_ends: dict[int, int] = defaultdict(lambda: 0)
next_belegungen = [] for end, amount in ends.items():
for belegung in belegungen: for startvalue in range(end + 1, len(line.springs) - group):
last_end = belegung[-1] if not (
# print(f"{last_start=}, {last_end=}, {group=}, {len(line.springs)=}") "#" in line.springs[end:startvalue]
# try: or (
# first_hash = line.springs[last_end:].index("#") + 1 i == len(line.groups) - 1
# # print("First hash", first_hash) and "#" in line.springs[startvalue + group :]
# except ValueError: )
# first_hash = len(line.springs) - 1 or not Day12.fits(line.springs, startvalue, group)
for startvalue in range(last_end + 1, len(line.springs)): ):
# print(startvalue) next_ends[startvalue + group] += amount
if "#" in line.springs[last_end:startvalue]:
continue
fits = self.fits(line.springs, startvalue, group)
if i == len(line.groups) - 1: ends = next_ends
if "#" in line.springs[startvalue + group :]: accum += sum(ends.values())
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 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: def main() -> None:
aoc = Day12(2023, 12, example_code_nr1=1, example_code_nr2=1) aoc = Day12(2023, 12, example_code_nr1=1, example_code_nr2=1)
aoc.run_example_2() aoc.run()
if __name__ == "__main__": if __name__ == "__main__":