1 package de.aikiit.game.kaiser;
2
3 import lombok.Getter;
4 import org.assertj.core.util.VisibleForTesting;
5
6 import java.math.BigDecimal;
7 import java.math.RoundingMode;
8 import java.security.SecureRandom;
9 import java.util.Random;
10
11 import static org.apache.commons.lang3.compare.ComparableUtils.is;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 @Getter
30 public class KaiserEngine {
31
32
33
34 private BigDecimal externalDamage = BigDecimal.ZERO;
35 private BigDecimal deathToll;
36 private BigDecimal increase;
37 private Integer zYear;
38 private BigDecimal population = BigDecimal.ZERO;
39 private BigDecimal area = BigDecimal.ZERO;
40 private BigDecimal yield = BigDecimal.ZERO;
41 private BigDecimal supplies = BigDecimal.ZERO;
42 private BigDecimal humans = BigDecimal.ZERO;
43 private BigDecimal deathTollSum;
44 private BigDecimal percentDeathToll;
45 private BigDecimal q = BigDecimal.ONE;
46 private BigDecimal cost = BigDecimal.ZERO;
47
48 private static final Random RANDOM = new SecureRandom();
49
50
51
52
53 public KaiserEngine() {
54 this.population = BigDecimal.valueOf(95L);
55 this.zYear = 0;
56 this.yield = BigDecimal.valueOf(3L);
57 this.supplies = BigDecimal.valueOf(2800L);
58 this.humans = BigDecimal.valueOf(3000L);
59 this.area = this.humans.divide(this.yield, RoundingMode.HALF_UP);
60 this.increase = BigDecimal.valueOf(5L);
61 this.deathToll = BigDecimal.ZERO;
62 this.percentDeathToll = BigDecimal.ZERO;
63 this.deathTollSum = BigDecimal.ZERO;
64 this.externalDamage = this.humans.subtract(this.supplies);
65 }
66
67
68
69
70 public void startNewRound() {
71 this.area = this.humans.divide(this.yield, 0, RoundingMode.HALF_UP);
72 this.externalDamage = this.humans.subtract(this.supplies);
73 this.zYear++;
74 this.population = this.population.add(this.increase);
75
76 processFamine();
77 this.cost = getRandomNumberUntil(10);
78 this.yield = cost.add(BigDecimal.valueOf(17L));
79 }
80
81
82
83
84
85
86
87 BigDecimal getRandomNumberUntil(int threshold) {
88 return BigDecimal.valueOf(RANDOM.nextInt(threshold + 1) + 1).setScale(0, RoundingMode.HALF_EVEN);
89 }
90
91
92
93
94
95 public void processFamine() {
96 if (is(q).lessThan(BigDecimal.ZERO)) {
97 this.population = this.population.divide(BigDecimal.valueOf(2L), 0, RoundingMode.HALF_UP);
98 System.out.println(KaiserEnginePrinter.ORANGE);
99 System.out.println("Eine fürchterliche Seuche hat die halbe Stadt dahingerafft!");
100 System.out.println(KaiserEnginePrinter.ANSI_RESET);
101 }
102 refreshFamineQuotient();
103 }
104
105
106
107
108 void refreshFamineQuotient() {
109 this.q = getRandomNumberUntil(10).divide(BigDecimal.TEN, 0, RoundingMode.HALF_UP).subtract(new BigDecimal("0.3"));
110
111 }
112
113 @VisibleForTesting
114 void setQ(BigDecimal q) {
115 this.q = q;
116 }
117
118 @VisibleForTesting
119 void setSupplies(BigDecimal supplies) {
120 this.supplies = supplies;
121 }
122
123
124 @VisibleForTesting
125 void setArea(BigDecimal area) {
126 this.area = area;
127 }
128
129
130
131
132
133
134 public BigDecimal getAreaPerCapita() {
135 return area.divide(population, 0, RoundingMode.HALF_UP);
136 }
137
138
139
140
141
142
143
144
145 public long buyLand(Long buy) {
146 if (buy < 0) {
147 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
148 }
149
150 if (buy > 0) {
151 if (is(this.yield.multiply(BigDecimal.valueOf(buy))).lessThanOrEqualTo(this.supplies)) {
152 this.area = this.area.add(BigDecimal.valueOf(buy));
153 this.supplies = this.supplies.subtract(this.yield.multiply(BigDecimal.valueOf(buy)));
154 this.cost = BigDecimal.ZERO;
155 } else {
156 throw new IllegalArgumentException("Not Enough Supplies");
157 }
158 }
159 return buy;
160 }
161
162
163
164
165
166
167
168 public void sellLand(Long sell) {
169 if (sell < 0) {
170 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
171 return;
172 }
173
174 if (is(BigDecimal.valueOf(sell)).lessThan(this.area)) {
175 this.area = this.area.subtract(BigDecimal.valueOf(sell));
176 this.supplies = this.supplies.add(this.yield.multiply(BigDecimal.valueOf(sell)));
177 this.cost = BigDecimal.ZERO;
178 } else {
179 throw new IllegalArgumentException("Not Enough Land");
180 }
181 }
182
183
184
185
186
187
188
189 public void feedToPopulation(Long feed) {
190 if (feed < 0) {
191 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
192 return;
193 }
194
195 if (feed != 0) {
196 if (is(BigDecimal.valueOf(feed)).lessThanOrEqualTo(this.supplies)) {
197 this.supplies = this.supplies.subtract(BigDecimal.valueOf(feed));
198 this.cost = BigDecimal.ONE;
199 } else {
200 throw new IllegalArgumentException("Not Enough in Stock");
201 }
202 }
203 }
204
205
206
207
208
209
210
211 public void cultivate(Long cultivate) {
212 if (cultivate == 0) {
213 calculateNewPrice();
214 return;
215 }
216
217 if (cultivate < 0) {
218 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
219 return;
220 }
221
222 if (is(this.area).lessThan(BigDecimal.valueOf(cultivate))) {
223 throw new IllegalArgumentException("You cannot cultivate more area than you have.");
224 }
225
226 BigDecimal halfCultivate = BigDecimal.valueOf(cultivate).divide(BigDecimal.valueOf(2L), 0, RoundingMode.HALF_UP);
227 if (is(this.supplies).lessThan(halfCultivate)) {
228 throw new IllegalArgumentException("You cannot cultivate more than you have.");
229 }
230
231 if (is(BigDecimal.valueOf(cultivate)).greaterThan(getPopulation().multiply(BigDecimal.TEN))) {
232 throw new IllegalArgumentException("Not enough workers available.");
233 }
234
235
236 this.supplies = this.supplies.subtract(halfCultivate);
237 calculateNewPrice();
238
239
240 this.yield = this.cost;
241 this.humans = this.yield.multiply(BigDecimal.valueOf(cultivate));
242
243
244 this.externalDamage = BigDecimal.ZERO;
245 calculateNewPrice();
246
247
248
249 if (this.cost.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN).intValue() == this.cost.divide(BigDecimal.valueOf(2L), 0, RoundingMode.UP).intValue()) {
250 this.externalDamage = this.supplies.divide(this.cost, 0, RoundingMode.HALF_UP);
251 }
252 this.supplies = this.supplies.subtract(this.externalDamage).add(this.humans);
253 calculateNewPrice();
254 }
255
256 @VisibleForTesting
257
258
259
260 void calculateNewPrice() {
261 this.cost = getRandomNumberUntil(5);
262 }
263
264
265
266
267
268
269
270
271
272 public void finishRoundAfterActions() {
273 BigDecimal factor = BigDecimal.valueOf(20L).multiply(this.area).add(this.supplies);
274 this.increase = cost.multiply(factor).divide(this.population, 0, RoundingMode.HALF_UP).divide(BigDecimal.valueOf(100).add(BigDecimal.ONE), 0, RoundingMode.HALF_UP);
275
276 this.cost = this.q.divide(BigDecimal.valueOf(20L), 0, RoundingMode.HALF_UP);
277 refreshFamineQuotient();
278
279 if (is(this.population).lessThan(this.cost)) {
280 this.deathToll = BigDecimal.ZERO;
281 return;
282 }
283
284
285 this.deathToll = this.population.subtract(this.cost);
286 if (is(this.deathToll).greaterThan(this.population.multiply(BigDecimal.valueOf(0.45)))) {
287 System.out.println(KaiserEnginePrinter.ANSI_YELLOW);
288 System.out.println("Sie haben " + this.deathToll + " Menschen in nur einem Jahr verhungern lassen!");
289 System.out.println("Auf Grund dieser extremen Misswirtschaft, werden Sie nicht nur aus Amt und Würden gejagt,");
290 System.out.println("sondern auch zum Versager des Jahres erklärt.");
291 System.out.println(KaiserEnginePrinter.ANSI_RESET);
292 return;
293 }
294
295
296
297 BigDecimal tempQuotient = this.percentDeathToll.multiply(BigDecimal.valueOf(this.zYear - 1)).add(this.deathToll.multiply(BigDecimal.valueOf(100)).divide(this.population, 0, RoundingMode.HALF_UP));
298 this.percentDeathToll = tempQuotient.divide(BigDecimal.valueOf(this.zYear), 0, RoundingMode.HALF_UP);
299
300 this.population = this.cost;
301 this.deathTollSum = this.deathTollSum.add(this.deathToll);
302 }
303 }