Early game termination
[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.gameengine.module.entities.GraphicEntityModule;
13 import com.codingame.gameengine.module.entities.Rectangle;
14 import com.codingame.gameengine.module.entities.Sprite;
15 import com.codingame.gameengine.module.entities.Text;
16 import com.codingame.gameengine.module.entities.Curve;
17 import com.google.inject.Inject;
18 import com.google.inject.Provider;
19
20 public class Referee extends AbstractReferee {
21     @Inject private MultiplayerGameManager<Player> gameManager;
22     @Inject private GraphicEntityModule graphicEntityModule;
23
24     @Inject private View view;
25     @Inject private Model model;
26
27     boolean disqual = false;
28
29     @Override
30     public void init() {
31         model.init(gameManager.getSeed());
32         gameManager.getPlayer(0).model = model.p0;
33         gameManager.getPlayer(1).model = model.p1;
34
35         for (Player p: gameManager.getPlayers()) {
36             p.gameInit(model.roadLength, model.initialStones,
37                        gameManager.getSeed());
38         }
39
40         view.init(model);
41         gameManager.getPlayer(0).view = view.p0;
42         gameManager.getPlayer(1).view = view.p1;
43         gameManager.setFrameDuration(2000);
44     }
45
46     private void disqualify(Player player, String popup, String message) {
47         player.deactivate(player.getNicknameToken() + " " + popup);
48         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " " + message));
49         player.setScore(-1);
50     }
51
52     @Override
53     public void gameTurn(int turn) {
54         // System.err.println("Starting turn " + turn);
55
56         view.startTurn();
57
58         // Did I mention I hate Java? It didn't *have* to be this ugly!
59         if (disqual) { endGame(); return; }
60         if (model.exhausted()) { finishStones(); return ;}
61         if (model.haveWinner()) { endGame(); return; }
62
63         for (Player player : gameManager.getActivePlayers()) {
64             player.sendGameTurn();
65         }
66         // SDK @#%^&! arbitrary sequence point: last input < first output
67
68         /* Parse player actions and decide basic disqualifications.
69          * Display their optional message right now: if their action
70          * is ill-formed it could help them debug.  Or shame them, at
71          * least.
72          */
73         for (Player player : gameManager.getActivePlayers()) {
74             player.receiveGameTurn();
75             switch (player.type) {
76             case Timeout:
77                 disqualify(player, "T/O", "timed out!");
78                 player.view.markTimeout();
79                 disqual = true;
80                 break;
81             case Invalid:
82                 disqualify(player, "INVALID", "provided an ill-formed action");
83                 player.view.markIllegal();
84                 disqual = true;
85                 break;
86             case Throw:
87                 try { player.model.consumeStones(player.stoneThrow); }
88                 catch (Model.Player.ThrewMoreStonesThanHad e) {
89                     if (model.random.nextInt(10) > 0) {
90                         player.view.threwMoreStonesThanHad();
91                         player.stoneThrow = player.model.consumeMaxStones();
92                     }
93                     else {
94                         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.");
95                         player.view.markIllegal();
96                         disqual = true;
97                     }
98                 }
99                 catch (Model.Player.FailedToThrowStonesAndShouldHave e) {
100                     if (model.random.nextInt(10) > 0) {
101                         player.view.failedToThrowStonesAndShouldHave();
102                         player.stoneThrow = player.model.consumeMinStones();
103                     }
104                     else {
105                         disqualify(player, "ILLEGAL", "tried not throwing any stones.  They were then eaten by a grue.");
106                         disqual = true;
107                     }
108                 }
109                 break;
110             }
111             player.view.displayMessage(player.messageString);
112         }
113
114         /* Update game model and view.
115          *
116          * As a special case, the "cheater" (sending out negative
117          * stones) handling is deferred here because we need to update
118          * its view for it to be funny.  In the end, they're still
119          * disqualified.
120          *
121          * Gather other game end scenarios (actual victory or stone
122          * exhaustion).
123          */
124         int delta = 0;
125         for (Player player : gameManager.getActivePlayers()) {
126             player.view.throwStones(player.stoneThrow);
127             delta += player.model.getMultiplier() * player.stoneThrow;
128
129             if (player.stoneThrow < 0) {
130                 disqualify(player, "CHEAT", "cheated.  Banning account.");
131                 player.view.markCheat();
132                 disqual = true;
133             }
134             if (player.stoneThrow != 0) {
135                 player.view.animateStones(player.stoneThrow);
136                 player.view.updateStoneCounter();
137             }
138         }
139
140         /* If a player cheated, delta is unusable as is.
141          * (Consider the case the player on the right sent
142          * INT_MIN.  INT_MIN * (-1) = INT_MIN, so that player
143          * would both glean the stones *and* push the troll away.
144          * It would be unfair to have a cheating player "win"
145          * (earn the opponent castle destruction animation) this
146          * way.
147          */
148         boolean cheat0 = gameManager.getPlayer(0).isActive()
149             && gameManager.getPlayer(0).stoneThrow < 0;
150         boolean cheat1 = gameManager.getPlayer(1).isActive()
151             && gameManager.getPlayer(1).stoneThrow < 0;
152         if (cheat0 && cheat1); // here we can actually keep delta's value
153         else if (cheat0) delta = -1;
154         else if (cheat1) delta =  1;
155
156         if (delta > 0) {
157             model.moveTroll(+1);
158             view.moveTroll(View.Dir.RIGHT);
159         }
160         else if (delta < 0) {
161             model.moveTroll(-1);
162             view.moveTroll(View.Dir.LEFT);
163         }
164         else {
165             view.moveTroll(View.Dir.STILL);
166             // XXX animate
167         }
168     }
169
170     // XXX very similar to main turn pendant
171     private void finishStones() {
172         boolean noStones = true;
173         int delta = 0;
174         for (Player player : gameManager.getActivePlayers()) {
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 }