docs: add initial project documentation

This commit is contained in:
2026-09-02 23:44:01 +03:00
parent 4ff152ea32
commit 65fe630333
13 changed files with 5200 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
.pio/
.vscode/
.obsidian/
+81
View File
@@ -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
View File
@@ -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`.
+3
View File
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(maze32)
+499
View File
@@ -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.
+84
View File
@@ -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
View File
@@ -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.
+5
View File
@@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x6000
3 phy_init data phy 0xf000 0x1000
4 factory app factory 0x10000 0x200000
5 littlefs data littlefs 0x210000 0x1f0000
+22
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
CONFIG_LWIP_MAX_SOCKETS=16
File diff suppressed because it is too large Load Diff
+6
View File
@@ -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})
+1
View File
@@ -0,0 +1 @@
void app_main() {}