Повысить устойчивость восьмого уровня к задержкам
This commit is contained in:
@@ -43,19 +43,39 @@ public final class LevelEightPlaythroughTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intendedRouteSurvivesStressedDecisionScheduling() {
|
||||
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
|
||||
LevelEightScenario.Result result = new LevelEightScenario()
|
||||
.completeIntendedRoute(driver(activity, 96L), 20_000L);
|
||||
|
||||
assertTrue("Stressed scheduling: " + result.diagnostic(), result.completed());
|
||||
assertEquals("The stressed intended route is deathless", 0, result.deaths());
|
||||
}
|
||||
}
|
||||
|
||||
private static AndroidDriver driver(ActivityScenario<MainActivity> activity) {
|
||||
return driver(activity, 0L);
|
||||
}
|
||||
|
||||
private static AndroidDriver driver(ActivityScenario<MainActivity> activity,
|
||||
long extraDecisionDelayMillis) {
|
||||
final GameView[] view = new GameView[1];
|
||||
activity.onActivity(mainActivity -> view[0] = mainActivity.gameViewForAutomation());
|
||||
return new AndroidDriver(InstrumentationRegistry.getInstrumentation(), view[0]);
|
||||
return new AndroidDriver(InstrumentationRegistry.getInstrumentation(), view[0],
|
||||
extraDecisionDelayMillis);
|
||||
}
|
||||
|
||||
private static final class AndroidDriver implements LevelPlaytestBot.Driver {
|
||||
private final Instrumentation instrumentation;
|
||||
private final GameView gameView;
|
||||
private final long extraDecisionDelayMillis;
|
||||
|
||||
AndroidDriver(Instrumentation instrumentation, GameView gameView) {
|
||||
AndroidDriver(Instrumentation instrumentation, GameView gameView,
|
||||
long extraDecisionDelayMillis) {
|
||||
this.instrumentation = instrumentation;
|
||||
this.gameView = gameView;
|
||||
this.extraDecisionDelayMillis = extraDecisionDelayMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,7 +100,7 @@ public final class LevelEightPlaythroughTest {
|
||||
@Override
|
||||
public void waitForNextDecision(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
Thread.sleep(millis + extraDecisionDelayMillis);
|
||||
} catch (InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new AssertionError("Level-8 bot interrupted", interrupted);
|
||||
|
||||
@@ -109,10 +109,10 @@ public final class LevelCatalog {
|
||||
8, "Borrowed Time", "Jump for the far blue relay if the timed gate closes.",
|
||||
2450, 130, 760, goal(2220, 600), 0, 38,
|
||||
platforms(
|
||||
floor(0, 900), floor(1510, 940), ledge(1980, 710, 470),
|
||||
floor(0, 970), floor(1510, 940), ledge(1980, 710, 470),
|
||||
moving(990, 770, 240, 34, 380, 0, 4.0f, "clock")
|
||||
),
|
||||
hazards(hazard(PIT, 900, 880, 610, 200)),
|
||||
hazards(hazard(PIT, 970, 880, 540, 200)),
|
||||
switches(
|
||||
sw(TIMED, 560, 810, 90, 70, "clock", 6.0f, 1),
|
||||
sw(TIMED, 1600, 650, 90, 70, "clock", 3.5f, 1)
|
||||
|
||||
@@ -199,7 +199,7 @@ Door закрывает путь к goal
|
||||
|
||||
**Статус:** intended route и восстановление после истечения таймера доказаны production-input ботом, по три раза подряд.
|
||||
**Ожидаемые смерти:** `0` (`targetDeaths=0`).
|
||||
**Источник:** `LevelCatalog.level8()`; основной `TIMED=(560,810,90,70)`, `signalId="clock"`, duration `6.0`; recovery relay `TIMED=(1600,650,90,70)`, duration `3.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)`.
|
||||
**Источник:** `LevelCatalog.level8()`; основной `TIMED=(560,810,90,70)`, `signalId="clock"`, duration `6.0`; recovery relay `TIMED=(1600,650,90,70)`, duration `3.5`; moving platform `(990,770,240,34)`, travel `(380,0)`, period `4.0`, `controlId="clock"`; `PIT=(970,880,540,200)`; дверь `(1770,560,82,320)` требует `"clock"`; `goal=(2220,600,86,110)`.
|
||||
|
||||
### Solution graph 8
|
||||
|
||||
@@ -207,7 +207,7 @@ Door закрывает путь к goal
|
||||
PIT разрывает floor, а Door закрывает дальнейший путь
|
||||
→ войти в TIMED и запустить signal "clock" на 6.0 s
|
||||
→ "clock" одновременно запускает horizontal moving platform и открывает Door
|
||||
→ без задержки добежать до края floor x=900
|
||||
→ без задержки добежать до края floor x=970
|
||||
→ прыгнуть на moving platform и пересечь PIT
|
||||
→ сойти на правый floor x≥1510
|
||||
→ пройти Door x=1770..1852 до истечения "clock"
|
||||
@@ -222,9 +222,9 @@ PIT разрывает floor, а Door закрывает дальнейший п
|
||||
→ пройти Door и достичь goal без смерти/restart
|
||||
```
|
||||
|
||||
**Механика и сигналы:** фронт контакта основного `TIMED` → `timedSignals["clock"]=6.0` → движение платформы по `travelX=380` и открытие Door; высокий relay выставляет тот же сигнал на `3.5 s`. Обычный бег проходит под relay, поэтому original route не активирует восстановление автоматически. Строгий grounded-checkpoint выявил нестабильное закрытие Door на `5.5 s`; добавленные `0.5 s` дают запас на проход двери, не меняя crossing или recovery.
|
||||
**Механика и сигналы:** фронт контакта основного `TIMED` → `timedSignals["clock"]=6.0` → движение платформы по `travelX=380` и открытие Door; высокий relay выставляет тот же сигнал на `3.5 s`. Обычный бег проходит под relay, поэтому original route не активирует восстановление автоматически. Строгий grounded-checkpoint выявил нестабильное закрытие Door на `5.5 s`; добавленные `0.5 s` дают запас на проход двери. Android 16 при задержках между решениями бота выявил отдельное падение с узкого стартового края: floor продлён на `70` px, а начало PIT сдвинуто с `900` на `970` без изменения его правого края `1510`, платформы, таймеров или recovery relay.
|
||||
|
||||
**Bot checkpoints:** основной timer активен; игрок двумя прыжками пересекает PIT через moving platform; перед движением к relay явно подтверждены far floor `playerX≥1510`, `playerY≥779` и всё ещё открытая Door. Intended route проходит Door на первом таймере. Recovery route ждёт `allDoorsOpen=false` справа от PIT, прыжком получает `allDoorsOpen=true`, проходит Door и завершает уровень. Оба маршрута финально дают `completed=true`, `deaths=0`; каждый выполнен три раза подряд на Android 16.
|
||||
**Bot checkpoints:** основной timer активен; игрок двумя прыжками пересекает PIT через moving platform; перед движением к relay явно подтверждены far floor `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` задержки на каждое решение также завершён без смерти. Попытка расширить платформу была отклонена тестом: она могла унести ожидающего игрока с дальнего floor обратно над PIT после закрытия двери.
|
||||
|
||||
**Softlock/неопределённость:** прежний blocker устранён: после истечения `"clock"` справа от PIT высокий relay гарантирует restart-free повторное открытие двери. Relay не помогает слева от PIT и не отменяет timed-platform crossing. Оба пути проверены без прямой мутации состояния, teleport, auto-complete или restart.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user