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
114
115
116
117 @VisibleForTesting
118 void setQ(BigDecimal q) {
119 this.q = q;
120 }
121
122
123
124
125
126 @VisibleForTesting
127 void setSupplies(BigDecimal supplies) {
128 this.supplies = supplies;
129 }
130
131
132
133
134
135 @VisibleForTesting
136 void setArea(BigDecimal area) {
137 this.area = area;
138 }
139
140
141
142
143
144
145 public BigDecimal getAreaPerCapita() {
146 return area.divide(population, 0, RoundingMode.HALF_UP);
147 }
148
149
150
151
152
153
154
155
156 public long buyLand(Long buy) {
157 if (buy < 0) {
158 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
159 }
160
161 if (buy > 0) {
162 if (is(this.yield.multiply(BigDecimal.valueOf(buy))).lessThanOrEqualTo(this.supplies)) {
163 this.area = this.area.add(BigDecimal.valueOf(buy));
164 this.supplies = this.supplies.subtract(this.yield.multiply(BigDecimal.valueOf(buy)));
165 this.cost = BigDecimal.ZERO;
166 } else {
167 throw new IllegalArgumentException("Not Enough Supplies");
168 }
169 }
170 return buy;
171 }
172
173
174
175
176
177
178
179 public void sellLand(Long sell) {
180 if (sell < 0) {
181 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
182 return;
183 }
184
185 if (is(BigDecimal.valueOf(sell)).lessThan(this.area)) {
186 this.area = this.area.subtract(BigDecimal.valueOf(sell));
187 this.supplies = this.supplies.add(this.yield.multiply(BigDecimal.valueOf(sell)));
188 this.cost = BigDecimal.ZERO;
189 } else {
190 throw new IllegalArgumentException("Not Enough Land");
191 }
192 }
193
194
195
196
197
198
199
200 public void feedToPopulation(Long feed) {
201 if (feed < 0) {
202 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
203 return;
204 }
205
206 if (feed != 0) {
207 if (is(BigDecimal.valueOf(feed)).lessThanOrEqualTo(this.supplies)) {
208 this.supplies = this.supplies.subtract(BigDecimal.valueOf(feed));
209 this.cost = BigDecimal.ONE;
210 } else {
211 throw new IllegalArgumentException("Not Enough in Stock");
212 }
213 }
214 }
215
216
217
218
219
220
221
222 public void cultivate(Long cultivate) {
223 if (cultivate == 0) {
224 calculateNewPrice();
225 return;
226 }
227
228 if (cultivate < 0) {
229 System.out.println(KaiserEnginePrinter.ANSI_PURPLE + "Ignoriere negative Eingaben - Du willst mich wohl verkackeiern." + KaiserEnginePrinter.ANSI_RESET);
230 return;
231 }
232
233 if (is(this.area).lessThan(BigDecimal.valueOf(cultivate))) {
234 throw new IllegalArgumentException("You cannot cultivate more area than you have.");
235 }
236
237 BigDecimal halfCultivate = BigDecimal.valueOf(cultivate).divide(BigDecimal.valueOf(2L), 0, RoundingMode.HALF_UP);
238 if (is(this.supplies).lessThan(halfCultivate)) {
239 throw new IllegalArgumentException("You cannot cultivate more than you have.");
240 }
241
242 if (is(BigDecimal.valueOf(cultivate)).greaterThan(getPopulation().multiply(BigDecimal.TEN))) {
243 throw new IllegalArgumentException("Not enough workers available.");
244 }
245
246
247 this.supplies = this.supplies.subtract(halfCultivate);
248 calculateNewPrice();
249
250
251 this.yield = this.cost;
252 this.humans = this.yield.multiply(BigDecimal.valueOf(cultivate));
253
254
255 this.externalDamage = BigDecimal.ZERO;
256 calculateNewPrice();
257
258
259
260 if (this.cost.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN).intValue() == this.cost.divide(BigDecimal.valueOf(2L), 0, RoundingMode.UP).intValue()) {
261 this.externalDamage = this.supplies.divide(this.cost, 0, RoundingMode.HALF_UP);
262 }
263 this.supplies = this.supplies.subtract(this.externalDamage).add(this.humans);
264 calculateNewPrice();
265 }
266
267
268
269
270 @VisibleForTesting
271 void calculateNewPrice() {
272 this.cost = getRandomNumberUntil(5);
273 }
274
275
276
277
278
279
280
281
282
283 public void finishRoundAfterActions() {
284 BigDecimal factor = BigDecimal.valueOf(20L).multiply(this.area).add(this.supplies);
285 this.increase = cost.multiply(factor).divide(this.population, 0, RoundingMode.HALF_UP).divide(BigDecimal.valueOf(100).add(BigDecimal.ONE), 0, RoundingMode.HALF_UP);
286
287 this.cost = this.q.divide(BigDecimal.valueOf(20L), 0, RoundingMode.HALF_UP);
288 refreshFamineQuotient();
289
290 if (is(this.population).lessThan(this.cost)) {
291 this.deathToll = BigDecimal.ZERO;
292 return;
293 }
294
295
296 this.deathToll = this.population.subtract(this.cost);
297 if (is(this.deathToll).greaterThan(this.population.multiply(BigDecimal.valueOf(0.45)))) {
298 System.out.println(KaiserEnginePrinter.ANSI_YELLOW);
299 System.out.println("Sie haben " + this.deathToll + " Menschen in nur einem Jahr verhungern lassen!");
300 System.out.println("Auf Grund dieser extremen Misswirtschaft, werden Sie nicht nur aus Amt und Würden gejagt,");
301 System.out.println("sondern auch zum Versager des Jahres erklärt.");
302 System.out.println(KaiserEnginePrinter.ANSI_RESET);
303 return;
304 }
305
306
307
308 BigDecimal tempQuotient = this.percentDeathToll.multiply(BigDecimal.valueOf(this.zYear - 1)).add(this.deathToll.multiply(BigDecimal.valueOf(100)).divide(this.population, 0, RoundingMode.HALF_UP));
309 this.percentDeathToll = tempQuotient.divide(BigDecimal.valueOf(this.zYear), 0, RoundingMode.HALF_UP);
310
311 this.population = this.cost;
312 this.deathTollSum = this.deathTollSum.add(this.deathToll);
313 }
314 }