Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
CityEval.java
Go to the documentation of this file.
1 // This file is part of MicropolisJ.
2 // Copyright (C) 2013 Jason Long
3 // Portions Copyright (C) 1989-2007 Electronic Arts Inc.
4 //
5 // MicropolisJ is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU GPLv3, with additional terms.
7 // See the README file, included in this distribution, for details.
8 
9 package micropolisj.engine;
10 
11 import java.io.Serializable;
12 import java.util.Arrays;
13 import java.util.Comparator;
14 import java.util.EnumMap;
15 import java.util.Map;
16 import java.util.Random;
17 
21 public class CityEval implements Serializable {
22  private transient Micropolis engine;
23  private final Random PRNG;
24 
26  setEngine(engine);
27  this.PRNG = engine.PRNG;
28 
29  assert PRNG != null;
30  }
31 
33  public int cityYes;
34 
39  public int cityNo;
40 
42  public int cityAssValue;
43 
45  public int cityScore;
46 
48  public int deltaCityScore;
49 
51  public int cityPop;
52 
54  public int deltaCityPop;
55 
57  public int cityClass; // 0..5
58 
61 
66  public EnumMap<CityProblem, Integer> problemVotes = new EnumMap<CityProblem, Integer>(CityProblem.class);
67 
69  public EnumMap<CityProblem, Integer> problemTable = new EnumMap<CityProblem, Integer>(CityProblem.class);
70 
71  // in aniamteCycle
75  void cityEvaluation() {
76  if (engine.playerInfo.totalPop != 0) {
77  calculateAssValue();
78  doPopNum();
79  doProblems();
80  calculateScore();
81  doVotes();
82  } else {
83  evalInit();
84  }
86  }
87 
89  void evalInit() {
90  cityYes = 0;
91  cityNo = 0;
92  cityAssValue = 0;
93  cityClass = 0;
94  cityScore = 500;
95  deltaCityScore = 0;
96  problemVotes.clear();
97  problemOrder = new CityProblem[0];
98  }
99 
100  // in animateCycle
101  void calculateAssValue() {
102  int z = 0;
103  z += engine.playerInfo.roadTotal * 5;
104  z += engine.playerInfo.railTotal * 10;
105  z += engine.playerInfo.policeCount * 1000;
106  z += engine.playerInfo.researchCount * 1000; // changeswp
107  z += engine.playerInfo.fireStationCount * 1000;
108  z += engine.playerInfo.hospitalCount * 400;
109  z += engine.playerInfo.stadiumCount * 3000;
110  z += engine.playerInfo.seaportCount * 5000;
111  z += engine.playerInfo.airportCount * 10000;
112  z += engine.playerInfo.coalCount * 3000;
113  z += engine.playerInfo.nuclearCount * 6000;
114  cityAssValue = z * 1000;
115  }
116 
117  void doPopNum() {
118  int oldCityPop = cityPop;
120  deltaCityPop = cityPop - oldCityPop;
121 
122  cityClass = cityPop > 500000 ? 5 : // megalopolis
123  cityPop > 100000 ? 4 : // metropolis
124  cityPop > 50000 ? 3 : // capital
125  cityPop > 10000 ? 2 : // city
126  cityPop > 2000 ? 1 : // town
127  0; // village
128  }
129 
130  void doProblems() {
131  problemTable.clear();
132  problemTable.put(CityProblem.CRIME, engine.playerInfo.crimeAverage);
133  problemTable.put(CityProblem.POLLUTION, engine.playerInfo.pollutionAverage);
134  problemTable.put(CityProblem.HOUSING, (int) Math.round(engine.playerInfo.landValueAverage * 0.7));
135  problemTable.put(CityProblem.TAXES, engine.playerInfo.cityTax * 10);
136  problemTable.put(CityProblem.TRAFFIC, averageTrf());
137  problemTable.put(CityProblem.UNEMPLOYMENT, getUnemployment());
138  problemTable.put(CityProblem.FIRE, getFire());
139 
140  problemVotes = voteProblems(problemTable);
141 
142  CityProblem[] probOrder = CityProblem.values();
143  Arrays.sort(probOrder, new Comparator<CityProblem>() {
144  public int compare(CityProblem a, CityProblem b) {
145  return -(problemVotes.get(a).compareTo(problemVotes.get(b)));
146  }
147  });
148 
149  int c = 0;
150  while (c < probOrder.length && problemVotes.get(probOrder[c]).intValue() != 0 && c < 4)
151  c++;
152 
153  problemOrder = new CityProblem[c];
154  for (int i = 0; i < c; i++) {
155  problemOrder[i] = probOrder[i];
156  }
157  }
158 
159  EnumMap<CityProblem, Integer> voteProblems(Map<CityProblem, Integer> probTab) {
160  CityProblem[] pp = CityProblem.values();
161  int[] votes = new int[pp.length];
162 
163  int countVotes = 0;
164  for (int i = 0; i < 600; i++) {
165  if (PRNG.nextInt(301) < probTab.get(pp[i % pp.length])) {
166  votes[i % pp.length]++;
167  countVotes++;
168  if (countVotes >= 100)
169  break;
170  }
171  }
172 
173  EnumMap<CityProblem, Integer> rv = new EnumMap<CityProblem, Integer>(CityProblem.class);
174  for (int i = 0; i < pp.length; i++) {
175  rv.put(pp[i], votes[i]);
176  }
177  return rv;
178  }
179 
180  int averageTrf() {
181  int count = 1;
182  int total = 0;
183 
184  for (int y = 0; y < engine.getHeight(); y++) {
185  for (int x = 0; x < engine.getWidth(); x++) {
186  // only consider tiles that have nonzero landvalue
187  if (engine.getLandValue(x, y) != 0) {
188  total += engine.getTrafficDensity(x, y);
189  count++;
190  }
191  }
192  }
193 
194  // part of animateCycle
195  engine.playerInfo.trafficAverage = (int) Math.round(((double) total / (double) count) * 2.4);
197  }
198 
199  int getUnemployment() {
201  if (b == 0)
202  return 0;
203 
204  double r = (double) engine.playerInfo.resPop / (double) b;
205  b = (int) Math.floor((r - 1.0) * 255);
206  if (b > 255) {
207  b = 255;
208  }
209  return b;
210  }
211 
212  int getFire() {
213  int z = engine.playerInfo.firePop * 5;
214  return Math.min(255, z);
215  }
216 
217  static double clamp(double x, double min, double max) {
218  return Math.max(min, Math.min(max, x));
219  }
220 
221  void calculateScore() {
222  int oldCityScore = cityScore;
223 
224  int x = 0;
225  for (Integer z : problemTable.values()) {
226  x += z.intValue();
227  }
228 
229  x /= 3;
230  x = Math.min(256, x);
231 
232  double z = clamp((256 - x) * 4, 0, 1000);
233 
234  if (engine.playerInfo.resCap) {
235  z = 0.85 * z;
236  }
237  if (engine.playerInfo.comCap) {
238  z = 0.85 * z;
239  }
240  if (engine.playerInfo.indCap) {
241  z = 0.85 * z;
242  }
243  if (engine.playerInfo.roadEffect < 32) {
244  z -= (32 - engine.playerInfo.roadEffect);
245  }
246  if (engine.playerInfo.policeEffect < 1000) {
247  z *= (0.9 + (engine.playerInfo.policeEffect / 10000.1));
248  }
249  if (engine.playerInfo.fireEffect < 1000) {
250  z *= (0.9 + (engine.playerInfo.fireEffect / 10000.1));
251  }
252  if (engine.playerInfo.resValve < -1000) {
253  z *= 0.85;
254  }
255  if (engine.playerInfo.comValve < -1000) {
256  z *= 0.85;
257  }
258  if (engine.playerInfo.indValve < -1000) {
259  z *= 0.85;
260  }
261 
262  double SM = 1.0;
263  if (cityPop == 0 && deltaCityPop == 0) {
264  SM = 1.0;
265  } else if (deltaCityPop == cityPop) {
266  SM = 1.0;
267  } else if (deltaCityPop > 0) {
268  SM = (double) deltaCityPop / (double) cityPop + 1.0;
269  } else if (deltaCityPop < 0) {
270  SM = 0.95 + ((double) deltaCityPop / (double) (cityPop - deltaCityPop));
271  }
272  z *= SM;
273  z -= getFire();
275 
277  SM = TM != 0 ? ((double) engine.playerInfo.poweredZoneCount / (double) TM) : 1.0;
278  z *= SM;
279 
280  z = clamp(z, 0, 1000);
281 
282  cityScore = (int) Math.round((cityScore + z) / 2.0);
283  deltaCityScore = cityScore - oldCityScore;
284  }
285 
286  void doVotes() {
287  cityYes = cityNo = 0;
288  for (int i = 0; i < 100; i++) {
289  if (PRNG.nextInt(1001) < cityScore) {
290  cityYes++;
291  } else {
292  cityNo++;
293  }
294  }
295  }
296 
297  public void setEngine(Micropolis engine) {
298  this.engine = engine;
299  }
300 
301 }