4.3 KiB
4.3 KiB
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,
espressif327.0.1, ESP-IDF 6.0.1, built-inesp_http_serverWebSocket support, andesp_littlefs1.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
xandyin[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_HUMANandHUMAN_VS_BOT. The bot isESP32; it uses only previously visible shot results and acts after a bounded 500–900 ms server timer. - Phases are
LOBBY,PREPARING,IN_PROGRESS,FINISHED, andREMATCH_WAIT. Every accepted state change incrementsversion.
Sessions and capacity
- Roles are
PLAYER_1,PLAYER_2, andSPECTATOR; 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
joinassigns player 1 if vacant and requested, then player 2 only in the two-player mode; otherwise it assigns spectator.resumenever changes a role.- Player 1 may configure a mode only in
LOBBY. Switching to bot reserves player 2 asESP32; switching back requires no human player 2 conflict. - Player 1 may start only when player 2 is present in human mode, or bot mode
is selected.
PREPARINGis internal and advances atomically toIN_PROGRESSafter both fleets validate. - 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
abortcommand while a human opponent is disconnected, returning toLOBBYand incrementingversion. - Destroying all ten opponent ships transitions to
FINISHED, records match and cumulative statistics, reveals both boards, and rejects shots. - In
FINISHED, a player may confirmrematch. Human mode requires both player confirmations; bot mode requires player 1 only. The server entersREMATCH_WAITuntil the required confirmations exist, then creates a new game ID and returns toPREPARING. Names, roles, and cumulative statistics remain; per-match statistics reset. - 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
0in every unauthorized view. FINISHEDis the sole phase that exposes full boards to every role.- HTTP commands include the token in the JSON body. WebSocket authentication
is the first
hellomessage; 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.