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> @
11 KiB
11 KiB
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+ множество untrackedtmp_*/мусорных файлов. ⛔ НЕ трогать и НЕ коммитить этот WIP — стейджить только свои файлы поимённо. - Phase 0 реализован (pending review): слой целей в движке
_sim_engine.js(блокgoal, компиляция when/fail/stars через SimExpr, состояние результата, HUD-оверлей, APIonGoal/getResult/resetResult) + серверный гейтvalidateSpecпропускаетgoal/game. Изменены:frontend/js/labs/_sim_engine.js,backend/src/controllers/customSimController.js. Аддитивно: спека безgoalведёт себя ровно как раньше (HUD не создаётся, побед не считается). Смоук 40/40;npm test238 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(iconrocket) виден всем. Новые: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 test251 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); скин тинтует героя+нарратора (localStoragequantik-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 test259/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 уровней: луч/синус/парабола/модуль/экспонента,unlockStars9..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 test261 / 253 pass / 8 baseline fail (без новых); lint:routes 0; всеnode --checkOK.
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_simscat='game' остаётся целевым хранилищем для авторённых уровней (Ф5); реестр тогда станет асинхронным (загрузка опубликованных + слияние со встроенными той же формы записи).
- Уточнение Ф1: для MVP уровни — ВСТРОЕННЫЕ ДАННЫЕ в
- Герой Квантик: в уровне = 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).