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