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> @
89 lines
7.8 KiB
Markdown
89 lines
7.8 KiB
Markdown
# Phase 4: Квантовые способности + SR-комнаты
|
||
|
||
**Status:** ✅ Done (reviewed — PASS, committed)
|
||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||
**Domain:** fullstack
|
||
|
||
## Objective
|
||
Фирменные «квантовые» механики, дающие герою идентичность, плюс связка с флешкарт-SR:
|
||
**суперпозиция** (раздвоение), **коллапс/пауза** (точный прицел), **туннелирование**
|
||
(проход сквозь тонкую стену за «энергию», которую даёт быстрое SR-повторение).
|
||
|
||
## Tasks
|
||
- [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` у стены + расход энергии (аддитивно, документировать).
|
||
- `frontend/js/game/quantik-game.js` — способности, HUD энергии, SR-комната-модалка.
|
||
- интеграция с флешкарт-SR (клиентский модуль повторения / `LS` API).
|
||
- `frontend/js/game/levels.js` — уровни способностей.
|
||
- тест(ы).
|
||
|
||
## Acceptance Criteria
|
||
- Суперпозиция: победа только когда обе копии выполнили условие.
|
||
- Коллапс: на паузе виден предсказанный путь.
|
||
- Туннелирование тратит энергию; SR-повторение её пополняет; стена проницаема только при заряде.
|
||
- Без eval/эмодзи; existing симуляции/SR не сломаны; тесты зелёные; lint baseline 0.
|
||
|
||
## Notes
|
||
- Имя param `e` зарезервировано (число Эйлера в SimExpr) — для энергии брать `energy`/`charge`.
|
||
- SR-движок повторения уже существует — переиспользовать, не дублировать расписание.
|
||
|
||
## Review Checklist
|
||
- [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` (L12–L16) + `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.
|