from dataclasses import dataclass from .helpers import rotate_r @dataclass(kw_only=True, slots=True) class Board: rows: list[str] buttons: tuple[int, ...] @classmethod @property def default_puzzle(cls) -> "Board": return cls( rows=["011", "011", "100", "x1x"], buttons=(3, 4, 5, 7), ) @property def height(self) -> int: return len(self.rows) @property def width(self) -> int: if not self.rows: return 0 else: return len(self.rows[0]) def __len__(self) -> int: return self.height * self.width def __post_init__(self) -> None: # check char set chars = {c for c in "".join(self.rows)} assert chars.issubset({"0", "1", "x"}), "Invalid char set!" # check defined width assert all( len(row) == self.width for row in self.rows ), "Inconsistent width!" # check buttons assert all( button in range(len(self)) for button in self.buttons ), "Invalid buttons!" def __str__(self) -> str: result = "\n".join(self.rows) result = result\ .replace("0", "⚪️")\ .replace("1", "🟢️")\ .replace("x", "⚫️") return result @property def columns(self) -> tuple[str, ...]: return tuple( "".join(row[x] for row in self.rows) for x in range(self.width) ) @property def binary_repr(self) -> tuple[int, ...]: return tuple( int(column.replace("x", "0")[::-1], base=2) for column in self.columns ) @property def solution_class(self) -> tuple[int, ...]: return tuple( column.count("1") for column in self.columns ) def __getitem__(self, key: tuple[int, int]) -> str: row, col = key if row not in range(self.height) or col not in range(self.width): return "x" return self.rows[row][col] def __setitem__(self, key: tuple[int, int], value: str): row, col = key if row not in range(self.height) or col not in range(self.width): return old_row = self.rows[row] self.rows[row] = old_row[:col] + value + old_row[col+1:] def click(self, index: int) -> None: # check is clickable row, col = index // self.width, index % self.width assert self.rows[row][col] == "1", f"{index} is not clickable!" nb_rc = [ (row - 1, col), (row, col + 1), (row + 1, col), (row, col - 1), ] nb_str = "".join(self[*rc] for rc in nb_rc) for rc, val in zip(nb_rc, rotate_r(nb_str)): self[rc] = val