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