@
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> @
This commit is contained in:
@@ -38,6 +38,18 @@
|
||||
`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.
|
||||
|
||||
## Key Architecture Decisions
|
||||
- **«Атом» = блок `goal` в спеке** (булево SimExpr). Любой уровень = спека SimForge + `goal`.
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
- [x] Phase 0: Слой целей в движке (goal/HUD/result) [domain: frontend] → [subplan](./phase-0-objective-layer.md)
|
||||
- [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)
|
||||
- [ ] Phase 3: Граф-уровни (движение по f(x)) + зоны-препятствия [domain: fullstack] → [subplan](./phase-3-graph-levels.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)
|
||||
- [ ] Phase 5: Авторинг уровней в sim-builder + раздача классу [domain: fullstack] → [subplan](./phase-5-authoring-sharing.md)
|
||||
- [ ] Phase 6: Класс-лидерборд / живая гонка (classroom SSE) [domain: fullstack] → [subplan](./phase-6-leaderboard-live.md)
|
||||
@@ -73,7 +73,7 @@
|
||||
| Phase 0: Слой целей в движке | frontend | ✅ Done | ✅ | ✅ | ✅ |
|
||||
| Phase 1: Оболочка + 1 уровень + прогресс | fullstack | ✅ Done | ✅ | ✅ | ✅ |
|
||||
| Phase 2: Карта + мир + XP/скины | fullstack | ✅ Done | ✅ (1 🟡 fixed) | ✅ | ✅ |
|
||||
| Phase 3: Граф-уровни + зоны | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 3: Граф-уровни + зоны | fullstack | ✅ Done | ✅ | ✅ | ✅ |
|
||||
| Phase 4: Квантовые способности + SR | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 5: Авторинг + раздача | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 6: Лидерборд / живая гонка | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Phase 3: Граф-уровни (движение по f(x)) + зоны-препятствия
|
||||
|
||||
**Status:** ⬜ Not Started
|
||||
**Status:** ✅ Done (reviewed — PASS, committed)
|
||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||
**Domain:** fullstack
|
||||
|
||||
@@ -10,21 +10,34 @@
|
||||
Реюз `plot` + `SimExpr`. Сид граф-главы.
|
||||
|
||||
## Tasks
|
||||
- [ ] Task 1: «Бегунок по кривой»: герой-точка с `x` = функция t (напр. линейный проход xmin→xmax),
|
||||
- [x] Task 1: «Бегунок по кривой»: герой-точка с `x` = функция t (напр. линейный проход xmin→xmax),
|
||||
`y = f(x)` через ту же скомпилированную функцию, что у `plot`. Кривая рисуется (P3 plot),
|
||||
герой едет по ней с glow/trail. Без физики (кинематический проход), либо мягкая физика — на выбор уровня.
|
||||
- [ ] Task 2: Тип объекта/поле «зона» (forbidden/target): прямоугольник/круг в мире + удобные
|
||||
→ `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` ссылались на них.
|
||||
- [ ] Task 3: Цель = добраться до конца/в целевую зону, не задев запретные (`fail`). Звёзды: пройти
|
||||
→ `type:'zone'` (shape rect/circle, kind forbidden/target/collect, track). Движок кладёт `<zoneId>.hit`
|
||||
(1/0) в env. ⛔ Никаких inzone()-предикатов в грамматике — только именованные булевы env-поля.
|
||||
- [x] Task 3: Цель = добраться до конца/в целевую зону, не задев запретные (`fail`). Звёзды: пройти
|
||||
под нормативом, собрать бонус-точки (зоны-сборы).
|
||||
- [ ] Task 4: Управление: слайдеры коэффициентов `f(x)` (a·sin(b·x+c)+d и т.п.) ИЛИ выбор/набор
|
||||
→ 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). Безопасно.
|
||||
- [ ] Task 5: Контент: сид граф-главы (~4–5 уровней): синус под мостом, парабола над ямой,
|
||||
→ коэффициенты = обычные `params`-слайдеры движка; крутишь → кривая+путь героя перестраиваются.
|
||||
Свободный ввод выражения не понадобился (слайдеры коэффициентов достаточны для MVP-главы).
|
||||
- [x] Task 5: Контент: сид граф-главы (~4–5 уровней): синус под мостом, парабола над ямой,
|
||||
кусочная подгонка, экспонента/логарифм — растущая сложность, привязка к темам алгебры.
|
||||
- [ ] Task 6: Интеграция в карту (Ф2): новая глава-созвездие; общий конвейер результата/XP.
|
||||
- [ ] Task 7: Тесты: проход по кривой достигает цели; задевание зоны → fail; смоук рендера кривой+героя.
|
||||
→ 5 уровней в `functions`: луч (a·x+b), синус (A·sin(k·x)), парабола (a·(x−5)²+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` — поддержка «бегунка по кривой» (если не выразимо текущими полями)
|
||||
@@ -43,7 +56,41 @@
|
||||
- Переиспользовать P3 plot (несколько кривых, заливка, маркеры) для визуала «земли»/препятствий.
|
||||
|
||||
## Review Checklist
|
||||
- [ ] Все задачи; аддитивность движка; без эмодзи/eval; тесты зелёные; lint baseline 0
|
||||
- [x] Все задачи; аддитивность движка; без эмодзи/eval; тесты зелёные; lint baseline 0
|
||||
|
||||
## Handoff to Next Phase
|
||||
<!-- Заполняет агент-имплементер. -->
|
||||
|
||||
### Контракт «бегунка по кривой» (движок, `_sim_engine.js`)
|
||||
- На объекте `plot`: `runner:{ duration?:8, hold?:true }`. Делает из ПЕРВОЙ кривой plot дорожку.
|
||||
- Движок кладёт в env (в `_buildEnv`, ДО формульных центров): `<plotId>.runX` (= `a + (b−a)·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) пройдут гейт.
|
||||
|
||||
Reference in New Issue
Block a user