4b5c8077d3
feat(quantik-game): фаза 0 — слой целей в движке (goal/HUD/result) Декларативный блок goal в спеке SimForge (булево SimExpr-условие победы), вычисляемый каждый кадр: фиксация результата (победа/время/попытки/звёзды), callback onGoal, HUD-оверлей (цель/звёзды/подсказка/баннер, inline SVG). API инстанса: onGoal/getResult/resetResult. Серверный validateSpec пропускает goal/game (длина выражений + escape текста, без исполнения). Аддитивно: спека без goal ведёт себя как раньше. Смоук 40/40; npm test 238 pass/8 baseline; lint:routes 0. План фичи (7 фаз) + CONTEXT. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
64 lines
5.6 KiB
Markdown
64 lines
5.6 KiB
Markdown
# Phase 1: Оболочка игры + 1 физ-уровень + прогресс (MVP)
|
||
|
||
**Status:** ⬜ Not Started
|
||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||
**Domain:** fullstack
|
||
|
||
## Objective
|
||
Сквозной играбельный срез: страница `/quantik` грузит уровень-спеку, монтирует движок в
|
||
«игровом режиме» (управление = слайдеры закона + кнопка «Запуск»), на победу шлёт результат
|
||
на сервер, показывает экран успеха со звёздами/временем. Прогресс сохраняется в БД.
|
||
Первый уровень — «Артиллерия Квантика»: угол+скорость, попасть в портал, собрать звезду.
|
||
|
||
## Tasks
|
||
- [ ] Task 1: Миграция (следующий свободный номер) `game_progress`: `id, user_id, level_id TEXT,
|
||
best_time_ms INTEGER, best_stars INTEGER, attempts INTEGER, completed_at`. Индекс по (user_id, level_id) UNIQUE.
|
||
- [ ] Task 2: Контроллер `gameController.js` + роутер `game.js`, смонтировать в `server.js`
|
||
(после `/api/custom-sims`). Эндпоинты: `GET /api/game/progress` (свой прогресс по всем
|
||
уровням), `POST /api/game/progress` `{level_id, time_ms, stars}` (upsert: пишем лучший
|
||
результат — min time / max stars; attempts++). auth-only; валидация входа.
|
||
- [ ] Task 3: Клиент `LS.gameProgressList()` / `LS.gameProgressSubmit(levelId, {time_ms, stars})` в js/api.js.
|
||
- [ ] Task 4: Уровень как ДАННЫЕ: модуль `frontend/js/game/levels.js` (или сид в `custom_sims`).
|
||
Для MVP — встроенная спека уровня `phys-artillery-1` (physics + goal + 1 star + portal/star объекты).
|
||
Решение источника уровней зафиксировать в CONTEXT.md (встроенные данные сейчас; custom_sims в Ф5).
|
||
- [ ] Task 5: Страница `frontend/quantik.html` + `frontend/js/game/quantik-game.js`:
|
||
доступ всем авторизованным (LS.initPage()); подключает `_sim_expr.js`+`_sim_engine.js`
|
||
тем же путём, что lab.html/sim-builder.html. Монтирует уровень, ставит `onGoal` → submit + экран успеха.
|
||
- [ ] Task 6: «Игровой режим» движка/обёртки: цель видна (HUD из Ф0), управление = существующие
|
||
слайдеры params; кнопки «Запуск»(play)/«Сброс»(reset). Без редакторских панелей.
|
||
- [ ] Task 7: Экран успеха (DOM-оверлей страницы): звёзды, время, попытки, кнопки «Ещё раз»/«Дальше»
|
||
(для MVP «Дальше» неактивна/возврат). Inline SVG, без эмодзи.
|
||
- [ ] Task 8: Пункт в сайдбаре `js/sidebar.js` — `/quantik` в группе practice (по примеру `/sim-builder`),
|
||
видимость по роли (доступно ученикам — это игра). `isActive('/quantik')` подсветка.
|
||
- [ ] Task 9: Тест бэкенда `backend/tests/game.test.js` (паттерн lab-links.test.js: свой app.use
|
||
нового роутера, getToken/inject): submit пишет лучший результат, не ухудшает, attempts++,
|
||
требует auth, валидирует вход. Headless-смоук страницы по возможности (vm + стаб), иначе ручная проверка логики.
|
||
|
||
## Files to Modify/Create
|
||
- `backend/src/db/migrations/0NN_game_progress.sql` — таблица прогресса.
|
||
- `backend/src/controllers/gameController.js`, `backend/src/routes/game.js` — API.
|
||
- `backend/src/server.js` — монтаж роутера.
|
||
- `frontend/quantik.html`, `frontend/js/game/quantik-game.js`, `frontend/js/game/levels.js` — клиент+уровень.
|
||
- `frontend/js/api.js` — `LS.gameProgress*`.
|
||
- `frontend/js/sidebar.js` — пункт меню.
|
||
- `backend/tests/game.test.js` — тест.
|
||
|
||
## Acceptance Criteria
|
||
- `/quantik` грузится, монтирует уровень, цель видна; «Запуск» проигрывает физику.
|
||
- Попадание в портал (+звезда) → экран успеха с временем/звёздами; результат записан в `game_progress`.
|
||
- Повторный худший результат не перезаписывает лучший; attempts растёт.
|
||
- `npm run migrate` применяет миграцию; `npm test` зелёный (+ новый тест); `lint:routes` baseline 0.
|
||
|
||
## Notes
|
||
- Маршрутизация `/js/game/*`: помнить гочу sim-builder — `/js` мапится на корневой `js/`, а файлы
|
||
лежат во `frontend/js/game/` → отдаются через `express.static(frontendDir)`. Не трогать server.js static.
|
||
- Роуты `:id` прикрыть `authMiddleware` на уровне роутера (lint:routes baseline 0).
|
||
- Время — из `getResult().timeMs` (Ф0).
|
||
|
||
## Review Checklist
|
||
- [ ] Все задачи; конвенции (ownership/auth как studentMaterials/customSim); без эмодзи/eval
|
||
- [ ] Миграция применяется; API безопасен; тест зелёный; lint baseline 0; existing тесты не сломаны
|
||
|
||
## Handoff to Next Phase
|
||
<!-- Заполняет агент-имплементер. -->
|