Leaguify cheating
[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                     if (model.random.nextInt(10) > 0) {
85                         player.view.threwMoreStonesThanHad();
86                         player.stoneThrow = player.model.consumeMaxStones();
87                     }
88                     else {
89                         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.");
90                         player.view.markIllegal();
91                         disqual = true;
92                     }
93                 }
94                 catch (Model.Player.FailedToThrowStonesAndShouldHave e) {
95                     if (model.random.nextInt(10) > 0) {
96                         player.view.failedToThrowStonesAndShouldHave();
97                         player.stoneThrow = player.model.consumeMinStones();
98                     }
99                     else {
100                         disqualify(player, "ILLEGAL", "tried not throwing any stones.  They were then eaten by a grue.");
101                         disqual = true;
102                     }
103                 }
104                 break;
105             }
106             player.view.displayMessage(player.messageString);
107         }
108         if (disqual) return;
109
110         /* Update game model and view, stones' part.
111          *
112          * As a special case, the "cheater" (sending out negative
113          * stones) handling is deferred here because we need to update
114          * its view for it to be funny.  In the end, they're still
115          * disqualified.
116          *
117          * Gather other game end scenarios (actual victory or stone
118          * exhaustion).
119          */
120         int delta = 0;
121         gm.update(gameManager.getPlayers());
122         for (Player player : gameManager.getActivePlayers()) {
123             player.view.throwStones(player.stoneThrow);
124             delta += player.model.getMultiplier() * player.stoneThrow;
125
126             if (player.stoneThrow < 0) {
127                 switch(league.cheatLevel) {
128                 case ALLOWED:
129                     break;
130                 case TOLERATED:
131                     player.view.markCheat();
132                     if (model.random.nextInt(2) == 0) player.model.loseRound();
133                     break;
134                 case FORBIDDEN:
135                     disqualify(player, "CHEAT", "cheated.  Banning account.");
136                     player.view.markCheat();
137                     disqual = true;
138                     break;
139                 }
140             }
141             if (player.stoneThrow != 0) {
142                 player.view.animateStones(player.stoneThrow);
143                 player.view.updateStoneCounter();
144             }
145         }
146
147         /* Update game model and view, troll part.
148          *
149          * If a player cheated, delta is unusable as is.
150          * (Consider the case the player on the right sent
151          * INT_MIN.  INT_MIN * (-1) = INT_MIN, so that player
152          * would both glean the stones *and* push the troll away.
153          * It would be unfair to have a cheating player "win"
154          * (earn the opponent castle destruction animation) this
155          * way.
156          */
157         boolean cheat0 = gameManager.getPlayer(0).isActive()
158             && gameManager.getPlayer(0).stoneThrow < 0;
159         boolean cheat1 = gameManager.getPlayer(1).isActive()
160             && gameManager.getPlayer(1).stoneThrow < 0;
161         if (cheat0 && cheat1); // here we can actually keep delta's value
162         else if (cheat0) delta = -1;
163         else if (cheat1) delta =  1;
164
165         if (delta > 0) {
166             model.moveTroll(+1);
167             view.moveTroll(View.Dir.RIGHT);
168         }
169         else if (delta < 0) {
170             model.moveTroll(-1);
171             view.moveTroll(View.Dir.LEFT);
172         }
173         else {
174             view.moveTroll(View.Dir.STILL);
175             // XXX animate
176         }
177     }
178
179     // XXX very similar to main turn pendant
180     private void finishStones() {
181         boolean noStones = true;
182         int delta = 0;
183         for (Player player : gameManager.getActivePlayers()) {
184             if (model.haveWinner() && player.getIndex() == model.getLoser())
185                 continue;
186             player.stoneThrow = player.model.getStones();
187             player.model.setStones(0);
188             delta += player.stoneThrow * player.model.getMultiplier();
189             player.view.throwStones(player.stoneThrow);
190             if (player.stoneThrow != 0) {
191                 noStones = false;
192                 player.view.animateStones(player.stoneThrow);
193                 player.view.updateStoneCounter();
194             }
195         }
196         if (noStones) { endGame(); return; }
197         model.moveTroll(delta);
198         view.moveTroll();
199     }
200
201     private void endGame() {
202         gameManager.endGame();
203
204         if (model.haveWinner()) {
205             int loser = model.getLoser();
206             gameManager.getPlayer(loser).view.defeat();
207         }
208
209         Player p0 = gameManager.getPlayer(0);
210         Player p1 = gameManager.getPlayer(1);
211
212         int s0 = p0.getScore();
213         int s1 = p1.getScore();
214
215         if (s0 > s1) {
216             p0.view.victory();
217             p1.view.markLoser();
218         }
219         else if (s0 < s1) {
220             p1.view.victory();
221             p0.view.markLoser();
222         }
223         else if (s0 < 0) {
224             view.doubleDefeat();
225             p0.view.markLoser();
226             p1.view.markLoser();
227         }
228         else {
229             view.draw();
230         }
231     }
232 }