This repository has been archived on 2025-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
citylimitsj/src/micropolisj/engine/CityEval.java
2013-02-24 14:01:36 +00:00

282 lines
6.8 KiB
Java

// This file is part of MicropolisJ.
// Copyright (C) 2013 Jason Long
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
//
// MicropolisJ is free software; you can redistribute it and/or modify
// it under the terms of the GNU GPLv3, with additional terms.
// See the README file, included in this distribution, for details.
package micropolisj.engine;
import java.util.*;
/**
* Contains the code for performing a city evaluation.
*/
public class CityEval
{
private final Micropolis engine;
private final Random PRNG;
public CityEval(Micropolis engine)
{
this.engine = engine;
this.PRNG = engine.PRNG;
assert PRNG != null;
}
/** Percentage of population "approving" the mayor. Derived from cityScore. */
public int cityYes;
/** Percentage of population "disapproving" the mayor. Derived from cityScore. */
public int cityNo;
/** City assessment value. */
public int cityAssValue;
/** Player's score, 0-1000. */
public int cityScore;
/** Change in cityScore since last evaluation. */
public int deltaCityScore;
/** City population as of current evaluation. */
public int cityPop;
/** Change in cityPopulation since last evaluation. */
public int deltaCityPop;
/** Classification of city size. 0==village, 1==town, etc. */
public int cityClass; // 0..5
/** City's top 4 (or fewer) problems as reported by citizens. */
public CityProblem [] problemOrder = new CityProblem[0];
/** Number of votes given for the various problems identified by problemOrder[]. */
public EnumMap<CityProblem,Integer> problemVotes = new EnumMap<CityProblem,Integer>(CityProblem.class);
/** Score for various problems. */
public EnumMap<CityProblem,Integer> problemTable = new EnumMap<CityProblem,Integer>(CityProblem.class);
/**
* Perform an evaluation.
*/
void cityEvaluation()
{
if (engine.totalPop != 0) {
calculateAssValue();
doPopNum();
doProblems();
calculateScore();
doVotes();
} else {
evalInit();
}
engine.fireEvaluationChanged();
}
/** Evaluate an empty city. */
void evalInit()
{
cityYes = 0;
cityNo = 0;
cityAssValue = 0;
cityClass = 0;
cityScore = 500;
deltaCityScore = 0;
problemVotes.clear();
problemOrder = new CityProblem[0];
}
void calculateAssValue()
{
int z = 0;
z += engine.roadTotal * 5;
z += engine.railTotal * 10;
z += engine.policeCount * 1000;
z += engine.fireStationCount * 1000;
z += engine.hospitalCount * 400;
z += engine.stadiumCount * 3000;
z += engine.seaportCount * 5000;
z += engine.airportCount * 10000;
z += engine.coalCount * 3000;
z += engine.nuclearCount * 6000;
cityAssValue = z * 1000;
}
void doPopNum()
{
int oldCityPop = cityPop;
cityPop = engine.getCityPopulation();
deltaCityPop = cityPop - oldCityPop;
cityClass =
cityPop > 500000 ? 5 : //megalopolis
cityPop > 100000 ? 4 : //metropolis
cityPop > 50000 ? 3 : //capital
cityPop > 10000 ? 2 : //city
cityPop > 2000 ? 1 : //town
0; //village
}
void doProblems()
{
problemTable.clear();
problemTable.put(CityProblem.CRIME, engine.crimeAverage);
problemTable.put(CityProblem.POLLUTION, engine.pollutionAverage);
problemTable.put(CityProblem.HOUSING, (int)Math.round(engine.landValueAverage * 0.7));
problemTable.put(CityProblem.TAXES, engine.cityTax * 10);
problemTable.put(CityProblem.TRAFFIC, averageTrf());
problemTable.put(CityProblem.UNEMPLOYMENT, getUnemployment());
problemTable.put(CityProblem.FIRE, getFire());
problemVotes = voteProblems(problemTable);
CityProblem [] probOrder = CityProblem.values();
Arrays.sort(probOrder, new Comparator<CityProblem>() {
public int compare(CityProblem a, CityProblem b) {
return -(problemVotes.get(a).compareTo(problemVotes.get(b)));
}});
int c = 0;
while (c < probOrder.length &&
problemVotes.get(probOrder[c]).intValue() != 0 &&
c < 4)
c++;
problemOrder = new CityProblem[c];
for (int i = 0; i < c; i++) {
problemOrder[i] = probOrder[i];
}
}
EnumMap<CityProblem,Integer> voteProblems(Map<CityProblem,Integer> probTab)
{
CityProblem [] pp = CityProblem.values();
int [] votes = new int[pp.length];
int countVotes = 0;
for (int i = 0; i < 600; i++) {
if (PRNG.nextInt(301) < probTab.get(pp[i%pp.length])) {
votes[i%pp.length]++;
countVotes++;
if (countVotes >= 100)
break;
}
}
EnumMap<CityProblem,Integer> rv = new EnumMap<CityProblem,Integer>(CityProblem.class);
for (int i = 0; i < pp.length; i++) {
rv.put(pp[i], votes[i]);
}
return rv;
}
int averageTrf()
{
int count = 1;
int total = 0;
for (int hy = 0; hy < engine.trfDensity.length; hy++) {
for (int hx = 0; hx < engine.trfDensity[hy].length; hx++) {
if (engine.landValueMem[hy][hx] != 0) {
total += engine.trfDensity[hy][hx];
count++;
}
}
}
engine.trafficAverage = (int)Math.round(((double)total / (double)count) * 2.4);
return engine.trafficAverage;
}
int getUnemployment()
{
int b = (engine.comPop + engine.indPop) * 8;
if (b == 0)
return 0;
double r = (double)engine.resPop / (double)b;
b = (int)Math.floor((r-1.0)*255);
if (b > 255) {
b = 255;
}
return b;
}
int getFire()
{
int z = engine.firePop * 5;
return Math.min(255, z);
}
static double clamp(double x, double min, double max)
{
return Math.max(min, Math.min(max, x));
}
void calculateScore()
{
int oldCityScore = cityScore;
int x = 0;
for (Integer z : problemTable.values()) {
x += z.intValue();
}
x /= 3;
x = Math.min(256, x);
double z = clamp((256 - x) * 4, 0, 1000);
if (engine.resCap) { z = 0.85 * z; }
if (engine.comCap) { z = 0.85 * z; }
if (engine.indCap) { z = 0.85 * z; }
if (engine.roadEffect < 32) { z -= (32 - engine.roadEffect); }
if (engine.policeEffect < 1000) { z *= (0.9 + (engine.policeEffect / 10000.1)); }
if (engine.fireEffect < 1000) { z *= (0.9 + (engine.fireEffect / 10000.1)); }
if (engine.resValve < -1000) { z *= 0.85; }
if (engine.comValve < -1000) { z *= 0.85; }
if (engine.indValve < -1000) { z *= 0.85; }
double SM = 1.0;
if (cityPop == 0 && deltaCityPop == 0) {
SM = 1.0;
}
else if (deltaCityPop == cityPop) {
SM = 1.0;
}
else if (deltaCityPop > 0) {
SM = (double)deltaCityPop / (double)cityPop + 1.0;
}
else if (deltaCityPop < 0) {
SM = 0.95 + ((double)deltaCityPop / (double)(cityPop-deltaCityPop));
}
z *= SM;
z -= getFire();
z -= engine.cityTax;
int TM = engine.unpoweredZoneCount + engine.poweredZoneCount;
SM = TM != 0 ? ((double)engine.poweredZoneCount / (double)TM) : 1.0;
z *= SM;
z = clamp(z, 0, 1000);
cityScore = (int)Math.round((cityScore + z) / 2.0);
deltaCityScore = cityScore - oldCityScore;
}
void doVotes()
{
cityYes = cityNo = 0;
for (int i = 0; i < 100; i++) {
if (PRNG.nextInt(1001) < cityScore) {
cityYes++;
} else {
cityNo++;
}
}
}
}