Bug report from @Illedan
[troll.git] / src / main / java / com / codingame / game / Referee.java
1 package com.codingame.game;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Random;
7
8 import com.codingame.gameengine.core.AbstractPlayer.TimeoutException;
9 import com.codingame.gameengine.core.AbstractReferee;
10 import com.codingame.gameengine.core.GameManager;
11 import com.codingame.gameengine.core.MultiplayerGameManager;
12 import com.codingame.game.GodModeManager;
13 import com.google.inject.Inject;
14 import com.google.inject.Provider;
15
16 public class Referee extends AbstractReferee {
17     @Inject private MultiplayerGameManager<Player> gameManager;
18     @Inject private GodModeManager gm;
19
20     @Inject private View view;
21     @Inject private Model model;
22
23     boolean disqual = false;
24
25     @Override
26     public void init() {
27         gm.init();
28         model.init(gameManager.getSeed());
29         gameManager.getPlayer(0).model = model.p0;
30         gameManager.getPlayer(1).model = model.p1;
31
32         for (Player p: gameManager.getPlayers()) {
33             p.gameInit(model.roadLength, model.initialStones,
34                        gameManager.getSeed(), gm.getSalt());
35         }
36
37         view.init(model);
38         gameManager.getPlayer(0).view = view.p0;
39         gameManager.getPlayer(1).view = view.p1;
40         gameManager.setFrameDuration(2000);
41     }
42
43     private void disqualify(Player player, String popup, String message) {
44         player.deactivate(player.getNicknameToken() + " " + popup);
45         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " " + message));
46         player.setScore(-1);
47     }
48
49     @Override
50     public void gameTurn(int turn) {
51         // System.err.println("Starting turn " + turn);
52
53         view.startTurn();
54
55         // Did I mention I hate Java? It didn't *have* to be this ugly!
56         if (disqual) { endGame(); return; }
57         if (model.exhausted()) { finishStones(); return ;}
58         if (model.haveWinner()) { endGame(); return; }
59
60         for (Player player : gameManager.getActivePlayers()) {
61             player.sendGameTurn();
62         }
63         // SDK @#%^&! arbitrary sequence point: last input < first output
64
65         /* Parse player actions and decide basic disqualifications.
66          * Display their optional message right now: if their action
67          * is ill-formed it could help them debug.  Or shame them, at
68          * least.
69          */
70         for (Player player : gameManager.getActivePlayers()) {
71             player.receiveGameTurn(); gm.transcend(player);
72             switch (player.type) {
73             case Timeout:
74                 disqualify(player, "T/O", "timed out!");
75                 player.view.markTimeout();
76                 disqual = true;
77                 break;
78             case Invalid:
79                 disqualify(player, "INVALID", "provided an ill-formed action");
80                 player.view.markIllegal();
81                 disqual = true;
82                 break;
83             case Throw:
84                 try { player.model.consumeStones(player.stoneThrow); }
85                 catch (Model.Player.ThrewMoreStonesThanHad e) {
86                     if (model.random.nextInt(10) > 0) {
87                         player.view.threwMoreStonesThanHad();
88                         player.stoneThrow = player.model.consumeMaxStones();
89                     }
90                     else {
91                         disqualify(player, "ILLEGAL", "tried to throw more stones than they had.  They went into debt trying to provide.  The economy tanked, recession and famine ensued; even the troll wouldn't have wanted to bash them anymore.  But that's no victory.");
92                         player.view.markIllegal();
93                         disqual = true;
94                     }
95                 }
96                 catch (Model.Player.FailedToThrowStonesAndShouldHave e) {
97                     if (model.random.nextInt(10) > 0) {
98                         player.view.failedToThrowStonesAndShouldHave();
99                         player.stoneThrow = player.model.consumeMinStones();
100                     }
101                     else {
102                         disqualify(player, "ILLEGAL", "tried not throwing any stones.  They were then eaten by a grue.");
103                         disqual = true;
104                     }
105                 }
106                 break;
107             }
108             player.view.displayMessage(player.messageString);
109         }
110
111         /* Update game model and view.
112          *
113          * As a special case, the "cheater" (sending out negative
114          * stones) handling is deferred here because we need to update
115          * its view for it to be funny.  In the end, they're still
116          * disqualified.
117          *
118          * Gather other game end scenarios (actual victory or stone
119          * exhaustion).
120          */
121         int delta = 0;
122         gm.update(gameManager.getPlayers());
123         for (Player player : gameManager.getActivePlayers()) {
124             player.view.throwStones(player.stoneThrow);
125             delta += player.model.getMultiplier() * player.stoneThrow;
126
127             if (player.stoneThrow < 0) {
128                 disqualify(player, "CHEAT", "cheated.  Banning account.");
129                 player.view.markCheat();
130                 disqual = true;
131             }
132             if (player.stoneThrow != 0) {
133                 player.view.animateStones(player.stoneThrow);
134                 player.view.updateStoneCounter();
135             }
136         }
137
138         /* If a player cheated, delta is unusable as is.
139          * (Consider the case the player on the right sent
140          * INT_MIN.  INT_MIN * (-1) = INT_MIN, so that player
141          * would both glean the stones *and* push the troll away.
142          * It would be unfair to have a cheating player "win"
143          * (earn the opponent castle destruction animation) this
144          * way.
145          */
146         boolean cheat0 = gameManager.getPlayer(0).isActive()
147             && gameManager.getPlayer(0).stoneThrow < 0;
148         boolean cheat1 = gameManager.getPlayer(1).isActive()
149             && gameManager.getPlayer(1).stoneThrow < 0;
150         if (cheat0 && cheat1); // here we can actually keep delta's value
151         else if (cheat0) delta = -1;
152         else if (cheat1) delta =  1;
153
154         if (delta > 0) {
155             model.moveTroll(+1);
156             view.moveTroll(View.Dir.RIGHT);
157         }
158         else if (delta < 0) {
159             model.moveTroll(-1);
160             view.moveTroll(View.Dir.LEFT);
161         }
162         else {
163             view.moveTroll(View.Dir.STILL);
164             // XXX animate
165         }
166     }
167
168     // XXX very similar to main turn pendant
169     private void finishStones() {
170         boolean noStones = true;
171         int delta = 0;
172         for (Player player : gameManager.getActivePlayers()) {
173             if (model.haveWinner() && player.getIndex() == model.getLoser())
174                 continue;
175             player.stoneThrow = player.model.getStones();
176             player.model.setStones(0);
177             delta += player.stoneThrow * player.model.getMultiplier();
178             player.view.throwStones(player.stoneThrow);
179             if (player.stoneThrow != 0) {
180                 noStones = false;
181                 player.view.animateStones(player.stoneThrow);
182                 player.view.updateStoneCounter();
183             }
184         }
185         if (noStones) { endGame(); return; }
186         model.moveTroll(delta);
187         view.moveTroll();
188     }
189
190     private void endGame() {
191         gameManager.endGame();
192
193         if (model.haveWinner()) {
194             int loser = model.getLoser();
195             gameManager.getPlayer(loser).view.defeat();
196         }
197
198         Player p0 = gameManager.getPlayer(0);
199         Player p1 = gameManager.getPlayer(1);
200
201         int s0 = p0.getScore();
202         int s1 = p1.getScore();
203
204         if (s0 > s1) {
205             p0.view.victory();
206             p1.view.markLoser();
207         }
208         else if (s0 < s1) {
209             p1.view.victory();
210             p0.view.markLoser();
211         }
212         else if (s0 < 0) {
213             view.doubleDefeat();
214             p0.view.markLoser();
215             p1.view.markLoser();
216         }
217         else {
218             view.draw();
219         }
220     }
221 }