Early game termination
[troll.git] / src / main / java / com / codingame / game / Model.java
1 package com.codingame.game;
2
3 import java.util.Random;
4 import java.util.Properties;
5
6 import com.codingame.gameengine.core.GameManager;
7 import com.codingame.gameengine.core.MultiplayerGameManager;
8 import com.google.inject.Inject;
9
10 class Model {
11     @Inject private MultiplayerGameManager<com.codingame.game.Player> gameManager;
12     Random random;
13     int roadLength;
14     int initialStones;
15     int trollPosition;
16     Player p0, p1;
17
18     class Player {
19         com.codingame.game.Player gp;
20         int index;
21
22         Player(int i) { index = i; }
23
24         private int castlePosition;
25         public int getCastlePosition() { return castlePosition; }
26         public void setCastlePosition(int pos) { castlePosition = pos; }
27
28         private int multiplier;
29         public int getMultiplier() { return multiplier; }
30         public void setMultiplier(int m) { multiplier = m; }
31
32         class FailedToThrowStonesAndShouldHave extends Exception {}
33         class ThrewMoreStonesThanHad extends Exception {}
34
35         private int stones;
36         public int getStones() { return stones; }
37         public void consumeStones(int n)
38             throws ThrewMoreStonesThanHad,
39                    FailedToThrowStonesAndShouldHave
40         {
41             if (n > stones) {
42                 throw new ThrewMoreStonesThanHad();
43             }
44             if (n == 0 && stones > 0) {
45                 throw new FailedToThrowStonesAndShouldHave();
46             }
47             setStones(stones - n);
48         }
49         public int consumeMaxStones() {
50             int r = stones;
51             stones = 0;
52             return r;
53         }
54         public int consumeMinStones() {
55             if (stones < 1) {
56                 throw new Error("Internal error: tried to consume min stones on an empty heap.");
57             }
58             stones--;
59             return 1;
60         }
61         public void setStones(int n) {
62             stones = n;
63         }
64         public int getOppStones() {
65             return p0.stones + p1.stones - stones;
66         }
67
68         public void adjustScore(int trollPosition) {
69             if (gp.isActive()) {
70                 gp.setScore(Math.abs(castlePosition - trollPosition));
71             }
72         }
73
74         public int getTrollDistance() {
75             return Math.abs(castlePosition - trollPosition);
76         }
77     }
78
79     void init(long seed) {
80         random = new Random(seed);
81         switch (random.nextInt(4)) {
82         case 0:
83             roadLength = 6;
84             initialStones = 15;
85             break;
86         case 1:
87             roadLength = 6;
88             initialStones = 30;
89             break;
90         case 2:
91             roadLength = 14;
92             initialStones = 30;
93             break;
94         case 3:
95             roadLength = 14;
96             initialStones = 50;
97             break;
98         }
99
100         Properties ps = gameManager.getGameParameters();
101         String buf = ps.getProperty("roadLength");
102         if (buf != null) {
103             try {
104                 int i = Integer.parseInt(buf);
105                 if (i < 0 || i > 20 || (i & 1) != 0) {
106                     gameManager.addToGameSummary(GameManager.formatErrorMessage("Ignoring invalid road length: " + buf));
107                 }
108                 else {
109                     roadLength = i;
110                     gameManager.addToGameSummary("Road length overridden to " + i);
111                 }
112             }
113             catch(NumberFormatException e) {
114                 gameManager.addToGameSummary(GameManager.formatErrorMessage("Ill-formed road length: " + buf));
115             }
116         }
117         ps.setProperty("roadLength", new Integer(roadLength).toString());
118
119         buf = ps.getProperty("initialStones");
120         if (buf != null) {
121             try {
122                 int i = Integer.parseInt(buf);
123                 if (i > 50) {
124                     gameManager.addToGameSummary(GameManager.formatErrorMessage("Ignoring invalid initial stone count: " + buf));
125                 }
126                 else {
127                     initialStones = i;
128                     gameManager.addToGameSummary("Initial stone count overridden to " + buf);
129                 }
130             }
131             catch (NumberFormatException e) {
132                 gameManager.addToGameSummary(GameManager.formatErrorMessage("Ill-formed initial stone count: " + buf));
133             }
134         }
135         ps.setProperty("initialStones", new Integer(initialStones).toString());
136
137         trollPosition = roadLength / 2;
138
139         p0 = new Player(0);
140         p0.gp = gameManager.getPlayer(0);
141         p0.setCastlePosition(0);
142         p0.setMultiplier(1);
143         p0.adjustScore(trollPosition);
144         p0.setStones(initialStones);
145
146         p1 = new Player(1);
147         p1.gp = gameManager.getPlayer(1);
148         p1.setCastlePosition(roadLength);
149         p1.setMultiplier(-1);
150         p1.adjustScore(trollPosition);
151         p1.setStones(initialStones);
152     }
153
154     void moveTroll(int delta) {
155         trollPosition += delta;
156         if (trollPosition <= 0) {
157             trollPosition = 0;
158             winner = 1;
159         }
160         if (trollPosition >= roadLength) {
161             trollPosition = roadLength;
162             winner = 0;
163         }
164
165         p0.adjustScore(trollPosition);
166         p1.adjustScore(trollPosition);
167     }
168
169     private Integer winner;
170     boolean haveWinner() {
171         return winner != null;
172     }
173
174     int getWinner() { return winner; }
175     int getLoser() { return 1 - winner; }
176
177     boolean exhausted() {
178         return p0.getStones() <= 0 || p1.getStones() <= 0;
179     }
180 }