Стабилизировать бота уровня 10 при задержках
This commit is contained in:
@@ -107,19 +107,56 @@ public final class LateLevelPlaythroughTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void masteryProcessionSurvivesThrottledFinalRelayDecisions() {
|
||||
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
|
||||
GameDriver driver = GameDriver.withFinalRelayDelay(activity, 96L);
|
||||
|
||||
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
|
||||
driver,
|
||||
LateLevelScenarios.levelTen(),
|
||||
65_000L
|
||||
);
|
||||
|
||||
assertTrue(result.diagnostic(), result.completed());
|
||||
assertEquals("throttling must not add accidental volunteers", 3, result.deaths());
|
||||
assertEquals("all three intended bodies must remain", 3, result.corpseCount());
|
||||
assertTrue("the conductive relay must still open the final door",
|
||||
result.doorOpened());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GameDriver implements LevelPlaytestBot.Driver {
|
||||
private final Instrumentation instrumentation;
|
||||
private final GameView gameView;
|
||||
private final long minimumDecisionMillis;
|
||||
private final boolean throttleFinalRelayOnly;
|
||||
private GameView.AutomationSnapshot lastSnapshot;
|
||||
|
||||
private GameDriver(Instrumentation instrumentation, GameView gameView) {
|
||||
private GameDriver(Instrumentation instrumentation, GameView gameView,
|
||||
long minimumDecisionMillis, boolean throttleFinalRelayOnly) {
|
||||
this.instrumentation = instrumentation;
|
||||
this.gameView = gameView;
|
||||
this.minimumDecisionMillis = minimumDecisionMillis;
|
||||
this.throttleFinalRelayOnly = throttleFinalRelayOnly;
|
||||
}
|
||||
|
||||
static GameDriver from(ActivityScenario<MainActivity> activity) {
|
||||
return create(activity, 32L, false);
|
||||
}
|
||||
|
||||
static GameDriver withFinalRelayDelay(ActivityScenario<MainActivity> activity,
|
||||
long minimumDecisionMillis) {
|
||||
return create(activity, minimumDecisionMillis, true);
|
||||
}
|
||||
|
||||
private static GameDriver create(ActivityScenario<MainActivity> activity,
|
||||
long minimumDecisionMillis,
|
||||
boolean throttleFinalRelayOnly) {
|
||||
GameView[] view = new GameView[1];
|
||||
activity.onActivity(current -> view[0] = current.gameViewForAutomation());
|
||||
return new GameDriver(InstrumentationRegistry.getInstrumentation(), view[0]);
|
||||
return new GameDriver(InstrumentationRegistry.getInstrumentation(), view[0],
|
||||
minimumDecisionMillis, throttleFinalRelayOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,13 +175,22 @@ public final class LateLevelPlaythroughTest {
|
||||
public GameView.AutomationSnapshot snapshot() {
|
||||
GameView.AutomationSnapshot[] value = new GameView.AutomationSnapshot[1];
|
||||
instrumentation.runOnMainSync(() -> value[0] = gameView.automationSnapshot());
|
||||
return value[0];
|
||||
lastSnapshot = value[0];
|
||||
return lastSnapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForNextDecision(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
boolean inFinalRelayWindow = throttleFinalRelayOnly
|
||||
&& lastSnapshot != null
|
||||
&& lastSnapshot.levelNumber() == 10
|
||||
&& lastSnapshot.deaths() == 3
|
||||
&& lastSnapshot.playerX() >= 1_200f
|
||||
&& lastSnapshot.playerX() <= 1_700f;
|
||||
Thread.sleep(inFinalRelayWindow
|
||||
? Math.max(millis, minimumDecisionMillis)
|
||||
: millis);
|
||||
} catch (InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new AssertionError("Late-level bot interrupted", interrupted);
|
||||
|
||||
@@ -155,10 +155,10 @@ final class LateLevelScenarios {
|
||||
snapshot -> snapshot.playerX() >= 730f)
|
||||
.step("Clear the lift body again", 2_000L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 900f)
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 860f)
|
||||
.step("Prepare the frozen-block jump", 2_000L,
|
||||
LevelPlaytestBot.Input.none(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() >= 740f)
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() >= 720f)
|
||||
.step("Jump over the frozen block", 2_500L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 1_240f)
|
||||
@@ -185,25 +185,42 @@ final class LateLevelScenarios {
|
||||
snapshot -> snapshot.playerX() >= 730f)
|
||||
.step("Clear the retained lift body", 2_000L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 900f)
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 860f)
|
||||
.step("Prepare the final frozen-block jump", 2_000L,
|
||||
LevelPlaytestBot.Input.none(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() >= 740f)
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() >= 720f)
|
||||
.step("Clear the frozen block", 2_500L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 1_240f)
|
||||
.step("Approach the powered relay", 2_500L,
|
||||
LevelPlaytestBot.Input.right(),
|
||||
snapshot -> snapshot.playerX() >= 1_390f)
|
||||
.step("Clear the powered relay", 2_500L,
|
||||
snapshot -> snapshot.playerX() >= 1_320f)
|
||||
.step("Take off before the final relay", 1_000L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() <= 700f)
|
||||
.step("Clear the powered relay", 2_000L,
|
||||
LevelPlaytestBot.Input.right(),
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerX() >= 1_650f)
|
||||
.step("Approach the lift", 2_000L, LevelPlaytestBot.Input.right(),
|
||||
snapshot -> snapshot.playerX() >= 1_730f)
|
||||
.step("Board the moving lift", 3_000L,
|
||||
.step("Wait for the lift at floor height", 6_000L,
|
||||
LevelPlaytestBot.Input.none(),
|
||||
snapshot -> snapshot.playerAlive()
|
||||
&& snapshot.playerY() >= 770f
|
||||
&& !Float.isNaN(snapshot.firstMovingPlatformY())
|
||||
&& snapshot.firstMovingPlatformY() >= 740f)
|
||||
.step("Take off toward the moving lift", 1_000L,
|
||||
LevelPlaytestBot.Input.rightJump(),
|
||||
snapshot -> snapshot.playerX() >= 1_790f
|
||||
&& snapshot.playerY() <= 670f)
|
||||
snapshot -> snapshot.playerAlive() && snapshot.playerY() <= 670f)
|
||||
.step("Board the moving lift", 2_000L,
|
||||
LevelPlaytestBot.Input.right(),
|
||||
GameView.AutomationSnapshot::playerOnMovingPlatform)
|
||||
.step("Brake toward the lift center", 1_000L,
|
||||
LevelPlaytestBot.Input.left(),
|
||||
snapshot -> snapshot.playerOnMovingPlatform()
|
||||
&& snapshot.playerX() <= 1_900f)
|
||||
.step("Center on the moving lift", 1_000L,
|
||||
LevelPlaytestBot.Input.right(),
|
||||
snapshot -> snapshot.playerOnMovingPlatform()
|
||||
&& snapshot.playerX() >= 1_920f)
|
||||
.step("Ride the lift to the upper door", 5_000L,
|
||||
LevelPlaytestBot.Input.none(),
|
||||
snapshot -> snapshot.playerY() <= 310f)
|
||||
|
||||
@@ -53,11 +53,23 @@ public final class GameView extends View implements Runnable {
|
||||
private final float playerY;
|
||||
private final int deaths;
|
||||
private final int corpseCount;
|
||||
private final float firstMovingPlatformY;
|
||||
private final boolean playerOnMovingPlatform;
|
||||
|
||||
AutomationSnapshot(boolean completed, boolean playerAlive, boolean allDoorsOpen,
|
||||
boolean visiblyPaused, boolean inputActive, int levelNumber,
|
||||
long elapsedMilliseconds,
|
||||
float playerX, float playerY, int deaths, int corpseCount) {
|
||||
this(completed, playerAlive, allDoorsOpen, visiblyPaused, inputActive, levelNumber,
|
||||
elapsedMilliseconds, playerX, playerY, deaths, corpseCount,
|
||||
Float.NaN, false);
|
||||
}
|
||||
|
||||
AutomationSnapshot(boolean completed, boolean playerAlive, boolean allDoorsOpen,
|
||||
boolean visiblyPaused, boolean inputActive, int levelNumber,
|
||||
long elapsedMilliseconds,
|
||||
float playerX, float playerY, int deaths, int corpseCount,
|
||||
float firstMovingPlatformY, boolean playerOnMovingPlatform) {
|
||||
this.completed = completed;
|
||||
this.playerAlive = playerAlive;
|
||||
this.allDoorsOpen = allDoorsOpen;
|
||||
@@ -69,6 +81,8 @@ public final class GameView extends View implements Runnable {
|
||||
this.playerY = playerY;
|
||||
this.deaths = deaths;
|
||||
this.corpseCount = corpseCount;
|
||||
this.firstMovingPlatformY = firstMovingPlatformY;
|
||||
this.playerOnMovingPlatform = playerOnMovingPlatform;
|
||||
}
|
||||
|
||||
public boolean completed() { return completed; }
|
||||
@@ -82,6 +96,8 @@ public final class GameView extends View implements Runnable {
|
||||
public float playerY() { return playerY; }
|
||||
public int deaths() { return deaths; }
|
||||
public int corpseCount() { return corpseCount; }
|
||||
public float firstMovingPlatformY() { return firstMovingPlatformY; }
|
||||
public boolean playerOnMovingPlatform() { return playerOnMovingPlatform; }
|
||||
}
|
||||
|
||||
private static final class Player {
|
||||
@@ -1128,6 +1144,17 @@ public final class GameView extends View implements Runnable {
|
||||
if (level != null) {
|
||||
for (LevelDefinition.Door door : level.doors) allOpen &= doorOpen(door);
|
||||
}
|
||||
float firstMovingPlatformY = Float.NaN;
|
||||
for (PlatformState state : platformStates) {
|
||||
if (state.definition.isMoving()) {
|
||||
firstMovingPlatformY = state.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean playerOnMovingPlatform = player.alive
|
||||
&& player.supportIndex >= 0
|
||||
&& player.supportIndex < platformStates.size()
|
||||
&& platformStates.get(player.supportIndex).definition.isMoving();
|
||||
return new AutomationSnapshot(
|
||||
screen == Screen.COMPLETE,
|
||||
player.alive,
|
||||
@@ -1139,7 +1166,9 @@ public final class GameView extends View implements Runnable {
|
||||
player.x,
|
||||
player.y,
|
||||
deaths,
|
||||
corpses.size()
|
||||
corpses.size(),
|
||||
firstMovingPlatformY,
|
||||
playerOnMovingPlatform
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ public final class LevelPlaytestBotTest {
|
||||
assertTrue(result.doorOpened());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void automationSnapshotDefaultsToNoMovingPlatform() {
|
||||
GameView.AutomationSnapshot snapshot = new GameView.AutomationSnapshot(
|
||||
false, true, false, false, false,
|
||||
1, 0L, 130f, 760f, 0, 0);
|
||||
|
||||
assertTrue(Float.isNaN(snapshot.firstMovingPlatformY()));
|
||||
assertFalse(snapshot.playerOnMovingPlatform());
|
||||
}
|
||||
|
||||
private static final class ScriptedDriver implements LevelPlaytestBot.Driver {
|
||||
private int startedLevel;
|
||||
private long nowMillis;
|
||||
|
||||
Reference in New Issue
Block a user