-
Notifications
You must be signed in to change notification settings - Fork 14
7. In depth look at districts
This page mostly refers to the DistrictFacetComponent.
Districts divide the city into distinct areas, providing a more diverse and interesting city layout. They contain a list of zones so that only a distinct pool of buildings can spawn in one district.
The process of district creation involves two steps:
- Divide the area into distinct clusters using K-Means clustering.
- Map the clusters to district types while preserving correlation to the demands of zones of a culture.
Using the K-means algorithm (namely Lloyd's algorithm) with the positions and some random numbers with a low weight as input vectors, distinct clusters are created. I wrote my own implementation but any other would be likewise fitting.
The different district types are currently assigned by having a probability distribution p(x)
determined by the needs of BuildingNeedsPerZone
values of the culture and the outstanding demand for each zone area. As a district can contain several zones, each zone counts evenly to the total district area. For example, if you have a district with zone A and zone B of size S, the total area of zone A and B will be S/2 bigger.
If the demand for a zone is exceeded, the probability is set to 0 for that district.
The relevant code explains how p(x)
is calculated probably better than I can:
Map<DistrictType, Float> probabilites = new HashMap<>(districtManager.getDistrictTypes().size());
float totalDiff = 0;
for (DistrictType districtType : districtManager.getDistrictTypes()) {
float diff = 0;
Map<String, Float> tempZoneArea = new HashMap<>(zoneArea);
for (String zone : districtType.zones) {
float area = districtSize.get(i) / districtType.zones.size();
tempZoneArea.put(zone, tempZoneArea.getOrDefault(zone, 0f) + area);
if (!culturalNeedsPercentage.containsKey(zone)) {
diff = Float.MAX_VALUE;
}
else if (tempZoneArea.get(zone) / totalAssignedArea > culturalNeedsPercentage.get(zone)) {
diff = 9999999f;
} else {
diff += TeraMath.fastAbs(tempZoneArea.get(zone) / totalAssignedArea - culturalNeedsPercentage.get(zone));
}
}
diff = (diff == 0) ? 0 : 1 / diff;
probabilites.put(districtType, diff);
totalDiff += diff;
}
for (DistrictType districtType : districtManager.getDistrictTypes()) {
probabilites.put(districtType, probabilites.getOrDefault(districtType, 0f) / totalDiff);
}
afterward a random value x is applied to p(x) which will get us the relevant district.
Although I haven't made a statistical analysis of that mapping, samples showed a pretty nice consistence with the BuildingNeedsPerZone
with negligible deviations. After all it isn't something immediately visible and the random size of the clusters don't allow 100% precision.