Files
Learn_System/plans/quantik-game/CONTEXT.md
T
Maxim Dolgolyov 0b1925fd3b @
feat(quantik-game): фаза 4 — квантовые способности + SR-комнаты

Глава-созвездие quantum (L12–L16) и фирменные механики — всё через
безопасную модель спеки, движок и бэкенд НЕ тронуты (engine touch = 0):
- Суперпозиция: два тела ball+ball2, goal.when требует ОБА (зеркальный
  закон). Туннелирование: forbidden-зона wall + fail wall.hit && tunnel<1;
  способность тратит энергию → setParam(tunnel,1). Коллапс/прицел: пунктир-
  plot предсказанной траектории на паузе.
- Энергия — клиентский ресурс (localStorage quantik-energy, QuantikEnergy).
- SR-комната: мини-сессия повторения флешкарт в модалке (НЕ iframe),
  LS.fcStudySession/fcReview; «Знаю/Легко» дают энергию; текст карт
  экранируется, картинки — по regex-вайтлисту.
Все 5 уровней проверены на реальном движке (2★ достижимы; суперпозиция
требует оба тела; туннель-гейт блокирует без заряда). npm test 253/8
baseline; lint:routes 0; цепочка разблокировки проходима.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-06-14 10:29:35 +03:00

122 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Feature Context: Квантик — Законы Мира
## Current State
- Ветка `feature/quantik-game` ответвлена от `feature/sim-builder` (движок P1–P3 там, не в master).
- При ответвлении унаследован **чужой uncommitted WIP** sim-builder: `frontend/js/sim-builder.js`,
`frontend/sim-builder.html`, `.claude/settings.json` + множество untracked `tmp_*`/мусорных файлов.
⛔ НЕ трогать и НЕ коммитить этот WIP — стейджить только свои файлы поимённо.
- **Phase 0 реализован** (pending review): слой целей в движке `_sim_engine.js` (блок `goal`,
компиляция when/fail/stars через SimExpr, состояние результата, HUD-оверлей, API
`onGoal/getResult/resetResult`) + серверный гейт `validateSpec` пропускает `goal`/`game`.
Изменены: `frontend/js/labs/_sim_engine.js`, `backend/src/controllers/customSimController.js`.
Аддитивно: спека без `goal` ведёт себя ровно как раньше (HUD не создаётся, побед не считается).
Смоук 40/40; `npm test` 238 pass / 8 baseline fail; lint:routes 0.
- **Phase 1 реализован** (pending review): сквозной играбельный срез. Страница `/quantik`
(`frontend/quantik.html` + `frontend/js/game/quantik-game.js`) монтирует уровень-спеку через
`SimEngine.mount`; «игровой режим» = HUD из Ф0 (сам по наличию `goal`) + слайдеры params +
play/reset. Уровень `phys-artillery-1` — данные в `frontend/js/game/levels.js`
(`window.QuantikLevels`): physics-гравитация + body-запуск под углом θ/скоростью v, портал-цель,
бонус-звезда. На победу `onGoal``LS.gameProgressSubmit` + DOM-оверлей успеха (звёзды/время/попытки).
Прогресс: таблица `game_progress` (мигр.**076**), API `/api/game/progress` (GET/POST,
`gameController.js`+`routes/game.js`, смонтировано в `server.js` после `/api/custom-sims`),
клиент `LS.gameProgressList/Submit`. Сайдбар: `/quantik` (icon `rocket`) виден всем.
Новые: `076_game_progress.sql`, `gameController.js`, `routes/game.js`, `quantik.html`,
`js/game/levels.js`, `js/game/quantik-game.js`, `tests/game.test.js`. Изменены: `server.js`,
`js/api.js`, `js/sidebar.js`. `npm test` 251 pass / 8 baseline fail (game.test.js 13/13);
lint:routes 0; миграция применяется чисто.
- **Phase 2 реализован** (pending review): одиночный уровень превращён в **играбельный мир**.
Карта-созвездие (`frontend/js/game/map.js`, `window.QuantikMap`) на звёздном фоне: 6 физ-уровней
в 2 главах (Кинематика 1–3, Динамика 4–6), узлы-«звёзды» со статусом (locked/available/completed+
звёзды), линии-связи, поэтапное появление. Шапка: нарратор-Квантик (`PetSprite`), XP-бар + «уровень
Квантика», всего звёзд, скин-пикер (8 скинов, часть за XP/звёзды). Контент уровней расширен в
`levels.js` (метаданные `chapter/order/par_ms/unlockStars`, по 2 звезды: кристалл + норматив времени).
Разблокировка/XP/группировка — ЧИСТЫЕ функции в новом `frontend/js/game/progress-logic.js`
(`window.QuantikProgress`), покрыты тестом. Навигация: карта→интро(нарратор)→уровень→успех
(нарратор по звёздам)→карта; «Дальше» активирована (`nextPlayable`); скин тинтует героя+нарратора
(localStorage `quantik-skin`). **Backend НЕ тронут** — XP клиентская агрегация из `game_progress`.
Новые: `js/game/map.js`, `js/game/progress-logic.js`. Изменены: `quantik.html`, `js/game/levels.js`,
`js/game/quantik-game.js`. `node --check` все OK; смоуки (логика 16/16, рендер 7/7, winnability 6/6
на реальном движке) зелёные и удалены; `npm test` 259/251 pass / 8 baseline fail (без изменений);
lint:routes 0.
- **Phase 3 реализован** (pending review): новый ТИП уровня — Квантик едет по кривой `y=f(x)`,
которую СОБИРАЕТ игрок (слайдеры коэффициентов). Движок (`_sim_engine.js`, аддитивно):
(1) «бегунок по кривой» — на `plot` поле `runner:{duration,hold}` кладёт в env `<id>.runX/.runY/.runDone`;
герой = обычный point на `curve.runX/runY` (f компилируется 1 раз, питает И кривую, И бегунок — нет само-ссылки);
(2) `type:'zone'` (rect/circle, kind forbidden/target/collect, track) → булево env-поле `<zoneId>.hit` (1/0);
goal/fail/stars ссылаются на него. ⛔ Предикаты в грамматику SimExpr НЕ добавлялись. Новая глава-созвездие
`functions` в `levels.js` (5 уровней: луч/синус/парабола/модуль/экспонента, `unlockStars` 9..17 ≤ 18 макс
физ-звёзд → нет дедлока); map.js НЕ тронут (рисует по метаданным). Сервер `validateSpec` принимает
`zone`+`runner` (OBJECT_TYPES + поля). Изменены: `_sim_engine.js`, `levels.js`, `customSimController.js`,
`quantik.html` (per-level бейдж темы). Новые тесты: custom-sims.test.js +2 (приём zone+runner, отказ
unknown type) — 26/26. Headless vm-смоук (per-level solvability + logic 29/29) зелёный и удалён.
`npm test` 261 / 253 pass / 8 baseline fail (без новых); lint:routes 0; все `node --check` OK.
- **Phase 4 реализован** (pending review): фирменные квантовые способности + SR-связка, ВСЁ через
безопасную модель (движок `_sim_engine.js` НЕ тронут). Новый `frontend/js/game/quantik-abilities.js`:
`window.QuantikEnergy` (клиентский ресурс энергии, localStorage `quantik-energy`, 0..99;
grant/spend/canSpend/rewardForQuality; TUNNEL_COST=3, GOOD=1/EASY=2) + `window.QuantikAbilities`
(`mountBar` — HUD энергии + кнопки «Повторение/Туннель/Прицел» оверлеем на сцене; `openRestRoom`
мини-сессия повторения флешкарт в модалке, реюз `LS.fcListDecks/fcStudySession/fcReview`, НЕ iframe).
**Туннель** = тратит энергию → `inst.setParam('tunnel',1)`; барьер = `forbidden`-зона `wall`,
`fail:'wall.hit && tunnel<1'` (tunnel — не слайдер, отсутствует в env → 0 → стена сплошная).
**Прицел** = пауза-тоггл над пунктир-plot предсказанной траектории. **Суперпозиция** = чистый
контент: 2 тела `ball`+`ball2`, `goal.when` с обоими. Глава `quantum` (L12L16) + `CHAPTERS.quantum`
в `levels.js`; карта рисует автоматически (map.js не тронут). `js/api.js` +2 врапера
(`fcStudySession`, `fcReview`). `quantik.html` +script-тег +CSS `.qa-*`. **Backend НЕ тронут.**
Все `node --check` OK (вкл. инлайн quantik.html); headless vm-смоук (РЕАЛЬНЫЕ движки):
энергия + суперпозиция-оба-тела + tunnel-flips-fail + per-level solvability sweep (5/5 выигрываемы,
full-star достижим, L15/L16 без tunnel = 0 win) + регресс 11 существующих уровней — 48/48, удалён.
Контент-фикс: монета L16 (5,6)r0.7 → (5,6.9)r0.85 (была несовместима со 2-й звездой k≥6.8).
`npm test` 261 / 253 pass / 8 baseline fail (без новых); lint:routes 0.
## Key Architecture Decisions
- **«Атом» = блок `goal` в спеке** (булево SimExpr). Любой уровень = спека SimForge + `goal`.
Движок вычисляет `goal.when` каждый кадр; победа → result + callback. Нет `goal` → no-op.
- **Уровни хранятся в `custom_sims`** (cat='game'), а не в новой таблице. Реюз авторинга/шаринга/embed.
Новые таблицы — только под ПРОГРЕСС игрока и лидерборд (мигр.).
- **Уточнение Ф1**: для MVP уровни — ВСТРОЕННЫЕ ДАННЫЕ в `frontend/js/game/levels.js`
(`window.QuantikLevels`, форма `{ id, title, subject?, hint?, spec }`), а не записи `custom_sims`.
`custom_sims` cat='game' остаётся целевым хранилищем для авторённых уровней (Ф5); реестр тогда
станет асинхронным (загрузка опубликованных + слияние со встроенными той же формы записи).
- **Герой Квантик**: в уровне = engine point с `body` + glow + trail (визуал P2). На карте/в
диалогах = `PetSprite.render(level, mood, accessories, colorKey, streak, pattern)` (DOM SVG).
- **Управление = чинить закон**, а не WASD: игрок крутит `params`-слайдеры движка (угол/скорость/
гравитация) или собирает `f(x)`; затем «Запуск» — симуляция проигрывается к цели.
- **Безопасность**: цвета — только в canvas-стоки; текст спеки — escape (`& < >`); выражения —
только длина на сервере, исполняет безопасный SimExpr на клиенте.
## Engine touch-points (Phase 0)
- Спека v1 формат — в шапке `_sim_engine.js`. Добавить `goal`/`game` СЮДА (документировать).
- rAF-цикл `_renderFrame` (вычисляет env). Добавить `_evalGoal()` после построения env.
- `mount()` возвращает инстанс — добавить `onGoal`, `getResult`, `resetResult`.
- HUD — DOM-оверлей `_labelLayer`/новый слой (как readout-бейджи). Без эмодзи, inline SVG.
- Серверный гейт `customSimController.validateSpec` (:93) — разрешить `goal`/`stars`/`hint`/`game`.
## Cross-Phase Dependencies
- Phase 1+ зависят от `goal`/`getResult`/`onGoal` из Phase 0.
- Phase 2 (XP/скины) зависит от прогресса Phase 1.
- Phase 4 (туннелирование) зависит от флешкарт-SR API.
- Phase 5 (авторинг) трогает sim-builder — к этому моменту чужой P4-WIP должен быть смержен в
sim-builder; свериться перед стартом фазы (возможен мерж base-ветки).
- Phase 6 (живая гонка) зависит от моста `sim_state` (Ф7 sim-builder) — он на base-ветке.
## Temporary Workarounds
(пока нет)
## Phase 0 — API/гочи (для следующих фаз)
- Движковое API цели: `inst.onGoal(cb)` (1 раз при победе, cb получает `getResult()`),
`inst.getResult()``{won,failed,timeMs,attempts,stars:{got,total}}` (без goal → `null`),
`inst.resetResult()` (сброс результата, НЕ считается попыткой). `inst.reset()` = полный
перезапуск уровня + `attempts++` (пользовательская попытка; первый авто-reset при mount НЕ считается).
- HUD появляется **автоматически** при наличии `goal` в спеке (отдельного флага «game mode» нет).
- `timeMs` = **мировое время** `t` от старта (`max(1, round(t*1000))`), детерминизм; не wallclock.
- Env цели = весь env кадра + единственный доп.идентификатор **`tries`** (= attempts). Других не вводить.
- Серверный `validateSpec` принимает `goal{when,title,hint,hold,fail,stars≤3}` и `game{...}` (резерв Ф1/5);
выражения не исполняются (только длина ≤500), текст escape+обрезка.
- Победа делает `pause()` в кадре; следующий queued-rAF выходит рано → `onGoal` не задвоится.
## Open Questions / Notes
- Категория `cat='game'` — проверить список `CATS` в customSimController.js, расширить при необходимости.
- Ассеты: разрешены CC0/открытые из интернета (выбор пользователя) — фиксировать источник+лицензию
в коммите/доке; визуальная база остаётся in-house (PetSprite/canvas/FLUX).
- Маршрут страницы игры: clean URL `/quantik` (паттерн `/sim-builder`, `/lab`).