package com.projectsacrifice.game; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; public final class LevelPlaytestBotTest { @Test public void scenarioProgressesThroughNamedSteps() { ScriptedDriver driver = new ScriptedDriver(); LevelPlaytestBot.Scenario scenario = LevelPlaytestBot.Scenario .builder(7, "Reusable scenario") .step("Create a corpse", 1_000L, LevelPlaytestBot.Input.right(), snapshot -> snapshot.deaths() >= 1) .step("Reach the goal", 1_000L, LevelPlaytestBot.Input.left(), GameView.AutomationSnapshot::completed) .build(); LevelPlaytestBot.Result result = new LevelPlaytestBot().run(driver, scenario, 5_000L); assertTrue(result.diagnostic(), result.completed()); assertEquals(7, driver.startedLevel); assertEquals("Reach the goal", result.lastStep()); } @Test public void stepTimeoutReportsItsNameAndObservableSnapshot() { ScriptedDriver driver = new ScriptedDriver(false); LevelPlaytestBot.Scenario scenario = LevelPlaytestBot.Scenario .builder(5, "Diagnostic scenario") .step("Keep the plate pressed", 64L, LevelPlaytestBot.Input.right(), GameView.AutomationSnapshot::allDoorsOpen) .build(); LevelPlaytestBot.Result result = new LevelPlaytestBot().run(driver, scenario, 5_000L); assertFalse(result.completed()); assertEquals("Keep the plate pressed", result.lastStep()); assertTrue(result.diagnostic(), result.diagnostic().contains("step=Keep the plate pressed")); assertTrue(result.diagnostic(), result.diagnostic().contains("x=100.0")); assertTrue(result.diagnostic(), result.diagnostic().contains("y=760.0")); assertTrue(result.diagnostic(), result.diagnostic().contains("alive=true")); assertTrue(result.diagnostic(), result.diagnostic().contains("deaths=0")); assertTrue(result.diagnostic(), result.diagnostic().contains("corpses=0")); assertTrue(result.diagnostic(), result.diagnostic().contains("allDoorsOpen=false")); } @Test public void levelFiveUsesTheGenericNamedScenario() { LevelFiveDriver driver = new LevelFiveDriver(); LevelPlaytestBot.Result result = new LevelPlaytestBot().proveLevelFive(driver, 25_000L); assertTrue(result.diagnostic(), result.completed()); assertEquals(5, driver.startedLevel); assertEquals("Reach the relic", result.lastStep()); assertEquals(1, result.deaths()); assertEquals(1, result.corpseCount()); assertTrue(result.doorOpened()); } private static final class ScriptedDriver implements LevelPlaytestBot.Driver { private int startedLevel; private long nowMillis; private boolean moveLeft; private boolean moveRight; private int deaths; private boolean completed; private final boolean progresses; ScriptedDriver() { this(true); } ScriptedDriver(boolean progresses) { this.progresses = progresses; } @Override public void startLevel(int oneBasedLevelNumber) { startedLevel = oneBasedLevelNumber; } @Override public void setInput(boolean moveLeft, boolean moveRight, boolean jump) { this.moveLeft = moveLeft; this.moveRight = moveRight; } @Override public GameView.AutomationSnapshot snapshot() { return new GameView.AutomationSnapshot( completed, true, false, false, moveLeft || moveRight, startedLevel, nowMillis, 100f, 760f, deaths, deaths ); } @Override public void waitForNextDecision(long millis) { nowMillis += millis; if (progresses && moveRight && deaths == 0) { deaths = 1; } else if (progresses && moveLeft && deaths == 1) { completed = true; } } @Override public long currentTimeMillis() { return nowMillis; } } private static final class LevelFiveDriver implements LevelPlaytestBot.Driver { private int startedLevel; private long nowMillis; private boolean moveRight; private boolean jump; private boolean alive = true; private boolean doorOpen; private boolean completed; private int deaths; private int corpses; private float x = 130f; @Override public void startLevel(int oneBasedLevelNumber) { startedLevel = oneBasedLevelNumber; } @Override public void setInput(boolean moveLeft, boolean moveRight, boolean jump) { this.moveRight = moveRight; this.jump = jump; } @Override public GameView.AutomationSnapshot snapshot() { return new GameView.AutomationSnapshot( completed, alive, doorOpen, false, moveRight || jump, startedLevel, nowMillis, x, 760f, deaths, corpses); } @Override public void waitForNextDecision(long millis) { nowMillis += millis; if (!alive) { alive = true; x = 130f; } else if (deaths == 0 && moveRight && !jump) { deaths = 1; corpses = 1; alive = false; doorOpen = true; x = 570f; } else if (moveRight && jump) { x += 60f; } else if (moveRight && x < 390f) { x = 400f; } else if (moveRight && x < 750f) { x = 760f; } else if (moveRight && x < 1_390f) { x = 1_500f; } else if (moveRight) { x = 2_080f; completed = true; } } @Override public long currentTimeMillis() { return nowMillis; } } }