539 lines
38 KiB
Markdown
539 lines
38 KiB
Markdown
# 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/session/leave` | Освобождение только текущей сессии |
|
|
| `POST` | `/api/session/profile-reset` | Освобождение сессии перед локальной очисткой профиля |
|
|
| `POST` | `/api/game/config` | Выбор режима игроком 1 |
|
|
| `POST` | `/api/game/start` | Запуск готовой партии |
|
|
| `POST` | `/api/game/shot` | Выстрел по координатам |
|
|
| `POST` | `/api/game/rematch` | Подтверждение повторной игры |
|
|
| `POST` | `/api/game/reset` | Аварийный полный сброс игровой памяти игроком |
|
|
| `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-обновление прошивки и веб-ресурсов.
|
|
- Режим собственной точки доступа для игры без домашнего роутера.
|