Factor protocol and gameTurn() loop
[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         }
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     @Override
44     public void gameTurn(int turn) {
45         // System.err.println("Starting turn " + turn);
46
47         view.startTurn();
48
49         for (Player player : gameManager.getActivePlayers()) {
50             player.sendGameTurn();
51         }
52         // SDK @#%^&! arbitrary sequence point: last input < first output
53
54         /* Parse player actions and decide basic disqualifications.
55          * Display their optional message right now: if their action
56          * is ill-formed it could help them debug.  Or shame them, at
57          * least.
58          */
59         boolean disqual = false;
60         for (Player player : gameManager.getActivePlayers()) {
61             player.receiveGameTurn();
62             switch (player.type) {
63             case Timeout:
64                 gameManager.addToGameSummary(player.getNicknameToken() + " timed out!");
65                 player.deactivate(player.getNicknameToken() + " T/O");
66                 player.setScore(-1);
67                 disqual = true;
68                 break;
69             case Invalid:
70                 player.deactivate(player.getNicknameToken() + " INVALID");
71                 gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " provided an ill-formed action"));
72                 player.setScore(-1);
73                 disqual = true;
74                 break;
75             case Throw:
76                 try { player.model.consumeStones(player.stoneThrow); }
77                 catch (Model.Player.ThrewMoreStonesThanHad e) {
78                     if (model.random.nextInt(10) > 0) {
79                         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " tried to throw more stones than they had.  I'll let it slide for this time.  (But not let them throw that much!)"));
80                         player.stoneThrow = player.model.consumeMaxStones();
81                     }
82                     else {
83                         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " 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."));
84                         player.deactivate(player.getNicknameToken() + " ILLEGAL");
85                         player.setScore(-1);
86                         disqual = true;
87                     }
88                 }
89                 catch (Model.Player.FailedToThrowStonesAndShouldHave e) {
90                     if (model.random.nextInt(10) > 0) {
91                         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " tried not throwing any stones.  Fixing that for them because I'm in a good mood today."));
92                         player.stoneThrow = player.model.consumeMinStones();
93                     }
94                     else {
95                         gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + "tried not throwing any stones.  They were then eaten by a grue."));
96                         player.deactivate(player.getNicknameToken() + " ILLEGAL");
97                         player.setScore(-1);
98                         disqual = true;
99                     }
100                 }
101                 break;
102             }
103             player.view.displayMessage(player.messageString);
104         }
105
106         /* Update game model and view.
107          *
108          * As a special case, the "cheater" (sending out negative
109          * stones) handling is deferred here because we need to update
110          * its view for it to be funny.  In the end, they're still
111          * disqualified.
112          *
113          * Gather other game end scenarios (actual victory or stone
114          * exhaustion).
115          */
116         int delta = 0;
117         boolean victory = false;
118         boolean exhausted = false;
119         if (! disqual) {
120             for (Player player : gameManager.getActivePlayers()) {
121                 gameManager.addToGameSummary(String.format("%s throws %d stone%s at the troll.", player.getNicknameToken(), player.stoneThrow, player.stoneThrow == 1 ? "" : "s"));
122                 delta += player.model.getMultiplier() * player.stoneThrow;
123
124                 if (player.stoneThrow < 0) {
125                     player.deactivate(player.getNicknameToken() + " CHEAT");
126                     gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " cheated.  Banning account."));
127                     player.setScore(-1);
128                     disqual = true;
129                 }
130                 else 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).stoneThrow < 0;
145             boolean cheat1 = gameManager.getPlayer(1).stoneThrow < 0;
146             if (cheat0 && cheat1); // here we can actually keep delta's value
147             else if (cheat0) delta = -1;
148             else if (cheat1) delta =  1;
149
150             if (delta > 0) {
151                 gameManager.addToGameSummary("Troll walks right.");
152                 model.trollPosition++;
153             }
154             else if (delta < 0) {
155                 gameManager.addToGameSummary("Troll walks left.");
156                 model.trollPosition--;
157             }
158             else {
159                 gameManager.addToGameSummary("Troll stands still.");
160                 // XXX animate
161             }
162             view.moveTroll();
163
164             for (Player player : gameManager.getActivePlayers()) {
165                 player.model.adjustScore(model.trollPosition);
166             }
167
168             if (model.haveWinner()) {
169                 int loser = model.getLoser();
170                 gameManager.addToGameSummary(GameManager.formatErrorMessage("Troll destroys " + gameManager.getPlayer(loser).getNicknameToken()) + ".");
171                 victory = true;
172             }
173             else if (model.exhausted()) exhausted = true;
174         }
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             gameManager.addToGameSummary(GameManager.formatSuccessMessage(p0.getNicknameToken() + " wins."));
190             p1.view.destroy();
191         }
192         else if (s0 < s1) {
193             gameManager.addToGameSummary(GameManager.formatSuccessMessage(p1.getNicknameToken() + " wins."));
194             p0.view.destroy();
195         }
196         else if (s0 < 0) {
197             gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!"));
198             p0.view.destroy();
199             p1.view.destroy();
200         }
201         else {
202             gameManager.addToGameSummary("Draw.");
203         }
204     }
205 }