Files
goon-game/docs/LEVEL_SOLUTION_GRAPHS.md
T

25 KiB
Raw 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 и recovery route доказаны ботом, каждый по 3/3 последовательных прохождения без смертей. Ожидаемые смерти: 0 (targetDeaths=0). Источник: LevelCatalog.level8(); TIMED=(560,810,90,70), signalId="clock", duration 5.5; moving platform (990,770,240,34), travel (380,0), period 4.0, controlId="clock"; PIT=(900,880,610,200); дверь (1770,560,82,320) требует "clock"; goal=(2220,600,86,110).

Solution graph 8

PIT разрывает floor, а Door закрывает дальнейший путь
→ войти в TIMED и запустить signal "clock" на 5.5 s
→ "clock" одновременно запускает horizontal moving platform и открывает Door
→ без задержки добежать до края floor x=900
→ прыгнуть на moving platform и пересечь PIT
→ сойти на правый floor x≥1510
→ пройти Door x=1770..1852 до истечения "clock"
→ запрыгнуть на ledge x=1980..2450
→ пересечь goal и получить COMPLETE без смерти

Механика и сигналы: фронт контакта TIMED → timedSignals["clock"]=5.5 → движение платформы по travelX=380 и открытие Door; таймер уменьшается каждый update.

Bot checkpoints: "clock" active с remaining time; platform x>990; игрок получает её supportIndex; затем playerX≥1510; Door остаётся open при playerX>1852; финально completed=true, deaths=0. Требуется добавить в snapshot remaining time и координаты платформы.

Softlock/неопределённость: исходный blocker-softlock был воспроизведён после истечения "clock" справа от PIT. Добавлен высокий recovery relay того же сигнала, недоступный при обычном беге по floor; intended route и восстановление после истечения таймера отдельно подтверждены по три раза.

Уровень 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-маршрут доказан ботом, 3/3 последовательных прохождения с тремя телами. Ожидаемые смерти: 3 (targetDeaths=3). Источник: LevelCatalog.level10(); SAW=(520,760,96,96), impulse (700,-240); PRESSURE=(860,860,160,20), signalId="lift"; FREEZER=(1110,750,100,130); ELECTRIC=(1510,780,110,100), relayId="power"; moving platform (1840,790,250,34), travel (0,-410), period 5.0, controlId="lift"; PIT=(1880,880,260,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
→ тело удерживает signal "lift" и запускает вертикальную moving platform
→ следующий доброволец погибает в FREEZER
→ frozen corpse становится стабильной ступенью для повторного прохода этой зоны
→ следующий доброволец погибает в ELECTRIC
→ conductive corpse удерживает relay signal "power" и открывает Door
→ четвёртый доброволец respawn-ится и пересекает сохранённые тела/опасности
→ садится на активную platform у x=1840..2090
→ поднимается над PIT и в верхней фазе прыгает вправо через открытую Door
→ приземляется на ledge x=2420..3100
→ достигает goal и получает COMPLETE с тремя смертями

Механика и сигналы: launched saw corpse → "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 supportIndex, в верхней фазе playerY≤280; после прыжка playerX≥2420, playerY≤340; финально completed=true. Snapshot должен различать типы тел, оба сигнала, platform position/support.

Softlock/неопределённость: доставка saw-тела, три сохранённых тела, подъём и lift→ledge прыжок подтверждены тремя запусками. Обязательность frozen corpse всё ещё не доказана отрицательным тестом: возможный двухсмертный sequence break остаётся follow-up.

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

  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 и скорректировать подсказки/допуски без ослабления механик.