feat: lock production decisions and resource budgets
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# Production decisions
|
||||
|
||||
This document is the binding implementation contract for the MVP. It resolves
|
||||
the choices left open in `MVP.md`; future code must not widen these limits
|
||||
without updating this document and `RESOURCE_BUDGET.md`.
|
||||
|
||||
## Platform and ownership
|
||||
|
||||
- Target: ESP32-C6FH4 (4 MB flash, no PSRAM assumed), custom 2 MiB app and
|
||||
1,984 KiB LittleFS partitions.
|
||||
- Stack: PlatformIO Core 6.1.19, `espressif32` 7.0.1, ESP-IDF 6.0.1,
|
||||
built-in `esp_http_server` WebSocket support, and `esp_littlefs` 1.20.4.
|
||||
- The ESP32 is authoritative. HTTP and WebSocket callbacks only validate and
|
||||
enqueue commands; one application task owns game, session, and statistics
|
||||
mutation.
|
||||
- Internal coordinates are unsigned `x` and `y` in `[0, 9]`. The browser
|
||||
renders Russian column labels; it never sends them.
|
||||
|
||||
## Fixed game rules
|
||||
|
||||
- Board size is 10 by 10. Each side has ships of lengths `4, 3, 3, 2, 2, 2,
|
||||
1, 1, 1, 1`, placed randomly by the server without touching, including
|
||||
diagonally.
|
||||
- A hit retains the turn. A miss changes it. Sinking a ship marks its
|
||||
surrounding cells as misses. A repeated shot is rejected without state
|
||||
change. First turn and each fleet are independently random.
|
||||
- Modes are `HUMAN_VS_HUMAN` and `HUMAN_VS_BOT`. The bot is `ESP32`; it uses
|
||||
only previously visible shot results and acts after a bounded 500–900 ms
|
||||
server timer.
|
||||
- Phases are `LOBBY`, `PREPARING`, `IN_PROGRESS`, `FINISHED`, and
|
||||
`REMATCH_WAIT`. Every accepted state change increments `version`.
|
||||
|
||||
## Sessions and capacity
|
||||
|
||||
- Roles are `PLAYER_1`, `PLAYER_2`, and `SPECTATOR`; capacity is two players
|
||||
and eight spectators. New clients become spectators when player slots are
|
||||
occupied, subject to the spectator limit.
|
||||
- Display names are 1–20 Unicode scalar values after removing controls and
|
||||
markup. The server stores a bounded UTF-8 encoding of at most 80 bytes and
|
||||
escapes it before HTML rendering.
|
||||
- A session token is 16 cryptographically random bytes, transported as 32
|
||||
lowercase hexadecimal characters. It is opaque, never logged, and remains
|
||||
valid only until board reboot or explicit slot release.
|
||||
- Active player sessions survive disconnects. Spectator disconnects free their
|
||||
slot immediately. A player may resume only with the same token; a resumed
|
||||
session receives a complete role-safe snapshot.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
1. `join` assigns player 1 if vacant and requested, then player 2 only in the
|
||||
two-player mode; otherwise it assigns spectator. `resume` never changes a
|
||||
role.
|
||||
2. Player 1 may configure a mode only in `LOBBY`. Switching to bot reserves
|
||||
player 2 as `ESP32`; switching back requires no human player 2 conflict.
|
||||
3. Player 1 may start only when player 2 is present in human mode, or bot mode
|
||||
is selected. `PREPARING` is internal and advances atomically to
|
||||
`IN_PROGRESS` after both fleets validate.
|
||||
4. A player disconnect does not abort a game. The game waits for that player;
|
||||
the bot continues only when it is the bot's turn. Player 1 may issue the
|
||||
explicit `abort` command while a human opponent is disconnected, returning
|
||||
to `LOBBY` and incrementing `version`.
|
||||
5. Destroying all ten opponent ships transitions to `FINISHED`, records match
|
||||
and cumulative statistics, reveals both boards, and rejects shots.
|
||||
6. In `FINISHED`, a player may confirm `rematch`. Human mode requires both
|
||||
player confirmations; bot mode requires player 1 only. The server enters
|
||||
`REMATCH_WAIT` until the required confirmations exist, then creates a new
|
||||
game ID and returns to `PREPARING`. Names, roles, and cumulative statistics
|
||||
remain; per-match statistics reset.
|
||||
7. Board reboot clears sessions, game, and all statistics. Wi-Fi loss does not
|
||||
mutate them; clients use HTTP polling while WebSocket reconnects.
|
||||
|
||||
## Visibility and transport
|
||||
|
||||
- Player 1 receives its complete board and only known opponent shot results;
|
||||
player 2 is symmetric. Spectators see known shot results from both boards.
|
||||
Unhit ships are replaced by `0` in every unauthorized view.
|
||||
- `FINISHED` is the sole phase that exposes full boards to every role.
|
||||
- HTTP commands include the token in the JSON body. WebSocket authentication
|
||||
is the first `hello` message; the token is never placed in a URL or log.
|
||||
- WebSocket reconnect delay is 1, 2, 5, then 10 seconds. HTTP polls a complete
|
||||
snapshot every two seconds only while WebSocket is unavailable. Any skipped
|
||||
version triggers a full snapshot request.
|
||||
Reference in New Issue
Block a user