73 lines
2.2 KiB
Python
73 lines
2.2 KiB
Python
from __future__ import annotations
|
|
from dataclasses import dataclass
|
|
from typing import NamedTuple, NewType, Self
|
|
import re
|
|
import logging
|
|
|
|
|
|
def im_replace(line: str, mask: str) -> str:
|
|
idx = 0
|
|
new_line = ""
|
|
for c in line:
|
|
if c != "?":
|
|
new_line += c
|
|
else:
|
|
new_line += mask[idx]
|
|
idx += 1
|
|
return new_line
|
|
|
|
|
|
class SpringLine(NamedTuple):
|
|
line: str
|
|
groups: tuple[int, ...]
|
|
|
|
def arrangments(self) -> list[str]:
|
|
count_im = self.line.count("?")
|
|
logging.debug(count_im)
|
|
possibles = []
|
|
for i in range(pow(2, count_im)):
|
|
debug_str = ""
|
|
rep_str = f"{i:b}".rjust(count_im, "0").replace("0", ".").replace("1", "#")
|
|
debug_str += rep_str + " / "
|
|
tentative = im_replace(self.line, rep_str)
|
|
possible = SpringLine(tentative, self.groups)
|
|
debug_str += str(possible) + ": "
|
|
if possible.is_valid():
|
|
possibles.append(im_replace(self.line, rep_str))
|
|
debug_str += "valid"
|
|
else:
|
|
debug_str += "invalid"
|
|
logging.debug(debug_str)
|
|
return possibles
|
|
|
|
def is_valid(self) -> bool:
|
|
pattern = "^\.*" + "\.+".join(["#" * i for i in self.groups]) + "\.*$"
|
|
# logging.debug(pattern)
|
|
return re.match(pattern, self.line)
|
|
|
|
def unfold(self) -> SpringLine:
|
|
new_line = []
|
|
new_groups = []
|
|
for i in range(5):
|
|
new_line.append(self.line)
|
|
new_groups += self.groups
|
|
return SpringLine("?".join(new_line), new_groups)
|
|
|
|
|
|
Spring = NewType("Spring", list[SpringLine])
|
|
|
|
|
|
def parse(input_file: str) -> Spring:
|
|
spring_lines = []
|
|
with open(input_file, "r", encoding="utf-8") as inputfd:
|
|
while line := inputfd.readline():
|
|
if match := re.match("([?#.]+)\s([0-9,]+)", line):
|
|
# logging.debug(match.group(0))
|
|
spring_lines.append(
|
|
SpringLine(
|
|
match.group(1),
|
|
tuple(map(lambda c: int(c), match.group(2).split(","))),
|
|
)
|
|
)
|
|
return Spring(spring_lines)
|