File size: 1,839 Bytes
4be6a52 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | """Immutable types shared by the engine, players, replay, and HTTP service."""
from dataclasses import dataclass
WIDTH = 10
HEIGHT = 20
RULES_VERSION = "stackcraft-v1"
PIECES = ("I", "O", "T", "S", "Z", "J", "L")
COLORS = {piece: index + 1 for index, piece in enumerate(PIECES)}
type Cells = tuple[tuple[int, int], ...]
type Board = tuple[tuple[int, ...], ...]
@dataclass(frozen=True)
class Placement:
id: str
rotation: int
x: int
y: int
cells: Cells
@dataclass(frozen=True)
class GameState:
board: Board
seed: int
piece_index: int
current: str
next_piece: str
score: int = 0
lines: int = 0
terminal: bool = False
def __post_init__(self) -> None:
if not isinstance(self.board, tuple) or len(self.board) != HEIGHT:
raise ValueError(f"board must contain {HEIGHT} immutable rows")
if any(
not isinstance(row, tuple)
or len(row) != WIDTH
or any(type(cell) is not int or not 0 <= cell <= 7 for cell in row)
for row in self.board
):
raise ValueError(f"board rows must contain {WIDTH} integer cells from 0 to 7")
if type(self.seed) is not int:
raise ValueError("seed must be an integer")
for name in ("piece_index", "score", "lines"):
value = getattr(self, name)
if type(value) is not int or value < 0:
raise ValueError(f"{name} must be a nonnegative integer")
if self.current not in PIECES or self.next_piece not in PIECES:
raise ValueError("current and next_piece must be tetromino names")
if type(self.terminal) is not bool:
raise ValueError("terminal must be a boolean")
@dataclass(frozen=True)
class Transition:
state: GameState
action: Placement
cleared: int
|