mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-09 23:39:40 +07:00
Performance improvements (#9271)
* Speed up WorkerAutomation.findTileToWork - apparently tileCanBeImproved is quite expensive * Add cache for rankTileForCityWork in CityPopulationManager.autoAssignPopulation * Optimize NextTurnAutomation.declareWar by moving expensive BFSs to the end and potentially short-circuiting evaluation if result won't be promising anyways. * No need to throw if atLeast is negative. * Revert changes to CityPopulationManager.kt * Revert changes to CityPopulationManager.kt * Speed up WorkerAutomation.findTileToWork - apparently tileCanBeImproved is quite expensive * Add cache for rankTileForCityWork in CityPopulationManager.autoAssignPopulation * Optimize NextTurnAutomation.declareWar by moving expensive BFSs to the end and potentially short-circuiting evaluation if result won't be promising anyways. * No need to throw if atLeast is negative. * Revert changes to CityPopulationManager.kt * Revert changes to CityPopulationManager.kt
This commit is contained in:
@ -817,15 +817,18 @@ object NextTurnAutomation {
|
|||||||
|
|
||||||
if (enemyCivs.none()) return
|
if (enemyCivs.none()) return
|
||||||
|
|
||||||
|
val minMotivationToAttack = 20
|
||||||
val civWithBestMotivationToAttack = enemyCivs
|
val civWithBestMotivationToAttack = enemyCivs
|
||||||
.map { Pair(it, motivationToAttack(civInfo, it)) }
|
.map { Pair(it, hasAtLeastMotivationToAttack(civInfo, it, minMotivationToAttack)) }
|
||||||
.maxByOrNull { it.second }!!
|
.maxByOrNull { it.second }!!
|
||||||
|
|
||||||
if (civWithBestMotivationToAttack.second >= 20)
|
if (civWithBestMotivationToAttack.second >= minMotivationToAttack)
|
||||||
civInfo.getDiplomacyManager(civWithBestMotivationToAttack.first).declareWar()
|
civInfo.getDiplomacyManager(civWithBestMotivationToAttack.first).declareWar()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun motivationToAttack(civInfo: Civilization, otherCiv: Civilization): Int {
|
/** Will return the motivation to attack, but might short circuit if the value is guaranteed to
|
||||||
|
* be lower than `atLeast`. So any values below `atLeast` should not be used for comparison. */
|
||||||
|
private fun hasAtLeastMotivationToAttack(civInfo: Civilization, otherCiv: Civilization, atLeast: Int): Int {
|
||||||
val closestCities = getClosestCities(civInfo, otherCiv) ?: return 0
|
val closestCities = getClosestCities(civInfo, otherCiv) ?: return 0
|
||||||
val baseForce = 30f
|
val baseForce = 30f
|
||||||
|
|
||||||
@ -859,12 +862,6 @@ object NextTurnAutomation {
|
|||||||
&& (owner == otherCiv || owner == null || civInfo.diplomacyFunctions.canPassThroughTiles(owner))
|
&& (owner == otherCiv || owner == null || civInfo.diplomacyFunctions.canPassThroughTiles(owner))
|
||||||
}
|
}
|
||||||
|
|
||||||
val reachableEnemyCitiesBfs = BFS(civInfo.getCapital()!!.getCenterTile()) { isTileCanMoveThrough(it) }
|
|
||||||
reachableEnemyCitiesBfs.stepToEnd()
|
|
||||||
val reachableEnemyCities = otherCiv.cities.filter { reachableEnemyCitiesBfs.hasReachedTile(it.getCenterTile()) }
|
|
||||||
if (reachableEnemyCities.isEmpty()) return 0 // Can't even reach the enemy city, no point in war.
|
|
||||||
|
|
||||||
|
|
||||||
val modifierMap = HashMap<String, Int>()
|
val modifierMap = HashMap<String, Int>()
|
||||||
val combatStrengthRatio = ourCombatStrength / theirCombatStrength
|
val combatStrengthRatio = ourCombatStrength / theirCombatStrength
|
||||||
val combatStrengthModifier = when {
|
val combatStrengthModifier = when {
|
||||||
@ -880,14 +877,6 @@ object NextTurnAutomation {
|
|||||||
if (closestCities.aerialDistance > 7)
|
if (closestCities.aerialDistance > 7)
|
||||||
modifierMap["Far away cities"] = -10
|
modifierMap["Far away cities"] = -10
|
||||||
|
|
||||||
val landPathBFS = BFS(ourCity.getCenterTile()) {
|
|
||||||
it.isLand && isTileCanMoveThrough(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
landPathBFS.stepUntilDestination(theirCity.getCenterTile())
|
|
||||||
if (!landPathBFS.hasReachedTile(theirCity.getCenterTile()))
|
|
||||||
modifierMap["No land path"] = -10
|
|
||||||
|
|
||||||
val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)
|
val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)
|
||||||
if (diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement))
|
if (diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement))
|
||||||
modifierMap["Research Agreement"] = -5
|
modifierMap["Research Agreement"] = -5
|
||||||
@ -924,7 +913,35 @@ object NextTurnAutomation {
|
|||||||
modifierMap["About to win"] = 15
|
modifierMap["About to win"] = 15
|
||||||
}
|
}
|
||||||
|
|
||||||
return modifierMap.values.sum()
|
var motivationSoFar = modifierMap.values.sum()
|
||||||
|
|
||||||
|
// We don't need to execute the expensive BFSs below if we're below the threshold here
|
||||||
|
// anyways, since it won't get better from those, only worse.
|
||||||
|
if (motivationSoFar < atLeast) {
|
||||||
|
return motivationSoFar
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val landPathBFS = BFS(ourCity.getCenterTile()) {
|
||||||
|
it.isLand && isTileCanMoveThrough(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
landPathBFS.stepUntilDestination(theirCity.getCenterTile())
|
||||||
|
if (!landPathBFS.hasReachedTile(theirCity.getCenterTile()))
|
||||||
|
motivationSoFar -= -10
|
||||||
|
|
||||||
|
// We don't need to execute the expensive BFSs below if we're below the threshold here
|
||||||
|
// anyways, since it won't get better from those, only worse.
|
||||||
|
if (motivationSoFar < atLeast) {
|
||||||
|
return motivationSoFar
|
||||||
|
}
|
||||||
|
|
||||||
|
val reachableEnemyCitiesBfs = BFS(civInfo.getCapital()!!.getCenterTile()) { isTileCanMoveThrough(it) }
|
||||||
|
reachableEnemyCitiesBfs.stepToEnd()
|
||||||
|
val reachableEnemyCities = otherCiv.cities.filter { reachableEnemyCitiesBfs.hasReachedTile(it.getCenterTile()) }
|
||||||
|
if (reachableEnemyCities.isEmpty()) return 0 // Can't even reach the enemy city, no point in war.
|
||||||
|
|
||||||
|
return motivationSoFar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -941,8 +958,10 @@ object NextTurnAutomation {
|
|||||||
.filter { it.tradeRequests.none { tradeRequest -> tradeRequest.requestingCiv == civInfo.civName && tradeRequest.trade.isPeaceTreaty() } }
|
.filter { it.tradeRequests.none { tradeRequest -> tradeRequest.requestingCiv == civInfo.civName && tradeRequest.trade.isPeaceTreaty() } }
|
||||||
|
|
||||||
for (enemy in enemiesCiv) {
|
for (enemy in enemiesCiv) {
|
||||||
val motivationToAttack = motivationToAttack(civInfo, enemy)
|
if(hasAtLeastMotivationToAttack(civInfo, enemy, 10) >= 10) {
|
||||||
if (motivationToAttack >= 10) continue // We can still fight. Refuse peace.
|
// We can still fight. Refuse peace.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// pay for peace
|
// pay for peace
|
||||||
val tradeLogic = TradeLogic(civInfo, enemy)
|
val tradeLogic = TradeLogic(civInfo, enemy)
|
||||||
|
@ -285,7 +285,7 @@ class WorkerAutomation(
|
|||||||
.filter {
|
.filter {
|
||||||
(it.civilianUnit == null || it == currentTile)
|
(it.civilianUnit == null || it == currentTile)
|
||||||
&& (it.owningCity == null || it.getOwner()==civInfo)
|
&& (it.owningCity == null || it.getOwner()==civInfo)
|
||||||
&& (tileCanBeImproved(unit, it) || it.isPillaged())
|
&& getPriority(it) > 1
|
||||||
&& it.getTilesInDistance(2) // don't work in range of enemy cities
|
&& it.getTilesInDistance(2) // don't work in range of enemy cities
|
||||||
.none { tile -> tile.isCityCenter() && tile.getCity()!!.civ.isAtWarWith(civInfo) }
|
.none { tile -> tile.isCityCenter() && tile.getCity()!!.civ.isAtWarWith(civInfo) }
|
||||||
&& it.getTilesInDistance(3) // don't work in range of enemy units
|
&& it.getTilesInDistance(3) // don't work in range of enemy units
|
||||||
@ -293,13 +293,10 @@ class WorkerAutomation(
|
|||||||
}
|
}
|
||||||
.sortedByDescending { getPriority(it) }
|
.sortedByDescending { getPriority(it) }
|
||||||
|
|
||||||
// the tile needs to be actually reachable - more difficult than it seems,
|
// These are the expensive calculations (tileCanBeImproved, canReach), so we only apply these filters after everything else it done.
|
||||||
// which is why we DON'T calculate this for every possible tile in the radius,
|
val selectedTile = workableTiles.firstOrNull { unit.movement.canReach(it) && (tileCanBeImproved(unit, it) || it.isPillaged()) }
|
||||||
// but only for the tile that's about to be chosen.
|
|
||||||
val selectedTile = workableTiles.firstOrNull { unit.movement.canReach(it) }
|
|
||||||
|
|
||||||
return if (selectedTile != null
|
return if (selectedTile != null
|
||||||
&& getPriority(selectedTile) > 1
|
|
||||||
&& (!workableTiles.contains(currentTile)
|
&& (!workableTiles.contains(currentTile)
|
||||||
|| getPriority(selectedTile) > getPriority(currentTile)))
|
|| getPriority(selectedTile) > getPriority(currentTile)))
|
||||||
selectedTile
|
selectedTile
|
||||||
|
Reference in New Issue
Block a user