performance: Cache uniques for calculating specialist stats

This commit is contained in:
Yair Morgenstern 2023-04-03 11:03:59 +03:00
parent 9c121086ea
commit ad299a8a62
3 changed files with 17 additions and 15 deletions

View File

@ -26,8 +26,8 @@ object Automation {
return rankStatsForCityWork(stats, city, cityStats) return rankStatsForCityWork(stats, city, cityStats)
} }
fun rankSpecialist(specialist: String, city: City, cityStats: Stats): Float { fun rankSpecialist(specialist: String, city: City, cityStats: Stats, localUniqueCache: LocalUniqueCache): Float {
val stats = city.cityStats.getStatsOfSpecialist(specialist) val stats = city.cityStats.getStatsOfSpecialist(specialist, localUniqueCache)
var rank = rankStatsForCityWork(stats, city, cityStats, true) var rank = rankStatsForCityWork(stats, city, cityStats, true)
// derive GPP score // derive GPP score
var gpp = 0f var gpp = 0f
@ -337,10 +337,11 @@ object Automation {
/** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */ /** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */
fun getTileForConstructionImprovement(city: City, improvement: TileImprovement): Tile? { fun getTileForConstructionImprovement(city: City, improvement: TileImprovement): Tile? {
val localUniqueCache = LocalUniqueCache()
return city.getTiles().filter { return city.getTiles().filter {
it.improvementFunctions.canBuildImprovement(improvement, city.civ) it.improvementFunctions.canBuildImprovement(improvement, city.civ)
}.maxByOrNull { }.maxByOrNull {
rankTileForCityWork(it, city, city.cityStats.currentCityStats) rankTileForCityWork(it, city, city.cityStats.currentCityStats, localUniqueCache)
} }
} }

View File

@ -178,14 +178,14 @@ class CityStats(val city: City) {
return !city.containsBuildingUnique(UniqueType.RemoveAnnexUnhappiness) return !city.containsBuildingUnique(UniqueType.RemoveAnnexUnhappiness)
} }
fun getStatsOfSpecialist(specialistName: String): Stats { fun getStatsOfSpecialist(specialistName: String, localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)): Stats {
val specialist = city.getRuleset().specialists[specialistName] val specialist = city.getRuleset().specialists[specialistName]
?: return Stats() ?: return Stats()
val stats = specialist.cloneStats() val stats = specialist.cloneStats()
for (unique in city.getMatchingUniques(UniqueType.StatsFromSpecialist)) for (unique in localUniqueCache.get(UniqueType.StatsFromSpecialist.name, city.getMatchingUniques(UniqueType.StatsFromSpecialist)))
if (city.matchesFilter(unique.params[1])) if (city.matchesFilter(unique.params[1]))
stats.add(unique.stats) stats.add(unique.stats)
for (unique in city.civ.getMatchingUniques(UniqueType.StatsFromObject)) for (unique in localUniqueCache.get(UniqueType.StatsFromObject.name, city.civ.getMatchingUniques(UniqueType.StatsFromObject)))
if (unique.params[1] == specialistName) if (unique.params[1] == specialistName)
stats.add(unique.stats) stats.add(unique.stats)
return stats return stats
@ -193,8 +193,9 @@ class CityStats(val city: City) {
private fun getStatsFromSpecialists(specialists: Counter<String>): Stats { private fun getStatsFromSpecialists(specialists: Counter<String>): Stats {
val stats = Stats() val stats = Stats()
val localUniqueCache = LocalUniqueCache()
for (entry in specialists.filter { it.value > 0 }) for (entry in specialists.filter { it.value > 0 })
stats.add(getStatsOfSpecialist(entry.key) * entry.value) stats.add(getStatsOfSpecialist(entry.key, localUniqueCache) * entry.value)
return stats return stats
} }

View File

@ -168,11 +168,11 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
val bestJob: String? = if (city.manualSpecialists) null else getMaxSpecialists() val bestJob: String? = if (city.manualSpecialists) null else getMaxSpecialists()
.filter { specialistAllocations[it.key]!! < it.value } .filter { specialistAllocations[it.key]!! < it.value }
.map { it.key } .map { it.key }
.maxByOrNull { Automation.rankSpecialist(it, city, cityStats) } .maxByOrNull { Automation.rankSpecialist(it, city, cityStats, localUniqueCache) }
var valueBestSpecialist = 0f var valueBestSpecialist = 0f
if (bestJob != null) { if (bestJob != null) {
valueBestSpecialist = Automation.rankSpecialist(bestJob, city, cityStats) valueBestSpecialist = Automation.rankSpecialist(bestJob, city, cityStats, localUniqueCache)
} }
//assign population //assign population
@ -208,7 +208,7 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
if (amount > maxSpecialists[specialistName]!!) if (amount > maxSpecialists[specialistName]!!)
specialistAllocations[specialistName] = maxSpecialists[specialistName]!! specialistAllocations[specialistName] = maxSpecialists[specialistName]!!
val localUniqueCache = LocalUniqueCache()
while (getFreePopulation() < 0) { while (getFreePopulation() < 0) {
//evaluate tiles //evaluate tiles
@ -217,19 +217,19 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
city.workedTiles.asSequence() city.workedTiles.asSequence()
.map { city.tileMap[it] } .map { city.tileMap[it] }
.minByOrNull { .minByOrNull {
Automation.rankTileForCityWork(it, city, city.cityStats.currentCityStats) Automation.rankTileForCityWork(it, city, city.cityStats.currentCityStats, localUniqueCache)
+(if (it.isLocked()) 10 else 0) +(if (it.isLocked()) 10 else 0)
}!! }!!
} }
val valueWorstTile = if (worstWorkedTile == null) 0f val valueWorstTile = if (worstWorkedTile == null) 0f
else Automation.rankTileForCityWork(worstWorkedTile, city, city.cityStats.currentCityStats) else Automation.rankTileForCityWork(worstWorkedTile, city, city.cityStats.currentCityStats, localUniqueCache)
//evaluate specialists //evaluate specialists
val worstAutoJob: String? = if (city.manualSpecialists) null else specialistAllocations.keys val worstAutoJob: String? = if (city.manualSpecialists) null else specialistAllocations.keys
.minByOrNull { Automation.rankSpecialist(it, city, city.cityStats.currentCityStats) } .minByOrNull { Automation.rankSpecialist(it, city, city.cityStats.currentCityStats, localUniqueCache) }
var valueWorstSpecialist = 0f var valueWorstSpecialist = 0f
if (worstAutoJob != null) if (worstAutoJob != null)
valueWorstSpecialist = Automation.rankSpecialist(worstAutoJob, city, city.cityStats.currentCityStats) valueWorstSpecialist = Automation.rankSpecialist(worstAutoJob, city, city.cityStats.currentCityStats, localUniqueCache)
// un-assign population // un-assign population
@ -249,7 +249,7 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
// and population goes below the number of specialists, e.g. city is razing. // and population goes below the number of specialists, e.g. city is razing.
// Let's give a chance to do the work automatically at least. // Let's give a chance to do the work automatically at least.
val worstJob = specialistAllocations.keys.minByOrNull { val worstJob = specialistAllocations.keys.minByOrNull {
Automation.rankSpecialist(it, city, city.cityStats.currentCityStats) } Automation.rankSpecialist(it, city, city.cityStats.currentCityStats, localUniqueCache) }
?: break // sorry, we can do nothing about that ?: break // sorry, we can do nothing about that
specialistAllocations.add(worstJob, -1) specialistAllocations.add(worstJob, -1)
} }