From 71d99dce512eb9fee2af1d321c2ae0ee72acd79d Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 21 Nov 2018 18:07:05 +0200 Subject: [PATCH] Improved Next Turn performance Dev: in tryGarrisonUnit, we now run canReach only when absolutely neccesary Dev: Center tile of CityInfo cached, we access it a lot and it never really changes --- .../unciv/logic/automation/UnitAutomation.kt | 45 ++++++++++--------- core/src/com/unciv/logic/city/CityInfo.kt | 5 ++- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 2253d9a2dc..b37eb62b78 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -157,7 +157,8 @@ class UnitAutomation{ // and then later we round it off to a whole. // So the poor unit thought it could attack from the tile, but when it comes to do so it has no movement points! // Silly floats, basically - var tilesToAttackFrom = unitDistanceToTiles.filter { unit.currentMovement - it.value > 0.1 } + var tilesToAttackFrom = unitDistanceToTiles.asSequence() + .filter { unit.currentMovement - it.value > 0.1 } .map { it.key } .filter { unit.canMoveTo(it) || it==unit.getTile() } if(unit.type.isLandUnit()) @@ -167,7 +168,7 @@ class UnitAutomation{ val tilesInAttackRange = if (unit.hasUnique("Indirect fire")) reachableTile.getTilesInDistance(rangeOfAttack) else reachableTile.getViewableTiles(rangeOfAttack) attackableTiles += tilesInAttackRange.asSequence().filter { it in tilesWithEnemies } - .map { AttackableTile(reachableTile,it) }.toList() + .map { AttackableTile(reachableTile,it) } } return attackableTiles } @@ -275,17 +276,17 @@ class UnitAutomation{ } private fun tryGarrisoningUnit(unit: MapUnit): Boolean { - if(unit.type.isMelee()) return false // don't garrison melee units, they're not that good at it - val reachableCitiesWithoutUnits = unit.civInfo.cities.filter { + if(unit.type.isMelee() || unit.type.isWaterUnit()) return false // don't garrison melee units, they're not that good at it + val citiesWithoutGarrison = unit.civInfo.cities.filter { val centerTile = it.getCenterTile() centerTile.militaryUnit==null && unit.canMoveTo(centerTile) - && unit.movementAlgs().canReach(centerTile) } - fun cityThatNeedsDefendingInWartime(city: CityInfo): Boolean { + fun isCityThatNeedsDefendingInWartime(city: CityInfo): Boolean { if (city.health < city.getMaxHealth()) return true // this city is under attack! - for (enemyCivCity in unit.civInfo.diplomacy.values.filter { it.diplomaticStatus == DiplomaticStatus.War } + for (enemyCivCity in unit.civInfo.diplomacy.values + .filter { it.diplomaticStatus == DiplomaticStatus.War } .map { it.otherCiv() }.flatMap { it.cities }) if (city.getCenterTile().arialDistanceTo(enemyCivCity.getCenterTile()) <= 5) return true// this is an edge city that needs defending return false @@ -293,23 +294,25 @@ class UnitAutomation{ if (!unit.civInfo.isAtWar()) { if (unit.getTile().isCityCenter()) return true // It's always good to have a unit in the city center, so if you haven't found anyone around to attack, forget it. - if (reachableCitiesWithoutUnits.isNotEmpty()) { - val closestCity = reachableCitiesWithoutUnits.minBy { it.getCenterTile().arialDistanceTo(unit.currentTile) }!! - unit.movementAlgs().headTowards(closestCity.getCenterTile()) - return true - } + + val closestReachableCityWithNoGarrison = citiesWithoutGarrison.asSequence() + .sortedBy{ it.getCenterTile().arialDistanceTo(unit.currentTile) } + .firstOrNull { unit.movementAlgs().canReach(it.getCenterTile()) } + if(closestReachableCityWithNoGarrison==null) return false // no + unit.movementAlgs().headTowards(closestReachableCityWithNoGarrison.getCenterTile()) + return true } else { if (unit.getTile().isCityCenter() && - cityThatNeedsDefendingInWartime(unit.getTile().getCity()!!)) return true - val citiesThatCanBeDefended = reachableCitiesWithoutUnits.filter { cityThatNeedsDefendingInWartime(it) } - if (citiesThatCanBeDefended.isNotEmpty()) { - val closestCityWithoutUnit = citiesThatCanBeDefended - .minBy { unit.movementAlgs().getShortestPath(it.getCenterTile()).size }!! - unit.movementAlgs().headTowards(closestCityWithoutUnit.getCenterTile()) - return true - } + isCityThatNeedsDefendingInWartime(unit.getTile().getCity()!!)) return true + + val closestReachableCityNeedsDefending = citiesWithoutGarrison.asSequence() + .filter { isCityThatNeedsDefendingInWartime(it) } + .sortedBy{ it.getCenterTile().arialDistanceTo(unit.currentTile) } + .firstOrNull { unit.movementAlgs().canReach(it.getCenterTile()) } + if(closestReachableCityNeedsDefending==null) return false + unit.movementAlgs().headTowards(closestReachableCityNeedsDefending.getCenterTile()) + return true } - return false } fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap): Boolean { diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 0714f18ff1..cc54060542 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -16,6 +16,8 @@ import kotlin.math.min class CityInfo { @Transient lateinit var civInfo: CivilizationInfo @Transient var isConnectedToCapital = false + @Transient lateinit var ccenterTile:TileInfo // 'cached' for better performance + var location: Vector2 = Vector2.Zero var name: String = "" var health = 200 @@ -86,7 +88,7 @@ class CityInfo { internal val tileMap: TileMap get() = civInfo.gameInfo.tileMap - fun getCenterTile(): TileInfo = tileMap[location] + fun getCenterTile(): TileInfo = ccenterTile fun getTiles(): List = tiles.map { tileMap[it] } fun getTilesInRange(): List = getCenterTile().getTilesInDistance( 3) @@ -151,6 +153,7 @@ class CityInfo { //region state-changing functions fun setTransients() { + ccenterTile = tileMap[location] population.cityInfo = this expansion.cityInfo = this expansion.setTransients()