+ // win despite fastforward after winner exhaustion
+ // (harder to construct :-D )
+ assertWinLose(agent(1,2,2,2,8), agent(3,1,1,1,8));
+ }
+
+ @Test
+ public void cheatingGames() {
+ // win by cheating (works in league 1, which is the default)
+ assertWinLose(agentCheat, agentTwo);
+
+ // league 2 randomizes: we should be able to get a win and a loss
+ branch().setLeague(2).setSeed(0)
+ .setRoadLength(6).setInitialStones(15)
+ .assertWinLose(agentCheat, agentTwo);
+ branch().setLeague(2).setSeed(1)
+ .setRoadLength(6).setInitialStones(15)
+ .assertWinLose(agentTwo, agentCheat);
+ }
+
+ static private class GameMap {
+ int roadLength;
+ int initialStones;
+ @Override public boolean equals(Object gm) {
+ if (gm instanceof GameMap) {
+ return roadLength == ((GameMap) gm).roadLength
+ && initialStones == ((GameMap) gm).initialStones;
+ }
+ else return false;
+ }
+ @Override public int hashCode() {
+ return roadLength << 16 | initialStones;
+ }
+ }
+
+ @Test
+ public void maps() {
+ TrollTest test = branch();
+
+ // league 1, expect a single map
+ for (long s = 0; s < 16; s++) {
+ test.setSeed(s);
+ GameMap map = test.measureMap();
+ assertEquals("Level 1 roadLength", 6, map.roadLength);
+ assertEquals("Level 1 initialStones", 15, map.initialStones);
+ }
+
+ // league 2, expect one of four maps
+ test.setLeague(2);
+ HashSet<GameMap> maps = new HashSet<GameMap>();
+ Random r = new Random();
+ for (int i = 0; i < 16; i++) {
+ test.setSeed(r.nextLong());
+ GameMap map = test.measureMap();
+ maps.add(map);
+ }
+ assertEquals("Level 2 has four maps", 4, maps.size());
+
+ // league 3, maps simply have constraints
+ test.setLeague(3);
+ for (long s = 0; s < 16; s++) {
+ test.setSeed(s);
+ GameMap map = test.measureMap();
+ assertTrue("Level 3 road length is at least 6",
+ map.roadLength >= 6);
+ assertTrue("Level 3 road length is at most 14",
+ map.roadLength <= 14);
+ assertTrue("Level 3 road length is even",
+ map.roadLength % 2 == 0);
+ assertTrue("Level 3 stones is at least 15",
+ map.initialStones >= 15);
+ assertTrue("Level 3 stones is at most 50",
+ map.initialStones <= 50);
+ }
+ }
+
+ /*
+ * There's currently no way to extract the parameters (and verify
+ * them!) from a game. So for now we measure it from other
+ * traces.
+ */
+ private GameMap measureMap() {
+ GameMap result = new GameMap();
+
+ // roadLength is twice the number of moves a troll takes
+ // before the end of the game in a
+ // position-independent-strategy game.
+ GameResult game = runGame(agentOne, agentTwo);
+ result.roadLength = 2 * game.summaries.stream()
+ .filter(s -> s.contains("walks"))
+ .collect(Collectors.counting()).intValue();
+
+ // initialStones is the number of times a troll stands still
+ // in a one-stone-throw draw.
+ game = runGame(agentOne, agentOne);
+ result.initialStones = game.summaries.stream()
+ .filter(s -> s.contains("still"))
+ .collect(Collectors.counting()).intValue();
+
+ return result;