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)