Files
Learn_System/plans/quantik-game/phase-3-graph-levels.md
T
Maxim Dolgolyov 978448d99b @
feat(quantik-game): фаза 3 — граф-уровни (движение по f(x)) + зоны

Новый тип уровня: Квантик едет по кривой y=f(x), которую игрок собирает
слайдерами коэффициентов, проходя сквозь зоны-препятствия. Движок
(аддитивно): plot.runner → env-поля curve.runX/runY/runDone (f компилится
1 раз, питает И кривую, И бегунок-героя, без само-ссылки); type zone
(forbidden/target/collect) → булево env-поле zone.hit. Грамматика
выражений ЗАКРЫТА — никаких inzone()-предикатов, только именованные
env-поля (модель t/tries из Ф0), без eval. Глава-созвездие functions из
5 уровней (луч/синус/парабола/модуль/экспонента), разблокировка 9/11/13/
15/17 (цепочка проходима). validateSpec принимает zone+runner. Все 5
уровней независимо проверены на движке (2★ достижимы). npm test 253/8
baseline; custom-sims 26/26; lint:routes 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-06-13 17:07:33 +03:00

97 lines
10 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.
# Phase 3: Граф-уровни (движение по f(x)) + зоны-препятствия
**Status:** ✅ Done (reviewed — PASS, committed)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
## Objective
Новый тип уровня: Квантик движется по кривой `y=f(x)`, которую **собирает игрок** (настраивает
параметры/выбирает выражение). Препятствия — «запретные зоны»; цель/звёзды/проигрыш — выражения.
Реюз `plot` + `SimExpr`. Сид граф-главы.
## Tasks
- [x] Task 1: «Бегунок по кривой»: герой-точка с `x` = функция t (напр. линейный проход xmin→xmax),
`y = f(x)` через ту же скомпилированную функцию, что у `plot`. Кривая рисуется (P3 plot),
герой едет по ней с glow/trail. Без физики (кинематический проход), либо мягкая физика — на выбор уровня.
`plot.runner:{duration,hold}` кладёт в env `<plotId>.runX/.runY/.runDone`; герой = обычный point
с `x:'curve.runX', y:'curve.runY'`, glow+trail. f компилируется 1 раз и питает И кривую, И бегунок.
- [x] Task 2: Тип объекта/поле «зона» (forbidden/target): прямоугольник/круг в мире + удобные
env-предикаты (или документированный паттерн: `fail:'inzone(...)'`). Реализовать helper-предикаты
БЕЗ расширения небезопасного синтаксиса — предпочесть готовить булевы поля зон в env
(напр. `zone1.hit`) на основе позиции героя, чтобы `goal`/`fail` ссылались на них.
`type:'zone'` (shape rect/circle, kind forbidden/target/collect, track). Движок кладёт `<zoneId>.hit`
(1/0) в env. ⛔ Никаких inzone()-предикатов в грамматике — только именованные булевы env-поля.
- [x] Task 3: Цель = добраться до конца/в целевую зону, не задев запретные (`fail`). Звёзды: пройти
под нормативом, собрать бонус-точки (зоны-сборы).
→ goal.when=`'gate.hit'`, fail=`'pit.hit'`, stars=[collect-zone hit, доп. условие формы кривой].
- [x] Task 4: Управление: слайдеры коэффициентов `f(x)` (a·sin(b·x+c)+d и т.п.) ИЛИ выбор/набор
выражения с inline-проверкой `SimExpr.compile(...).error` (как в sim-builder). Безопасно.
→ коэффициенты = обычные `params`-слайдеры движка; крутишь → кривая+путь героя перестраиваются.
Свободный ввод выражения не понадобился (слайдеры коэффициентов достаточны для MVP-главы).
- [x] Task 5: Контент: сид граф-главы (~4–5 уровней): синус под мостом, парабола над ямой,
кусочная подгонка, экспонента/логарифм — растущая сложность, привязка к темам алгебры.
→ 5 уровней в `functions`: луч (a·x+b), синус (A·sin(k·x)), парабола (a·(x5)²+k),
модуль (a·|x−m|+1), экспонента (c·e^(r·x)). Все solvable (см. Concerns).
- [x] Task 6: Интеграция в карту (Ф2): новая глава-созвездие; общий конвейер результата/XP.
→ глава `functions` в `CHAPTERS`; map.js НЕ тронут (рисует по метаданным). Бейдж темы в quantik.html
стал per-level (`subject` → Физика/Алгебра) — аддитивно.
- [x] Task 7: Тесты: проход по кривой достигает цели; задевание зоны → fail; смоук рендера кривой+героя.
→ headless vm-смоук (логика+per-level solvability, 29/29, удалён); серверный тест приёма
zone+runner спеки (custom-sims.test.js, +2 теста, остаётся).
## Files to Modify/Create
- `frontend/js/labs/_sim_engine.js` — поддержка «бегунка по кривой» (если не выразимо текущими полями)
и подготовка булевых полей зон в env. Аддитивно, документировать в шапке.
- `frontend/js/game/levels.js` — граф-глава.
- `frontend/js/game/quantik-game.js` / `map.js` — новая глава, управление коэффициентами.
- тест(ы).
## Acceptance Criteria
- Квантик едет по собранной игроком кривой; правильная `f(x)` проводит между препятствиями к цели.
- Задевание запретной зоны → проигрыш; норматив/сборы дают звёзды.
- Кривая безопасна (SimExpr, без eval); existing симуляции/уровни не затронуты; тесты зелёные.
## Notes
- НЕ вводить произвольные функции-предикаты в синтаксис выражений (безопасность). Зоны → булевы env-поля.
- Переиспользовать P3 plot (несколько кривых, заливка, маркеры) для визуала «земли»/препятствий.
## Review Checklist
- [x] Все задачи; аддитивность движка; без эмодзи/eval; тесты зелёные; lint baseline 0
## Handoff to Next Phase
### Контракт «бегунка по кривой» (движок, `_sim_engine.js`)
- На объекте `plot`: `runner:{ duration?:8, hold?:true }`. Делает из ПЕРВОЙ кривой plot дорожку.
- Движок кладёт в env (в `_buildEnv`, ДО формульных центров): `<plotId>.runX` (= `a + (ba)·clamp(t/duration,0,1)`),
`<plotId>.runY` (= f(runX) ТОЙ ЖЕ скомпил. функции, что рисует кривую), `<plotId>.runDone` (1 при t≥duration).
- Герой = обычный `point` с `x:'curve.runX', y:'curve.runY'` + glow + trail. НЕ тело → нет само-ссылки
(f компилируется один раз, питает И кривую, И бегунок). `hold:true` — остаётся на конце; иначе зацикливание по `time.loop`.
- ⛔ Никакого eval: f — обычное SimExpr-выражение кривой.
### Контракт зон (движок)
- `type:'zone'`, `id`, `shape:'rect'|'circle'`, `kind:'forbidden'|'target'|'collect'` (цвет/семантика),
геометрия (rect: x,y центр + w,h; circle: x,y + r — числа ИЛИ выражения), `track?:'ball'` (чью позицию тестить), `label?`, `color?`.
- Движок кладёт `<zoneId>.hit` (1/0) в env (последним — нужна актуальная позиция героя). `goal.when/fail/stars[].when` ссылаются на него.
- ⛔ Предикаты в синтаксис выражений НЕ добавлялись — только именованные булевы env-поля (модель безопасности `t`/`tries` из Ф0).
- Рисуется в `_drawObject`/`_drawZone`: forbidden=красный пунктир, target=зелёный, collect=золотой пунктир. Цвета — только canvas-стоки.
- Зона НЕ кладёт `<zoneId>.x/.y` как центр объекта (`hasCenter` пропущен для type==='zone').
### Как определяется граф-уровень (данные, `levels.js`)
- Хелперы: `road(exprStr,a,b,dur)` (plot+runner, id 'curve'), `graphHero()` (point ball на curve.runX/runY),
`rectZone/circZone(id,kind,...)`, `startMarker`. Уровень = спека с этими объектами + `goal{when:'gate.hit',fail:'<forb>.hit',stars}`.
- ⚠️ ГОЧА: имена param `t/w/h/pi/e/E/PI/tau` зарезервированы движком (`h`=высота вьюпорта!). abs-уровень
использует `m` (вершина), НЕ `h`. При добавлении уровней проверять имена коэффициентов.
- `time:{duration,loop:false}` синхронизирован с `runner.duration` — герой доезжает до конца за один проход.
### Карта / запуск
- Глава `functions` добавлена в `CHAPTERS` (key/title/subtitle/accent). map.js НЕ тронут — узлы рисуются по метаданным,
тип спеки карте безразличен. Разблокировка: `unlockStars` 9/11/13/15/17 (≤ 18 макс. звёзд физ-глав → нет дедлока).
- Запуск тот же (`QuantikGame.start``SimEngine.mount`); граф-уровни используют те же слайдеры params, спец-вайринг
НЕ нужен. Бейдж темы в quantik.html — per-level по `level.subject` (аддитивно).
### Для Ф4 (квантовые способности)
- `runDone`/`runX`/`.hit` — готовые env-поля для условий способностей (напр. «туннель» = временно игнорить forbidden.hit
в `fail`). Способность может менять `params` (коэффициенты) или подменять выражение кривой — всё через тот же SimExpr-конвейер.
- Зоны kind:'collect' уже «залипают» через механизм stars (Ф0). Новая способность = новый env-флаг + условие, БЕЗ eval.
- Сервер уже принимает `zone`+`runner` (validateSpec, OBJECT_TYPES) — авторённые граф-уровни (Ф5) пройдут гейт.