Выделить event-driven PuzzleSystem
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package com.projectsacrifice.game;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public final class PuzzleSystemTest {
|
||||
@Test
|
||||
public void toggleChannelChangesOnlyOnRisingEdgesAndPublishesEvents() {
|
||||
PuzzleSystem system = new PuzzleSystem();
|
||||
PuzzleSystem.Input held = PuzzleSystem.Input.toggle("lever-1", "gate", true);
|
||||
|
||||
system.update(0.016f, Collections.singletonList(held));
|
||||
assertTrue(system.signalActive("gate"));
|
||||
assertEquals(PuzzleSystem.Event.Kind.SIGNAL_ACTIVATED,
|
||||
system.events().get(0).kind());
|
||||
assertEquals("gate", system.events().get(0).channelId());
|
||||
|
||||
system.update(0.016f, Collections.singletonList(held));
|
||||
assertTrue(system.signalActive("gate"));
|
||||
assertTrue(system.events().isEmpty());
|
||||
|
||||
system.update(0.016f, Collections.singletonList(
|
||||
PuzzleSystem.Input.toggle("lever-1", "gate", false)));
|
||||
system.update(0.016f, Collections.singletonList(held));
|
||||
assertFalse(system.signalActive("gate"));
|
||||
assertEquals(PuzzleSystem.Event.Kind.SIGNAL_DEACTIVATED,
|
||||
system.events().get(0).kind());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pressureChannelAggregatesEveryActiveSourceWithoutFlicker() {
|
||||
PuzzleSystem system = new PuzzleSystem();
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate-left", "weight", true),
|
||||
PuzzleSystem.Input.pressure("plate-right", "weight", false)));
|
||||
assertTrue(system.signalActive("weight"));
|
||||
assertEquals(1, system.events().size());
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate-left", "weight", false),
|
||||
PuzzleSystem.Input.pressure("plate-right", "weight", true)));
|
||||
assertTrue(system.signalActive("weight"));
|
||||
assertTrue(system.events().isEmpty());
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate-left", "weight", false),
|
||||
PuzzleSystem.Input.pressure("plate-right", "weight", false)));
|
||||
assertFalse(system.signalActive("weight"));
|
||||
assertEquals(PuzzleSystem.Event.Kind.SIGNAL_DEACTIVATED,
|
||||
system.events().get(0).kind());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timedChannelStartsOnRisingEdgeExpiresAndCanBeRearmed() {
|
||||
PuzzleSystem system = new PuzzleSystem();
|
||||
|
||||
system.update(0f, Collections.singletonList(
|
||||
PuzzleSystem.Input.timed("clock-switch", "clock", true, 0.5f)));
|
||||
assertTrue(system.signalActive("clock"));
|
||||
assertEquals(0.5f, system.remainingSeconds("clock"), 0f);
|
||||
|
||||
system.update(0.2f, Collections.singletonList(
|
||||
PuzzleSystem.Input.timed("clock-switch", "clock", true, 0.5f)));
|
||||
assertEquals(0.3f, system.remainingSeconds("clock"), 0.0001f);
|
||||
assertTrue(system.events().isEmpty());
|
||||
|
||||
system.update(0.31f, Collections.singletonList(
|
||||
PuzzleSystem.Input.timed("clock-switch", "clock", false, 0.5f)));
|
||||
assertFalse(system.signalActive("clock"));
|
||||
assertEquals(PuzzleSystem.Event.Kind.SIGNAL_DEACTIVATED,
|
||||
system.events().get(0).kind());
|
||||
|
||||
system.update(0f, Collections.singletonList(
|
||||
PuzzleSystem.Input.timed("clock-switch", "clock", true, 0.5f)));
|
||||
assertTrue(system.signalActive("clock"));
|
||||
assertEquals(0.5f, system.remainingSeconds("clock"), 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void andDoorPublishesExplicitOpenAndCloseTransitions() {
|
||||
DoorController exit = new DoorController("exit",
|
||||
Arrays.asList("weight", "power"));
|
||||
PuzzleSystem system = new PuzzleSystem(Collections.singletonList(exit));
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate", "weight", true),
|
||||
PuzzleSystem.Input.relay("conductor", "power", false)));
|
||||
assertEquals(DoorController.State.CLOSED, exit.state());
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate", "weight", true),
|
||||
PuzzleSystem.Input.relay("conductor", "power", true)));
|
||||
assertEquals(DoorController.State.OPEN, exit.state());
|
||||
assertTrue(hasEvent(system, PuzzleSystem.Event.Kind.DOOR_OPENED, "exit"));
|
||||
|
||||
system.update(0.016f, Arrays.asList(
|
||||
PuzzleSystem.Input.pressure("plate", "weight", false),
|
||||
PuzzleSystem.Input.relay("conductor", "power", true)));
|
||||
assertEquals(DoorController.State.CLOSED, exit.state());
|
||||
assertTrue(hasEvent(system, PuzzleSystem.Event.Kind.DOOR_CLOSED, "exit"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetAndRestoreOwnAnImmutablePuzzleStateSnapshot() {
|
||||
DoorController exit = new DoorController("exit", Arrays.asList("gate", "clock"));
|
||||
PuzzleSystem source = new PuzzleSystem(Collections.singletonList(exit));
|
||||
source.update(0f, Arrays.asList(
|
||||
PuzzleSystem.Input.toggle("lever", "gate", true),
|
||||
PuzzleSystem.Input.timed("timer", "clock", true, 2f)));
|
||||
PuzzleSystem.State saved = source.snapshot();
|
||||
|
||||
source.reset();
|
||||
assertFalse(source.signalActive("gate"));
|
||||
assertFalse(source.signalActive("clock"));
|
||||
assertEquals(DoorController.State.CLOSED, exit.state());
|
||||
|
||||
DoorController restoredDoor = new DoorController("exit",
|
||||
Arrays.asList("gate", "clock"));
|
||||
PuzzleSystem restored = new PuzzleSystem(Collections.singletonList(restoredDoor));
|
||||
restored.restore(saved, Collections.singletonMap("lever", true));
|
||||
|
||||
assertTrue(restored.signalActive("gate"));
|
||||
assertEquals(2f, restored.remainingSeconds("clock"), 0f);
|
||||
assertEquals(DoorController.State.OPEN, restoredDoor.state());
|
||||
restored.update(0f, Collections.singletonList(
|
||||
PuzzleSystem.Input.toggle("lever", "gate", true)));
|
||||
assertTrue("Held restored contact must not toggle again",
|
||||
restored.signalActive("gate"));
|
||||
assertEquals(Boolean.TRUE, saved.toggleSignals().get("gate"));
|
||||
assertEquals(2f, saved.timedSignals().get("clock"), 0f);
|
||||
try {
|
||||
saved.toggleSignals().put("other", true);
|
||||
throw new AssertionError("Puzzle state maps must be immutable");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
// Contract proven.
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasEvent(PuzzleSystem system, PuzzleSystem.Event.Kind kind,
|
||||
String targetId) {
|
||||
for (PuzzleSystem.Event event : system.events()) {
|
||||
if (event.kind() == kind && targetId.equals(event.targetId())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user