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