File size: 3,587 Bytes
4be6a52 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # Initial implementation contract
The Python engine is authoritative. These names are shared across independently implemented engine, service, and UI. All board coordinates have x increasing right and y increasing down; rows are top to bottom. Board cells are 0 for empty or 1 through 7 for piece colors. Board width is 10, height is 20. Four normalized orientations are enumerated and duplicate shapes removed.
`schema.py` defines frozen dataclasses: `Placement(id: str, rotation: int, x: int, y: int, cells: tuple[tuple[int,int], ...])`; `GameState(board: tuple[tuple[int,...],...], seed: int, piece_index: int, current: str, next_piece: str, score: int=0, lines: int=0, terminal: bool=False)`; `Transition(state: GameState, action: Placement, cleared: int)`.
`pieces.py`: `piece_at(seed: int, index: int) -> str`, `rotations(piece: str) -> tuple[tuple[tuple[int,int],...],...]`. Piece colors map in order `I,O,T,S,Z,J,L` to 1..7. Deterministic sequence is versioned, uses a local random generator, and is identical regardless of player pace.
`engine.py`: `new_game(seed: int) -> GameState`, `legal_actions(state: GameState) -> tuple[Placement,...]`, `step(state: GameState, action_id: str) -> Transition`. Action IDs are `r{rotation}x{x}`. Placements are straight vertical hard drops starting fully within the board. No legal placement means terminal. An illegal or terminal-state action raises `ValueError` without mutating state. `legal_actions` includes landing y and absolute cell positions so clients can display a ghost without computing game rules. Clear rows simultaneously, score 100/300/500/800 for 1/2/3/4 lines, advance piece index, and detect next-piece top-out.
`replay.py`: `make_replay(seed: int, actions: list[str]) -> dict` computes a JSON-serializable artifact containing `schema_version: 1`, `rules_version: "stackcraft-v1"`, seed, action IDs, and final score/lines/pieces/terminal. `replay_states(artifact: dict) -> list[GameState]` validates versions and actions and returns initial plus successive states; validate final summary if provided.
Service HTTP endpoints (all JSON unless noted): `GET /` serves the game; `POST /api/games` with `{seed: int}` returns a session snapshot; `POST /api/games/{id}/moves` with `{action_id: str, expected_pieces: int}` returns its next snapshot; `GET /api/games/{id}` snapshot; `GET /api/games/{id}/replay` returns replay artifact; `POST /api/replays` with an artifact returns `{frames: [snapshot_without_id,...]}`. `GET /health` returns `{status:"ok"}`. Snapshots contain `id`, `board`, `current`, `next_piece`, `score`, `lines`, `pieces` (piece index), `terminal`, `legal_actions` (serialized Placement). Error responses have readable `detail`; unknown session 404, invalid action/replay 422, stale expected_pieces 409. On uncertain move response the client refreshes state before allowing another move; replay retries must not advance a second piece. API never reveals future piece stream to model adapters (seed exists only in server storage/replay).
UI uses these routes directly with fetch; no second simulator. Arrow left/right selects legal placements at current rotation, up rotates selection, space/Enter drops. Buttons allow all actions. Restart requests the seed input again. Download replay and load replay use the above endpoints, with frame stepping. UI source belongs in `src/stackcraft/web/` and uses vanilla JS/CSS/HTML to avoid a second package manager. Label the game as turn-based placement; no hold, tucks, kicks, T-spin bonuses or gravity timer. Comparison boards will be added after baselines.
|