Work / Chess / Wiki / Entities
saved-game
Entitydraftverified 2026-10-12
STORAGE.SAVED-GAMESummary
The one unfinished game the phone keeps, and the shape it is stored in. Draft: the app is built to this; the owner's schema review (T004) is still open.
Canonical values
| Key | Value | Last changed |
|---|---|---|
| STORE | react-native-mmkv, the same store tic-tac-toe, charades and imposter use. localStorage on web |
2026-10-12 |
| KEY | chess.game.v1 — one JSON string. Absent when there is no unfinished game |
2026-10-12 |
| COUNT | one. Starting a new game replaces it | 2026-10-12 |
| WRITTEN | after every move, on Undo, on Flip, and when the pause sheet opens (that is when the clocks are stamped) | 2026-10-12 |
| REMOVED | the moment the game ends, by any of the six results. A finished game is never stored | 2026-10-12 |
| SOURCE-OF-TRUTH | the move list. The board, captures, castling rights, en passant square, fifty-move count, repetition count and the notation are all replayed from it and never stored | 2026-10-12 |
| UNREADABLE | wrong version, bad JSON, or a move the engine rejects on replay: the key is removed and Home shows its empty state. Nothing crashes |
2026-10-12 |
| UNDO | every move can be taken back, to the first. Against the computer, Undo takes back your move and its reply. Was: the last 10 positions | 2026-10-12 |
Shape
interface SavedGame {
version: 1;
startedAt: number; // epoch ms
updatedAt: number; // epoch ms
setup: GameSetup;
moves: MoveRecord[]; // in order, White's first
clock: ClockState | null; // null when setup.clock is "none"
flipped: boolean; // the Flip button under the board
}
interface GameSetup {
opponent: "computer" | "friend";
level: "easy" | "medium" | "hard" | null; // null for a friend game
humanSide: "white" | "black" | null; // resolved: Random is rolled once, at Start. null for a friend game
seating: "face-to-face" | "pass" | null; // null against the computer
clock: "none" | "5" | "10" | "15+10";
}
interface MoveRecord {
from: string; // "e2"
to: string; // "e4"
promotion?: "queen" | "rook" | "bishop" | "knight";
}
interface ClockState {
whiteMs: number; // time left when last written
blackMs: number;
}
Rules
- The move list is the game. Storing the board beside it gives two copies that can disagree; replaying 80 moves costs under a millisecond.
- A saved clock is a stopped clock. Time does not run while the app is closed. Resume starts the mover's clock from the stored value.
- Random is never stored. The roll happens once, when Start game is tapped, so Resume cannot hand the player the other colour.
- Castling and en passant squares are not stored.
fromandtoare enough: the engine works out that e1→g1 by a king is castling. - A version bump is a new key.
chess.game.v2would readv1once, convert or discard it, and remove it. - Nothing leaves the phone. There is no server to send it to.
Not stored
- The result. A result screen is in memory; killing the app on it opens Home empty.
- The selected square, the open sheet, the "computer is thinking" state.
- How many rematches since the last full-screen ad. That count lives in memory and resets when the app restarts.
Related
- preferences — the other stored key
- home — the Resume card reads this
- pause — "The game is saved"
- rules — what replay enforces
- privacy-policy — has to say this is stored (T009)
Sources
- .context/features/0005-resume-game.md
- .context/designs/mobile/home/home.html, game/setup-computer.html, game/setup-friend.html