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
This commit is contained in:
Yair Morgenstern 2018-11-21 18:07:05 +02:00
parent edada1182b
commit 71d99dce51
2 changed files with 28 additions and 22 deletions

View File

@ -157,7 +157,8 @@ class UnitAutomation{
// and then later we round it off to a whole. // 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! // 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 // 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 } .map { it.key }
.filter { unit.canMoveTo(it) || it==unit.getTile() } .filter { unit.canMoveTo(it) || it==unit.getTile() }
if(unit.type.isLandUnit()) if(unit.type.isLandUnit())
@ -167,7 +168,7 @@ class UnitAutomation{
val tilesInAttackRange = if (unit.hasUnique("Indirect fire")) reachableTile.getTilesInDistance(rangeOfAttack) val tilesInAttackRange = if (unit.hasUnique("Indirect fire")) reachableTile.getTilesInDistance(rangeOfAttack)
else reachableTile.getViewableTiles(rangeOfAttack) else reachableTile.getViewableTiles(rangeOfAttack)
attackableTiles += tilesInAttackRange.asSequence().filter { it in tilesWithEnemies } attackableTiles += tilesInAttackRange.asSequence().filter { it in tilesWithEnemies }
.map { AttackableTile(reachableTile,it) }.toList() .map { AttackableTile(reachableTile,it) }
} }
return attackableTiles return attackableTiles
} }
@ -275,17 +276,17 @@ class UnitAutomation{
} }
private fun tryGarrisoningUnit(unit: MapUnit): Boolean { private fun tryGarrisoningUnit(unit: MapUnit): Boolean {
if(unit.type.isMelee()) return false // don't garrison melee units, they're not that good at it if(unit.type.isMelee() || unit.type.isWaterUnit()) return false // don't garrison melee units, they're not that good at it
val reachableCitiesWithoutUnits = unit.civInfo.cities.filter { val citiesWithoutGarrison = unit.civInfo.cities.filter {
val centerTile = it.getCenterTile() val centerTile = it.getCenterTile()
centerTile.militaryUnit==null centerTile.militaryUnit==null
&& unit.canMoveTo(centerTile) && 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! 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 }) .map { it.otherCiv() }.flatMap { it.cities })
if (city.getCenterTile().arialDistanceTo(enemyCivCity.getCenterTile()) <= 5) return true// this is an edge city that needs defending if (city.getCenterTile().arialDistanceTo(enemyCivCity.getCenterTile()) <= 5) return true// this is an edge city that needs defending
return false return false
@ -293,23 +294,25 @@ class UnitAutomation{
if (!unit.civInfo.isAtWar()) { 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 (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) }!! val closestReachableCityWithNoGarrison = citiesWithoutGarrison.asSequence()
unit.movementAlgs().headTowards(closestCity.getCenterTile()) .sortedBy{ it.getCenterTile().arialDistanceTo(unit.currentTile) }
return true .firstOrNull { unit.movementAlgs().canReach(it.getCenterTile()) }
} if(closestReachableCityWithNoGarrison==null) return false // no
unit.movementAlgs().headTowards(closestReachableCityWithNoGarrison.getCenterTile())
return true
} else { } else {
if (unit.getTile().isCityCenter() && if (unit.getTile().isCityCenter() &&
cityThatNeedsDefendingInWartime(unit.getTile().getCity()!!)) return true isCityThatNeedsDefendingInWartime(unit.getTile().getCity()!!)) return true
val citiesThatCanBeDefended = reachableCitiesWithoutUnits.filter { cityThatNeedsDefendingInWartime(it) }
if (citiesThatCanBeDefended.isNotEmpty()) { val closestReachableCityNeedsDefending = citiesWithoutGarrison.asSequence()
val closestCityWithoutUnit = citiesThatCanBeDefended .filter { isCityThatNeedsDefendingInWartime(it) }
.minBy { unit.movementAlgs().getShortestPath(it.getCenterTile()).size }!! .sortedBy{ it.getCenterTile().arialDistanceTo(unit.currentTile) }
unit.movementAlgs().headTowards(closestCityWithoutUnit.getCenterTile()) .firstOrNull { unit.movementAlgs().canReach(it.getCenterTile()) }
return true if(closestReachableCityNeedsDefending==null) return false
} unit.movementAlgs().headTowards(closestReachableCityNeedsDefending.getCenterTile())
return true
} }
return false
} }
fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean { fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {

View File

@ -16,6 +16,8 @@ import kotlin.math.min
class CityInfo { class CityInfo {
@Transient lateinit var civInfo: CivilizationInfo @Transient lateinit var civInfo: CivilizationInfo
@Transient var isConnectedToCapital = false @Transient var isConnectedToCapital = false
@Transient lateinit var ccenterTile:TileInfo // 'cached' for better performance
var location: Vector2 = Vector2.Zero var location: Vector2 = Vector2.Zero
var name: String = "" var name: String = ""
var health = 200 var health = 200
@ -86,7 +88,7 @@ class CityInfo {
internal val tileMap: TileMap internal val tileMap: TileMap
get() = civInfo.gameInfo.tileMap get() = civInfo.gameInfo.tileMap
fun getCenterTile(): TileInfo = tileMap[location] fun getCenterTile(): TileInfo = ccenterTile
fun getTiles(): List<TileInfo> = tiles.map { tileMap[it] } fun getTiles(): List<TileInfo> = tiles.map { tileMap[it] }
fun getTilesInRange(): List<TileInfo> = getCenterTile().getTilesInDistance( 3) fun getTilesInRange(): List<TileInfo> = getCenterTile().getTilesInDistance( 3)
@ -151,6 +153,7 @@ class CityInfo {
//region state-changing functions //region state-changing functions
fun setTransients() { fun setTransients() {
ccenterTile = tileMap[location]
population.cityInfo = this population.cityInfo = this
expansion.cityInfo = this expansion.cityInfo = this
expansion.setTransients() expansion.setTransients()