When pathfinding, only consider ZOC for the *current* turn since things will likely change next turn! This gives a massive performance boost by removing a possible wrong check entirely :)

This commit is contained in:
Yair Morgenstern 2022-02-10 14:01:11 +02:00
parent dcdb0ca892
commit 86450c54eb
2 changed files with 20 additions and 21 deletions

View File

@ -387,16 +387,16 @@ object UnitAutomation {
private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean {
if (unit.civInfo.cities.isEmpty()) return false
var enemyCities = unit.civInfo.gameInfo.civilizations
var enemyCities = unit.civInfo.gameInfo.civilizations.asSequence()
.filter { unit.civInfo.isAtWarWith(it) }
.flatMap { it.cities }.asSequence()
.flatMap { it.cities }
.filter { it.location in unit.civInfo.exploredTiles }
if (unit.baseUnit.isRanged()) // ranged units don't harm capturable cities, waste of a turn
enemyCities = enemyCities.filterNot { it.health == 1 }
val closestReachableEnemyCity = enemyCities
.asSequence().map { it.getCenterTile() }
.map { it.getCenterTile() }
.sortedBy { cityCenterTile ->
// sort enemy cities by closeness to our cities, and only then choose the first reachable - checking canReach is comparatively very time-intensive!
unit.civInfo.cities.asSequence()

View File

@ -82,6 +82,16 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
return terrainCost + extraCost // no road or other movement cost reduction
}
fun getTilesExertingZoneOfControl(tileInfo: TileInfo, civInfo: CivilizationInfo): Sequence<TileInfo> {
return tileInfo.neighbors.filter {
it.isCityCenter() && civInfo.isAtWarWith(it.getOwner()!!)
||
it.militaryUnit != null &&
civInfo.isAtWarWith(it.militaryUnit!!.civInfo) &&
(it.militaryUnit!!.type.isWaterUnit() || (!it.militaryUnit!!.isEmbarked() && unit.type.isLandUnit()))
}
}
/** Returns whether the movement between the adjacent tiles [from] and [to] is affected by Zone of Control */
private fun isMovementAffectedByZoneOfControl(from: TileInfo, to: TileInfo, civInfo: CivilizationInfo): Boolean {
// Sources:
@ -100,22 +110,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
// these two tiles can perhaps be optimized. Using a hex-math-based "commonAdjacentTiles"
// function is surprisingly less efficient than the current neighbor-intersection approach.
// See #4085 for more details.
if (from.neighbors.none{
(
(
it.isCityCenter() &&
civInfo.isAtWarWith(it.getOwner()!!)
)
||
(
it.militaryUnit != null &&
civInfo.isAtWarWith(it.militaryUnit!!.civInfo) &&
(it.militaryUnit!!.type.isWaterUnit() || (!it.militaryUnit!!.isEmbarked() && unit.type.isLandUnit()))
)
)
&&
to.neighbors.contains(it)
})
val tilesExertingZoneOfControl = getTilesExertingZoneOfControl(from, civInfo)
if (tilesExertingZoneOfControl.none { to.neighbors.contains(it)})
return false
// Even though this is a very fast check, we perform it last. This is because very few units
@ -203,13 +199,16 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
var distance = 1
val newTilesToCheck = ArrayList<TileInfo>()
val distanceToDestination = HashMap<TileInfo, Float>()
var considerZoneOfControl = true // only for first distance!
while (true) {
if (distance == 2) // only set this once after distance > 1
if (distance == 2) { // only set this once after distance > 1
movementThisTurn = unit.getMaxMovement().toFloat()
considerZoneOfControl = false // by then units would have moved around, we don't need to consider untenable futures when it harms performance!
}
newTilesToCheck.clear()
distanceToDestination.clear()
for (tileToCheck in tilesToCheck) {
val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn)
val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn, considerZoneOfControl)
for (reachableTile in distanceToTilesThisTurn.keys) {
// Avoid damaging terrain on first pass
if (avoidDamagingTerrain && unit.getDamageFromTerrain(reachableTile) > 0)