Intial commit
This commit is contained in:
+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-обновление прошивки и веб-ресурсов.
|
||||
- Режим собственной точки доступа для игры без домашнего роутера.
|
||||
Reference in New Issue
Block a user