472 lines
40 KiB
Markdown
472 lines
40 KiB
Markdown
# Графы решений уровней 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`.
|
||
|
||
## Проверки, следующие из графов
|
||
|
||
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 и скорректировать подсказки/допуски без ослабления механик.
|