From 87e2764733d7c55abd49811ba81ff39f38d990e4 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 10 Feb 2022 15:40:41 +0200 Subject: [PATCH] Better AI coordination - try and attack the area around the closest city to ours. Also improves performance as AI only try and head towards a specific subset of cities. --- .../unciv/logic/automation/UnitAutomation.kt | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 68f32d91dc..c448482450 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -387,25 +387,43 @@ object UnitAutomation { private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean { if (unit.civInfo.cities.isEmpty()) return false - var enemyCities = unit.civInfo.gameInfo.civilizations.asSequence() - .filter { unit.civInfo.isAtWarWith(it) } - .flatMap { it.cities } - .filter { it.location in unit.civInfo.exploredTiles } + // only focus on *attacking* 1 enemy at a time otherwise you'll lose on both fronts + + val enemies = unit.civInfo.getKnownCivs().filter { unit.civInfo.isAtWarWith(it) && it.cities.isNotEmpty() } + + val ourCities = unit.civInfo.cities + + var closestEnemyCity:CityInfo?=null + var closestDistance = 10000 + for (enemy in enemies) { + val knownEnemyCities = enemy.cities.filter { it.location in unit.civInfo.exploredTiles } + if (knownEnemyCities.isEmpty()) continue + for(enemyCity in knownEnemyCities) { + val distanceToClosestCityOfOurs = ourCities.minOf { + it.getCenterTile().aerialDistanceTo(enemyCity.getCenterTile()) + } + if (distanceToClosestCityOfOurs < closestDistance){ + closestDistance = distanceToClosestCityOfOurs + closestEnemyCity = enemyCity + } + } + } + if (closestEnemyCity==null) return false // no attackable cities found + + // Our main attack target is the closest city, but we're fine with deviating from that a bit + var enemyCitiesByPriority = closestEnemyCity.civInfo.cities + .associateWith { it.getCenterTile().aerialDistanceTo(closestEnemyCity.getCenterTile()) } + .filterNot { it.value > 10 } // anything 10 tiles away from the target is irrelevant + .asSequence().sortedBy { it.value }.map { it.key } // sort the list by closeness to target - least is best! if (unit.baseUnit.isRanged()) // ranged units don't harm capturable cities, waste of a turn - enemyCities = enemyCities.filterNot { it.health == 1 } + enemyCitiesByPriority = enemyCitiesByPriority.filterNot { it.health == 1 } - val closestReachableEnemyCity = enemyCities - .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() - .map { cityCenterTile.aerialDistanceTo(it.getCenterTile()) }.minOrNull()!! - } - .firstOrNull { unit.movement.canReach(it) } + val closestReachableEnemyCity = enemyCitiesByPriority + .firstOrNull { unit.movement.canReach(it.getCenterTile()) } if (closestReachableEnemyCity != null) { - return headTowardsEnemyCity(unit, closestReachableEnemyCity) + return headTowardsEnemyCity(unit, closestReachableEnemyCity.getCenterTile()) } return false }