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> @
5.6 KiB
5.6 KiB
Phase 1: Оболочка игры + 1 физ-уровень + прогресс (MVP)
Status: ⬜ Not Started Parent plan: 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:routesbaseline 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 тесты не сломаны