day 12
This commit is contained in:
parent
ef470926c0
commit
d606451b8c
2 changed files with 35 additions and 119 deletions
12
aoc/aoc.py
12
aoc/aoc.py
|
@ -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}")
|
||||||
|
|
142
aoc/day12.py
142
aoc/day12.py
|
@ -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__":
|
||||||
|
|
Loading…
Add table
Reference in a new issue