From d9d704da60faea39b241a3ebe715ead3dde9975a Mon Sep 17 00:00:00 2001 From: yairm210 Date: Fri, 21 Jun 2024 16:23:46 +0300 Subject: [PATCH] perf: Performance improvements for worker automation by caching uniques --- .../com/unciv/logic/automation/Automation.kt | 2 +- .../logic/automation/unit/WorkerAutomation.kt | 44 +++++++++---------- core/src/com/unciv/logic/city/CityFocus.kt | 13 +++--- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 9eb27f4751..224f889d1f 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -72,7 +72,7 @@ object Automation { if (surplusFood > 0 && city.avoidGrowth) { yieldStats.food = 0f // don't need more food! - } else if (cityAIFocus in CityFocus.zeroFoodFocuses()) { + } else if (cityAIFocus in CityFocus.zeroFoodFocuses) { // Focus on non-food/growth if (surplusFood < 0) yieldStats.food *= 8 // Starving, need Food, get to 0 diff --git a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt index 355369381e..26701d33a6 100644 --- a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt @@ -68,16 +68,16 @@ class WorkerAutomation( /** * Automate one Worker - decide what to do and where, move, start or continue work. */ - fun automateWorkerAction(unit: MapUnit, dangerousTiles: HashSet) { + fun automateWorkerAction(unit: MapUnit, dangerousTiles: HashSet, localUniqueCache: LocalUniqueCache = LocalUniqueCache()) { val currentTile = unit.getTile() // Must be called before any getPriority checks to guarantee the local road cache is processed val citiesToConnect = roadBetweenCitiesAutomation.getNearbyCitiesToConnect(unit) // Shortcut, we are working a good tile (like resource) and don't need to check for other tiles to work - if (!dangerousTiles.contains(currentTile) && getFullPriority(unit.getTile(), unit) >= 10 + if (!dangerousTiles.contains(currentTile) && getFullPriority(unit.getTile(), unit, localUniqueCache) >= 10 && currentTile.improvementInProgress != null) { return } - val tileToWork = findTileToWork(unit, dangerousTiles) + val tileToWork = findTileToWork(unit, dangerousTiles, localUniqueCache) if (tileToWork != currentTile) { debug("WorkerAutomation: %s -> head towards %s", unit.toString(), tileToWork) @@ -106,7 +106,7 @@ class WorkerAutomation( return } if (reachedTile.improvementInProgress == null && reachedTile.isLand - && tileHasWorkToDo(reachedTile, unit) + && tileHasWorkToDo(reachedTile, unit, localUniqueCache) ) { debug("WorkerAutomation: $unit -> start improving $reachedTile") return reachedTile.startWorkingOnImprovement(tileRankings[reachedTile]!!.bestImprovement!!, civInfo, unit) @@ -117,7 +117,7 @@ class WorkerAutomation( if (currentTile.improvementInProgress != null) return // we're working! - if (tileHasWorkToDo(currentTile, unit)) { + if (tileHasWorkToDo(currentTile, unit, localUniqueCache)) { val tileRankings = tileRankings[currentTile]!! if (tileRankings.repairImprovment!!) { debug("WorkerAutomation: $unit -> repairs $currentTile") @@ -140,7 +140,9 @@ class WorkerAutomation( val citiesToNumberOfUnimprovedTiles = HashMap() for (city in unit.civ.cities) { citiesToNumberOfUnimprovedTiles[city.id] = city.getTiles() - .count { tile -> tile.isLand && tile.getUnits().any { unit -> unit.cache.hasUniqueToBuildImprovements } && (tile.isPillaged() || tileHasWorkToDo(tile, unit)) } + .count { tile -> tile.isLand + && tile.getUnits().any { unit -> unit.cache.hasUniqueToBuildImprovements } + && (tile.isPillaged() || tileHasWorkToDo(tile, unit, localUniqueCache)) } } val closestUndevelopedCity = unit.civ.cities.asSequence() @@ -171,10 +173,10 @@ class WorkerAutomation( * Looks for a worthwhile tile to improve * @return The current tile if no tile to work was found */ - private fun findTileToWork(unit: MapUnit, tilesToAvoid: Set): Tile { + private fun findTileToWork(unit: MapUnit, tilesToAvoid: Set, localUniqueCache: LocalUniqueCache): Tile { val currentTile = unit.getTile() if (currentTile !in tilesToAvoid && getBasePriority(currentTile, unit) >= 5 - && (tileHasWorkToDo(currentTile, unit) || currentTile.isPillaged() || currentTile.hasFalloutEquivalent())) { + && (tileHasWorkToDo(currentTile, unit, localUniqueCache) || currentTile.isPillaged() || currentTile.hasFalloutEquivalent())) { return currentTile } val workableTilesCenterFirst = currentTile.getTilesInDistance(4) @@ -197,10 +199,10 @@ class WorkerAutomation( var bestTile: Tile? = null for (tileInGroup in tilePriorityGroup.value.sortedBy { unit.getTile().aerialDistanceTo(it) }) { // These are the expensive calculations (tileCanBeImproved, canReach), so we only apply these filters after everything else it done. - if (!tileHasWorkToDo(tileInGroup, unit)) continue + if (!tileHasWorkToDo(tileInGroup, unit, localUniqueCache)) continue if (unit.getTile() == tileInGroup) return unit.getTile() if (!unit.movement.canReach(tileInGroup)) continue - if (bestTile == null || getFullPriority(tileInGroup, unit) > getFullPriority(bestTile, unit)) { + if (bestTile == null || getFullPriority(tileInGroup, unit, localUniqueCache) > getFullPriority(bestTile, unit, localUniqueCache)) { bestTile = tileInGroup } } @@ -252,23 +254,23 @@ class WorkerAutomation( /** * Calculates the priority building the improvement on the tile */ - private fun getImprovementPriority(tile: Tile, unit: MapUnit): Float { + private fun getImprovementPriority(tile: Tile, unit: MapUnit, localUniqueCache: LocalUniqueCache): Float { getBasePriority(tile, unit) val rank = tileRankings[tile] - if(rank!!.improvementPriority == null) { + if (rank!!.improvementPriority == null) { // All values of rank have to be initialized rank.improvementPriority = -100f rank.bestImprovement = null rank.repairImprovment = false - val bestImprovement = chooseImprovement(unit, tile) + val bestImprovement = chooseImprovement(unit, tile, localUniqueCache) if (bestImprovement != null) { rank.bestImprovement = bestImprovement // Increased priority if the improvement has been worked on longer val timeSpentPriority = if (tile.improvementInProgress == bestImprovement.name) bestImprovement.getTurnsToBuild(unit.civ,unit) - tile.turnsToImprovement else 0 - rank.improvementPriority = getImprovementRanking(tile, unit, rank.bestImprovement!!.name, LocalUniqueCache()) + timeSpentPriority + rank.improvementPriority = getImprovementRanking(tile, unit, rank.bestImprovement!!.name, localUniqueCache) + timeSpentPriority } if (tile.improvement != null && tile.isPillaged() && tile.owningCity != null) { @@ -293,15 +295,15 @@ class WorkerAutomation( /** * Calculates the full priority of the tile */ - private fun getFullPriority(tile: Tile, unit: MapUnit): Float { - return getBasePriority(tile, unit) + getImprovementPriority(tile, unit) + private fun getFullPriority(tile: Tile, unit: MapUnit, localUniqueCache: LocalUniqueCache): Float { + return getBasePriority(tile, unit) + getImprovementPriority(tile, unit, localUniqueCache) } /** * Returns the best improvement */ - private fun tileHasWorkToDo(tile: Tile, unit: MapUnit): Boolean { - if (getImprovementPriority(tile, unit) <= 0) return false + private fun tileHasWorkToDo(tile: Tile, unit: MapUnit, localUniqueCache: LocalUniqueCache): Boolean { + if (getImprovementPriority(tile, unit, localUniqueCache) <= 0) return false if (!(tileRankings[tile]!!.bestImprovement != null || tileRankings[tile]!!.repairImprovment!!)) throw IllegalStateException("There was an improvementPriority > 0 and nothing to do") return true @@ -311,7 +313,7 @@ class WorkerAutomation( * Determine the improvement appropriate to a given tile and worker * Returns null if * */ - private fun chooseImprovement(unit: MapUnit, tile: Tile): TileImprovement? { + private fun chooseImprovement(unit: MapUnit, tile: Tile, localUniqueCache: LocalUniqueCache): TileImprovement? { // You can keep working on half-built improvements, even if they're unique to another civ if (tile.improvementInProgress != null) return ruleSet.tileImprovements[tile.improvementInProgress!!] @@ -322,8 +324,6 @@ class WorkerAutomation( } if (potentialTileImprovements.isEmpty()) return null - val localUniqueCache = LocalUniqueCache() - var bestBuildableImprovement = potentialTileImprovements.values.asSequence() .map { Pair(it, getImprovementRanking(tile, unit, it.name, localUniqueCache)) } .filter { it.second > 0f } @@ -410,7 +410,7 @@ class WorkerAutomation( newTile.removeTerrainFeature(removedFeature) if (removedImprovement != null) newTile.removeImprovement() - val wantedFinalImprovement = chooseImprovement(unit, newTile) + val wantedFinalImprovement = chooseImprovement(unit, newTile, localUniqueCache) if (wantedFinalImprovement != null) stats.add(newTile.stats.getStatDiffForImprovement(wantedFinalImprovement, civInfo, newTile.getCity(), localUniqueCache)) } diff --git a/core/src/com/unciv/logic/city/CityFocus.kt b/core/src/com/unciv/logic/city/CityFocus.kt index 873a3c3155..a75e9905b9 100644 --- a/core/src/com/unciv/logic/city/CityFocus.kt +++ b/core/src/com/unciv/logic/city/CityFocus.kt @@ -68,7 +68,8 @@ enum class CityFocus( fun applyWeightTo(stats: Stats) { for (stat in Stat.values()) { - stats[stat] *= getStatMultiplier(stat) + val currentStat = stats[stat] + if (currentStat != 0f) stats[stat] *= getStatMultiplier(stat) } } @@ -78,12 +79,10 @@ enum class CityFocus( } // set used in Automation. All non-Food Focuses, so targets 0 Surplus Food - fun zeroFoodFocuses(): Set { - return setOf( - CultureFocus, FaithFocus, GoldFocus, - HappinessFocus, ProductionFocus, ScienceFocus - ) - } + val zeroFoodFocuses = setOf( + CultureFocus, FaithFocus, GoldFocus, + HappinessFocus, ProductionFocus, ScienceFocus + ) } }