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 }