Files
goon-game/docs/LEVEL_SOLUTION_GRAPHS.md
T

617 lines
49 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.
# Графы решений уровней 1–18
Документ фиксирует **авторский маршрут** каждого уровня текущего vertical slice. Он получен из `LevelCatalog`, моделей `LevelDefinition` и фактической логики `GameView`, затем проверен device-level ботами через production-команды движения и прыжка. `targetDeaths` ниже — целевое число жертв для результата уровня, но оно не доказывает минимальность решения.
На 19 августа 2026 года intended route всех уровней 1–18 автоматически выполнен по три раза подряд без retry masking. Device-тесты управляют production `GameView` через те же флаги, что и touch-контролы. Для уровня 8 отдельно проверено восстановление после истечения таймера на дальней стороне `PIT`.
## Семантика, общая для графов
- `PRESSURE` активен, пока с его `box` пересекается достаточная масса живого игрока и/или тел (`requiredMass`). Сигнал не защёлкивается.
- `TOGGLE` меняет значение сигнала только на фронте контакта; повторный вход переключает его обратно.
- `TIMED` запускает таймер только на фронте контакта; удержание на переключателе не перезапускает истёкший таймер.
- `Door.requiredSignals` использует AND-семантику: дверь открыта, только когда активны все указанные сигналы.
- Управляемая движущаяся платформа накапливает `motionTime` только при активном `controlId`; при потере сигнала она замирает в текущей фазе `GameMath.pingPong`.
- `SPIKES` оставляет закреплённое тело над шипами; `SAW` передаёт телу `impulseX`/`impulseY`; `FREEZER` создаёт неподвижный блок `104×104`; `ELECTRIC` оставляет закреплённое проводящее тело внутри опасности; `CRUSHER` создаёт низкое `CRUSHED`-тело; `FIRE` оставляет твёрдое `BURNED`-тело без pressure mass; `PIT` тела не оставляет.
- Тела являются твёрдыми опорами для игрока. Смерть сохраняет тела и состояния уровня, respawn возвращает нового игрока в `spawnX`/`spawnY`; restart очищает состояние.
- Пересечение живого игрока с `goal` переводит сцену в `COMPLETE`.
## Уровень 1 — `The Volunteer`
**Статус:** доказан ботом, 3/3 последовательных прохождения без смертей.
**Ожидаемые смерти:** `0` (`targetDeaths=0`).
**Источник:** `LevelCatalog.level1()`; уступы `(640,760,230)` и `(1060,690,250)`; `goal=(1690,770,86,110)`.
### Solution graph 1
```text
Goal расположен справа от двух перекрывающих путь уступов
→ разогнаться вправо и запрыгнуть на уступ x=640..870
→ сохранить движение и перепрыгнуть на более высокий уступ x=1060..1310
→ спуститься на сплошной floor(0,1920)
→ пересечь goal x=1690..1776
→ COMPLETE без смерти
```
**Механика и сигналы:** только движение, прыжок и статические `Platform`; сигналов и дверей нет.
**Bot checkpoints:** `playerX>640` при `playerY≤660` → `playerX>1060` при `playerY≤590` → `playerX>1310` → `completed=true`, `deaths=0`.
**Softlock/неопределённость:** intended route и окна двух прыжков воспроизводимо подтверждены. Альтернативный проход под/мимо уступов отдельно не проверялся.
## Уровень 2 — `A Useful End`
**Статус:** доказан ботом, 3/3 последовательных прохождения с тремя телами.
**Ожидаемые смерти:** `3` (`targetDeaths=3`).
**Источник:** `LevelCatalog.level2()`; `SPIKES=(610,850,260,30)`; статичный уступ `(1180,740,240)`; `goal=(1830,770,86,110)`.
### Solution graph 2
```text
Spike lane x=610..870 перекрывает наземный маршрут
→ первый доброволец погибает на ближнем краю SPIKES
→ закреплённое тело становится первой твёрдой опорой
→ следующие добровольцы продвигаются прыжками и оставляют ещё два тела
→ цепочка из трёх тел образует предусмотренный путь через lane
→ следующий доброволец пересекает тела, возвращается на floor
→ проходит уступ x=1180..1420 и достигает goal
→ COMPLETE с целевыми тремя смертями
```
**Механика и сигналы:** `SPIKES` закрепляет каждое тело на `hazard.box.y-corpse.height`; двери и сигналы отсутствуют.
**Bot checkpoints:** после каждой предусмотренной жертвы `deaths/corpseCount=1/1`, `2/2`, `3/3`; затем живой игрок `playerX>870`; финально `completed=true`, `deaths=3`.
**Softlock/неопределённость:** intended route с тремя телами воспроизводимо подтверждён. Отдельная попытка прямого zero-death прохода не подтвердила sequence break, но не является исчерпывающим negative-тестом; минимальность трёх смертей остаётся открытым follow-up.
## Уровень 3 — `Dead Weight`
**Статус:** доказан ботом, 3/3 последовательных прохождения с одной жертвой.
**Ожидаемые смерти:** `1` (`targetDeaths=1`).
**Источник:** `LevelCatalog.level3()`; `SAW=(565,760,92,92)`, impulse `(0,-150)`; `PRESSURE=(540,860,150,20)`, `signalId="gate"`; дверь `(1110,570,78,310)` требует `"gate"`.
### Solution graph 3
```text
Door закрывает путь к goal
→ для Door требуется активный signal "gate"
→ PRESSURE удерживает "gate" только при постоянной массе
→ первый доброволец входит в SAW над зоной плиты
→ тело с нулевым horizontal impulse оседает на PRESSURE
→ "gate" становится active и Door открывается
→ respawn следующего добровольца
→ перепрыгнуть SAW/тело, пройти открытую Door
→ пересечь goal и получить COMPLETE
```
**Механика и сигналы:** checkpoint цепочки — `corpseCount=1` → `switchContact(PRESSURE)=true` → `signalActive("gate")=true` → `doorOpen=true`.
**Bot checkpoints:** `deaths=1`, `corpseCount=1`; после respawn `playerAlive=true`; требуется расширить snapshot состоянием `"gate"` либо использовать `allDoorsOpen=true`; затем `playerX>1188` и `completed=true`.
**Softlock/неопределённость:** доставка тела на плиту, удержание двери и последующий проход подтверждены в трёх последовательных запусках. Альтернативные точки контакта с `SAW` отдельно не перебирались.
## Уровень 4 — `The Long Lift`
**Статус:** доказан ботом, 3/3 последовательных прохождения без смертей.
**Ожидаемые смерти:** `0` (`targetDeaths=0`).
**Источник:** `LevelCatalog.level4()`; `TOGGLE=(520,820,72,60)`, `signalId="lift"`; moving platform `(1040,790,260,34)`, travel `(0,-390)`, period `5.2`, `controlId="lift"`; `PIT=(1500,880,320,200)`; верхний уступ `(1660,500,540)`; `goal=(1960,390,86,110)`.
### Solution graph 4
```text
Goal находится на верхней галерее за PIT
→ обычный прыжок с floor не даёт требуемой высоты
→ один раз войти в TOGGLE и включить signal "lift"
→ активный "lift" запускает вертикальный цикл moving platform
→ запрыгнуть на platform x=1040..1300 и подняться
→ в верхней фазе прыгнуть вправо через край PIT на ledge x=1660..2200
→ пройти по галерее до goal
→ COMPLETE без смерти
```
**Механика и сигналы:** фронт контакта `TOGGLE` → `toggleSignals["lift"]=true` → растёт `motionTime` платформы → `travelY*pingPong` поднимает её максимум на `390`.
**Bot checkpoints:** `signal "lift"=true`; координата платформы уменьшается от `y=790`; игрок получает `supportIndex` движущейся платформы; затем `playerX≥1660`, `playerY≤400`; финально `completed=true`, `deaths=0`. Для этого snapshot нужно расширить сигналами, `supportIndex` и состоянием платформы.
**Softlock/неопределённость:** lift→gallery прыжок подтверждён в трёх последовательных запусках. Повторный контакт с `TOGGLE` по-прежнему выключает сигнал и может заморозить платформу; это предусмотренное состояние, доступное для restart.
## Уровень 5 — `Express Delivery`
**Статус:** доказан `LevelFivePlaythroughTest`, 3/3 последовательных прохождения.
**Ожидаемые смерти:** `1` (`targetDeaths=1`); acceptance-тест требует ровно одну.
**Источник:** `LevelCatalog.level5()`; `SAW=(570,760,96,96)`, impulse `(500,-700)`; уступ `(850,800,330)`; `PRESSURE=(930,780,160,20)`, `signalId="gate"`; дверь `(1390,560,82,320)` требует `"gate"`; `goal=(2080,770,86,110)`.
### Solution graph 5
```text
Door закрывает путь к goal
→ для Door требуется signal "gate"
→ удалённую PRESSURE нужно удерживать постоянной массой
→ первый доброволец идёт вправо в SAW
→ SAW убивает и запускает тело impulse (500,-700)
→ тело приземляется на уступе/плите x=930..1090
→ PRESSURE активирует "gate", Door открывается
→ следующий доброволец respawn-ится
→ прыжками пересекает SAW и зону доставленного тела
→ проходит открытую Door и достигает goal
→ COMPLETE
```
**Механика и сигналы:** `SAW` создаёт динамическое тело → контакт тела с `PRESSURE` → `signalActive("gate")=true` → `allDoorsOpen=true`.
**Bot checkpoints:** реализованы в `LevelPlaytestBot.proveLevelFive`: первая смерть; `corpseCount≥1`; `allDoorsOpen=true`; прыжок после `playerX≥390`, дополнительный прыжок после `playerX≥750`; затем `completed=true`. Тест проверяет `completed`, смерть, сохранённое тело и факт открытия двери.
**Softlock/неопределённость:** основной маршрут воспроизводимо подтверждён тремя последовательными запусками. Альтернативные решения и физическая стабильность на нескольких frame rates этим тестом не покрыты.
## Уровень 6 — `Cold Comfort`
**Статус:** доказан ботом, 3/3 последовательных прохождения с frozen stepping stone.
**Ожидаемые смерти:** `1` (`targetDeaths=1`).
**Источник:** `LevelCatalog.level6()`; `FREEZER=(690,776,100,104)`; floor заканчивается на `x=1120`; уступы `(810,590,520)` и `(1430,610,570)`; `goal=(1880,500,86,110)`.
### Solution graph 6
```text
Высокий уступ x=810..1330 недоступен с уровня floor обычным прыжком
→ первый доброволец входит в FREEZER
→ смерть создаёт неподвижный frozen corpse block 104×104 у x=688
→ следующий доброволец respawn-ится
→ запрыгивает на frozen block как на промежуточную ступень
→ с блока прыгает на высокий уступ x=810..1330
→ пересекает разрыв до уступа x=1430..2000
→ достигает goal на втором уступе
→ COMPLETE с одной смертью
```
**Механика и сигналы:** `FREEZER` принудительно ставит `frozen=true`, размеры `104×104`, позицию `(source.x-2, 880-height)`; тело не обновляется физикой и остаётся твёрдой ступенью. Сигналов нет.
**Bot checkpoints:** `deaths=1`, `corpseCount=1`; после respawn игрок стоит на блоке около `playerY=676`; затем на первом уступе `playerY=490`, `playerX≥810`; на втором `playerY=510`, `playerX≥1430`; финально `completed=true`.
**Softlock/неопределённость:** первоначальная зона `FREEZER` перекрывала игрока, стоящего на блоке, на 26 px и делала intended route невозможным. Высота зоны минимально исправлена до `104`; floor runner всё ещё погибает, а оба прыжка подтверждены тремя запусками.
## Уровень 7 — `Live Wire`
**Статус:** доказан ботом, 3/3 последовательных прохождения с conductive corpse.
**Ожидаемые смерти:** `1` (`targetDeaths=1`).
**Источник:** `LevelCatalog.level7()`; `ELECTRIC=(650,780,110,100)`, `relayId="power"`; дверь `(1130,560,84,320)` требует `"power"`; `goal=(1930,770,86,110)`.
### Solution graph 7
```text
Door закрывает путь к goal
→ Door требует signal "power"
→ "power" активен только от conductive corpse внутри ELECTRIC relay
→ первый доброволец входит в ELECTRIC
→ смерть оставляет закреплённое проводящее тело внутри hazard.box
→ relay "power" становится active и Door открывается
→ следующий доброволец respawn-ится
→ перепрыгивает ELECTRIC/тело и проходит открытую Door
→ достигает goal
→ COMPLETE с одной смертью
```
**Механика и сигналы:** `ELECTRIC` выставляет `conductive=true`, центрирует тело внутри hazard и закрепляет его; `signalActive("power")` требует пересечения этого тела с relay hazard.
**Bot checkpoints:** `deaths=1`, `corpseCount=1`; `allDoorsOpen=true` после появления conductive corpse; после respawn `playerX>1214`; финально `completed=true`, `deaths=1`. Для прямой проверки механики snapshot должен отдельно сообщать conductive state или активный `"power"`.
**Softlock/неопределённость:** relay-сигнал и прыжок через совмещённые hazard/body подтверждены тремя запусками ровно с одной смертью.
## Уровень 8 — `Borrowed Time`
**Статус:** intended route и восстановление после истечения таймера доказаны production-input ботом, по три раза подряд.
**Ожидаемые смерти:** `0` (`targetDeaths=0`).
**Источник:** `LevelCatalog.level8()`; основной `TIMED=(560,810,90,70)`, `signalId="clock"`, duration `8.0`; recovery relay `TIMED=(1600,650,90,70)`, duration `3.5`; moving platform `(910,770,320,34)`, travel `(380,0)`, period `4.0`, `controlId="clock"`; `PIT=(1040,880,470,200)`; дверь `(1770,560,82,320)` требует `"clock"`; `goal=(2220,600,86,110)`.
### Solution graph 8
```text
PIT разрывает floor, а Door закрывает дальнейший путь
→ войти в TIMED и запустить signal "clock" на 8.0 s
→ "clock" одновременно запускает horizontal moving platform и открывает Door
→ без задержки добежать до края floor x=1040
→ прыгнуть на moving platform и пересечь PIT
→ сойти на правый floor x≥1510
→ пройти Door x=1770..1852 до истечения "clock"
→ запрыгнуть на ledge x=1980..2450
→ пересечь goal и получить COMPLETE без смерти
Ветка восстановления после пересечения PIT:
→ дождаться истечения "clock" на правом floor x≥1510
→ Door закрывается и подтверждает прежнюю trap-state
→ намеренно прыгнуть в высокий recovery relay x=1600, y=650
→ повторно запустить "clock" на 3.5 s
→ пройти Door и достичь goal без смерти/restart
```
**Механика и сигналы:** фронт контакта основного `TIMED` → `timedSignals["clock"]=8.0` → движение платформы по `travelX=380` и открытие Door; высокий relay выставляет тот же сигнал на `3.5 s`. Обычный бег проходит под relay, поэтому original route не активирует восстановление автоматически. Android 16 при задержках между решениями выявил узкое окно посадки: launch floor продлён до `x=1040`, а платформа расширена влево до `x=910` при сохранении прежнего правого края `x=1230`, дальнего края PIT `x=1510`, travel, period и recovery relay.
**Bot checkpoints:** основной timer активен; первый прыжок подтверждает `playerOnMovingPlatform=true`, второй доставляет игрока на дальний floor. Перед движением к relay явно подтверждены `playerX≥1510`, `playerY≥779` и всё ещё открытая Door. Intended route проходит Door на первом таймере. Recovery route ждёт `allDoorsOpen=false` справа от PIT, прыжком получает `allDoorsOpen=true`, проходит Door и завершает уровень. Оба маршрута финально дают `completed=true`, `deaths=0`; каждый выполнен три раза подряд на Android 16. Дополнительный intended run с `96 ms` задержки на каждое решение также завершён без смерти. Дальний край платформы намеренно не расширен, чтобы она не могла унести ожидающего игрока обратно над PIT.
**Softlock/неопределённость:** прежний blocker устранён: после истечения `"clock"` справа от PIT высокий relay гарантирует restart-free повторное открытие двери. Relay не помогает слева от PIT и не отменяет timed-platform crossing. Оба пути проверены без прямой мутации состояния, teleport, auto-complete или restart.
## Уровень 9 — `Two Good Reasons`
**Статус:** доказан ботом, 3/3 последовательных прохождения с AND из двух тел.
**Ожидаемые смерти:** `2` (`targetDeaths=2`).
**Источник:** `LevelCatalog.level9()`; `SPIKES=(520,850,130,30)`; `PRESSURE=(510,842,160,38)`, `signalId="weight"`; `ELECTRIC=(910,780,110,100)`, `relayId="power"`; дверь `(1510,540,86,340)` требует `"weight"` и `"power"`; `goal=(2310,770,86,110)`.
### Solution graph 9
```text
Final Door использует AND из "weight" и "power"
→ первый доброволец погибает на SPIKES внутри зоны PRESSURE
→ закреплённое spike-тело удерживает signal "weight"
→ следующий доброволец пересекает spike-тело и погибает в ELECTRIC
→ закреплённое conductive body удерживает relay signal "power"
→ "weight" AND "power" одновременно active
→ Door открывается
→ третий доброволец respawn-ится
→ перепрыгивает обе опасности, проходит открытую Door
→ достигает goal и получает COMPLETE
```
**Механика и сигналы:** spike corpse → `switchContact(PRESSURE)` → `"weight"`; conductive corpse в relay → `"power"`; `doorOpen` проверяет оба `requiredSignals`.
**Bot checkpoints:** после первой смерти `deaths/corpses=1/1`, `"weight"=true`, Door ещё closed; после второй `2/2`, `"power"=true`, `allDoorsOpen=true`; после respawn `playerX>1596`; финально `completed=true`, `deaths=2`. Snapshot нужно расширить поимёнными сигналами, чтобы доказать AND, а не только открытие двери.
**Softlock/неопределённость:** первая смерть стабильно удерживает `"weight"`, но дверь остаётся закрытой; вторая активирует `"power"` и открывает AND-door. Цепочка подтверждена тремя запусками ровно с двумя телами.
## Уровень 10 — `The Last Procession`
**Статус:** mastery-маршрут доказан production-input ботом: 3/3 последовательных прохождения и ограниченный стресс финального перехода `96 ms` без ретраев.
**Ожидаемые и доказанные смерти:** `3` (`targetDeaths=3`).
**Источник:** `LevelCatalog.level10()`; `SAW=(520,760,96,96)`, impulse `(700,-240)`; `PRESSURE=(860,860,160,20)`, `signalId="lift"`; `FREEZER=(1110,776,100,104)`; `ELECTRIC=(1510,780,110,100)`, `relayId="power"`; moving platform `(1800,790,290,34)`, travel `(0,-410)`, period `10.0`, `controlId="lift"`; стартовый floor заканчивается на `x=1940`; `PIT=(1940,880,200,200)`; дверь `(2310,310,82,570)` требует `"power"`; верхний уступ `(2420,440,680)`; `goal=(2880,330,86,110)`.
### Solution graph 10
```text
Goal расположен на верхнем уступе за PIT и закрытой высокой Door
→ первый доброволец входит в SAW
→ impulse (700,-240) доставляет тело на PRESSURE x=860..1020
→ тело оседает и фиксируется на PRESSURE, удерживает signal "lift" и запускает vertical moving platform
→ следующий доброволец погибает в FREEZER
→ frozen corpse становится стабильной ступенью для повторного прохода этой зоны
→ следующий доброволец погибает в ELECTRIC
→ conductive corpse удерживает relay signal "power" и открывает Door
→ четвёртый доброволец respawn-ится и пересекает сохранённые тела/опасности
→ садится на активную platform у x=1800..2090
→ поднимается над PIT и в верхней фазе прыгает вправо через открытую Door
→ приземляется на ledge x=2420..3100
→ достигает goal и получает COMPLETE с тремя смертями
```
**Механика и сигналы:** launched saw corpse при контакте с `PRESSURE` обнуляет скорость и закрепляется внутри плиты → `"lift"` → vertical platform; frozen corpse → твёрдая ступень; conductive corpse → `"power"` → Door open; platform и Door используют независимые постоянные сигналы от сохранённых тел.
**Bot checkpoints:** после SAW `deaths=1`, `"lift"=true`, platform `y<790`; после FREEZER `deaths=2` и наличие frozen corpse; после ELECTRIC `deaths=3`, `"power"=true`, `allDoorsOpen=true`; перед посадкой первая moving platform должна вернуться к `y≥780`, затем snapshot подтверждает `playerOnMovingPlatform=true`; в верхней фазе `playerY≤280`; после прыжка `playerX≥2420`, `playerY≤340`; финально `completed=true`.
**Softlock/неопределённость:** исправлены два независимых blocker-состояния: saw-тело больше не соскальзывает с плиты после доставки, а перекрытие floor/lift увеличено до 140 px при сохранении 200 px `PIT` и обязательного вертикального подъёма. Период lift увеличен до `10.0 s`, чтобы нижняя фаза была читаемой при задержанном вводе. Бот теперь явно приземляется на frozen corpse и прыгает с него; доставка saw-тела, три сохранённых тела, посадка, подъём и lift→ledge прыжок подтверждены тремя запусками и стресс-сценарием. Возможная альтернативная двухсмертная трасса не считается blocker, пока intended three-body route остаётся стабильным.
## Уровень 11 — `The Brass Breather`
**Статус:** доказан production-input ботом, 3/3 изолированно и 3/3 в общей hazard-матрице.
**Ожидаемые и доказанные смерти:** `1`.
```text
CRUSHER перекрывает единственный проход
→ первый доброволец входит в активный пресс и оставляет тело
→ следующий доброволец подходит к безопасной границе
→ наблюдает закрытие и следующий переход пресса в REST
→ прыгает через сохранённое тело и проходит пресс в полном окне отдыха
→ достигает goal с одной смертью
```
**Контракт:** `cycleSeconds=4`, `activeSeconds=2.5`; collision активен только в
CRUSH-фазе. Bot snapshot наблюдает `firstHazardActive`, не использует sleeps,
restart или прямую мутацию состояния.
## Уровень 12 — `Blue Funeral`
**Статус:** доказан production-input ботом, 3/3 изолированно и 3/3 в общей hazard-матрице.
**Ожидаемые и доказанные смерти:** `1`.
```text
FIRE перекрывает floor
→ первый доброволец демонстрирует смертельность синего огня
→ после respawn игрок подходит к рассчитанной точке x≈535
→ отдельным takeoff подтверждает airborne state
→ пересекает весь flame box до приземления
→ достигает goal, тело остаётся в уровне
```
**Контракт:** постоянный `FIRE=(700,790,120,90)` с отдельными visual/audio IDs.
Маршрут запрещает вторую смерть на approach, takeoff и clear checkpoints.
## Уровень 13 — `Crossfire Clock`
**Статус:** доказан production-input ботом, 3/3 изолированно и 3/3 в общей hazard-матрице.
**Ожидаемые и доказанные смерти:** `1`.
```text
PROJECTILE каждые 3 s проходит справа налево
→ первый доброволец погибает и показывает линию снаряда
→ после respawn игрок останавливается у x≈600
→ ждёт наблюдаемое окно projectile x=850..930 и прыгает через первый проход
→ останавливается за линией и ждёт следующий цикл x=1350..1450
→ вторым прыжком проходит возвращённый снаряд
→ добегает за spawn снаряда и достигает goal без новых смертей
```
**Контракт:** runtime вычисляет текущий box из `speedX=-400` и сбрасывает его
на исходную позицию по `cycleSeconds=3`. Оба прыжка привязаны к snapshot
координате, а не к wall-clock.
## Уровень 14 — `Steam Charge`
**Статус:** доказан production-input ботом, 3/3 изолированно и 3/3 в общей hazard-матрице.
**Ожидаемые и доказанные смерти:** `1`; дверь обязана открыться.
```text
Door требует signal "blast-gate" от удалённой PRESSURE
→ первый доброволец входит в активный EXPLOSION
→ impulse (550,-500) доставляет тело на PRESSURE x=980..1160
→ тело закрепляется на плите и открывает Door
→ следующий доброволец наблюдает новый BLAST и следующую REST-фазу
→ проходит inactive explosion
→ перепрыгивает сохранённое тело на плите
→ проходит открытую Door и достигает goal
```
**Контракт:** `EXPLOSION` использует data-driven цикл `4/2 s`; inactive-фаза
отличается визуально (`REST`) и не убивает. Device RED с `impulseX=1400`
доказал перелёт тела за плиту; минимальное authored значение `550` стабильно
помещает тело в `firstCorpseX=980`, после чего `allDoorsOpen=true`.
## Уровень 15 — `Pressed Into Service`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; сохранённое тело обязано иметь состояние
`CRUSHED`, размер `140×24` и оставаться твёрдой опорой.
```text
Верхний ledge начинается на высоте y=720 и недоступен прыжком с floor
→ первый доброволец входит в активный CRUSHER
→ пресс превращает тело в низкую закреплённую плиту 140×24
→ следующий доброволец наблюдает новый активный цикл и ждёт REST
→ проходит безопасный пресс и приземляется на crushed body
→ отдельным прыжком использует 24 px ступень для достижения ledge
→ проходит по верхней платформе к goal
→ COMPLETE ровно с одной смертью и одним crushed body
```
**Контракт:** `CRUSHED` композиционно сохраняется в `CorpseManager`, уменьшает
коллайдер и не удаляет физическую опору. Save version 3 сохраняет флаг; v1/v2
мигрируют его в `false`. Snapshot отдельно публикует state, размеры и pressure
capability, а Canvas добавляет текстовую метку `CRUSHED` помимо цвета.
## Уровень 16 — `Ashen Counterweight`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; сохранённое тело обязано иметь состояние
`BURNED`, оставаться твёрдым и не активировать `PRESSURE`.
```text
Moving bridge над PIT управляется signal "burn-weight"
→ первый доброволец пересекает PRESSURE и погибает в FIRE над плитой
→ burned body остаётся на плите как твёрдый объект
→ burned body не даёт pressure mass: signal остаётся inactive
→ мост остаётся в нижней фазе у y≈840 вместо подъёма
→ следующий доброволец перепрыгивает FIRE и burned body
→ садится на неподвижный мост и пересекает PIT
→ достигает goal на правом floor
→ COMPLETE ровно с одной смертью и одним burned body
```
**Контракт:** `BURNED` сохраняет коллайдер, но `activatesPressure=false`; это
проверяется одновременно состоянием тела, неактивным `firstMovingPlatformPowered`
и нижней координатой моста. Save version 3 сохраняет флаг; Canvas добавляет
текстовую метку `BURNED`, поэтому состояние не кодируется только цветом.
## Уровень 17 — `The Whole Weight`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `0`; дверь обязана удерживаться только
физическими объектами после отпускания player input.
```text
Door требует signal "weight-gate" с requiredMass=9
→ на PRESSURE уже стоят Box(1), Rock(2), Ball(1) и IceBlock(2): сумма 6
→ Counterweight(3) находится слева от плиты, поэтому Door закрыта
→ игрок подходит и толкает Counterweight вправо
→ при x≥920 Counterweight пересекает plate x=1000..1650
→ общий IWeightedObject resolver получает массу 6+3=9 и открывает Door
→ игрок отпускает input; объект продолжает удерживать сигнал без player mass
→ игрок двумя отдельными прыжками пересекает Counterweight и ряд объектов
→ проходит открытую Door и достигает goal
→ COMPLETE без смерти и без создания тел
```
**Контракт:** `Player`, `CorpseManager.Body` и `PhysicalObjectState` реализуют
один `IWeightedObject`; plate агрегирует только геометрически пересекающуюся
активную массу, не проверяя тип. Все пять authored типов имеют type/box/mass в
JSON, детерминированный runtime, non-color type+weight label, SVG/TSV preview и
save-v4 состояние. V1–v3 active sessions мигрируют с пустым runtime-object list.
**Bot checkpoints:** каталог публикует пять objects и Counterweight mass 3;
до push `allDoorsOpen=false`; после push `firstPhysicalObjectX≥920` и Door open;
после release Door остаётся open; затем два отдельных jump edge и `completed=true`.
## Уровень 18 — `Threefold Proof`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `0`; обязательный challenge-route получает
casualty, time и collectible stars.
```text
LevelID="foundations.threefold-proof", WorldID="foundations"
→ игрок проходит безопасный teaching floor без смертей
→ у x≈650 начинает прыжок к cache=(850,650,58,58)
→ пересекает cache: collectibleFound=true, cache исчезает только для этой попытки
→ продолжает вправо и достигает goal x=2000..2086
→ COMPLETE с deaths=0 и time≤30 s
→ ChallengeResult выдаёт casualty/time/collectible = true, stars=3
→ ProgressStore сохраняет лучшие критерии по стабильному LevelID
→ три последовательных restart-free прохождения воспроизводят 3/3 stars
```
**Контракт:** collectible является опциональным world-space объектом и сохраняется
в active session v5. Restart сбрасывает состояние текущей попытки; завершённый
collectible challenge накапливается в progress v3 независимо от лучших casualty и
time runs. Legacy levels без collectible получают третью звезду за core completion.
**Bot checkpoints:** `deaths=0`; после прыжка `collectibleFound=true` и
`collectibleVisible=false`; затем `completed=true`; `CompletionResult.stars=3`,
`bestStars=3`; новый `ProgressStore` после трёх запусков возвращает 3 звезды для
`foundations.threefold-proof`.
## Уровень 19 — `First Return`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `0`.
```text
LevelID="foundations.first-return", WorldID="foundations"
→ игрок подходит к нижнему уступу x=650..890 и перепрыгивает его
→ отпускает jump, подтверждает новое касание пола и создаёт второй jump edge
→ вторым прыжком проходит высокий уступ x=1080..1380
→ на безопасной дальней прямой забирает collectible x=1500
→ достигает goal x=2090..2176 без смертей
```
**Контракт:** уровень повторно калибрует движение и раздельные jump edges перед
production-комбинациями. На нём нет hazard, switch или Door; failure означает
ошибку управления, а не скрытое состояние головоломки.
## Уровень 20 — `Covered Path`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `2`; оба тела сохраняются.
```text
LevelID="foundations.covered-path", WorldID="foundations"
→ первый доброволец идёт в ближние spikes x=700..800 и оставляет pinned body
→ второй прыгает с x≈520 через первое тело и погибает на spikes x=930..1030
→ третий возвращается и одним прыжком пересекает две раздельные опоры
→ отдельным jump edge входит на ledge x=1240..1520 и забирает collectible
→ достигает goal с deaths=2, corpses=2
```
**Контракт:** две раздельные spike-зоны делают обе жертвы наблюдаемыми и не
позволяют одним ранним телом случайно закрыть весь hazard. Bot fail-fast отклоняет
третью смерть; три последовательных запуска доказывают очистку старых тел.
## Уровень 21 — `Held Open`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; Door остаётся открыта телом.
```text
LevelID="foundations.held-open", WorldID="foundations"
→ первый доброволец достигает saw x=650..742
→ тело оседает на pressure plate x=625..785 и активирует held-gate
→ следующий доброволец подтверждает allDoorsOpen=true
→ начинает прыжок из рассчитанного окна x≈470 и очищает saw + retained body
→ прыгает через collectible x=1160 до Door x=1260
→ проходит удерживаемую Door и достигает goal с deaths=1, corpses=1
```
**Контракт:** plate и Door используют один signal ID `held-gate`. Коллекционный
маршрут лежит внутри того же доказанного прыжка и не требует второй жертвы.
## Уровень 22 — `Clockwork Step`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `0`.
```text
LevelID="foundations.clockwork-step", WorldID="foundations"
→ игрок один раз касается toggle ferry x=760..832
→ подходит к launch overlap и прыгает на platform x=940..1260
→ snapshot подтверждает playerOnGround && playerOnMovingPlatform
→ без input едет до platformX≥1280 и по пути забирает collectible x=1435
→ сходит на дальний floor x=1600..2500, не касаясь PIT x=1100..1600
→ достигает goal без смерти
```
**Контракт:** toggle и moving platform разделяют signal `ferry`. Платформа имеет
160 px launch overlap до движения и 20 px far overlap в конечной фазе; bot ждёт
реальную поддержку, а не единичное пересечение в воздухе.
## Уровень 23 — `Remote Volunteer`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; доставленное тело удерживает Door.
```text
LevelID="foundations.remote-volunteer", WorldID="foundations"
→ первый доброволец входит в saw x=600..696 и получает impulse (550,-700)
→ тело попадает на raised pressure plate x=1030..1200 и открывает remote-gate
→ следующий доброволец стартует прыжок до saw и остаётся жив
→ перепрыгивает доставленное тело, забирает cache x=1180
→ проходит Door x=1540..1622 и достигает goal с deaths=1, corpses=1
```
**Контракт:** дистанционно доставленное тело является единственным источником
pressure-сигнала. Маршрут не маскирует повторную смерть и ограничен максимум одной
жертвой на каждом шаге.
## Уровень 24 — `Frozen Step`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; одно замороженное тело становится ступенью.
```text
LevelID="foundations.frozen-step", WorldID="foundations"
→ первый доброволец замерзает в FREEZER x=720..820
→ следующий прыгает и стабилизируется на frozen body на y≈680
→ отдельным jump edge поднимается на ledge x=850..1410, y=590
→ забирает cache x=1280 и перепрыгивает на ledge x=1500..2200
→ достигает верхнего goal с deaths=1, corpses=1
```
**Контракт:** без сохранённого frozen body высота первого уступа недостижима
доказанным маршрутом; бот отдельно наблюдает устойчивую опору на теле.
## Уровень 25 — `Living Wire`
**Статус:** доказан production-input ботом, 3/3 последовательных прохождения.
**Ожидаемые и доказанные смерти:** `1`; проводящее тело постоянно питает Door.
```text
LevelID="foundations.living-wire", WorldID="foundations"
→ первый доброволец погибает в ELECTRIC x=720..830
→ conductive corpse активирует relayId="living-power" и открывает Door
→ следующий стартует прыжок до live relay
→ перелетает тело, забирает cache x=1100 на безопасном полу
→ проходит Door x=1350..1434 и достигает goal с deaths=1, corpses=1
```
**Контракт:** Door открывается только живым relay-сигналом сохранённого conductive
body; каждый шаг fail-fast запрещает вторую смерть.
## Уровень 26 — `Second Hand`
**Статус:** доказаны два production-input маршрута, каждый 3/3.
**Ожидаемые и доказанные смерти:** `0` для original timer и recovery.
```text
LevelID="foundations.borrowed-time", WorldID="foundations"
→ TIMED switch x=760 запускает second-hand на 9 s и outbound ferry
→ игрок ждёт boarding phase, садится на platform x=940..1300
→ едет до platformX≥1250 и прыгает на far floor x=1580..2700
→ intended: проходит Door x=1920 на исходном таймере
→ recovery: ждёт закрытия Door и прыгает в высокий relay x=1700,y=650 на 4 s
→ оба маршрута поднимаются на ledge x=2230..2700, забирают cache x=2350
→ достигают верхнего goal без смерти
```
**Контракт:** recovery relay использует тот же `second-hand`, но расположен над
обычным беговым маршрутом. Отдельный тест сначала наблюдает реально закрытую Door,
затем её повторное открытие — softlock после истечения исходного таймера исключён.
## Проверки, следующие из графов
1. Сохранять полный integrated device-regression: intended route принятых уровней по три раза и отдельный recovery route уровня 8.
2. Отдельно проверить предполагаемый sequence break уровня 2 и необязательность `FREEZER` на уровне 10.
3. Добавить параметризованные проверки restart из ключевых промежуточных состояний.
4. Повторить bot matrix на дополнительных frame rates и поддерживаемых версиях Android.
5. Провести ручные сессии этапа 2 и скорректировать подсказки/допуски без ослабления механик.