docs: add initial project documentation
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
.pio/
|
||||||
|
.vscode/
|
||||||
|
.obsidian/
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# ChatGPT project context
|
||||||
|
|
||||||
|
This directory is a local mirror of the ChatGPT project “maze32”.
|
||||||
|
|
||||||
|
- Treat every file under `sources/` as read-only reference material.
|
||||||
|
- Do not edit, rename, move, or delete synced project files.
|
||||||
|
- These files may be replaced the next time a task is created from this ChatGPT project.
|
||||||
|
|
||||||
|
|
||||||
|
## Project instructions
|
||||||
|
|
||||||
|
### Language
|
||||||
|
|
||||||
|
- Write all canonical documentation in English.
|
||||||
|
- Write source comments, test names, protocol identifiers, log messages, and
|
||||||
|
user-facing diagnostic text in English.
|
||||||
|
|
||||||
|
### Canonical documents
|
||||||
|
|
||||||
|
- `MVP.md` defines product scope, gameplay rules, and MVP acceptance criteria.
|
||||||
|
- `ARCHITECTURE.md` defines implementation contracts and technical boundaries.
|
||||||
|
- `PLANS.md` defines the current implementation milestone and its evidence.
|
||||||
|
- `docs/BOARD_PASSPORT.md` defines confirmed hardware facts and explicit
|
||||||
|
unknowns.
|
||||||
|
- Files under `sources/` remain read-only references and are never canonical
|
||||||
|
implementation files.
|
||||||
|
|
||||||
|
When documents disagree, stop and resolve the conflict before implementation.
|
||||||
|
Do not silently choose one requirement over another.
|
||||||
|
|
||||||
|
### Milestone workflow
|
||||||
|
|
||||||
|
- Follow `PLANS.md` strictly.
|
||||||
|
- Milestones may be planned and added to `PLANS.md` in batches.
|
||||||
|
- Number milestones from 1 without gaps using `Milestone 001`, `Milestone 002`,
|
||||||
|
and so on.
|
||||||
|
- Execute milestones strictly in ascending numerical order.
|
||||||
|
- Do not start a milestone until every preceding milestone is `COMPLETE`.
|
||||||
|
- Keep at most one milestone `IN_PROGRESS`.
|
||||||
|
- Append newly discovered milestones to the end of `PLANS.md`; do not insert or
|
||||||
|
renumber existing milestones without explicit project-owner approval.
|
||||||
|
- Every milestone must include objective, scope, deliverables, acceptance
|
||||||
|
criteria, evidence, and status.
|
||||||
|
- Do not mark a milestone `COMPLETE` without concrete recorded evidence for
|
||||||
|
every acceptance criterion.
|
||||||
|
|
||||||
|
### Locked platform baseline
|
||||||
|
|
||||||
|
- Target ESP32-C6FH4 with 4 MB Flash.
|
||||||
|
- Do not require PSRAM.
|
||||||
|
- Use PlatformIO with `espressif32@7.0.1`, ESP-IDF, and the compatible
|
||||||
|
`esp32-c6-devkitm-1` board profile.
|
||||||
|
- Use `partitions.csv` and `sdkconfig.defaults` from the project root.
|
||||||
|
- Do not add an OTA partition for MVP.
|
||||||
|
- Do not assume a board GPIO map, built-in LED, or exact commercial PCB model.
|
||||||
|
|
||||||
|
### Architectural invariants
|
||||||
|
|
||||||
|
- The ESP is the sole authoritative owner of lobby and match state.
|
||||||
|
- Browsers send commands and render server-approved projections; they do not
|
||||||
|
decide movement, inventory, timers, visibility, or results.
|
||||||
|
- Never serialize hidden maze or object state to player clients.
|
||||||
|
- Observer sessions are read-only.
|
||||||
|
- Bound all connection counts, message sizes, queues, retries, and allocations.
|
||||||
|
- Active game state need not survive reboot; Wi-Fi configuration must persist
|
||||||
|
in NVS.
|
||||||
|
|
||||||
|
### Build and verification
|
||||||
|
|
||||||
|
- Parse the project configuration with
|
||||||
|
`/home/sasa/.platformio/penv/bin/pio project config` after changing
|
||||||
|
`platformio.ini`.
|
||||||
|
- Build with `/home/sasa/.platformio/penv/bin/pio run` once firmware sources
|
||||||
|
exist.
|
||||||
|
- Add host-side deterministic tests for portable game logic before relying only
|
||||||
|
on device tests.
|
||||||
|
- Verify the final application and filesystem sizes against `partitions.csv`.
|
||||||
|
- Test on the physical board before recording hardware acceptance evidence.
|
||||||
|
- Preserve exact commands and relevant outputs in the active milestone's
|
||||||
|
evidence section.
|
||||||
|
|
||||||
+399
@@ -0,0 +1,399 @@
|
|||||||
|
# maze32 Architecture
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
This document defines the implementation boundaries and technical contracts for
|
||||||
|
the maze32 MVP described in [MVP.md](MVP.md). It explains how the firmware,
|
||||||
|
browser application, network transport, game simulation, and persistent
|
||||||
|
configuration fit together.
|
||||||
|
|
||||||
|
Product behavior belongs in `MVP.md`. Implementation sequencing and milestone
|
||||||
|
evidence belong in `PLANS.md`.
|
||||||
|
|
||||||
|
## 2. Architectural drivers
|
||||||
|
|
||||||
|
The design is constrained by the following facts:
|
||||||
|
|
||||||
|
- One ESP32-C6FH4 hosts the entire application.
|
||||||
|
- The board has 4 MB Flash and must not require PSRAM.
|
||||||
|
- Two players and up to four observers connect through browsers.
|
||||||
|
- HTTP serves static assets and configuration endpoints.
|
||||||
|
- WebSocket carries live lobby, control, and match traffic.
|
||||||
|
- The ESP is the only authoritative source of game state.
|
||||||
|
- Player clients must not receive information hidden by fog of war.
|
||||||
|
- The same firmware supports normal Wi-Fi station operation and fallback
|
||||||
|
access-point provisioning.
|
||||||
|
- The MVP has no OTA partition and no requirement to recover an active match
|
||||||
|
after device reboot.
|
||||||
|
|
||||||
|
## 3. System context
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
P1["Player 1 browser"] -->|"HTTP + WebSocket"| ESP["ESP32-C6 maze32 server"]
|
||||||
|
P2["Player 2 browser"] -->|"HTTP + WebSocket"| ESP
|
||||||
|
O["Up to 4 observer browsers"] -->|"HTTP + WebSocket"| ESP
|
||||||
|
ESP --> NVS["NVS: Wi-Fi and device settings"]
|
||||||
|
ESP --> LFS["LittleFS: compressed web assets"]
|
||||||
|
ESP --> WIFI["Existing Wi-Fi network"]
|
||||||
|
ESP -. "fallback" .-> AP["Provisioning access point"]
|
||||||
|
```
|
||||||
|
|
||||||
|
The browser is a presentation and input client. It may animate confirmed state,
|
||||||
|
but it must not decide whether an action succeeded.
|
||||||
|
|
||||||
|
## 4. Firmware component boundaries
|
||||||
|
|
||||||
|
### 4.1 Platform layer
|
||||||
|
|
||||||
|
Owns ESP-IDF startup, logging, monotonic time, random seed acquisition, reboot
|
||||||
|
behavior, and error reporting. Code above this layer must not depend on an
|
||||||
|
unverified GPIO, LED, or PSRAM.
|
||||||
|
|
||||||
|
### 4.2 Persistent configuration
|
||||||
|
|
||||||
|
Owns NVS access for:
|
||||||
|
|
||||||
|
- Wi-Fi SSID and credentials;
|
||||||
|
- provisioning state;
|
||||||
|
- tunable device and gameplay settings that are intentionally persisted;
|
||||||
|
- schema version for safe future migration.
|
||||||
|
|
||||||
|
Session tokens, lobby membership, maze state, and active matches live in RAM
|
||||||
|
and are cleared on reboot.
|
||||||
|
|
||||||
|
### 4.3 Network manager
|
||||||
|
|
||||||
|
Implements this boot policy:
|
||||||
|
|
||||||
|
1. Load saved station credentials from NVS.
|
||||||
|
2. Attempt to connect in station mode.
|
||||||
|
3. If credentials are missing or the connection attempt fails, start the
|
||||||
|
fallback access point and provisioning interface.
|
||||||
|
4. Accept new network settings, save them to NVS, and retry station mode.
|
||||||
|
|
||||||
|
The exact connection timeout and access-point naming convention are tunable
|
||||||
|
implementation settings. Network reconfiguration must not silently continue an
|
||||||
|
active match.
|
||||||
|
|
||||||
|
### 4.4 HTTP and static asset service
|
||||||
|
|
||||||
|
Serves:
|
||||||
|
|
||||||
|
- the player/lobby application;
|
||||||
|
- the observer view;
|
||||||
|
- the fallback provisioning interface;
|
||||||
|
- minimal health and version information needed for diagnostics.
|
||||||
|
|
||||||
|
Production web assets are stored in LittleFS and should be precompressed during
|
||||||
|
the build. The asset compression script is not part of the current baseline and
|
||||||
|
must be introduced and tested in its own implementation milestone.
|
||||||
|
|
||||||
|
### 4.5 WebSocket gateway
|
||||||
|
|
||||||
|
Owns connections, role assignment, temporary session tokens, message parsing,
|
||||||
|
schema validation, rate limiting, reconnect attachment, and outgoing queues.
|
||||||
|
|
||||||
|
It translates accepted client messages into commands for the authoritative
|
||||||
|
game service. It never mutates match state directly.
|
||||||
|
|
||||||
|
### 4.6 Lobby service
|
||||||
|
|
||||||
|
Owns:
|
||||||
|
|
||||||
|
- two player slots;
|
||||||
|
- up to four observer slots;
|
||||||
|
- unique display-name validation;
|
||||||
|
- optional humorous name suggestions;
|
||||||
|
- identification of Player 1 as lobby owner;
|
||||||
|
- readiness and start authorization;
|
||||||
|
- reconnect reservations for player sessions.
|
||||||
|
|
||||||
|
Only Player 1 may start, and only when both player slots are connected.
|
||||||
|
|
||||||
|
### 4.7 Maze generator and validator
|
||||||
|
|
||||||
|
Generates a configurable rectangular maze and all initial placements. Generation
|
||||||
|
is complete only after the validator confirms:
|
||||||
|
|
||||||
|
- all required cells and objects are reachable;
|
||||||
|
- 180-degree rotational symmetry;
|
||||||
|
- symmetric player-relative starts, keys, exits, compasses, landmarks, and
|
||||||
|
vision artifacts;
|
||||||
|
- cycles and alternative routes;
|
||||||
|
- multiple connections between the conceptual halves;
|
||||||
|
- keys outside the initial owner views;
|
||||||
|
- equivalent placement and route metrics for both players;
|
||||||
|
- no placement collision that violates object rules.
|
||||||
|
|
||||||
|
Generation uses a server-owned seed recorded with the in-memory match for
|
||||||
|
diagnostics and reproducible tests. Clients do not generate the maze.
|
||||||
|
|
||||||
|
Search difficulty cannot be proven solely by geometric distance. The generator
|
||||||
|
therefore exposes route and branching metrics, while the final requirement that
|
||||||
|
key discovery be at least as challenging as subsequent exit discovery is
|
||||||
|
validated through gameplay telemetry and playtesting.
|
||||||
|
|
||||||
|
### 4.8 Authoritative game service
|
||||||
|
|
||||||
|
Owns the complete mutable match state:
|
||||||
|
|
||||||
|
- maze and generated-object state;
|
||||||
|
- player positions and connection states;
|
||||||
|
- inventories and ground keys;
|
||||||
|
- opened exits;
|
||||||
|
- active vision effects;
|
||||||
|
- countdown, pause, and result state;
|
||||||
|
- monotonic timestamps and accepted-action sequence numbers.
|
||||||
|
|
||||||
|
All commands are processed serially through one logical command queue. This
|
||||||
|
prevents concurrent WebSocket callbacks from producing inconsistent key,
|
||||||
|
collision, or victory results.
|
||||||
|
|
||||||
|
### 4.9 View projector
|
||||||
|
|
||||||
|
Builds role-specific snapshots and events from authoritative state:
|
||||||
|
|
||||||
|
- Player 1 receives only Player 1's allowed state.
|
||||||
|
- Player 2 receives only Player 2's allowed state.
|
||||||
|
- Observers receive the complete match view.
|
||||||
|
|
||||||
|
The projector is the security boundary for fog of war. Hidden cells and objects
|
||||||
|
must be omitted from serialized player payloads, not sent with a client-side
|
||||||
|
visibility flag.
|
||||||
|
|
||||||
|
## 5. Runtime state machines
|
||||||
|
|
||||||
|
### 5.1 Device lifecycle
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Booting
|
||||||
|
Booting --> StationConnecting: saved credentials
|
||||||
|
Booting --> ProvisioningAP: no credentials
|
||||||
|
StationConnecting --> Serving: connected
|
||||||
|
StationConnecting --> ProvisioningAP: connection failed
|
||||||
|
ProvisioningAP --> StationConnecting: settings saved
|
||||||
|
Serving --> [*]: reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Match lifecycle
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Lobby
|
||||||
|
Lobby --> Countdown: owner starts with 2 players
|
||||||
|
Countdown --> Running: countdown completes
|
||||||
|
Running --> Paused: player disconnects
|
||||||
|
Paused --> Running: player reconnects within 60 seconds
|
||||||
|
Paused --> Finished: reconnect timeout; draw
|
||||||
|
Running --> FinishPending: first outward exit
|
||||||
|
FinishPending --> Finished: 200 ms result window closes
|
||||||
|
Finished --> Lobby: return to lobby
|
||||||
|
```
|
||||||
|
|
||||||
|
During `Paused`, movement commands are rejected and move-counted effects do not
|
||||||
|
advance. During `FinishPending`, the first exit is retained while the server
|
||||||
|
waits long enough to determine whether the second exit creates a draw.
|
||||||
|
|
||||||
|
## 6. Authoritative command processing
|
||||||
|
|
||||||
|
Every gameplay command follows the same pipeline:
|
||||||
|
|
||||||
|
1. Authenticate the session token and resolve its role.
|
||||||
|
2. Verify that the role is allowed to issue the command.
|
||||||
|
3. Validate the match state and client command schema.
|
||||||
|
4. Enforce the server-side movement cooldown or other action constraints.
|
||||||
|
5. Apply the command to authoritative state.
|
||||||
|
6. Resolve cell entry, artifact activation, pickup, player meeting, key return,
|
||||||
|
exit opening, outward exit, and result transitions in a documented order.
|
||||||
|
7. Increment the authoritative sequence number.
|
||||||
|
8. Project and send the resulting role-specific state updates.
|
||||||
|
|
||||||
|
Observer sessions never reach mutation steps.
|
||||||
|
|
||||||
|
## 7. Game-state model
|
||||||
|
|
||||||
|
The implementation should favor fixed-size or bounded containers. Required
|
||||||
|
conceptual records include:
|
||||||
|
|
||||||
|
- `MazeConfig`: width, height, movement cooldown, object-density settings, and
|
||||||
|
balance thresholds;
|
||||||
|
- `Maze`: compact cell topology plus landmark identifiers;
|
||||||
|
- `PlayerState`: slot, display name, session token reference, position,
|
||||||
|
inventory, vision effect, connection state, and last accepted move time;
|
||||||
|
- `KeyState`: owner and either a ground position or carrier;
|
||||||
|
- `ExitState`: owner, position, and open state;
|
||||||
|
- `CompassState`: owner association and position;
|
||||||
|
- `VisionArtifactState`: position and consumed state;
|
||||||
|
- `MatchState`: lifecycle, seed, sequence, timers, players, objects, and result;
|
||||||
|
- `ConnectionState`: WebSocket handle, role, session association, and bounded
|
||||||
|
outgoing queue.
|
||||||
|
|
||||||
|
The maze representation must be sized from configured dimensions with checked
|
||||||
|
arithmetic. The default 8,192-cell map must not require a per-cell heap
|
||||||
|
allocation.
|
||||||
|
|
||||||
|
## 8. Visibility projection
|
||||||
|
|
||||||
|
For a player at `(px, py)` with radius `r`, a cell `(x, y)` is visible when:
|
||||||
|
|
||||||
|
```text
|
||||||
|
max(abs(x - px), abs(y - py)) <= r
|
||||||
|
```
|
||||||
|
|
||||||
|
The normal radius is three and the boosted radius is six. Map boundaries clip
|
||||||
|
the square. Walls do not occlude cells.
|
||||||
|
|
||||||
|
The projected player view contains only:
|
||||||
|
|
||||||
|
- visible maze cells and landmarks;
|
||||||
|
- visible ground objects;
|
||||||
|
- the player's own position, inventory, effect counter, and exit/compass hint
|
||||||
|
state;
|
||||||
|
- the opponent only when the opponent is currently visible, including the
|
||||||
|
identity of a carried key;
|
||||||
|
- public lobby, pause, countdown, and result information.
|
||||||
|
|
||||||
|
Previously visible map state is not retained by the server for client display.
|
||||||
|
|
||||||
|
## 9. WebSocket protocol contract
|
||||||
|
|
||||||
|
Messages use a versioned envelope. The initial logical shape is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"v": 1,
|
||||||
|
"type": "message_type",
|
||||||
|
"seq": 42,
|
||||||
|
"requestId": "optional-client-id",
|
||||||
|
"payload": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Protocol requirements:
|
||||||
|
|
||||||
|
- unknown versions or message types are rejected safely;
|
||||||
|
- malformed payloads never reach game logic;
|
||||||
|
- client request identifiers support acknowledgement without granting
|
||||||
|
authority;
|
||||||
|
- server sequence numbers let clients discard stale updates;
|
||||||
|
- reconnect produces a fresh role-appropriate snapshot;
|
||||||
|
- payload and queue sizes are bounded;
|
||||||
|
- slow observers may lose intermediate updates and receive a newer snapshot,
|
||||||
|
but must not block player command processing.
|
||||||
|
|
||||||
|
Initial client-to-server message families are:
|
||||||
|
|
||||||
|
- session creation or restoration;
|
||||||
|
- role request and display-name selection;
|
||||||
|
- lobby start;
|
||||||
|
- move intent;
|
||||||
|
- drop-key intent;
|
||||||
|
- heartbeat.
|
||||||
|
|
||||||
|
Initial server-to-client message families are:
|
||||||
|
|
||||||
|
- session and role result;
|
||||||
|
- lobby snapshot;
|
||||||
|
- countdown;
|
||||||
|
- player-view snapshot or update;
|
||||||
|
- observer snapshot or update;
|
||||||
|
- action acknowledgement or rejection;
|
||||||
|
- pause/reconnect status;
|
||||||
|
- match result;
|
||||||
|
- protocol error.
|
||||||
|
|
||||||
|
Exact field schemas belong beside the implementation and require protocol tests.
|
||||||
|
|
||||||
|
## 10. Browser application
|
||||||
|
|
||||||
|
The browser application has four presentation modes:
|
||||||
|
|
||||||
|
1. network provisioning;
|
||||||
|
2. name selection and lobby;
|
||||||
|
3. player match view;
|
||||||
|
4. observer full-map view.
|
||||||
|
|
||||||
|
Player controls support keyboard movement, touch swipes, and an explicit
|
||||||
|
drop-key action. The UI renders only server-provided visible state. The observer
|
||||||
|
view supports fitting, panning, or zooming the complete `128 x 64` map without
|
||||||
|
changing server authority.
|
||||||
|
|
||||||
|
A temporary session token is stored in browser-local storage so page refresh
|
||||||
|
can reattach to the existing slot. It is not a permanent account credential and
|
||||||
|
does not need to survive an ESP reboot.
|
||||||
|
|
||||||
|
## 11. Memory, storage, and concurrency
|
||||||
|
|
||||||
|
The locked Flash layout is defined in `partitions.csv`:
|
||||||
|
|
||||||
|
- 24 KiB NVS;
|
||||||
|
- 4 KiB PHY data;
|
||||||
|
- 2 MiB factory application;
|
||||||
|
- 1,984 KiB LittleFS.
|
||||||
|
|
||||||
|
The design must work without PSRAM. Memory rules are:
|
||||||
|
|
||||||
|
- no per-cell heap allocation;
|
||||||
|
- bounded WebSocket connections and outgoing queues;
|
||||||
|
- reuse serialized buffers where practical;
|
||||||
|
- prefer compact maze and object representations;
|
||||||
|
- do not retain separate full maze copies for each player;
|
||||||
|
- compute or serialize role projections from authoritative state;
|
||||||
|
- measure free heap and largest free block under the six-client acceptance
|
||||||
|
load.
|
||||||
|
|
||||||
|
`CONFIG_LWIP_MAX_SOCKETS=16` is an initial ceiling, not proof that six browser
|
||||||
|
clients fit in RAM. Hardware load evidence is required before the relevant
|
||||||
|
milestone can be completed.
|
||||||
|
|
||||||
|
## 12. Failure handling
|
||||||
|
|
||||||
|
- Invalid or rate-limited client actions receive a rejection and do not mutate
|
||||||
|
state.
|
||||||
|
- A player WebSocket loss pauses the match and starts a 60-second monotonic
|
||||||
|
reconnect deadline.
|
||||||
|
- Session restoration within the deadline resumes the same player state.
|
||||||
|
- Deadline expiry ends the match in a draw.
|
||||||
|
- Observer loss has no gameplay effect.
|
||||||
|
- A device reboot clears the active lobby and match.
|
||||||
|
- Failure to mount required production web assets is a startup error exposed in
|
||||||
|
logs and health state.
|
||||||
|
- A maze that fails validation is discarded and regenerated with a bounded
|
||||||
|
retry policy; exhausting that policy returns to the lobby with a visible
|
||||||
|
error rather than starting an invalid match.
|
||||||
|
|
||||||
|
## 13. Security and trust boundaries
|
||||||
|
|
||||||
|
The MVP is intended for a trusted local network, but the server still enforces:
|
||||||
|
|
||||||
|
- server-side role authorization;
|
||||||
|
- observer read-only behavior;
|
||||||
|
- unique player slots;
|
||||||
|
- bounded input lengths and message sizes;
|
||||||
|
- movement and message rate limits;
|
||||||
|
- no hidden-state disclosure to player payloads;
|
||||||
|
- no acceptance of client positions, timers, inventory, or results;
|
||||||
|
- restricted network-credential handling through the provisioning flow.
|
||||||
|
|
||||||
|
Password-protected accounts, internet exposure, TLS termination, and remote
|
||||||
|
matchmaking are outside MVP scope.
|
||||||
|
|
||||||
|
## 14. Verification strategy
|
||||||
|
|
||||||
|
Verification is layered:
|
||||||
|
|
||||||
|
- host-side unit tests for symmetry, reachability, visibility, movement, items,
|
||||||
|
collision resolution, timers, and state transitions;
|
||||||
|
- deterministic generator tests over many seeds;
|
||||||
|
- protocol tests for malformed messages, authorization, stale sequence handling,
|
||||||
|
and reconnect snapshots;
|
||||||
|
- browser tests for keyboard, swipe, drop-key, lobby, player, and observer flows;
|
||||||
|
- firmware build and partition-size checks;
|
||||||
|
- on-device tests for Wi-Fi fallback, NVS persistence, six simultaneous
|
||||||
|
WebSocket clients, disconnect recovery, heap use, and representative match
|
||||||
|
duration.
|
||||||
|
|
||||||
|
No milestone is complete merely because code exists. Completion requires the
|
||||||
|
evidence specified for that milestone in `PLANS.md`.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(maze32)
|
||||||
@@ -0,0 +1,499 @@
|
|||||||
|
# maze32 Implementation Plan
|
||||||
|
|
||||||
|
## Plan rules
|
||||||
|
|
||||||
|
1. Milestones may be planned and added in batches.
|
||||||
|
2. Milestone numbering starts at 1 and increases without gaps. Headings use the
|
||||||
|
zero-padded form `Milestone 001`, `Milestone 002`, and so on.
|
||||||
|
3. Milestones are executed strictly in ascending numerical order. Work on a
|
||||||
|
milestone may begin only after every preceding milestone is `COMPLETE`.
|
||||||
|
4. At most one milestone may have status `IN_PROGRESS`.
|
||||||
|
5. A newly discovered milestone is appended to the end of the plan. Existing
|
||||||
|
milestone numbers and execution order are not changed without explicit
|
||||||
|
project-owner approval.
|
||||||
|
6. Every milestone contains an objective, scope, deliverables, acceptance
|
||||||
|
criteria, evidence, and status.
|
||||||
|
7. Allowed statuses are `NOT_STARTED`, `IN_PROGRESS`, `BLOCKED`, and `COMPLETE`.
|
||||||
|
8. A milestone may be marked `COMPLETE` only when every acceptance criterion is
|
||||||
|
satisfied and concrete evidence is recorded in the milestone.
|
||||||
|
9. Evidence must identify the command, test result, inspected artifact, or
|
||||||
|
hardware observation that demonstrates completion. Statements such as
|
||||||
|
"implemented" or "looks correct" are not sufficient evidence.
|
||||||
|
10. A `NOT_STARTED` milestone explicitly records that completion evidence is
|
||||||
|
not yet available. Expected evidence may be listed in advance.
|
||||||
|
11. If later work invalidates recorded evidence, reopen the affected milestone
|
||||||
|
and change its status before continuing.
|
||||||
|
12. All project documentation, milestone text, source comments, test names, and
|
||||||
|
user-facing diagnostic messages are written in English.
|
||||||
|
|
||||||
|
## Milestone 001 — Requirements and technical baseline
|
||||||
|
|
||||||
|
**Status:** `COMPLETE`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Establish a consistent, implementation-ready product specification,
|
||||||
|
architecture, board passport, build profile, and Flash layout without modifying
|
||||||
|
the read-only project sources.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Consolidate the agreed gameplay, browser, lobby, observer, reconnection, and
|
||||||
|
Wi-Fi provisioning requirements.
|
||||||
|
- Establish the ESP32-C6/ESP-IDF/PlatformIO baseline inherited from Battleship.
|
||||||
|
- Resolve the stale 2 MB versus confirmed 4 MB Flash configuration.
|
||||||
|
- Define the authoritative-server architecture and verification boundaries.
|
||||||
|
- Establish the sequential milestone and evidence policy.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- `MVP.md`
|
||||||
|
- `ARCHITECTURE.md`
|
||||||
|
- `PLANS.md`
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `docs/BOARD_PASSPORT.md`
|
||||||
|
- `platformio.ini`
|
||||||
|
- `partitions.csv`
|
||||||
|
- `sdkconfig.defaults`
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. All canonical project documentation is in English.
|
||||||
|
2. `MVP.md` captures all agreed roles, gameplay rules, networking behavior,
|
||||||
|
technical constraints, exclusions, and acceptance criteria.
|
||||||
|
3. `ARCHITECTURE.md` defines server authority, component boundaries, state
|
||||||
|
machines, role-specific projection, protocol direction, storage, and
|
||||||
|
verification strategy.
|
||||||
|
4. The build profile pins PlatformIO `espressif32@7.0.1`, ESP-IDF, the compatible
|
||||||
|
`esp32-c6-devkitm-1` profile, and 4 MB Flash.
|
||||||
|
5. The partition layout is contiguous and ends exactly at `0x400000`.
|
||||||
|
6. `sdkconfig.defaults` explicitly selects 4 MB Flash and 16 LwIP sockets.
|
||||||
|
7. The board passport matches the canonical configuration and does not claim an
|
||||||
|
unverified physical board model, GPIO mapping, LED, or PSRAM.
|
||||||
|
8. No file under `sources/` is modified.
|
||||||
|
9. PlatformIO can parse and resolve the canonical project configuration.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
- `MVP.md` contains the complete English product and acceptance specification.
|
||||||
|
- `ARCHITECTURE.md` contains the English runtime, component, state-machine,
|
||||||
|
protocol, memory, failure, and verification contracts.
|
||||||
|
- `platformio.ini` resolves the environment `esp32-c6-mini` with
|
||||||
|
`espressif32@7.0.1`, ESP-IDF, explicit 4 MB build/upload size, `partitions.csv`,
|
||||||
|
LittleFS, and `sdkconfig.defaults`.
|
||||||
|
- Running `/home/sasa/.platformio/penv/bin/pio project config` successfully
|
||||||
|
parsed and printed the canonical configuration. PlatformIO subsequently
|
||||||
|
emitted a sandbox-local telemetry cache permission warning during process
|
||||||
|
shutdown; it did not prevent configuration parsing.
|
||||||
|
- Static partition arithmetic verified that the 2 MiB factory application ends
|
||||||
|
at `0x210000` and the 1,984 KiB LittleFS partition ends at `0x400000`.
|
||||||
|
- `sdkconfig.defaults` contains `CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y` and
|
||||||
|
`CONFIG_LWIP_MAX_SOCKETS=16`.
|
||||||
|
- `docs/BOARD_PASSPORT.md` records the confirmed ESP32-C6FH4, 4 MB Flash,
|
||||||
|
no-required-PSRAM baseline and the remaining explicit hardware unknowns.
|
||||||
|
- Canonical files were created outside `sources/`; the reference files under
|
||||||
|
`sources/` were not edited.
|
||||||
|
|
||||||
|
### Completion note
|
||||||
|
|
||||||
|
Milestone 001 establishes documentation and configuration only. It does not
|
||||||
|
claim that firmware source code exists, that a firmware image builds, or that
|
||||||
|
the target board has passed runtime tests. Those claims require evidence in
|
||||||
|
later milestones.
|
||||||
|
|
||||||
|
## Milestone 002 — Firmware skeleton and reproducible build
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Create the smallest maintainable ESP-IDF firmware and web-asset skeleton that
|
||||||
|
builds reproducibly with the locked platform and partition configuration.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Establish firmware, component, test, web, and script directories.
|
||||||
|
- Add startup logging, version information, and a minimal health state.
|
||||||
|
- Mount LittleFS and serve a minimal static page.
|
||||||
|
- Implement deterministic compression and filesystem-image packaging for web
|
||||||
|
assets.
|
||||||
|
- Add build-time application and filesystem size checks.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Minimal bootable firmware source tree.
|
||||||
|
- Minimal static web application.
|
||||||
|
- Web-asset compression and LittleFS packaging script.
|
||||||
|
- Host-test entry point for portable code.
|
||||||
|
- Documented build, filesystem-build, upload, and monitor commands.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. A clean PlatformIO build succeeds with the locked environment.
|
||||||
|
2. The application image fits the 2 MiB factory partition.
|
||||||
|
3. The LittleFS image fits the 1,984 KiB filesystem partition.
|
||||||
|
4. Web-asset generation is deterministic and does not require manual edits.
|
||||||
|
5. The firmware boots on the target board, mounts LittleFS, and reports its
|
||||||
|
version and health state.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes clean build output, image sizes, asset
|
||||||
|
reproducibility checks, and a target-board boot log.
|
||||||
|
|
||||||
|
## Milestone 003 — Wi-Fi provisioning and persistent configuration
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Implement station-first Wi-Fi operation with NVS persistence and a fallback
|
||||||
|
access-point provisioning flow.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Versioned NVS configuration storage.
|
||||||
|
- Station connection using saved credentials.
|
||||||
|
- Fallback access point when configuration is missing or connection fails.
|
||||||
|
- Browser flow to scan, configure, save, and retry an existing network.
|
||||||
|
- Safe handling of invalid credentials and configuration reset.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Persistent configuration component.
|
||||||
|
- Network manager and provisioning state machine.
|
||||||
|
- Provisioning HTTP interface.
|
||||||
|
- Automated configuration tests and on-device recovery procedure.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. First boot exposes the fallback provisioning interface.
|
||||||
|
2. Valid credentials are stored in NVS and used after reboot.
|
||||||
|
3. Failed station connection returns to a usable fallback access point.
|
||||||
|
4. Invalid requests cannot corrupt stored configuration.
|
||||||
|
5. Network reconfiguration cannot leave an active game silently running.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes NVS tests and target-board logs for first
|
||||||
|
boot, successful station connection, reboot persistence, and fallback recovery.
|
||||||
|
|
||||||
|
## Milestone 004 — HTTP, WebSocket, sessions, and lobby
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Provide the bounded network and session layer required for two players, four
|
||||||
|
observers, and an owner-controlled lobby.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- HTTP application delivery and diagnostic endpoint.
|
||||||
|
- Versioned WebSocket envelope, parser, validation, and bounded queues.
|
||||||
|
- Temporary session-token creation and browser refresh restoration.
|
||||||
|
- Unique player names and humorous name suggestions.
|
||||||
|
- Two player slots, four observer slots, and Player 1 start authority.
|
||||||
|
- Lobby snapshots, readiness, and the authoritative `3-2-1` countdown.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- HTTP and WebSocket gateway components.
|
||||||
|
- Session and lobby services.
|
||||||
|
- Protocol schemas and protocol tests.
|
||||||
|
- Minimal browser lobby UI and non-browser protocol test client.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Two players and four observers can connect concurrently within fixed limits.
|
||||||
|
2. Duplicate player names and excess role claims are rejected safely.
|
||||||
|
3. Page refresh restores the same session and player slot.
|
||||||
|
4. Only Player 1 can start, and only with both player slots occupied.
|
||||||
|
5. Malformed, oversized, unauthorized, and stale messages do not mutate state.
|
||||||
|
6. Both players receive one authoritative countdown and activation instant.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes protocol-test output, six-client connection
|
||||||
|
results, session restoration tests, and lobby authorization tests.
|
||||||
|
|
||||||
|
## Milestone 005 — Symmetric maze generation and validation
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Generate connected, cyclic, reproducible, 180-degree symmetric mazes with fair
|
||||||
|
object placement and bounded memory use.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Compact configurable maze representation.
|
||||||
|
- Seeded generation for the default `128 x 64` map.
|
||||||
|
- Cycles, alternative paths, and multiple cross-half passages.
|
||||||
|
- Symmetric starts, keys, exits, compass cells, landmarks, and vision artifacts.
|
||||||
|
- Reachability, symmetry, collision, and fairness validation.
|
||||||
|
- Bounded regeneration when a candidate fails validation.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Portable maze generator and validator.
|
||||||
|
- Deterministic host-side test suite over a large seed set.
|
||||||
|
- Diagnostic generation metrics and failure reports.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. The same configuration and seed always produce the same valid maze.
|
||||||
|
2. Every tested map is fully reachable and rotationally symmetric.
|
||||||
|
3. Every tested map contains cycles and multiple cross-half connections.
|
||||||
|
4. Player-relative starts and required objects have equal symmetric metrics.
|
||||||
|
5. Keys are outside their owners' initial views.
|
||||||
|
6. Default generation fits the agreed RAM and runtime budgets without per-cell
|
||||||
|
heap allocation.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes deterministic seed-test results, validator
|
||||||
|
statistics, memory measurements, and representative generated maps.
|
||||||
|
|
||||||
|
## Milestone 006 — Authoritative movement and visibility core
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Implement deterministic player movement, command ordering, cooldowns, and
|
||||||
|
server-enforced role-specific visibility.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Serial authoritative command queue and sequence numbers.
|
||||||
|
- Orthogonal one-cell movement and wall rejection.
|
||||||
|
- Independent player actions with configurable server cooldown.
|
||||||
|
- Shared-cell occupancy and meeting detection.
|
||||||
|
- Chebyshev-radius visibility with walls that do not occlude.
|
||||||
|
- Player-specific projections and complete observer projections.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Portable game-state and movement engine.
|
||||||
|
- View projector with fog-of-war security tests.
|
||||||
|
- Movement, cooldown, ordering, collision, and visibility tests.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Accepted inputs move exactly one valid orthogonal cell.
|
||||||
|
2. Invalid, premature, or rate-limited commands do not mutate state.
|
||||||
|
3. Both players may occupy the same cell and generate one meeting event.
|
||||||
|
4. Player payloads contain no hidden cells, objects, or stale opponent state.
|
||||||
|
5. Observer payloads contain the complete current state.
|
||||||
|
6. Radius-three and radius-six projections match the documented formula at
|
||||||
|
walls and map boundaries.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes host-test output, serialized payload
|
||||||
|
inspection, and deterministic command-order tests.
|
||||||
|
|
||||||
|
## Milestone 007 — Keys, artifacts, compasses, exits, and results
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Complete the authoritative gameplay mechanics and terminal match outcomes.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- One-slot key inventory, pickup, voluntary drop, and sabotage.
|
||||||
|
- Automatic return of keys to their owners when players meet.
|
||||||
|
- Owner-only key and exit restrictions.
|
||||||
|
- One-use vision artifacts lasting ten successful moves.
|
||||||
|
- Stationary compass activation and exit-direction hints.
|
||||||
|
- Exit opening, outward victory move, 200 ms draw window, and result state.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Gameplay object and result systems.
|
||||||
|
- Deterministic edge-case and simultaneous-event tests.
|
||||||
|
- Role-specific events for inventory, hints, effects, and results.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Inventory and ground-key state never duplicate or lose a key.
|
||||||
|
2. Meeting behavior returns or swaps carried keys atomically.
|
||||||
|
3. A player cannot use the opponent's key or exit to win.
|
||||||
|
4. Vision effects last exactly ten successful moves and ignore failed moves.
|
||||||
|
5. Compass activation points toward only the activating player's own exit.
|
||||||
|
6. Victory requires the outward move, and exits within 200 ms produce a draw.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes complete mechanic tests and timestamped
|
||||||
|
result-window tests.
|
||||||
|
|
||||||
|
## Milestone 008 — Player browser experience
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Deliver the complete responsive player interface without moving game authority
|
||||||
|
into the browser.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Name selection, lobby, countdown, running, paused, and result screens.
|
||||||
|
- Maze viewport and visual landmarks.
|
||||||
|
- Keyboard movement and touchscreen swipes.
|
||||||
|
- Accessible drop-key action.
|
||||||
|
- Inventory, vision duration, compass hint, connection, and error feedback.
|
||||||
|
- Session restoration after page refresh.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Responsive browser application and compressed production assets.
|
||||||
|
- Browser interaction tests for desktop and mobile input.
|
||||||
|
- Accessibility and viewport behavior checks.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Keyboard and swipe actions produce equivalent server commands.
|
||||||
|
2. The client renders only state received in the player projection.
|
||||||
|
3. Hidden and previously seen cells are not retained on screen.
|
||||||
|
4. Inventory, effects, hints, pause, and results are unambiguous.
|
||||||
|
5. Refresh restores the active player without changing position or inventory.
|
||||||
|
6. The production asset image fits its LittleFS partition.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes browser-test output, desktop/mobile visual
|
||||||
|
checks, refresh recovery results, and filesystem image size.
|
||||||
|
|
||||||
|
## Milestone 009 — Observer mode and disconnect recovery
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Complete the read-only full-map observer experience and reliable player pause
|
||||||
|
and reconnect behavior.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Full-map observer rendering with player and object updates.
|
||||||
|
- Observer access without a password and strict read-only authorization.
|
||||||
|
- Player disconnect detection and authoritative pause.
|
||||||
|
- Session reattachment and 60-second monotonic deadline.
|
||||||
|
- Timeout draw and observer-independent match continuity.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Observer browser mode.
|
||||||
|
- Pause/reconnect state-machine integration.
|
||||||
|
- Authorization, reconnect, refresh, and timeout tests.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Four observers can view the complete live state without control authority.
|
||||||
|
2. Observer disconnects do not pause or mutate a match.
|
||||||
|
3. Either player disconnect immediately pauses gameplay and effect progression.
|
||||||
|
4. Reconnection within 60 seconds restores and resumes the same state.
|
||||||
|
5. Reconnection timeout ends the match in a draw.
|
||||||
|
6. Observer attempts to send mutation commands are rejected and audited.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes role-authorization tests, four-observer
|
||||||
|
browser evidence, and timed reconnect/timeout scenarios.
|
||||||
|
|
||||||
|
## Milestone 010 — On-device integration and resource hardening
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Demonstrate that the integrated system remains correct and responsive within
|
||||||
|
the ESP32-C6 Flash, RAM, socket, and network limits.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Full firmware and LittleFS production build.
|
||||||
|
- Two-player plus four-observer load testing.
|
||||||
|
- Heap, largest-block, socket, queue, latency, and disconnect measurements.
|
||||||
|
- Slow-client backpressure and malformed-message stress.
|
||||||
|
- Station and fallback-access-point integration tests.
|
||||||
|
- Long-running match and repeated-match stability.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- On-device integration test procedure and results.
|
||||||
|
- Resource budget report.
|
||||||
|
- Fixed limits and defensive failure behavior.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Application and filesystem images fit their partitions with recorded margin.
|
||||||
|
2. Six simultaneous clients remain connected and responsive on target hardware.
|
||||||
|
3. Slow or malformed clients cannot exhaust memory or block player processing.
|
||||||
|
4. Heap and largest free block remain stable across repeated matches.
|
||||||
|
5. Station reconnect, provisioning fallback, and browser recovery work on the
|
||||||
|
physical device.
|
||||||
|
6. No test relies on PSRAM or an unverified GPIO.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes production build reports, physical-device
|
||||||
|
logs, six-client load results, memory graphs or samples, and repeated-match
|
||||||
|
stress results.
|
||||||
|
|
||||||
|
## Milestone 011 — Gameplay tuning and MVP acceptance
|
||||||
|
|
||||||
|
**Status:** `NOT_STARTED`
|
||||||
|
|
||||||
|
### Objective
|
||||||
|
|
||||||
|
Tune the game to its target experience and demonstrate every MVP acceptance
|
||||||
|
criterion on the release candidate.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- Tune movement cooldown, maze balance thresholds, landmarks, artifact density,
|
||||||
|
and compass placement.
|
||||||
|
- Measure key-search and exit-search difficulty.
|
||||||
|
- Run representative human playtests targeting approximately five minutes.
|
||||||
|
- Execute the complete MVP acceptance suite and document known limitations.
|
||||||
|
- Produce the reproducible release image and matching web filesystem image.
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
- Locked MVP gameplay configuration.
|
||||||
|
- Playtest and balance report.
|
||||||
|
- MVP acceptance report mapped to `MVP.md`.
|
||||||
|
- Release build artifacts and version record.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
1. Key discovery is demonstrably no easier than the subsequent exit search
|
||||||
|
under the agreed metrics and playtest evidence.
|
||||||
|
2. Representative completed matches normally target approximately five minutes
|
||||||
|
without a hard match timer.
|
||||||
|
3. Every acceptance criterion in `MVP.md` has recorded passing evidence or an
|
||||||
|
explicitly approved deviation.
|
||||||
|
4. The final firmware and web images build reproducibly and fit the target.
|
||||||
|
5. No unresolved defect prevents the two-player or observer MVP flows.
|
||||||
|
|
||||||
|
### Evidence
|
||||||
|
|
||||||
|
Completion evidence is not yet available because this milestone has not
|
||||||
|
started. Required evidence includes playtest data, the mapped acceptance report,
|
||||||
|
final build hashes and sizes, and project-owner approval of any deviations.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Board passport
|
||||||
|
|
||||||
|
## Identification status
|
||||||
|
|
||||||
|
The target PCB has not been identified from physical markings. No photograph
|
||||||
|
of the markings is available in the repository and no `sources/` directory is
|
||||||
|
present. A read-only `esptool.py flash_id` probe did identify the installed SoC
|
||||||
|
and flash, but it cannot identify the PCB manufacturer, board model/revision,
|
||||||
|
built-in LED, or board-specific pinout.
|
||||||
|
|
||||||
|
Required evidence to unblock Milestone 000:
|
||||||
|
|
||||||
|
- clear photographs of both sides of the board, including all PCB and module
|
||||||
|
markings;
|
||||||
|
- the manufacturer's product page, schematic, and pinout matching those
|
||||||
|
markings;
|
||||||
|
- the PCB/model ordering code that matches the photographs.
|
||||||
|
|
||||||
|
## Confirmed electronically
|
||||||
|
|
||||||
|
On 2026-08-27, the board appeared as `/dev/ttyACM0` with USB VID:PID
|
||||||
|
`303a:1001` and product name `Espressif USB JTAG/serial debug unit`.
|
||||||
|
`esptool.py` 4.11.0 reported:
|
||||||
|
|
||||||
|
- ESP32-C6FH4 in QFN32, chip revision 0.2;
|
||||||
|
- 4 MB embedded flash, with 4 MB detected;
|
||||||
|
- 40 MHz crystal;
|
||||||
|
- native USB-Serial/JTAG mode.
|
||||||
|
|
||||||
|
The ESP32-C6FH4 ordering code and probe establish the 4 MB in-package flash.
|
||||||
|
No PSRAM was reported. These are chip-level facts; they do not establish the
|
||||||
|
carrier PCB model or its LED and pin routing.
|
||||||
|
|
||||||
|
## Current repository baseline (not hardware confirmation)
|
||||||
|
|
||||||
|
| Item | Observed value | Status |
|
||||||
|
| ---------------------------- | ------------------------------- | --------------------------------------------------------------------- |
|
||||||
|
| PlatformIO Core | 6.1.19 | Installed locally |
|
||||||
|
| PlatformIO platform | `platformio/espressif32` 7.0.1 | Installed locally; `platformio.ini` is not version-pinned |
|
||||||
|
| Framework | ESP-IDF 6.0.1 | Current project setting conflicts with the Arduino-first MVP baseline |
|
||||||
|
| Arduino Core | Not installed | Cannot be pinned and verified against this board yet |
|
||||||
|
| PlatformIO board | `esp32-c6-devkitm-1` | Unverified candidate, not evidence of the physical model |
|
||||||
|
| Board-profile flash size | 4 MB | Unverified; profile metadata only |
|
||||||
|
| Detected physical flash size | 4 MB | Confirmed by `esptool.py flash_id` |
|
||||||
|
| Generated SDK flash size | 2 MB | Conflicts with the detected hardware and board profile |
|
||||||
|
| Generated partition table | Single application, no LittleFS | Does not meet Milestone 000 |
|
||||||
|
|
||||||
|
The current configuration is not yet a reproducible baseline: its generated
|
||||||
|
SDK configuration must be changed from 2 MB to the confirmed 4 MB and its
|
||||||
|
partition table must add LittleFS before firmware is uploaded.
|
||||||
|
|
||||||
|
## Candidate reference only
|
||||||
|
|
||||||
|
If the physical markings prove that the board is an Espressif
|
||||||
|
ESP32-C6-DevKitM-1, the matching authoritative references are:
|
||||||
|
|
||||||
|
- [ESP32-C6-DevKitM-1 user guide](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c6/esp32-c6-devkitm-1/user_guide.html)
|
||||||
|
- [ESP32-C6-DevKitM-1 schematic](https://docs.espressif.com/projects/esp-dev-kits/en/latest/_static/esp32-c6-devkitm-1/schematics/esp32-c6-devkitm-1-schematics.pdf)
|
||||||
|
- [ESP32-C6-MINI-1 datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6-mini-1_mini-1u_datasheet_en.pdf)
|
||||||
|
|
||||||
|
For that board only, Espressif documents an ESP32-C6-MINI-1(U) module with
|
||||||
|
4 MB in-package SPI flash, no PSRAM, a USB-C connector connected through a
|
||||||
|
USB-to-UART bridge, UART0 on GPIO16/GPIO17, native USB D-/D+ on GPIO12/GPIO13,
|
||||||
|
and an addressable RGB LED on GPIO8. These values are deliberately not adopted
|
||||||
|
as the target specifications until the markings match.
|
||||||
|
|
||||||
|
## Memory map and partition status
|
||||||
|
|
||||||
|
No partition CSV has been selected yet. The confirmed 4 MB capacity permits a
|
||||||
|
layout reserving, at minimum, NVS, PHY initialization, one application
|
||||||
|
partition, and one LittleFS data partition. Exact offsets and sizes still need
|
||||||
|
to be defined and checked against the framework's bootloader and partition
|
||||||
|
alignment requirements.
|
||||||
|
|
||||||
|
## Risks carried to the next action
|
||||||
|
|
||||||
|
- A generic “ESP32-C6 Mini” may not match Espressif's DevKitM-1 pinout, LED
|
||||||
|
polarity/type, or USB bridge. The flash capacity is confirmed independently.
|
||||||
|
- GPIO8, GPIO12, GPIO13, GPIO16, and GPIO17 must not be assigned from the
|
||||||
|
candidate documentation until the exact board is confirmed.
|
||||||
|
- Official `platformio/espressif32` 7.0.1 exposes the C6 DevKitM profile only
|
||||||
|
for ESP-IDF and its bundled Arduino Core 2.0.17 does not support ESP32-C6.
|
||||||
|
An exact Arduino-compatible PlatformIO platform/Core pairing must be chosen
|
||||||
|
and pinned after the board is identified, then proven by Milestone 001.
|
||||||
+280
@@ -0,0 +1,280 @@
|
|||||||
|
# maze32 MVP
|
||||||
|
|
||||||
|
## 1. Product goal
|
||||||
|
|
||||||
|
maze32 is a local browser-based multiplayer maze race hosted by one ESP32-C6.
|
||||||
|
Two players explore the same maze in real time, search for their own keys and
|
||||||
|
exits, and try to leave first. Up to four observers may watch the complete
|
||||||
|
match.
|
||||||
|
|
||||||
|
The MVP must produce a fair, playable match whose normal target duration is
|
||||||
|
approximately five minutes. There is no match time limit.
|
||||||
|
|
||||||
|
## 2. Runtime model
|
||||||
|
|
||||||
|
- One ESP32-C6 runs the HTTP server, WebSocket server, maze generator, and the
|
||||||
|
authoritative game simulation.
|
||||||
|
- Both players and all observers use ordinary web browsers.
|
||||||
|
- Clients send intentions such as move or drop-key; they never determine the
|
||||||
|
result of an action locally.
|
||||||
|
- The server validates action order, movement cooldowns, collisions, item
|
||||||
|
ownership, visibility, victory, and draws.
|
||||||
|
- The server must never send hidden game state to a player client. Fog of war
|
||||||
|
is enforced when constructing the server response, not merely by hiding
|
||||||
|
elements in the browser.
|
||||||
|
|
||||||
|
## 3. Roles and lobby
|
||||||
|
|
||||||
|
### Players
|
||||||
|
|
||||||
|
- Exactly two player slots exist: Player 1 and Player 2.
|
||||||
|
- A player joins with a unique display name and receives a temporary session
|
||||||
|
token.
|
||||||
|
- The UI may suggest humorous available names.
|
||||||
|
- The first player becomes the lobby owner and is the only player who may
|
||||||
|
start the match.
|
||||||
|
- The second player is a participant without start authority.
|
||||||
|
- The match can start only when both player slots are occupied.
|
||||||
|
- Refreshing the browser restores the same player, slot, position, inventory,
|
||||||
|
and match state through the session token.
|
||||||
|
|
||||||
|
### Observers
|
||||||
|
|
||||||
|
- Up to four observers may connect without a password.
|
||||||
|
- Observers do not consume player slots.
|
||||||
|
- Observers are read-only: they cannot start, pause, or control a match.
|
||||||
|
- An observer sees the entire maze without fog of war, both current player
|
||||||
|
positions, keys and their holders, exits, compasses, vision artifacts, match
|
||||||
|
status, connection state, and final result.
|
||||||
|
- An observer disconnect does not pause the match.
|
||||||
|
|
||||||
|
### Start sequence
|
||||||
|
|
||||||
|
- When the lobby owner starts a ready match, both players receive the same
|
||||||
|
`3-2-1` countdown.
|
||||||
|
- Movement is rejected until the countdown completes.
|
||||||
|
- Both players become active at the same authoritative server instant.
|
||||||
|
|
||||||
|
## 4. Maze
|
||||||
|
|
||||||
|
### Dimensions and representation
|
||||||
|
|
||||||
|
- The initial configuration is `width = 128`, `height = 64`, or 8,192 cells.
|
||||||
|
- Width and height are runtime configuration values and must not be hard-coded
|
||||||
|
into game logic.
|
||||||
|
- Movement is orthogonal between cells; diagonal movement does not exist.
|
||||||
|
- Every playable cell and required object must be reachable.
|
||||||
|
|
||||||
|
### Symmetry and topology
|
||||||
|
|
||||||
|
- Geometry uses central symmetry under a 180-degree rotation:
|
||||||
|
`(x, y) -> (width - 1 - x, height - 1 - y)`.
|
||||||
|
- Player 2's start, key, exit, and other fairness-relevant placements are the
|
||||||
|
rotated counterparts of Player 1's placements.
|
||||||
|
- The maze contains cycles, alternative routes, dead ends, and many passages
|
||||||
|
between its conceptual halves.
|
||||||
|
- Players can enter either half through multiple routes, but the UI must not
|
||||||
|
identify a cell as belonging to either player.
|
||||||
|
- The maze is not a perfect maze with only one route between points.
|
||||||
|
|
||||||
|
### Starts, keys, and exits
|
||||||
|
|
||||||
|
- Players start at opposite, rotationally symmetric entrance cells.
|
||||||
|
- An entrance is only the starting point; it is not the player's exit.
|
||||||
|
- Each player has one unique key and one unique exit.
|
||||||
|
- A key opens only its matching exit, and only the owner of that key and exit
|
||||||
|
can win through them.
|
||||||
|
- Key and exit pairs are placed rotationally symmetrically and at equal route
|
||||||
|
distances for both players.
|
||||||
|
- Each key begins outside its owner's initial field of view.
|
||||||
|
- Each exit may be at any distant valid symmetric location.
|
||||||
|
- Map generation must make finding the key at least as challenging as the
|
||||||
|
subsequent search for the correct exit. Exact balance thresholds are
|
||||||
|
tunable and must be validated through generation metrics and playtesting.
|
||||||
|
|
||||||
|
### Landmarks and generated objects
|
||||||
|
|
||||||
|
- The maze contains visually distinctive landmarks to support human spatial
|
||||||
|
memory even though explored territory is not retained.
|
||||||
|
- Fairness-relevant landmarks and vision artifacts are generated in
|
||||||
|
rotationally symmetric pairs.
|
||||||
|
- The exact number and density of landmarks and artifact pairs are tunable
|
||||||
|
balance parameters. The MVP must place more than one vision artifact.
|
||||||
|
|
||||||
|
## 5. Movement and player interaction
|
||||||
|
|
||||||
|
- One accepted input moves a player by exactly one traversable cell in one of
|
||||||
|
four directions: up, down, left, or right.
|
||||||
|
- Players act independently; the server does not wait for alternating or
|
||||||
|
simultaneous turns.
|
||||||
|
- A small server-enforced minimum interval exists between accepted moves. Its
|
||||||
|
value is configurable for playtesting.
|
||||||
|
- Rejected movement into a wall does not change position and is not a
|
||||||
|
successful move.
|
||||||
|
- Players do not block each other and may occupy the same cell.
|
||||||
|
- A meeting occurs when both players occupy the same cell.
|
||||||
|
- There are no attacks, health, damage, or other combat mechanics in MVP.
|
||||||
|
|
||||||
|
Browser controls must support:
|
||||||
|
|
||||||
|
- keyboard movement on computers;
|
||||||
|
- directional swipes on touchscreens;
|
||||||
|
- an accessible drop-key action for both control modes.
|
||||||
|
|
||||||
|
## 6. Keys and inventory
|
||||||
|
|
||||||
|
- A player may carry at most one key.
|
||||||
|
- A player may pick up either key if the inventory slot is empty.
|
||||||
|
- The carried key disappears from its map cell and becomes server-owned
|
||||||
|
inventory state.
|
||||||
|
- A player always sees which key they carry.
|
||||||
|
- A player may voluntarily drop either their own or the opponent's key. It is
|
||||||
|
placed on the player's current cell.
|
||||||
|
- Entering a cell containing a key while already carrying one does not pick up
|
||||||
|
the second key.
|
||||||
|
- A player may carry the opponent's key elsewhere and drop it as sabotage.
|
||||||
|
- When players meet, every carried key is automatically transferred to its
|
||||||
|
correct owner. If both carry each other's keys, the keys swap owners in the
|
||||||
|
same authoritative event.
|
||||||
|
- Outside the current field of view, a player receives no information that the
|
||||||
|
opponent found, dropped, or carries a key.
|
||||||
|
- When the opponent is visible, their carried key identity is visible too.
|
||||||
|
- A player cannot open or use the opponent's exit, even while carrying the
|
||||||
|
matching opponent key.
|
||||||
|
|
||||||
|
## 7. Fog of war
|
||||||
|
|
||||||
|
- Normal vision radius is three cells around the player.
|
||||||
|
- Walls do not block vision.
|
||||||
|
- For the MVP, radius uses a square/Chebyshev neighborhood: a radius of three
|
||||||
|
exposes at most a `7 x 7` cell area, clipped by map boundaries.
|
||||||
|
- Once a cell leaves the current vision area it becomes completely unknown
|
||||||
|
again; there is no explored-map memory.
|
||||||
|
- Hidden cells reveal neither maze geometry nor stale objects or player
|
||||||
|
positions.
|
||||||
|
- The opponent is visible only while inside the current vision area and
|
||||||
|
disappears immediately after leaving it.
|
||||||
|
- Observers are exempt from fog of war.
|
||||||
|
|
||||||
|
## 8. Vision artifacts and compasses
|
||||||
|
|
||||||
|
### Vision artifacts
|
||||||
|
|
||||||
|
- The map contains multiple one-use vision artifacts.
|
||||||
|
- Entering an artifact cell activates it immediately and removes it from the
|
||||||
|
map; it cannot be stored or reused.
|
||||||
|
- The effect increases vision radius from three to six cells.
|
||||||
|
- The effect lasts for the next ten successful moves by that player.
|
||||||
|
- Failed move attempts do not consume an effect move.
|
||||||
|
- Additional artifact types are outside the MVP.
|
||||||
|
|
||||||
|
### Compasses
|
||||||
|
|
||||||
|
- The maze contains two stationary compasses, one associated with each
|
||||||
|
player's exit and placed fairly as a symmetric pair.
|
||||||
|
- A compass cannot be picked up.
|
||||||
|
- A player must enter the appropriate compass cell to activate its hint.
|
||||||
|
- On activation, the UI displays an arrow pointing toward that player's own
|
||||||
|
exit.
|
||||||
|
- A compass does not reveal a route or the exit coordinates.
|
||||||
|
|
||||||
|
## 9. Exit, victory, and draw
|
||||||
|
|
||||||
|
- A player can open only their own exit and only with their own key.
|
||||||
|
- Opening the exit is not itself a victory.
|
||||||
|
- After opening it, the player must perform one additional valid outward move
|
||||||
|
beyond the maze boundary through that exit.
|
||||||
|
- The server records the authoritative exit time.
|
||||||
|
- The first player to complete the outward move wins.
|
||||||
|
- If both authoritative exit times fall within a 200 ms window, the result is
|
||||||
|
a draw.
|
||||||
|
- The normal target match duration is approximately five minutes, but a
|
||||||
|
connected match continues indefinitely until victory or another defined draw
|
||||||
|
condition.
|
||||||
|
|
||||||
|
## 10. Connectivity and recovery
|
||||||
|
|
||||||
|
### Wi-Fi provisioning
|
||||||
|
|
||||||
|
- In normal operation the ESP connects to a previously configured Wi-Fi
|
||||||
|
network in station mode.
|
||||||
|
- Network credentials are stored in NVS and reused after reboot.
|
||||||
|
- When the configured network is unavailable or credentials have not been
|
||||||
|
saved, the ESP starts a fallback access point.
|
||||||
|
- The fallback web interface allows the user to configure and save an existing
|
||||||
|
Wi-Fi network.
|
||||||
|
|
||||||
|
### Player disconnection
|
||||||
|
|
||||||
|
- Loss of either player's WebSocket connection pauses the authoritative match.
|
||||||
|
- No player movement or turn-based effect duration advances while paused.
|
||||||
|
- The disconnected player has 60 seconds to reconnect with the existing
|
||||||
|
session token.
|
||||||
|
- A successful reconnect restores the same slot and resumes the same match
|
||||||
|
state.
|
||||||
|
- If the player does not reconnect within 60 seconds, the match ends in a draw.
|
||||||
|
- Reloading a page is treated as a reconnect, not as leaving the match.
|
||||||
|
|
||||||
|
## 11. Technical constraints
|
||||||
|
|
||||||
|
- Target: ESP32-C6FH4, 4 MB Flash, no required PSRAM.
|
||||||
|
- Build: PlatformIO, `espressif32@7.0.1`, ESP-IDF, compatible
|
||||||
|
`esp32-c6-devkitm-1` profile.
|
||||||
|
- Flash layout: 2 MiB factory application and 1,984 KiB LittleFS; no OTA in
|
||||||
|
MVP.
|
||||||
|
- Static browser assets reside in LittleFS and should be compressed for the
|
||||||
|
production image.
|
||||||
|
- HTTP serves the application and provisioning pages; WebSocket carries live
|
||||||
|
lobby, control, and match messages.
|
||||||
|
- The initial LwIP socket limit is 16.
|
||||||
|
- The implementation must be tested with two player connections and four
|
||||||
|
simultaneous observer connections on the physical device.
|
||||||
|
- Active lobby and match state may live in RAM and need not survive an ESP
|
||||||
|
reboot. Wi-Fi settings must survive reboot.
|
||||||
|
|
||||||
|
## 12. MVP acceptance criteria
|
||||||
|
|
||||||
|
The MVP is accepted when all of the following are demonstrated on the target
|
||||||
|
board:
|
||||||
|
|
||||||
|
1. The firmware builds with the locked PlatformIO configuration and fits the
|
||||||
|
application and LittleFS partitions.
|
||||||
|
2. Wi-Fi station setup persists in NVS, and failed station connection exposes
|
||||||
|
the fallback access-point configuration flow.
|
||||||
|
3. Two browsers can claim unique player names, restore their slots after page
|
||||||
|
refresh, enter the lobby, and start through the owner-controlled countdown.
|
||||||
|
4. Four additional browsers can observe the complete match simultaneously
|
||||||
|
without receiving control authority.
|
||||||
|
5. Repeatedly generated `128 x 64` maps are connected, centrally symmetric,
|
||||||
|
contain cycles and multiple cross-half passages, and preserve equal
|
||||||
|
player-relative placement metrics.
|
||||||
|
6. Player clients never receive cells or dynamic objects outside their current
|
||||||
|
allowed view; observer clients receive the complete state.
|
||||||
|
7. Keyboard and touch controls produce server-validated single-cell movement
|
||||||
|
with an enforced configurable cooldown.
|
||||||
|
8. Key pickup, one-slot inventory, dropping, sabotage, automatic owner return,
|
||||||
|
door restrictions, and visible-carried-key rules behave as specified.
|
||||||
|
9. Vision artifacts expand the view for exactly ten successful moves, and
|
||||||
|
compass cells display the correct direction to the activating player's own
|
||||||
|
exit.
|
||||||
|
10. An outward move through an opened own exit determines victory, and exits
|
||||||
|
within 200 ms produce a draw.
|
||||||
|
11. A player disconnect pauses the match, reconnection restores it, and a
|
||||||
|
60-second reconnect timeout ends it as a draw.
|
||||||
|
12. A representative match normally completes in approximately five minutes
|
||||||
|
after balance tuning, without imposing a hard match timer.
|
||||||
|
|
||||||
|
## 13. Explicitly outside MVP
|
||||||
|
|
||||||
|
- More than two active players or more than one concurrent match.
|
||||||
|
- More than four observers.
|
||||||
|
- Persistent accounts, passwords, cloud services, matchmaking, or internet
|
||||||
|
play.
|
||||||
|
- Spectator control or moderation tools.
|
||||||
|
- Physical display or gameplay buttons.
|
||||||
|
- ESP-NOW, BLE multiplayer, or multiple-device game simulation.
|
||||||
|
- Additional artifact types, combat, health, traps, or player elimination.
|
||||||
|
- Persistent match recovery after ESP reboot.
|
||||||
|
- OTA firmware partitions and OTA update flow.
|
||||||
|
- Dependence on PSRAM, an unverified LED, or an assumed board pinout.
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 0x200000,
|
||||||
|
littlefs, data, littlefs,0x210000, 0x1f0000,
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32-c6-devkitm-1]
|
||||||
|
platform = espressif32 @ 7.0.1
|
||||||
|
board = esp32-c6-devkitm-1
|
||||||
|
framework = espidf
|
||||||
|
board_build.partitions = partitions.csv
|
||||||
|
board_build.filesystem = littlefs
|
||||||
|
board_build.sdkconfig_defaults = sdkconfig.defaults
|
||||||
|
lib_deps = https://github.com/joltwallet/esp_littlefs.git#v1.20.4
|
||||||
|
extra_scripts =
|
||||||
|
monitor_port = /dev/ttyACM0
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_LWIP_MAX_SOCKETS=16
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
|||||||
|
# This file was automatically generated for projects
|
||||||
|
# without default 'CMakeLists.txt' file.
|
||||||
|
|
||||||
|
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${app_sources})
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
void app_main() {}
|
||||||
Reference in New Issue
Block a user