1 import java.util.Properties;
2 import java.util.HashSet;
4 import java.util.ListIterator;
6 import java.util.Random;
7 import java.util.stream.Collectors;
8 import java.io.PrintStream;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.*;
12 import org.junit.Test;
14 import com.codingame.gameengine.runner.MultiplayerGameRunner;
15 import com.codingame.gameengine.runner.dto.*;
17 public class TrollTest implements Cloneable {
18 int leagueLevel = 1; // @#$%^&* league parameter is *global* despite API!
21 Integer initialStones;
23 try { return (TrollTest) clone(); }
24 catch (CloneNotSupportedException e) { throw new InternalError(e); }
26 TrollTest setLeague(int league) { leagueLevel = league; return this; }
27 TrollTest setSeed(long seed) { this.seed = seed; return this; }
28 TrollTest setRoadLength(int l) { roadLength = l; return this; }
29 TrollTest setInitialStones(int s) { initialStones = s; return this; }
31 GameResult runGame(String left, String right) {
32 MultiplayerGameRunner gameRunner = new MultiplayerGameRunner();
33 gameRunner.setLeagueLevel(leagueLevel);
34 if (seed != null) gameRunner.setSeed(seed); else gameRunner.setSeed(0l);
35 Properties gameParameters = new Properties();
36 gameRunner.setGameParameters(gameParameters);
37 if (roadLength != null)
38 gameParameters.setProperty("roadLength", roadLength.toString());
39 if (initialStones != null)
40 gameParameters.setProperty("initialStones", initialStones.toString());
42 gameRunner.addAgent(left);
43 gameRunner.addAgent(right);
45 return gameRunner.simulate();
49 public void drawGame() {
50 assertIsDraw(runGame(agentOne, agentOne));
54 public void defeatGames() {
55 assertIsDefeat(runGame(agentCrash, agentCrash));
56 assertIsDefeat(runGame(agentGarbage, agentGarbage));
60 public void simpleGames() {
61 // wins by direct reach, no fastforward
62 assertWinLose(agentTwo, agentOne);
64 // win by fastforward after loser exhaustion
65 assertWinLose(agentOne, agentAllIn);
67 // win despite fastforward after winner exhaustion
68 // (harder to construct :-D )
69 assertWinLose(agent(1,2,2,2,8), agent(3,1,1,1,8));
73 public void cheatingGames() {
74 // win by cheating (works in league 1, which is the default)
75 assertWinLose(agentCheat, agentTwo);
77 // league 2 randomizes: we should be able to get a win and a loss
78 branch().setLeague(2).setSeed(0)
79 .setRoadLength(6).setInitialStones(15)
80 .assertWinLose(agentCheat, agentTwo);
81 branch().setLeague(2).setSeed(1)
82 .setRoadLength(6).setInitialStones(15)
83 .assertWinLose(agentTwo, agentCheat);
86 static private class GameMap {
89 @Override public boolean equals(Object gm) {
90 if (gm instanceof GameMap) {
91 return roadLength == ((GameMap) gm).roadLength
92 && initialStones == ((GameMap) gm).initialStones;
96 @Override public int hashCode() {
97 return roadLength << 16 | initialStones;
103 TrollTest test = branch();
105 // league 1, expect a single map
106 for (long s = 0; s < 16; s++) {
108 GameMap map = test.measureMap();
109 assertEquals("Level 1 roadLength", 6, map.roadLength);
110 assertEquals("Level 1 initialStones", 15, map.initialStones);
113 // league 2, expect one of four maps
115 HashSet<GameMap> maps = new HashSet<GameMap>();
116 Random r = new Random();
117 for (int i = 0; i < 16; i++) {
118 test.setSeed(r.nextLong());
119 GameMap map = test.measureMap();
122 assertEquals("Level 2 has four maps", 4, maps.size());
124 // league 3, maps simply have constraints
126 for (long s = 0; s < 16; s++) {
128 GameMap map = test.measureMap();
129 assertTrue("Level 3 road length is at least 6",
130 map.roadLength >= 6);
131 assertTrue("Level 3 road length is at most 14",
132 map.roadLength <= 14);
133 assertTrue("Level 3 road length is even",
134 map.roadLength % 2 == 0);
135 assertTrue("Level 3 stones is at least 15",
136 map.initialStones >= 15);
137 assertTrue("Level 3 stones is at most 50",
138 map.initialStones <= 50);
143 * There's currently no way to extract the parameters (and verify
144 * them!) from a game. So for now we measure it from other
147 private GameMap measureMap() {
148 GameMap result = new GameMap();
150 // roadLength is twice the number of moves a troll takes
151 // before the end of the game in a
152 // position-independent-strategy game.
153 GameResult game = runGame(agentOne, agentTwo);
154 result.roadLength = 2 * game.summaries.stream()
155 .filter(s -> s.contains("walks"))
156 .collect(Collectors.counting()).intValue();
158 // initialStones is the number of times a troll stands still
159 // in a one-stone-throw draw.
160 game = runGame(agentOne, agentOne);
161 result.initialStones = game.summaries.stream()
162 .filter(s -> s.contains("still"))
163 .collect(Collectors.counting()).intValue();
168 // great thanks to @dbdr for the intense moral support leading to
170 static String agentOne = "yes 1";
171 static String agentTwo = "yes 2";
172 static String agentAllIn = "yes 15";
173 static String agentCrash = "false";
174 static String agentGarbage = "yes this_is_assuredly_not_an_int";
175 static String agentCheat = agent(-100,25,25,25,25);
177 static String agent(int... tosses) {
178 String cmd = "echo -e ";
179 for (int i = 0; i < tosses.length; i++) {
180 if (i > 0) cmd += "\\n";
186 static void dumpGameResult(PrintStream p, GameResult gameResult) {
187 for (AgentDto agent : gameResult.agents) dumpAgent(p, agent);
188 dumpMap(p, "errors", gameResult.errors);
189 dumpString(p, "failCause", gameResult.failCause);
190 dumpMap(p, "ids", gameResult.ids);
191 dumpString(p, "metadata", gameResult.metadata);
192 dumpMap(p, "outputs", gameResult.outputs);
193 dumpList(p, "summaries", gameResult.summaries);
194 dumpList(p, "tooltips", gameResult.tooltips);
195 // dumpList(p, "views", gameResult.views);
198 static <V> void dumpList(PrintStream p, String tag, List<V> list) {
199 ListIterator i = list.listIterator();
200 while (i.hasNext()) {
201 if (tag != null) p.print(tag + " ");
202 p.print(i.nextIndex() + ": ");
203 dumpGeneric(p, i.next());
207 static <K,V> void dumpMap(PrintStream p, String tag, Map<K,V> map) {
208 for (Map.Entry kv : map.entrySet()) {
209 p.print(tag + " for " + kv.getKey() + ": ");
210 dumpGeneric(p, kv.getValue());
214 static <E,V> void dumpGeneric(PrintStream p, E v) {
215 if (v instanceof List) {
216 dumpList(p, null, (List<?>) v);
223 static void dumpString(PrintStream p, String tag, String msg) {
224 p.println(tag + ": " + msg);
227 static void dumpAgent(PrintStream p, AgentDto agent) {
228 p.println("[agent] " + agent.agentId + ": " + agent.avatar + " " + agent.index + " " + agent.name);
231 void assertWinLose(String winner, String loser) {
232 assertLeftWin(runGame(winner, loser));
233 assertRightWin(runGame(loser, winner));
236 static void assertLeftWin(GameResult gameResult) {
237 assertLeftWin(gameResult, false);
239 static void assertLeftWin(GameResult gameResult, boolean strict) {
240 int[] scores = assertTwoScores(gameResult);
241 if (scores == null) return;
243 int s1 = scores[0], s2 = scores[1];
244 assertTrue("Left player has higher score than right player", s1 > s2);
245 if (strict) assertTrue("Right player isn't disqualified", s2 >= 0);
248 static void assertRightWin(GameResult gameResult) {
249 assertRightWin(gameResult, false);
251 static void assertRightWin(GameResult gameResult, boolean strict) {
252 int[] scores = assertTwoScores(gameResult);
253 if (scores == null) return;
255 int s1 = scores[0], s2 = scores[1];
256 assertTrue("Right player has higher score than right player", s2 > s1);
257 if (strict) assertTrue("Left player isn't disqualified", s1 >= 0);
260 static void assertIsDraw(GameResult gameResult) {
261 int[] scores = assertTwoScores(gameResult);
262 if (scores == null) return;
264 int s1 = scores[0], s2 = scores[1];
265 assertEquals("Player scores are equal", s1, s2);
266 assertTrue("Player scores are non-negative", s1 >= 0);
269 static void assertIsDefeat(GameResult gameResult) {
270 int[] scores = assertTwoScores(gameResult);
271 if (scores == null) return;
273 int s1 = scores[0], s2 = scores[1];
274 assertTrue("First player score is negative", s1 < 0);
275 assertTrue("Second player score is negative", s2 < 0);
278 static int[] assertTwoScores(GameResult gameResult) {
279 Map<Integer,Integer> scores = gameResult.scores;
280 assertEquals("Two scores are reported", scores.size(), 2);
281 assertTrue("Player 0 has a score", scores.containsKey(0));
282 assertTrue("Player 1 has a score", scores.containsKey(1));
283 return new int[] { scores.get(0), scores.get(1) };