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)
}
fun rankSpecialist(specialist: String, city: City, cityStats: Stats): Float {
val stats = city.cityStats.getStatsOfSpecialist(specialist)
fun rankSpecialist(specialist: String, city: City, cityStats: Stats, localUniqueCache: LocalUniqueCache): Float {
val stats = city.cityStats.getStatsOfSpecialist(specialist, localUniqueCache)
var rank = rankStatsForCityWork(stats, city, cityStats, true)
// derive GPP score
var gpp = 0f
@ -337,10 +337,11 @@ object Automation {
/** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */
fun getTileForConstructionImprovement(city: City, improvement: TileImprovement): Tile? {
val localUniqueCache = LocalUniqueCache()
return city.getTiles().filter {
it.improvementFunctions.canBuildImprovement(improvement, city.civ)
}.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)
}
fun getStatsOfSpecialist(specialistName: String): Stats {
fun getStatsOfSpecialist(specialistName: String, localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)): Stats {
val specialist = city.getRuleset().specialists[specialistName]
?: return Stats()
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]))
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)
stats.add(unique.stats)
return stats
@ -193,8 +193,9 @@ class CityStats(val city: City) {
private fun getStatsFromSpecialists(specialists: Counter<String>): Stats {
val stats = Stats()
val localUniqueCache = LocalUniqueCache()
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
}

View File

@ -168,11 +168,11 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
val bestJob: String? = if (city.manualSpecialists) null else getMaxSpecialists()
.filter { specialistAllocations[it.key]!! < it.value }
.map { it.key }
.maxByOrNull { Automation.rankSpecialist(it, city, cityStats) }
.maxByOrNull { Automation.rankSpecialist(it, city, cityStats, localUniqueCache) }
var valueBestSpecialist = 0f
if (bestJob != null) {
valueBestSpecialist = Automation.rankSpecialist(bestJob, city, cityStats)
valueBestSpecialist = Automation.rankSpecialist(bestJob, city, cityStats, localUniqueCache)
}
//assign population
@ -208,7 +208,7 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
if (amount > maxSpecialists[specialistName]!!)
specialistAllocations[specialistName] = maxSpecialists[specialistName]!!
val localUniqueCache = LocalUniqueCache()
while (getFreePopulation() < 0) {
//evaluate tiles
@ -217,19 +217,19 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
city.workedTiles.asSequence()
.map { city.tileMap[it] }
.minByOrNull {
Automation.rankTileForCityWork(it, city, city.cityStats.currentCityStats)
Automation.rankTileForCityWork(it, city, city.cityStats.currentCityStats, localUniqueCache)
+(if (it.isLocked()) 10 else 0)
}!!
}
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
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
if (worstAutoJob != null)
valueWorstSpecialist = Automation.rankSpecialist(worstAutoJob, city, city.cityStats.currentCityStats)
valueWorstSpecialist = Automation.rankSpecialist(worstAutoJob, city, city.cityStats.currentCityStats, localUniqueCache)
// un-assign population
@ -249,7 +249,7 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
// 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.
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
specialistAllocations.add(worstJob, -1)
}