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>
@
This commit is contained in:
Maxim Dolgolyov
2026-06-14 10:29:35 +03:00
parent 978448d99b
commit 0b1925fd3b
9 changed files with 1071 additions and 24 deletions
+17
View File
@@ -50,6 +50,23 @@
`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`.
+2 -2
View File
@@ -62,7 +62,7 @@
- [x] Phase 1: Оболочка игры + 1 физ-уровень + прогресс [domain: fullstack] → [subplan](./phase-1-shell-first-level.md)
- [x] Phase 2: Карта-созвездие + мир физ-уровней + XP/скины [domain: fullstack] → [subplan](./phase-2-map-world-xp.md)
- [x] Phase 3: Граф-уровни (движение по f(x)) + зоны-препятствия [domain: fullstack] → [subplan](./phase-3-graph-levels.md)
- [ ] Phase 4: Квантовые способности + SR-комнаты [domain: fullstack] → [subplan](./phase-4-quantum-abilities-sr.md)
- [x] Phase 4: Квантовые способности + SR-комнаты [domain: fullstack] → [subplan](./phase-4-quantum-abilities-sr.md)
- [ ] Phase 5: Авторинг уровней в sim-builder + раздача классу [domain: fullstack] → [subplan](./phase-5-authoring-sharing.md)
- [ ] Phase 6: Класс-лидерборд / живая гонка (classroom SSE) [domain: fullstack] → [subplan](./phase-6-leaderboard-live.md)
@@ -74,7 +74,7 @@
| Phase 1: Оболочка + 1 уровень + прогресс | fullstack | ✅ Done | ✅ | ✅ | ✅ |
| Phase 2: Карта + мир + XP/скины | fullstack | ✅ Done | ✅ (1 🟡 fixed) | ✅ | ✅ |
| Phase 3: Граф-уровни + зоны | fullstack | ✅ Done | ✅ | ✅ | ✅ |
| Phase 4: Квантовые способности + SR | fullstack | ⬜ Not Started | | | |
| Phase 4: Квантовые способности + SR | fullstack | ✅ Done | | | |
| Phase 5: Авторинг + раздача | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
| Phase 6: Лидерборд / живая гонка | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
@@ -1,6 +1,6 @@
# Phase 4: Квантовые способности + SR-комнаты
**Status:** ⬜ Not Started
**Status:** ✅ Done (reviewed — PASS, committed)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
@@ -10,19 +10,21 @@
(проход сквозь тонкую стену за «энергию», которую даёт быстрое SR-повторение).
## Tasks
- [ ] Task 1: Суперпозиция: уровень с двумя телами-копиями Квантика; общий «закон» (params) рулит
обеими; цель — обе достигают порталов/условий (`goal.when` ссылается на оба `<id>.x/.y`).
Реюз существующей мульти-body физики. Визуал — две glow-точки (полупрозрачные «фантомы»).
- [ ] Task 2: Коллапс/пауза-прицел: на паузе показать предсказанную траекторию (`plot trace`/
пунктир) текущего закона до запуска — «прицеливание». Реюз предпросмотра старта (P1/P2).
- [ ] Task 3: Туннелирование: «энергетический заряд» расходуется, чтобы пройти сквозь помеченную
`tunnelable:true` стену (стена временно проницаема). Энергия в HUD.
- [ ] Task 4: SR-комната: перед/в уровне — мини-сессия повторения флешкарт (реюз Tier-1 SR API,
мигр.074). Правильные ответы дают «энергию туннелирования». Открыть существующий движок
повторения в модалке/панели игры; начислять заряды по результату.
- [ ] Task 5: Контент: 2–3 уровня под каждую способность (обучающий + применение).
- [ ] Task 6: Тесты: суперпозиция (оба тела в `goal`), расход/начисление энергии (чистая логика),
проницаемость стены при заряде; смоук.
- [x] Task 1: Суперпозиция: уровни L12/L13 с двумя телами `ball`+`ball2`, общий закон (theta,v)
рулит обеими зеркально; `goal.when` ссылается на `ball.*` И `ball2.*` (победа — только обе).
Реюз мульти-body физики. ball2 — полупрозрачный «фантом» (tintHeroSpec в quantik-game.js).
- [x] Task 2: Коллапс/пауза-прицел: уровень L14 несёт пунктир-`plot` (id 'aim') с предсказанной
параболой текущего закона; способность «Прицел» = пауза-тоггл (`inst.pause/play`) для прицела.
- [x] Task 3: Туннелирование: барьер = `forbidden`-зона `wall`; `fail:'wall.hit && tunnel<1'`.
Способность «Туннель» тратит TUNNEL_COST энергии → `inst.setParam('tunnel',1)` (стена
проницаема). `tunnel` — НЕ слайдер (только способность); отсутствует в env → 0 → стена сплошная.
- [x] Task 4: SR-комната: `QuantikAbilities.openRestRoom` — мини-сессия повторения в модалке
(НЕ iframe). `LS.fcListDecks → fcStudySession → fcReview`; «Знаю/Легко» начисляют энергию.
Пусто (нет колод/нет due) → дружелюбное окно + ссылка `/flashcards`.
- [x] Task 5: Контент — глава `quantum` (5 уровней): L12/L13 суперпозиция, L14 прицел, L15/L16 туннель.
- [x] Task 6: Тесты (headless vm + РЕАЛЬНЫЕ движки): суперпозиция (оба тела), энергия (grant/spend/
reward чистая логика), tunnel flips fail, per-level solvability sweep (5/5 выигрываемы,
full-star достижим), регресс существующих 11 уровней без throw. 48/48; harness удалён.
## Files to Modify/Create
- `frontend/js/labs/_sim_engine.js` — поле `tunnelable` у стены + расход энергии (аддитивно, документировать).
@@ -42,7 +44,45 @@
- SR-движок повторения уже существует — переиспользовать, не дублировать расписание.
## Review Checklist
- [ ] Все задачи; аддитивность; без эмодзи/eval; тесты зелёные; lint baseline 0
- [x] Все задачи; аддитивность; без эмодзи/eval; тесты зелёные; lint baseline 0
## Handoff to Next Phase
<!-- Заполняет агент-имплементер. -->
### Что добавлено (файлы)
- **Новый** `frontend/js/game/quantik-abilities.js` (`window.QuantikEnergy` + `window.QuantikAbilities`).
- `frontend/js/game/levels.js` — глава `quantum` (L12L16) + `CHAPTERS.quantum`.
- `frontend/js/game/quantik-game.js``tintHeroSpec` тинтует `ball2` (фантом); `start()` монтирует
`QuantikAbilities.mountBar` (оборачивает `inst.destroy` для снятия панели); `openRest()` + сброс
`abilities.resetAbilities()` на «Ещё раз».
- `frontend/quantik.html``<script src="/js/game/quantik-abilities.js">` (после map.js, до game.js)
+ CSS `.qa-*` (панель/HUD/тост/SR-модалка/карточка/оценки).
- `js/api.js``fcStudySession(deckId)` (GET `/flashcards/decks/:id/study`) и
`fcReview(cardId, quality)` (POST `/flashcards/cards/:id/review` body `{quality}`).
- ⛔ Движок `_sim_engine.js` НЕ тронут — туннель/прицел/суперпозиция выражены через безопасную
модель спеки (зона+undefined-param / pause-toggle / два тела). Engine touch = 0.
### Энергия (клиентский ресурс)
- localStorage ключ **`quantik-energy`** (целое 0..`ENERGY_MAX`=99). `window.QuantikEnergy`:
`getEnergy/setEnergy/grantEnergy/spendEnergy/canSpend/rewardForQuality/onEnergyChange`.
`TUNNEL_COST=3`, `REWARD_GOOD=1` (q=4 «Знаю»), `REWARD_EASY=2` (q=5 «Легко»).
- Туннель тратит энергию ОДИН раз за попытку (`tunnelUsed`); «Ещё раз»/новый mount сбрасывают
`tunnel→0` (стена снова сплошная).
### Контракты для Phase 5 (авторинг)
- **Способность «Туннель»** активируется, если `goal.fail/when/stars` упоминают слово `tunnel`
(`QuantikAbilities.levelHasTunnel`). Авторский UI должен уметь добавить `forbidden`-зону `wall`
и `fail:'wall.hit && tunnel<1'`. `tunnel` — служебный param (не слайдер).
- **Способность «Прицел»** появляется, если на сцене есть `plot` с `id:'aim'` ИЛИ `lineStyle:'dashed'`
(`levelHasAim`). Авторский UI может предложить «предсказанную траекторию» как пунктир-plot.
- **Суперпозиция** = чистый контент: второе тело `id:'ball2'` (point/circle с `body`), `goal.when`
с обоими `ball.*`+`ball2.*`. `tintHeroSpec` уже тинтует `ball`/`ball2`; авторские id вне этих
двух тинтоваться скином не будут (Phase 5 при желании расширит).
- **Глава = метаданные**: `quantum` появилась на карте без правок map.js (`groupByChapter` +
`Levels.chapter`). Новая глава = новый `chapter`-ключ + `CHAPTERS`-запись (контракт Phase 2).
### Solvability (проверено на реальном движке, harness удалён)
- L12 Раздвоение: 52 win-комбо (есть выигрышная v почти для любой θ), full-star напр. θ70/v10.
- L13 Две двери: full-star θ45/v11. L14 Прицел: full-star θ60/v12 (2/2 звезды).
- L15 барьер: с tunnel=1 — 4 win, full-star a0/b3.7; БЕЗ tunnel — 0 win (гейт работает).
- L16 капстоун: с tunnel=1 — full-star a-0.25/k7.2 (монета сдвинута на (5,6.9) r0.85 — была (5,6)
r0.7, конфликтовала со 2-й звездой k≥6.8); БЕЗ tunnel — 0 win.