@
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> @
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
<!-- Заполняет агент-имплементер. -->
|
||||
Reference in New Issue
Block a user