Intial commit
This commit is contained in:
+1929
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,240 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Purpose
|
||||
|
||||
This repository contains firmware and a web application for an ESP32-C6 Mini built with Visual Studio Code and PlatformIO.
|
||||
|
||||
These instructions are intended for Codex and other coding agents working in this repository. Prefer small, reviewable changes and keep the firmware suitable for a resource-constrained embedded device.
|
||||
|
||||
## Sources and instruction scope
|
||||
|
||||
- Treat every file under `sources/` as read-only reference material.
|
||||
- Do not edit, rename, move, or delete files under `sources/`.
|
||||
- Files under `sources/` may be replaced when the ChatGPT project is synchronized.
|
||||
- A more deeply nested `AGENTS.md`, if present, takes precedence for files in its directory tree.
|
||||
- Read `MVP.md` before making architectural or gameplay changes.
|
||||
- Once present, treat `platformio.ini` as the source of truth for the PlatformIO environments, board identifier, framework, build flags, dependency versions, flash layout, and upload settings.
|
||||
- Do not assume that every board sold as “ESP32-C6 Mini” has the same flash size, pinout, LED polarity, or PlatformIO board identifier.
|
||||
|
||||
## Expected project layout
|
||||
|
||||
Use the conventional PlatformIO layout unless the existing project deliberately uses another structure:
|
||||
|
||||
```text
|
||||
.
|
||||
├── platformio.ini # PlatformIO environments and dependencies
|
||||
├── include/ # Public project headers
|
||||
├── src/ # Firmware sources and main.cpp
|
||||
├── lib/ # Project-specific reusable libraries
|
||||
├── data/ # LittleFS web assets
|
||||
├── test/ # PlatformIO unit tests
|
||||
├── scripts/ # Build and validation helpers
|
||||
├── MVP.md # Product and architecture requirements
|
||||
└── AGENTS.md # Repository instructions
|
||||
```
|
||||
|
||||
Do not edit generated content under `.pio/`. Do not commit build products, serial logs, local secrets, or editor-specific temporary files.
|
||||
|
||||
## Working method
|
||||
|
||||
Before editing:
|
||||
|
||||
1. Read the relevant requirements and existing implementation.
|
||||
2. Inspect `platformio.ini` and use an existing environment instead of inventing one.
|
||||
3. Search for repository conventions, existing abstractions, and tests.
|
||||
4. Identify whether the change affects firmware, browser assets, the API contract, stored data, or hardware configuration.
|
||||
|
||||
While editing:
|
||||
|
||||
- Make the smallest coherent change that satisfies the request.
|
||||
- Preserve unrelated user changes and avoid broad formatting rewrites.
|
||||
- Keep hardware-specific values in configuration rather than scattering them through the code.
|
||||
- Update documentation and tests when behavior or public interfaces change.
|
||||
- Do not add a dependency when a small, clear implementation using the existing stack is sufficient.
|
||||
- Pin third-party PlatformIO dependencies to known compatible versions in `platformio.ini`.
|
||||
- Never patch dependency sources inside `.pio/libdeps`.
|
||||
|
||||
After editing:
|
||||
|
||||
1. Run the narrowest relevant tests.
|
||||
2. Build every affected PlatformIO environment.
|
||||
3. Check compiler output for new warnings, memory pressure, and image-size regressions.
|
||||
4. Summarize what changed, what was verified, and anything that still requires physical hardware.
|
||||
|
||||
## PlatformIO commands
|
||||
|
||||
Use the repository's actual environment names. List environments or inspect `platformio.ini` before supplying `-e <environment>`.
|
||||
|
||||
Common non-destructive checks:
|
||||
|
||||
```sh
|
||||
pio run
|
||||
pio run -e <environment>
|
||||
pio test
|
||||
pio test -e <test-environment>
|
||||
pio run -t size
|
||||
```
|
||||
|
||||
Use project-provided formatting, linting, or validation scripts when available. Do not invent a formatting command without checking the repository configuration.
|
||||
|
||||
The following operations affect connected hardware and must only be performed when the user asks for them or clearly authorizes hardware interaction:
|
||||
|
||||
```sh
|
||||
pio run -e <environment> -t upload
|
||||
pio run -e <environment> -t uploadfs
|
||||
pio device monitor
|
||||
pio run -t erase
|
||||
```
|
||||
|
||||
Treat flash erase as destructive. Confirm the target device and environment before uploading or erasing. Never leave a serial monitor running indefinitely.
|
||||
|
||||
## Firmware design rules
|
||||
|
||||
### Architecture
|
||||
|
||||
- Keep domain logic independent from Arduino callbacks and transport code where practical.
|
||||
- Separate Wi-Fi, HTTP/WebSocket transport, session handling, persistence, device control, and application logic.
|
||||
- Route all state-changing requests through a single validated application layer.
|
||||
- Keep the ESP32 authoritative. Browser-side validation improves usability but must not enforce the only copy of a rule.
|
||||
- Use explicit state machines for multi-step workflows rather than loosely related Boolean flags.
|
||||
- Keep public headers small and avoid unnecessary global state.
|
||||
|
||||
### Memory and performance
|
||||
|
||||
- Prefer fixed-size arrays, fixed-width integer types, bounded buffers, and predictable ownership.
|
||||
- Avoid unbounded `String` concatenation, repeated heap allocation, large temporary JSON documents, and unnecessary copies.
|
||||
- Set explicit limits for sessions, WebSocket clients, request bodies, queue sizes, names, and messages.
|
||||
- Parse all external input with bounds checks.
|
||||
- Stream or serve files from LittleFS instead of embedding large duplicate assets in RAM.
|
||||
- Measure rather than guess when changing task stacks, JSON capacity, filesystem partitions, or connection limits.
|
||||
- Check final flash and RAM usage after meaningful changes.
|
||||
|
||||
### Timing and responsiveness
|
||||
|
||||
- Avoid long blocking calls and long `delay()` operations.
|
||||
- Use `millis()`-based scheduling, timers, queues, or tasks for delayed work.
|
||||
- Keep HTTP, WebSocket, timer, and interrupt callbacks short.
|
||||
- Never perform filesystem writes, large serialization, or expensive game logic in an interrupt service routine.
|
||||
- Ensure the main loop and network stack remain responsive under reconnects and multiple clients.
|
||||
- Add timeouts to network and peripheral operations.
|
||||
|
||||
### Concurrency
|
||||
|
||||
- Document which task or callback owns mutable state.
|
||||
- Protect state shared between callbacks or FreeRTOS tasks with an appropriate mutex, queue, or critical section.
|
||||
- Do not hold locks while sending network data or performing slow I/O.
|
||||
- Prefer taking a bounded snapshot before serializing state for clients.
|
||||
- Make repeated requests idempotent where retries are expected.
|
||||
|
||||
### C++ conventions
|
||||
|
||||
- Follow the C++ standard and warning flags configured by the project.
|
||||
- Use `enum class`, `constexpr`, `const`, fixed-width integers, and RAII where supported.
|
||||
- Prefer clear value types and explicit ownership over raw heap allocation.
|
||||
- Check return values from Wi-Fi, filesystem, JSON, and server operations.
|
||||
- Avoid macros except for compile-time configuration, platform compatibility, or logging wrappers.
|
||||
- Keep functions focused and name units in identifiers when values represent milliseconds, bytes, volts, or similar quantities.
|
||||
- Do not enable exceptions or RTTI unless the project already uses and budgets for them.
|
||||
|
||||
### Logging and errors
|
||||
|
||||
- Use consistent log levels and concise messages.
|
||||
- Never log Wi-Fi passwords, session tokens, private payloads, or other credentials.
|
||||
- Avoid high-frequency logs in normal operation.
|
||||
- Return stable machine-readable error codes from APIs and separate them from Russian user-facing messages.
|
||||
- Fail safely: reject invalid commands without partially modifying application state.
|
||||
|
||||
## Wi-Fi and configuration
|
||||
|
||||
- The ESP32-C6 Mini operates as a station on the home Wi-Fi network unless requirements say otherwise.
|
||||
- Keep SSIDs, passwords, tokens, certificates, and private endpoints out of tracked source files.
|
||||
- Store local secrets in a gitignored file or inject them through build-time configuration.
|
||||
- Provide a tracked example file with placeholder values when setup would otherwise be unclear.
|
||||
- Never replace real credentials or erase device configuration unless explicitly requested.
|
||||
- Reconnect with bounded exponential backoff; do not block the application indefinitely while Wi-Fi is unavailable.
|
||||
- The application must handle a changing DHCP address. mDNS may be added as a convenience but must not be the only documented access method unless verified on the target network.
|
||||
|
||||
## Web server and browser application
|
||||
|
||||
- Store required HTML, CSS, and JavaScript locally in `data/` so the application does not depend on internet access or a CDN.
|
||||
- Prefer framework-free browser code for small interfaces. Justify large client dependencies by a concrete requirement.
|
||||
- Keep the HTTP API versionable and document changes to request and response schemas.
|
||||
- Validate authentication token, role, current state, game ID, coordinates, lengths, and content type on the server.
|
||||
- Generate role-specific responses. Never send hidden or privileged state and rely on CSS or JavaScript to conceal it.
|
||||
- Escape user-provided text before rendering it in HTML.
|
||||
- Use WebSocket for immediate updates and provide the HTTP polling fallback required by `MVP.md`.
|
||||
- Implement reconnect backoff, heartbeat handling, state versioning, and full resynchronization after missed events.
|
||||
- Avoid broadcasting one privileged payload to all clients; construct a safe view for each role.
|
||||
- Set appropriate cache headers. Do not cache personalized API responses.
|
||||
- Compress static assets during the build when supported, but keep the uncompressed sources maintainable.
|
||||
- Design touch interactions for phones and tablets. Do not rely on hover or color alone.
|
||||
|
||||
## Filesystem and persistence
|
||||
|
||||
- Treat LittleFS layout and the flash partition table as part of the product configuration.
|
||||
- Check mount failures and report them clearly; do not silently format a filesystem unless the product explicitly requires that behavior.
|
||||
- Use atomic replace patterns for important persisted files where possible.
|
||||
- Minimize flash writes and avoid writing frequently changing counters on every event.
|
||||
- The MVP keeps active game state and statistics in RAM and resets them after reboot, as specified in `MVP.md`.
|
||||
|
||||
## Testing expectations
|
||||
|
||||
### Host or native tests
|
||||
|
||||
Keep pure application logic buildable without ESP32 hardware where practical. Unit-test at least:
|
||||
|
||||
- state transitions;
|
||||
- bounds and malformed input;
|
||||
- game or domain rules;
|
||||
- random-generation invariants;
|
||||
- serialization and role-specific data filtering;
|
||||
- retry and idempotency behavior;
|
||||
- bot or automation logic.
|
||||
|
||||
Use a deterministic random seed in tests and do not use the production seed as an assertion target.
|
||||
|
||||
### Device tests
|
||||
|
||||
Features involving Wi-Fi, LittleFS, WebSocket, timing, heap behavior, or peripherals require a final device check. When hardware is available, verify:
|
||||
|
||||
- cold boot and reconnect behavior;
|
||||
- static asset loading from LittleFS;
|
||||
- multiple simultaneous clients;
|
||||
- WebSocket loss and HTTP fallback;
|
||||
- repeated workflows without heap degradation;
|
||||
- flash/RAM usage and watchdog stability;
|
||||
- operation on the exact ESP32-C6 Mini variant.
|
||||
|
||||
Do not claim device verification when only a local build or host test was performed.
|
||||
|
||||
## Security and robustness
|
||||
|
||||
- Treat every HTTP, WebSocket, serial, and persisted value as untrusted input.
|
||||
- Reject oversized, malformed, stale, unauthorized, and out-of-state requests.
|
||||
- Use cryptographically strong random session tokens when the platform API provides suitable entropy.
|
||||
- Do not put credentials in browser assets, logs, examples, tests, or error responses.
|
||||
- Avoid undefined behavior, unchecked indexing, integer overflow, and unsafe string functions.
|
||||
- Add rate or queue limits where one client could exhaust device memory or monopolize processing.
|
||||
- A home network is not a security boundary. Document any deliberately simplified MVP security assumptions.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Keep setup steps reproducible for a new developer using VS Code and PlatformIO.
|
||||
- Document the exact board variant, USB/serial behavior, flash size, PlatformIO environment, filesystem upload step, and access URL once known.
|
||||
- Update `MVP.md` only when product scope or agreed behavior changes.
|
||||
- Update API documentation whenever routes, messages, events, error codes, or authorization rules change.
|
||||
- Record hardware-only verification steps rather than implying they were automated.
|
||||
|
||||
## Definition of done
|
||||
|
||||
A change is complete when all applicable conditions are met:
|
||||
|
||||
- The requested behavior is implemented with no unrelated changes.
|
||||
- Affected PlatformIO environments build successfully.
|
||||
- Relevant automated tests pass.
|
||||
- No new compiler warnings or exposed secrets are introduced.
|
||||
- Input bounds, failure paths, reconnects, and role permissions are handled.
|
||||
- Flash and RAM impact are acceptable for the target ESP32-C6 Mini.
|
||||
- Web assets work without an external internet dependency.
|
||||
- Documentation and examples reflect the new behavior.
|
||||
- Any remaining physical-device checks are explicitly listed in the handoff.
|
||||
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(battleship)
|
||||
@@ -0,0 +1,286 @@
|
||||
# ESP32 Battleship Implementation Plan
|
||||
|
||||
## Purpose of the initial milestones
|
||||
|
||||
Before implementing the complete game, experimentally verify that the specific ESP32-C6 Mini board:
|
||||
|
||||
- can be flashed and run reliably with the selected PlatformIO and Arduino stack;
|
||||
- can connect to the home Wi-Fi network;
|
||||
- can store and serve the web interface from LittleFS;
|
||||
- can support HTTP, WebSocket, and HTTP polling fallback;
|
||||
- has enough flash, RAM, and processing capacity for one game, two players, and up to eight spectators;
|
||||
- can provide role-specific state without exposing hidden game data.
|
||||
|
||||
Full MVP implementation may begin only after Milestone 004 is completed with a documented **Go** decision.
|
||||
|
||||
## Codex execution rules
|
||||
|
||||
This file is an ordered execution queue for Codex.
|
||||
|
||||
1. Work on milestones strictly in numerical order.
|
||||
2. In one task, execute only the first milestone whose status is `READY`.
|
||||
3. Do not start the next milestone during the same task unless the user explicitly requests it.
|
||||
4. A milestone may become `READY` only after all milestones listed under `Depends on` are `DONE`.
|
||||
5. Before implementation, change the selected milestone status from `READY` to `IN PROGRESS`.
|
||||
6. Do not mark a milestone `DONE` until every acceptance criterion has objective evidence.
|
||||
7. If a criterion cannot be met, set the status to `BLOCKED` and record the reason, evidence, and proposed next action in that milestone's execution record.
|
||||
8. If implementation changes the plan, preserve the intent and acceptance criteria of already completed milestones.
|
||||
9. Add future milestones only at the end of this file. Do not insert or renumber milestones after work has started.
|
||||
10. Never edit files under `sources/`; they are read-only reference material.
|
||||
11. Never store Wi-Fi credentials or other secrets in tracked project files.
|
||||
|
||||
Allowed statuses:
|
||||
|
||||
- `BLOCKED` — work cannot proceed until a recorded issue is resolved;
|
||||
- `READY` — all dependencies are complete and the milestone may be executed;
|
||||
- `IN PROGRESS` — this is the milestone currently being executed;
|
||||
- `DONE` — all acceptance criteria have passed and evidence is recorded.
|
||||
|
||||
At most one milestone may have the status `IN PROGRESS`.
|
||||
|
||||
## Execution record format
|
||||
|
||||
When completing or blocking a milestone, append an execution record inside that milestone using this format:
|
||||
|
||||
```text
|
||||
Execution record
|
||||
- Date:
|
||||
- Board model and revision:
|
||||
- Toolchain and library versions:
|
||||
- Result: PASS | FAIL | BLOCKED
|
||||
- Evidence:
|
||||
- Measurements:
|
||||
- Issues or deviations:
|
||||
- Next action:
|
||||
```
|
||||
|
||||
Evidence should include the exact test scenario, relevant logs, reset reasons, resource measurements, and paths to created project files. A statement such as “works in general” is not sufficient.
|
||||
|
||||
---
|
||||
|
||||
## Milestone 000 — Identify the board and lock the technical baseline
|
||||
|
||||
**Status:** `BLOCKED`
|
||||
**Depends on:** none
|
||||
|
||||
### Objective
|
||||
|
||||
Remove the uncertainty hidden by the generic name “ESP32-C6 Mini” and establish a reproducible build configuration.
|
||||
|
||||
### Work
|
||||
|
||||
- Record the manufacturer, exact board model, and revision from the physical markings and authoritative documentation.
|
||||
- Record the ESP32-C6 module variant, flash size, presence and size of PSRAM, USB connection type, and built-in LED.
|
||||
- Locate the schematic or pinout and identify pins used by USB, UART, flash, and the built-in LED.
|
||||
- Select the exact PlatformIO `board` identifier. If no exact profile exists, select the closest ESP32-C6 profile and explicitly configure flash and partition parameters.
|
||||
- Pin the PlatformIO, Espressif 32 platform, and Arduino Core versions.
|
||||
- Define an initial partition layout that provides space for both the application and LittleFS.
|
||||
- Create a short board passport containing a photo of the markings, documentation links, and confirmed specifications.
|
||||
|
||||
### Deliverables
|
||||
|
||||
- A board passport in the project documentation.
|
||||
- An initial `platformio.ini` with pinned versions.
|
||||
- A documented memory map and selected partition layout.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The exact model and flash size are confirmed rather than inferred from the generic product name.
|
||||
- The build configuration does not depend on floating package versions.
|
||||
- The partition layout includes both an application partition and LittleFS.
|
||||
- Every unknown board parameter is either resolved or recorded as a specific risk to test in Milestone 001.
|
||||
|
||||
### Blocking conditions
|
||||
|
||||
- The flash size or a safe flashing configuration cannot be determined.
|
||||
- The available toolchain has no usable ESP32-C6 support for this board.
|
||||
|
||||
### Completion action
|
||||
|
||||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 001 from `BLOCKED` to `READY`.
|
||||
|
||||
Execution record
|
||||
- Date: 2026-08-27
|
||||
- Board model and revision: Unknown; no physical markings or board photograph were supplied.
|
||||
- Toolchain and library versions: PlatformIO Core 6.1.19; locally installed `platformio/espressif32` 7.0.1; ESP-IDF 6.0.1; Arduino Core not installed.
|
||||
- Result: BLOCKED
|
||||
- Evidence: `docs/BOARD_PASSPORT.md`; no `sources/` directory or board image exists; no `/dev/ttyACM*`, `/dev/ttyUSB*`, `/dev/serial/by-id`, or `/dev/serial/by-path` device is exposed; the local `esp32-c6-devkitm-1` profile declares 4 MB while `sdkconfig.esp32-c6-devkitm-1` declares 2 MB and a single-app partition table without LittleFS.
|
||||
- Measurements: No hardware measurements possible. Repository metadata reports conflicting 4 MB and 2 MB flash configurations.
|
||||
- Issues or deviations: Exact manufacturer/model/revision, module, flash, PSRAM, USB implementation, LED, and board pinout cannot be confirmed. A safe flash/partition configuration and pinned Arduino baseline therefore cannot be selected. No firmware or hardware configuration was changed.
|
||||
- Next action: Supply clear photographs of both board sides and the matching manufacturer documentation, and expose the board for an `esptool.py flash_id` check. Then reconcile the flash size, select a LittleFS-capable partition CSV, pin the compatible PlatformIO platform and Arduino Core, and rerun Milestone 000. Milestone 001 remains `BLOCKED`.
|
||||
|
||||
---
|
||||
|
||||
## Milestone 001 — Prove build, flashing, and stable basic operation
|
||||
|
||||
**Status:** `BLOCKED`
|
||||
**Depends on:** Milestone 000
|
||||
|
||||
### Objective
|
||||
|
||||
Prove that the configuration selected in Milestone 000 works reliably on the physical board.
|
||||
|
||||
### Work
|
||||
|
||||
- Create a minimal firmware project with serial logging and reports for chip information, flash size, reset reason, and available heap.
|
||||
- Flash the board and test startup after a manual reset, a full power cycle, and several repeated flashing cycles.
|
||||
- Test the built-in LED if present and confirm that its pin does not conflict with USB or flash operation.
|
||||
- Add uptime and periodic free-heap reporting.
|
||||
- Run the firmware continuously for at least two hours without Wi-Fi and record every reset reason.
|
||||
|
||||
### Required measurements
|
||||
|
||||
- Firmware size and flash usage.
|
||||
- Free heap immediately after startup and minimum free heap during the run.
|
||||
- Boot and reset reasons.
|
||||
- Any errors, watchdog resets, or unexpected restarts.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The same project builds and flashes reproducibly.
|
||||
- The board starts correctly after reset and full power loss.
|
||||
- The two-hour run completes without a hang, watchdog reset, or unexpected restart.
|
||||
- Free heap does not show a persistent downward trend.
|
||||
- The detected flash size matches the configuration established in Milestone 000.
|
||||
|
||||
### Failure path
|
||||
|
||||
- First verify the board profile, USB mode, flash frequency, and partition layout.
|
||||
- If Arduino Core is unstable on this board, repeat only this bring-up test with ESP-IDF before rejecting the board.
|
||||
|
||||
### Completion action
|
||||
|
||||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 002 from `BLOCKED` to `READY`.
|
||||
|
||||
---
|
||||
|
||||
## Milestone 002 — Prove the Wi-Fi, LittleFS, and HTTP vertical slice
|
||||
|
||||
**Status:** `BLOCKED`
|
||||
**Depends on:** Milestone 001
|
||||
|
||||
### Objective
|
||||
|
||||
Prove the MVP's basic delivery path: home Wi-Fi, LittleFS, and HTTP served directly by the ESP32.
|
||||
|
||||
### Work
|
||||
|
||||
- Connect the board to a predefined home Wi-Fi network in station mode.
|
||||
- Keep Wi-Fi credentials in a local configuration file excluded from version control.
|
||||
- Implement automatic reconnection after a temporary Wi-Fi outage without a manual reset.
|
||||
- Mount LittleFS and upload test `index.html`, CSS, and JavaScript files.
|
||||
- Implement `GET /`, static asset delivery, and `GET /api/health`.
|
||||
- Return uptime, Wi-Fi state, RSSI, free heap, minimum free heap, and build version from `/api/health`, without exposing secrets.
|
||||
- Open the page from at least one phone and one tablet or second client device on the same network.
|
||||
- Verify MIME types, gzip delivery for static assets, and appropriate cache headers.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- After power-on, the board joins Wi-Fi and becomes reachable from two client devices without manual intervention.
|
||||
- The page and all assets load locally without a CDN or internet access.
|
||||
- After the router or Wi-Fi connection is interrupted and restored, the board becomes reachable again without reflashing or a user-triggered reset.
|
||||
- `/api/health` remains responsive during repeated page reloads.
|
||||
- A LittleFS mount failure is visible in the serial log and does not cause an endless reboot loop.
|
||||
- Measured firmware, filesystem, and heap usage are recorded.
|
||||
|
||||
### Blocking conditions
|
||||
|
||||
- Wi-Fi or HTTP repeatedly hangs and cannot recover in software.
|
||||
- The filesystem cannot hold the minimum interface while retaining safe space for firmware growth.
|
||||
|
||||
### Completion action
|
||||
|
||||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 003 from `BLOCKED` to `READY`.
|
||||
|
||||
---
|
||||
|
||||
## Milestone 003 — Prove real-time transport, fallback, and state isolation
|
||||
|
||||
**Status:** `BLOCKED`
|
||||
**Depends on:** Milestone 002
|
||||
|
||||
### Objective
|
||||
|
||||
Prove that the selected server stack works on ESP32-C6 and validate WebSocket delivery, HTTP fallback, versioning, and role-specific state before implementing game logic.
|
||||
|
||||
### Work
|
||||
|
||||
- Select and pin an HTTP/WebSocket library version compatible with the pinned Arduino Core and ESP32-C6.
|
||||
- Implement a test state containing a monotonically increasing `version`, public data, and simulated hidden data.
|
||||
- Add WebSocket endpoint `/ws`, role-specific state snapshots, and `GET /api/state?version=N`.
|
||||
- In the test page, retry WebSocket after 1, 2, 5, and 10 seconds and poll HTTP every 2 seconds while WebSocket is unavailable.
|
||||
- Connect two player clients and at least two spectator clients simultaneously.
|
||||
- Deliberately interrupt WebSocket, verify the transition to HTTP polling, and then restore WebSocket.
|
||||
- Inspect player and spectator traffic to confirm that simulated hidden data is absent.
|
||||
- Limit incoming message size and reject malformed JSON without crashing or leaking memory.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Test state changes reach every connected client through WebSocket.
|
||||
- While WebSocket is unavailable, clients receive current state through HTTP within approximately one polling interval.
|
||||
- After WebSocket recovers, polling stops and each client receives the current `version` without losing state.
|
||||
- Hidden data is absent from HTTP responses, WebSocket messages, and client-side source files.
|
||||
- Malformed and oversized messages are rejected while the server remains responsive.
|
||||
- A 30-minute run with four clients completes without hangs, restarts, or a persistent downward trend in free heap.
|
||||
|
||||
### Fallback path
|
||||
|
||||
If the asynchronous server library is unstable with the pinned Arduino Core, repeat only this vertical slice with ESP-IDF and `esp_http_server`. Keep the protocol and acceptance criteria unchanged.
|
||||
|
||||
### Completion action
|
||||
|
||||
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 004 from `BLOCKED` to `READY`.
|
||||
|
||||
---
|
||||
|
||||
## Milestone 004 — Prove MVP capacity and make the Go/No-Go decision
|
||||
|
||||
**Status:** `BLOCKED`
|
||||
**Depends on:** Milestone 003
|
||||
|
||||
### Objective
|
||||
|
||||
Test the expected worst-case MVP workload before investing in the complete game engine and user interface.
|
||||
|
||||
### Work
|
||||
|
||||
- Create a compact mock state for one game: two 10 × 10 boards, 20 ships, two player sessions, eight spectator sessions, statistics, and protocol metadata.
|
||||
- Generate separate safe views for player 1, player 2, and spectators without keeping a complete JSON copy for every client at the same time.
|
||||
- Connect two client devices and eight spectator connections. A local load script may simulate some spectator connections.
|
||||
- Simulate a game start and at least 200 state changes with WebSocket broadcasts and HTTP polling from a subset of clients.
|
||||
- Run at least 20 consecutive simulated games without rebooting the board.
|
||||
- Record firmware and LittleFS sizes, current and minimum free heap, maximum JSON message size, state generation and delivery time, reconnection count, and reset reasons.
|
||||
- Test recovery after Wi-Fi loss, client reconnections, and a sequence of malformed requests.
|
||||
- Document a **Go**, **Go with constraints**, or **No-Go** decision.
|
||||
|
||||
### Resource budgets
|
||||
|
||||
Before running the capacity test, define numerical limits based on the board specifications and evidence from Milestones 000–003 for:
|
||||
|
||||
- maximum firmware and static asset size;
|
||||
- minimum acceptable free-heap reserve;
|
||||
- maximum HTTP or WebSocket message size;
|
||||
- maximum API response and update-delivery time;
|
||||
- acceptable error and restart count.
|
||||
|
||||
Do not adjust a threshold after seeing the result unless the execution record contains an explicit justification.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Two players and eight spectators simultaneously receive the state intended for their roles.
|
||||
- No client receives hidden opponent cells before the simulated game ends.
|
||||
- Twenty simulated games complete without a hang, watchdog reset, or manual restart.
|
||||
- After the tests, measured flash and heap reserves remain sufficient for `GameEngine`, `FleetGenerator`, `BotPlayer`, and the complete interface.
|
||||
- Minimum free heap does not show a persistent game-to-game decline.
|
||||
- State delivery recovers after Wi-Fi and WebSocket interruptions without rebooting the board.
|
||||
|
||||
### Decision rules
|
||||
|
||||
- **Go:** all acceptance criteria pass with sufficient measured resource reserves. Full game-engine implementation may begin.
|
||||
- **Go with constraints:** the MVP is feasible only after reducing a nonessential target such as the spectator limit or interface size. Update `MVP.md` explicitly before further implementation.
|
||||
- **No-Go for the current stack:** the board works, but the Arduino/server stack is unstable. Evaluate the ESP-IDF fallback.
|
||||
- **No-Go for the board:** the fallback stack also fails the stability or resource criteria. Select a different board before implementing the complete game.
|
||||
|
||||
### Completion action
|
||||
|
||||
Set this milestone to `DONE` only for a documented **Go** decision. For any other decision, set it to `BLOCKED` and record the required scope or platform decision. Append every future milestone after this section without renumbering Milestones 000–004.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Board passport
|
||||
|
||||
## Identification status
|
||||
|
||||
The target board has not been identified. No photograph of the physical
|
||||
markings is available in the repository, no `sources/` directory is present,
|
||||
and no serial device is exposed to the build environment. Consequently, the
|
||||
manufacturer, exact board model and revision, module variant, flash size,
|
||||
PSRAM, USB connection, built-in LED, and board-specific pinout cannot be
|
||||
confirmed without guessing.
|
||||
|
||||
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 flash/module ordering code, or an `esptool.py flash_id` report from the
|
||||
connected board.
|
||||
|
||||
## 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 |
|
||||
| Generated SDK flash size | 2 MB | Conflicts with the board profile |
|
||||
| Generated partition table | Single application, no LittleFS | Does not meet Milestone 000 |
|
||||
|
||||
The current configuration is not a safe flashing baseline. It must not be used
|
||||
to upload firmware until the physical flash size and board identity are
|
||||
confirmed.
|
||||
|
||||
## 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 because the physical flash capacity is
|
||||
unknown. The eventual layout must reserve, at minimum, NVS, PHY initialization,
|
||||
one application partition, and one LittleFS data partition, with offsets and
|
||||
sizes validated against the confirmed flash capacity. Selecting a 4 MB layout
|
||||
now could make flashing unsafe on a smaller device.
|
||||
|
||||
## Risks carried to the next action
|
||||
|
||||
- A generic “ESP32-C6 Mini” may not match Espressif's DevKitM-1 pinout, LED
|
||||
polarity/type, USB bridge, or flash capacity.
|
||||
- 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.
|
||||
+535
@@ -0,0 +1,535 @@
|
||||
# MVP: «Морской бой» на ESP32-C6 Mini
|
||||
|
||||
## 1. Назначение
|
||||
|
||||
Создать первую рабочую версию игры «Морской бой», полностью обслуживаемую одной платой ESP32-C6 Mini. Контроллер подключается к домашней Wi-Fi-сети, раздаёт веб-интерфейс и хранит всю игровую логику и текущее состояние в оперативной памяти.
|
||||
|
||||
Играть можно с телефонов и планшетов в двух режимах:
|
||||
|
||||
1. игрок против игрока с двух разных устройств;
|
||||
2. игрок против ESP32.
|
||||
|
||||
К активной партии могут подключаться зрители. Одновременно ESP32 обслуживает только одну партию.
|
||||
|
||||
## 2. Цели MVP
|
||||
|
||||
- Полностью провести партию от входа игроков до определения победителя.
|
||||
- Поддержать режимы «игрок против игрока» и «игрок против ESP32».
|
||||
- Обеспечить синхронное обновление интерфейсов через WebSocket.
|
||||
- Использовать HTTP-опрос как автоматический резервный механизм при недоступности WebSocket.
|
||||
- Не передавать игрокам и зрителям скрытое расположение неповреждённых кораблей соперника.
|
||||
- Поддержать зрителей, повторную игру и статистику в пределах текущего запуска ESP32.
|
||||
- Сделать русскоязычный адаптивный интерфейс прежде всего для телефонов и планшетов.
|
||||
|
||||
## 3. Что не входит в MVP
|
||||
|
||||
- Несколько одновременных игровых комнат.
|
||||
- Игра через интернет без доступа к домашней сети.
|
||||
- Регистрация, пароли и полноценные учётные записи.
|
||||
- Сохранение партии и статистики после перезагрузки ESP32.
|
||||
- Ручная расстановка кораблей.
|
||||
- Чат, звук, таймер хода, рейтинги и история матчей.
|
||||
- Отдельная точка доступа Wi-Fi, создаваемая ESP32.
|
||||
- Гарантированная поддержка настольных компьютеров как целевой платформы.
|
||||
|
||||
## 4. Принятые правила
|
||||
|
||||
### 4.1. Поле и флот
|
||||
|
||||
- Размер поля: 10 × 10 клеток.
|
||||
- Столбцы обозначаются буквами А–К без буквы Ё, строки — числами 1–10.
|
||||
- Классический флот каждого участника:
|
||||
- 1 корабль длиной 4 клетки;
|
||||
- 2 корабля длиной 3 клетки;
|
||||
- 3 корабля длиной 2 клетки;
|
||||
- 4 корабля длиной 1 клетку.
|
||||
- Корабли располагаются только горизонтально или вертикально.
|
||||
- Корабли не могут соприкасаться ни сторонами, ни углами.
|
||||
- Расстановка создаётся сервером автоматически и случайно перед каждой партией.
|
||||
|
||||
### 4.2. Ходы
|
||||
|
||||
- Первый ход случайно назначается сервером после начала партии.
|
||||
- Игрок выбирает одну ещё не обстрелянную клетку поля соперника.
|
||||
- При попадании игрок сохраняет ход и стреляет снова.
|
||||
- При промахе ход переходит сопернику.
|
||||
- Потопленным считается корабль, у которого поражены все клетки.
|
||||
- После потопления сервер автоматически отмечает окружающие корабль клетки как гарантированные промахи.
|
||||
- Побеждает участник, первым уничтоживший все 10 кораблей соперника.
|
||||
- Повторный выстрел по уже обработанной клетке отклоняется сервером и не меняет ход.
|
||||
|
||||
Эти правила фиксируются для MVP и должны одинаково применяться к человеку и встроенному сопернику.
|
||||
|
||||
## 5. Роли
|
||||
|
||||
### Игрок 1
|
||||
|
||||
- Вводит отображаемое имя.
|
||||
- Выбирает режим игры.
|
||||
- Запускает партию, когда второй участник готов или выбран режим против ESP32.
|
||||
- Видит полностью своё поле и только результаты выстрелов на поле соперника.
|
||||
|
||||
### Игрок 2
|
||||
|
||||
- Доступен только в режиме «игрок против игрока».
|
||||
- Вводит отображаемое имя и занимает свободное место второго игрока.
|
||||
- Имеет тот же объём игровой информации, что и первый игрок.
|
||||
|
||||
### Зритель
|
||||
|
||||
- Вводит отображаемое имя и подключается без права выполнять игровые действия.
|
||||
- Видит имена игроков, текущий ход, статистику и результаты уже выполненных выстрелов на обоих полях.
|
||||
- Не видит неповреждённые корабли ни одного игрока во время партии.
|
||||
- После завершения партии видит полностью раскрытые поля.
|
||||
|
||||
### ESP32-соперник
|
||||
|
||||
- Отображается под именем «ESP32».
|
||||
- Ходит автоматически после небольшой задержки интерфейса, например 500–900 мс.
|
||||
- Использует серверную стратегию и не раскрывает своё поле клиенту.
|
||||
|
||||
## 6. Идентификация и сессии
|
||||
|
||||
- При первом открытии пользователь вводит имя длиной 1–20 символов.
|
||||
- Сервер очищает имя от управляющих символов и HTML-разметки.
|
||||
- После входа сервер выдаёт случайный непрозрачный `sessionToken` и назначает роль.
|
||||
- Браузер хранит токен и имя в `localStorage`.
|
||||
- При кратковременной потере связи пользователь может вернуться в прежнюю роль по токену, пока ESP32 не перезагружена и место не освобождено.
|
||||
- Одинаковые отображаемые имена допустимы: сервер различает клиентов по токену.
|
||||
- Если оба игровых места заняты, новый пользователь может подключиться только зрителем.
|
||||
- Рекомендуемый лимит MVP: до 2 игроков и 8 одновременно подключённых зрителей. Значение должно быть конфигурируемым.
|
||||
- После перезагрузки ESP32 все токены, партия и статистика сбрасываются.
|
||||
|
||||
Полноценная авторизация не требуется: приложение предназначено для доверенной домашней сети. Токен нужен для сохранения роли и предотвращения случайной отправки хода чужим браузером.
|
||||
|
||||
## 7. Пользовательские сценарии
|
||||
|
||||
### 7.1. Игрок против игрока
|
||||
|
||||
1. Первый пользователь открывает адрес ESP32, вводит имя и становится игроком 1.
|
||||
2. Он выбирает режим «Два игрока».
|
||||
3. Второй пользователь открывает тот же адрес, вводит имя и занимает место игрока 2.
|
||||
4. Сервер сообщает обоим игрокам, что партия готова к запуску.
|
||||
5. Игрок 1 нажимает «Начать игру».
|
||||
6. Сервер автоматически и независимо расставляет оба флота, выбирает первого игрока и рассылает разрешённое состояние всем клиентам.
|
||||
7. Игроки по очереди стреляют до победы одного из них.
|
||||
8. На экране результата показываются победитель, итоговая статистика и кнопка «Сыграть ещё».
|
||||
|
||||
### 7.2. Игрок против ESP32
|
||||
|
||||
1. Пользователь входит как игрок 1 и выбирает режим «Против ESP32».
|
||||
2. Место второго участника автоматически занимает ESP32.
|
||||
3. Игрок запускает партию.
|
||||
4. Сервер расставляет оба флота и выбирает первого участника.
|
||||
5. Во время хода ESP32 сервер сам выполняет один или несколько выстрелов с учётом правила продолжения хода после попадания.
|
||||
6. После завершения доступна повторная игра.
|
||||
|
||||
### 7.3. Зритель
|
||||
|
||||
1. Пользователь выбирает «Наблюдать» или автоматически получает роль зрителя, если игровые места заняты.
|
||||
2. Сервер отправляет ему публичное представление текущей партии.
|
||||
3. Зритель получает обновления в реальном времени, но сервер отклоняет любые команды выстрела, старта или повторной игры.
|
||||
|
||||
### 7.4. Повторная игра
|
||||
|
||||
- После завершения любой игрок может предложить повторную игру.
|
||||
- В режиме двух игроков новый матч начинается после подтверждения обоих игроков.
|
||||
- В режиме против ESP32 достаточно подтверждения человека.
|
||||
- Для нового матча сервер создаёт новые случайные расстановки и заново выбирает первого участника.
|
||||
- Имена, роли и накопленная статистика текущего запуска сохраняются.
|
||||
|
||||
## 8. Состояния игры
|
||||
|
||||
```text
|
||||
LOBBY -> PREPARING -> IN_PROGRESS -> FINISHED -> REMATCH_WAIT
|
||||
^ |
|
||||
+------------------------------------------------+
|
||||
```
|
||||
|
||||
- `LOBBY`: выбор режима и ожидание участников.
|
||||
- `PREPARING`: генерация и проверка расстановок.
|
||||
- `IN_PROGRESS`: активная партия.
|
||||
- `FINISHED`: победитель определён, поля раскрыты.
|
||||
- `REMATCH_WAIT`: ожидание подтверждений повторной игры.
|
||||
|
||||
Каждое изменение состояния увеличивает монотонный номер `version`. Клиенты используют его для обнаружения пропущенных событий и резервного HTTP-опроса.
|
||||
|
||||
## 9. Интерфейс
|
||||
|
||||
### 9.1. Экраны
|
||||
|
||||
1. **Подключение** — имя, кнопки «Играть» и «Наблюдать», сообщение о доступности мест.
|
||||
2. **Лобби** — участники, выбор режима игроком 1, состояние готовности и запуск.
|
||||
3. **Игра** — собственное поле, поле выстрелов, имя текущего игрока, статус последнего выстрела и краткая статистика.
|
||||
4. **Наблюдение** — два публичных поля и ход партии без элементов управления.
|
||||
5. **Результат** — победитель, раскрытые поля, статистика матча и повторная игра.
|
||||
|
||||
### 9.2. Адаптивность
|
||||
|
||||
- На телефоне поля показываются по одному с переключателем «Моё поле / Поле соперника».
|
||||
- На планшете при достаточной ширине поля показываются рядом.
|
||||
- Размер игрового поля подстраивается под ширину экрана, клетки остаются квадратными.
|
||||
- Основные кнопки имеют крупную сенсорную область.
|
||||
- Нельзя полагаться только на цвет: попадание, промах и корабль дополнительно различаются символом или формой.
|
||||
- Перед отправкой выстрела выбранная клетка визуально подтверждается. Для уменьшения ошибочных касаний допустим режим «выбрать клетку → нажать “Огонь”».
|
||||
- Интерфейс и все сообщения — только на русском языке.
|
||||
|
||||
### 9.3. Обозначения клеток
|
||||
|
||||
- вода — пустая синяя клетка;
|
||||
- собственный корабль — контрастная заливка;
|
||||
- промах — точка;
|
||||
- попадание — крест;
|
||||
- потопленный корабль — кресты с отдельным оформлением контура;
|
||||
- выбранная цель — заметная рамка до подтверждения выстрела.
|
||||
|
||||
## 10. Предлагаемая техническая реализация
|
||||
|
||||
### 10.1. Базовый стек
|
||||
|
||||
- **Среда:** Visual Studio Code + PlatformIO.
|
||||
- **Целевая плата:** ESP32-C6 Mini.
|
||||
- **Платформа:** Espressif 32. Точный идентификатор `board` в `platformio.ini` выбирается по производителю и маркировке конкретной ESP32-C6 Mini; если готового описания платы нет, используется совместимая конфигурация ESP32-C6 с явно заданными параметрами flash и разделов памяти.
|
||||
- **Фреймворк:** Arduino для ESP32 как наиболее быстрый путь к MVP в PlatformIO.
|
||||
- **Сеть:** штатный `WiFi` в режиме клиента домашней сети.
|
||||
- **Файловая система:** LittleFS для HTML, CSS и JavaScript.
|
||||
- **HTTP и WebSocket:** асинхронный веб-сервер с поддержкой ESP32-C6; конкретную совместимую библиотеку следует закрепить по версии в `platformio.ini`.
|
||||
- **JSON:** ArduinoJson либо небольшой собственный сериализатор с контролируемыми буферами.
|
||||
- **Фронтенд:** нативные HTML, CSS и JavaScript без обязательного фреймворка.
|
||||
|
||||
Vanilla JavaScript рекомендуется для MVP, потому что игровому интерфейсу не нужен крупный UI-фреймворк. Это уменьшает размер файлов, потребление памяти и зависимость от доступа в интернет. Все необходимые ресурсы должны храниться на ESP32; CDN не должен быть обязательным для запуска игры.
|
||||
|
||||
Если выбранная асинхронная библиотека окажется нестабильной на конкретной версии Arduino Core для ESP32-C6, запасной вариант — PlatformIO с ESP-IDF и встроенным `esp_http_server`, который поддерживает WebSocket. Игровая модель и протокол при этом останутся теми же.
|
||||
|
||||
### 10.2. Компоненты прошивки
|
||||
|
||||
- `WiFiManager` — подключение к заранее заданной домашней сети и отображение состояния связи.
|
||||
- `WebServer` — статические файлы, HTTP API и WebSocket.
|
||||
- `SessionManager` — токены, имена, роли, подключения и переподключения.
|
||||
- `GameEngine` — правила, очередь хода, выстрелы, победа и повторная игра.
|
||||
- `FleetGenerator` — случайная корректная расстановка флота.
|
||||
- `BotPlayer` — выбор цели ESP32.
|
||||
- `StatePresenter` — формирует отдельное безопасное представление состояния для каждого игрока и зрителей.
|
||||
- `Statistics` — статистика матча и накопительные показатели до перезагрузки.
|
||||
|
||||
Сетевая обработка не должна напрямую изменять массивы поля. Любая команда сначала проходит проверку сессии, роли, фазы, номера партии и очереди хода, после чего передаётся в `GameEngine`.
|
||||
|
||||
## 11. Модель данных
|
||||
|
||||
Пример внутренних структур на уровне концепции:
|
||||
|
||||
```cpp
|
||||
enum class Cell : uint8_t { Water, Ship, Miss, Hit };
|
||||
enum class Phase : uint8_t { Lobby, Preparing, InProgress, Finished, RematchWait };
|
||||
enum class Mode : uint8_t { HumanVsHuman, HumanVsBot };
|
||||
enum class Role : uint8_t { Player1, Player2, Spectator };
|
||||
|
||||
struct Ship {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t length;
|
||||
bool horizontal;
|
||||
uint8_t hits;
|
||||
};
|
||||
|
||||
struct Board {
|
||||
Cell cells[10][10];
|
||||
Ship ships[10];
|
||||
uint8_t shipsAlive;
|
||||
};
|
||||
```
|
||||
|
||||
Для каждой партии сервер хранит:
|
||||
|
||||
- уникальный `gameId`;
|
||||
- фазу и режим;
|
||||
- два внутренних поля;
|
||||
- текущего участника;
|
||||
- победителя;
|
||||
- номер версии состояния;
|
||||
- подтверждения повторной игры;
|
||||
- статистику обоих участников;
|
||||
- последнее публичное событие.
|
||||
|
||||
Координаты внутри прошивки и протокола рекомендуется хранить числами `x` и `y` от 0 до 9. Буквенные обозначения формирует интерфейс.
|
||||
|
||||
## 12. Скрытие информации
|
||||
|
||||
Сервер не должен отправлять единый полный объект партии всем клиентам.
|
||||
|
||||
- Игрок получает своё полное поле и только известные клетки поля соперника.
|
||||
- Зритель получает только известные клетки обоих полей.
|
||||
- Внутренняя расстановка ESP32 никогда не попадает в браузер до завершения партии.
|
||||
- После `FINISHED` сервер может включить в представление полные поля обоих участников.
|
||||
- Все игровые проверки выполняются на ESP32. Клиентская проверка нужна только для удобства интерфейса и не считается защитой.
|
||||
|
||||
## 13. HTTP API
|
||||
|
||||
Предлагаемый минимальный набор:
|
||||
|
||||
| Метод | Путь | Назначение |
|
||||
|---|---|---|
|
||||
| `GET` | `/` | Основная страница |
|
||||
| `GET` | `/assets/*` | CSS, JavaScript и локальные ресурсы |
|
||||
| `GET` | `/api/info` | Состояние устройства и доступность мест без скрытых данных |
|
||||
| `POST` | `/api/session/join` | Вход по имени и желаемой роли |
|
||||
| `POST` | `/api/session/resume` | Восстановление роли по токену |
|
||||
| `POST` | `/api/game/config` | Выбор режима игроком 1 |
|
||||
| `POST` | `/api/game/start` | Запуск готовой партии |
|
||||
| `POST` | `/api/game/shot` | Выстрел по координатам |
|
||||
| `POST` | `/api/game/rematch` | Подтверждение повторной игры |
|
||||
| `GET` | `/api/state?version=N` | Снимок разрешённого состояния и резервный опрос |
|
||||
| `GET` | `/api/health` | Проверка доступности сервера |
|
||||
|
||||
Все изменяющие запросы содержат токен сессии и `gameId`. Сервер возвращает JSON с полями `ok`, `code`, `message` и при необходимости новой `version`.
|
||||
|
||||
Основные коды ошибок:
|
||||
|
||||
- `INVALID_NAME`;
|
||||
- `NO_PLAYER_SLOT`;
|
||||
- `UNAUTHORIZED`;
|
||||
- `FORBIDDEN_ROLE`;
|
||||
- `WRONG_PHASE`;
|
||||
- `NOT_YOUR_TURN`;
|
||||
- `CELL_ALREADY_SHOT`;
|
||||
- `STALE_GAME`;
|
||||
- `SERVER_BUSY`.
|
||||
|
||||
## 14. WebSocket и резервный опрос
|
||||
|
||||
- Точка подключения: `/ws`.
|
||||
- После открытия клиент передаёт токен сессии и последнюю известную `version`.
|
||||
- Сервер подтверждает сессию и отправляет персонализированный снимок состояния.
|
||||
- После каждого принятого действия сервер увеличивает `version` и рассылает новые безопасные представления всем подключённым клиентам.
|
||||
|
||||
Рекомендуемые серверные события:
|
||||
|
||||
- `state` — полный разрешённый снимок;
|
||||
- `player_joined` и `player_left`;
|
||||
- `game_started`;
|
||||
- `shot_result`;
|
||||
- `turn_changed`;
|
||||
- `ship_sunk`;
|
||||
- `game_finished`;
|
||||
- `rematch_status`;
|
||||
- `error`;
|
||||
- `ping`/`pong`.
|
||||
|
||||
Клиентская стратегия соединения:
|
||||
|
||||
1. открыть WebSocket;
|
||||
2. при обрыве выполнить повторные подключения с растущей задержкой, например 1, 2, 5 и 10 секунд;
|
||||
3. пока WebSocket недоступен, запрашивать `/api/state` каждые 2 секунды;
|
||||
4. после восстановления WebSocket прекратить опрос;
|
||||
5. если полученная версия не следует за текущей, запросить полный снимок.
|
||||
|
||||
HTTP остаётся каналом для команд и резервной синхронизации, а WebSocket используется для немедленной доставки изменений. Такой подход проще отлаживать и позволяет выполнить ход даже во время кратковременного восстановления WebSocket.
|
||||
|
||||
## 15. Алгоритмы
|
||||
|
||||
### 15.1. Автоматическая расстановка
|
||||
|
||||
1. Очистить поле.
|
||||
2. Перемешать порядок кораблей или обрабатывать их от длинных к коротким.
|
||||
3. Случайно выбрать ориентацию и начальную клетку.
|
||||
4. Проверить границы поля.
|
||||
5. Проверить клетки корабля и все соседние клетки вокруг него.
|
||||
6. Разместить корабль либо повторить попытку.
|
||||
7. Если лимит попыток исчерпан, очистить поле и запустить генерацию заново.
|
||||
8. Перед стартом проверить количество и длины кораблей, отсутствие касаний и выходов за границы.
|
||||
|
||||
### 15.2. Соперник ESP32
|
||||
|
||||
Для MVP предлагается стратегия `hunt/target`:
|
||||
|
||||
- в режиме поиска выбирать случайную необстрелянную клетку, предпочтительно по шахматному шаблону;
|
||||
- после попадания добавлять соседние клетки по вертикали и горизонтали в очередь целей;
|
||||
- после второго попадания определять ориентацию корабля и продолжать стрелять вдоль неё;
|
||||
- после потопления удалять из кандидатов клетки вокруг корабля и возвращаться в режим поиска;
|
||||
- никогда не использовать скрытое знание о расположении кораблей человека при выборе выстрела.
|
||||
|
||||
Стратегия достаточно понятна и интереснее чистого случайного выбора, но остаётся небольшой по объёму кода и памяти.
|
||||
|
||||
## 16. Статистика
|
||||
|
||||
Для каждого участника в текущем матче показываются:
|
||||
|
||||
- количество выстрелов;
|
||||
- попадания;
|
||||
- промахи;
|
||||
- точность в процентах;
|
||||
- число потопленных кораблей;
|
||||
- результат матча.
|
||||
|
||||
До перезагрузки ESP32 дополнительно накапливаются:
|
||||
|
||||
- сыгранные партии;
|
||||
- победы и поражения;
|
||||
- общие выстрелы и попадания;
|
||||
- общая точность.
|
||||
|
||||
Статистика обновляется только сервером. Для зрителя доступна симметричная статистика обоих игроков. При повторной игре статистика матча обнуляется, накопительная — сохраняется.
|
||||
|
||||
## 17. Обработка отключений и ошибок
|
||||
|
||||
- При отключении игрока активная партия не завершается сразу; его место и состояние сохраняются в памяти ESP32.
|
||||
- Интерфейс остальных клиентов показывает статус «Игрок переподключается».
|
||||
- После возвращения с тем же токеном игрок получает актуальный снимок и продолжает партию.
|
||||
- Для MVP партия может ожидать отключившегося игрока неограниченно; игрок 1 может вернуть систему в лобби отдельной командой подтверждения.
|
||||
- Отключение зрителя не влияет на игру.
|
||||
- Потеря WebSocket автоматически включает HTTP-опрос.
|
||||
- Потеря Wi-Fi или перезапуск ESP32 завершает текущую партию. После восстановления пользователи входят заново.
|
||||
- При переполнении лимита подключений новый зритель получает понятное сообщение, а действующая партия продолжается.
|
||||
|
||||
## 18. Предлагаемая структура проекта
|
||||
|
||||
```text
|
||||
esp32-battleship/
|
||||
├── platformio.ini
|
||||
├── include/
|
||||
│ ├── AppConfig.h
|
||||
│ ├── GameTypes.h
|
||||
│ ├── GameEngine.h
|
||||
│ ├── FleetGenerator.h
|
||||
│ ├── BotPlayer.h
|
||||
│ ├── SessionManager.h
|
||||
│ ├── StatePresenter.h
|
||||
│ └── Statistics.h
|
||||
├── src/
|
||||
│ ├── main.cpp
|
||||
│ ├── GameEngine.cpp
|
||||
│ ├── FleetGenerator.cpp
|
||||
│ ├── BotPlayer.cpp
|
||||
│ ├── SessionManager.cpp
|
||||
│ ├── StatePresenter.cpp
|
||||
│ └── WebApi.cpp
|
||||
├── data/
|
||||
│ ├── index.html
|
||||
│ └── assets/
|
||||
│ ├── app.css
|
||||
│ └── app.js
|
||||
└── test/
|
||||
├── test_fleet_generator/
|
||||
├── test_game_engine/
|
||||
└── test_bot_player/
|
||||
```
|
||||
|
||||
Wi-Fi-данные не следует фиксировать в публичном репозитории. Для MVP их можно вынести в локальный файл конфигурации, исключённый из Git. В дальнейшем можно добавить страницу первичной настройки сети.
|
||||
|
||||
## 19. Ограничения для ESP32-C6 Mini
|
||||
|
||||
До начала реализации необходимо зафиксировать точного производителя/модель платы, объём flash и наличие встроенного светодиода. Название «ESP32-C6 Mini» используется несколькими платами, поэтому эти параметры нельзя надёжно определить только по общему названию. Игровая архитектура от них не зависит, но они влияют на `platformio.ini`, таблицу разделов и назначение выводов.
|
||||
|
||||
- Хранить игровые поля в компактных фиксированных массивах, а не в динамических коллекциях.
|
||||
- Не создавать отдельную полную JSON-копию состояния для каждого клиента одновременно.
|
||||
- Ограничить размер входящих HTTP- и WebSocket-сообщений.
|
||||
- Не загружать изображения и крупные внешние библиотеки без необходимости.
|
||||
- Сжимать статические файлы (`gzip`) при сборке и отдавать их с правильными заголовками.
|
||||
- Настроить кэширование CSS и JavaScript, но не кэшировать персонализированное API-состояние.
|
||||
- Не выполнять длительные циклы генерации, хода бота или рассылки внутри критического сетевого обработчика.
|
||||
- Добавить периодическую очистку истёкших зрительских сессий.
|
||||
|
||||
## 20. Этапы реализации
|
||||
|
||||
### Этап 1. Каркас устройства
|
||||
|
||||
- Создать PlatformIO-проект и конфигурацию конкретной ESP32-C6 Mini.
|
||||
- Подключить ESP32 к домашней сети.
|
||||
- Настроить LittleFS и выдачу тестовой страницы.
|
||||
- Добавить `/api/health` и журналирование через Serial.
|
||||
|
||||
### Этап 2. Игровое ядро
|
||||
|
||||
- Реализовать структуры поля и кораблей.
|
||||
- Реализовать и протестировать генератор флота.
|
||||
- Реализовать выстрел, попадание, промах, потопление, смену хода и победу.
|
||||
- Реализовать статистику матча.
|
||||
|
||||
### Этап 3. Сессии и API
|
||||
|
||||
- Реализовать вход по имени, роли и токены.
|
||||
- Добавить лобби и запуск партии.
|
||||
- Добавить персонализированные представления состояния.
|
||||
- Реализовать HTTP-команды и проверки доступа.
|
||||
|
||||
### Этап 4. Синхронизация
|
||||
|
||||
- Подключить WebSocket.
|
||||
- Добавить версионирование состояния и рассылку событий.
|
||||
- Реализовать переподключение и HTTP fallback.
|
||||
|
||||
### Этап 5. Веб-интерфейс
|
||||
|
||||
- Реализовать экраны подключения, лобби, игры и результата.
|
||||
- Добавить адаптивные поля для телефона и планшета.
|
||||
- Добавить режим зрителя и понятные сообщения об ошибках.
|
||||
|
||||
### Этап 6. Бот и повторная игра
|
||||
|
||||
- Реализовать стратегию `hunt/target`.
|
||||
- Добавить задержку и визуализацию хода ESP32.
|
||||
- Добавить подтверждение повторной игры и накопительную статистику.
|
||||
|
||||
### Этап 7. Проверка на устройстве
|
||||
|
||||
- Проверить два телефона и несколько зрителей одновременно.
|
||||
- Проверить переподключение, потерю WebSocket и переход на опрос.
|
||||
- Проверить расход памяти и устойчивость нескольких последовательных партий.
|
||||
- Оптимизировать и сжать статические ресурсы.
|
||||
|
||||
## 21. Тестирование
|
||||
|
||||
### Автоматические тесты логики
|
||||
|
||||
- Генератор создаёт ровно 10 кораблей нужных размеров.
|
||||
- Ни один корабль не выходит за поле и не касается другого.
|
||||
- Выстрел по воде создаёт промах и меняет ход.
|
||||
- Попадание сохраняет ход.
|
||||
- Повторный выстрел отклоняется без изменения состояния.
|
||||
- Потопление корректно отмечает корабль и соседние клетки.
|
||||
- Уничтожение последнего корабля завершает партию.
|
||||
- Бот никогда не стреляет дважды в одну клетку.
|
||||
- Представление игрока и зрителя не содержит скрытых кораблей.
|
||||
|
||||
### Ручные сценарии
|
||||
|
||||
- Полная партия «игрок против игрока» с двух устройств.
|
||||
- Полная партия против ESP32.
|
||||
- Подключение и отключение нескольких зрителей во время партии.
|
||||
- Обновление страницы игроком и восстановление по токену.
|
||||
- Отключение WebSocket с продолжением через HTTP-опрос.
|
||||
- Повторная игра в обоих режимах.
|
||||
- Перезапуск ESP32 с ожидаемым сбросом партии.
|
||||
- Проверка интерфейса на узком телефоне и планшете в обеих ориентациях.
|
||||
|
||||
## 22. Критерии готовности MVP
|
||||
|
||||
MVP считается готовым, когда:
|
||||
|
||||
1. ESP32-C6 Mini стабильно подключается к заданной домашней Wi-Fi-сети и открывает русскоязычный интерфейс.
|
||||
2. Два пользователя могут войти по именам и полностью сыграть одну партию с разных устройств.
|
||||
3. Один пользователь может полностью сыграть партию против ESP32.
|
||||
4. Все расстановки соответствуют классическому набору и запрету касаний.
|
||||
5. Сервер отклоняет недопустимые и несвоевременные выстрелы.
|
||||
6. Скрытые корабли не присутствуют в сетевых ответах для соперника или зрителя.
|
||||
7. Минимум два зрителя могут наблюдать партию без влияния на неё; целевая конфигурация поддерживает до восьми.
|
||||
8. Изменения обычно появляются через WebSocket, а при его отключении интерфейс автоматически продолжает обновляться через HTTP.
|
||||
9. Обновление страницы восстанавливает роль и актуальное состояние, если ESP32 не перезагружалась.
|
||||
10. После окончания отображаются победитель и корректная статистика.
|
||||
11. Повторная игра создаёт новые расстановки и сохраняет накопительную статистику до перезагрузки.
|
||||
12. Интерфейс остаётся удобным на телефоне и планшете.
|
||||
13. Не менее 20 последовательных тестовых партий проходят без зависания, заметной утечки памяти или необходимости перезапуска устройства.
|
||||
|
||||
## 23. Возможные расширения после MVP
|
||||
|
||||
- Настройка Wi-Fi через временную точку доступа и captive portal.
|
||||
- Сохранение статистики в NVS.
|
||||
- Несколько комнат и больше одновременных игроков.
|
||||
- PIN-код партии и управление зрительским доступом.
|
||||
- Ручная расстановка кораблей.
|
||||
- Выбор варианта правил, включая строго один выстрел за ход.
|
||||
- Таймер хода, чат, звук и анимации.
|
||||
- Уровни сложности ESP32.
|
||||
- Локальное имя устройства через mDNS, например `battleship.local`.
|
||||
- OTA-обновление прошивки и веб-ресурсов.
|
||||
- Режим собственной точки доступа для игры без домашнего роутера.
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the convention is to give header files names that end with `.h'.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into the executable file.
|
||||
|
||||
The source code of each library should be placed in a separate directory
|
||||
("lib/your_library_name/[Code]").
|
||||
|
||||
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
Example contents of `src/main.c` using Foo and Bar:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries by scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
@@ -0,0 +1,14 @@
|
||||
; 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
|
||||
board = esp32-c6-devkitm-1
|
||||
framework = espidf
|
||||
@@ -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() {}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user