Files
goon-game/docs/LEVEL_SOLUTION_GRAPHS.md

28 KiB
Raw Permalink Blame History

Графы решений уровней 1–10

Документ фиксирует авторский маршрут каждого уровня текущего vertical slice. Он получен из LevelCatalog, моделей LevelDefinition и фактической логики GameView, затем проверен device-level ботами через production-команды движения и прыжка. targetDeaths ниже — целевое число жертв для результата уровня, но оно не доказывает минимальность решения.

На 18 августа 2026 года intended route всех уровней 1–10 автоматически выполнен по три раза подряд без retry masking. EarlyLevelPlaythroughTest, LevelFivePlaythroughTest, LateLevelPlaythroughTest и LevelEightPlaythroughTest управляют 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 оставляет закреплённое проводящее тело внутри опасности; 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

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

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

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

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

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

Высокий уступ 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

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

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

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

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 остаётся стабильным.

Проверки, следующие из графов

  1. Сохранять полный integrated device-regression: intended route уровней 1–10 по три раза и отдельный recovery route уровня 8.
  2. Отдельно проверить предполагаемый sequence break уровня 2 и необязательность FREEZER на уровне 10.
  3. Добавить параметризованные проверки restart из ключевых промежуточных состояний.
  4. Повторить bot matrix на дополнительных frame rates и поддерживаемых версиях Android.
  5. Провести ручные сессии этапа 2 и скорректировать подсказки/допуски без ослабления механик.