AI knows not to try and heal units which would heal anyway

This commit is contained in:
Yair Morgenstern
2020-12-14 11:31:13 +02:00
parent fad967b31a
commit 24b46e8c2c

View File

@ -16,7 +16,7 @@ object UnitAutomation {
const val CLOSE_ENEMY_TILES_AWAY_LIMIT = 5 const val CLOSE_ENEMY_TILES_AWAY_LIMIT = 5
const val CLOSE_ENEMY_TURNS_AWAY_LIMIT = 3f const val CLOSE_ENEMY_TURNS_AWAY_LIMIT = 3f
private fun isGoodTileToExplore(unit:MapUnit, tile:TileInfo): Boolean { private fun isGoodTileToExplore(unit: MapUnit, tile: TileInfo): Boolean {
return unit.movement.canMoveTo(tile) return unit.movement.canMoveTo(tile)
&& (tile.getOwner() == null || !tile.getOwner()!!.isCityState()) && (tile.getOwner() == null || !tile.getOwner()!!.isCityState())
&& tile.neighbors.any { it.position !in unit.civInfo.exploredTiles } && tile.neighbors.any { it.position !in unit.civInfo.exploredTiles }
@ -87,7 +87,7 @@ object UnitAutomation {
if (unit.civInfo.isBarbarian()) if (unit.civInfo.isBarbarian())
throw IllegalStateException("Barbarians is not allowed here.") throw IllegalStateException("Barbarians is not allowed here.")
if(unit.type.isCivilian()) { if (unit.type.isCivilian()) {
if (unit.hasUnique(Constants.settlerUnique)) if (unit.hasUnique(Constants.settlerUnique))
return SpecificUnitAutomation.automateSettlerActions(unit) return SpecificUnitAutomation.automateSettlerActions(unit)
@ -125,7 +125,7 @@ object UnitAutomation {
// Accompany settlers // Accompany settlers
if (tryAccompanySettlerOrGreatPerson(unit)) return if (tryAccompanySettlerOrGreatPerson(unit)) return
if(tryHeadTowardsSiegedCity(unit)) return if (tryHeadTowardsSiegedCity(unit)) return
if (unit.health < 50 && tryHealUnit(unit)) return // do nothing but heal if (unit.health < 50 && tryHealUnit(unit)) return // do nothing but heal
@ -135,7 +135,7 @@ object UnitAutomation {
// if there is an attackable unit in the vicinity, attack! // if there is an attackable unit in the vicinity, attack!
if (BattleHelper.tryAttackNearbyEnemy(unit)) return if (BattleHelper.tryAttackNearbyEnemy(unit)) return
if(tryTakeBackCapturedCity(unit)) return if (tryTakeBackCapturedCity(unit)) return
if (tryGarrisoningUnit(unit)) return if (tryGarrisoningUnit(unit)) return
@ -171,9 +171,14 @@ object UnitAutomation {
} }
fun tryHealUnit(unit: MapUnit): Boolean { fun tryHealUnit(unit: MapUnit): Boolean {
if (unit.type.isRanged() && unit.hasUnique("Unit will heal every turn, even if it performs an action"))
return false // will heal anyway, and attacks don't hurt
val unitDistanceToTiles = unit.movement.getDistanceToTiles() val unitDistanceToTiles = unit.movement.getDistanceToTiles()
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.movement.canMoveTo(it) } val tilesInDistance = unitDistanceToTiles.keys.filter { unit.movement.canMoveTo(it) }
if (unitDistanceToTiles.isEmpty()) return true // can't move, so... if (unitDistanceToTiles.isEmpty()) return true // can't move, so...
val currentUnitTile = unit.getTile() val currentUnitTile = unit.getTile()
if (tryPillageImprovement(unit)) return true if (tryPillageImprovement(unit)) return true
@ -191,9 +196,11 @@ object UnitAutomation {
var bestTilesForHealing = tilesByHealingRate.maxBy { it.key }!!.value var bestTilesForHealing = tilesByHealingRate.maxBy { it.key }!!.value
// within the tiles with best healing rate (say 15), we'll prefer one which has the highest defensive bonuses // within the tiles with best healing rate (say 15), we'll prefer one which has the highest defensive bonuses
val bestTilesWithoutBombardableTiles = bestTilesForHealing.filterNot { it.getTilesInDistance(2) val bestTilesWithoutBombardableTiles = bestTilesForHealing.filterNot {
.any { it.isCityCenter() && it.getOwner()!!.isAtWarWith(unit.civInfo) } } it.getTilesInDistance(2)
if(bestTilesWithoutBombardableTiles.any()) bestTilesForHealing = bestTilesWithoutBombardableTiles .any { it.isCityCenter() && it.getOwner()!!.isAtWarWith(unit.civInfo) }
}
if (bestTilesWithoutBombardableTiles.any()) bestTilesForHealing = bestTilesWithoutBombardableTiles
val bestTileForHealing = bestTilesForHealing.maxBy { it.getDefensiveBonus() }!! val bestTileForHealing = bestTilesForHealing.maxBy { it.getDefensiveBonus() }!!
val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing) val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing)
@ -262,7 +269,7 @@ object UnitAutomation {
val settlerOrGreatPersonToAccompany = unit.civInfo.getCivUnits() val settlerOrGreatPersonToAccompany = unit.civInfo.getCivUnits()
.firstOrNull { .firstOrNull {
val tile = it.currentTile val tile = it.currentTile
it.type==UnitType.Civilian && it.type == UnitType.Civilian &&
(it.hasUnique(Constants.settlerUnique) || unit.name in GreatPersonManager().statToGreatPersonMapping.values) (it.hasUnique(Constants.settlerUnique) || unit.name in GreatPersonManager().statToGreatPersonMapping.values)
&& tile.militaryUnit == null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile) && tile.militaryUnit == null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile)
} }
@ -274,8 +281,10 @@ object UnitAutomation {
private fun tryHeadTowardsSiegedCity(unit: MapUnit): Boolean { private fun tryHeadTowardsSiegedCity(unit: MapUnit): Boolean {
val siegedCities = unit.civInfo.cities val siegedCities = unit.civInfo.cities
.asSequence() .asSequence()
.filter { unit.civInfo == it.civInfo && .filter {
it.health < it.getMaxHealth() * 0.75 } //Weird health issues and making sure that not all forces move to good defenses unit.civInfo == it.civInfo &&
it.health < it.getMaxHealth() * 0.75
} //Weird health issues and making sure that not all forces move to good defenses
val reachableTileNearSiegedCity = siegedCities val reachableTileNearSiegedCity = siegedCities
.flatMap { it.getCenterTile().getTilesAtDistance(2) } .flatMap { it.getCenterTile().getTilesAtDistance(2) }
@ -389,10 +398,12 @@ object UnitAutomation {
private fun tryTakeBackCapturedCity(unit: MapUnit): Boolean { private fun tryTakeBackCapturedCity(unit: MapUnit): Boolean {
var capturedCities = unit.civInfo.getKnownCivs().asSequence() var capturedCities = unit.civInfo.getKnownCivs().asSequence()
.flatMap { it.cities.asSequence() } .flatMap { it.cities.asSequence() }
.filter { unit.civInfo.isAtWarWith(it.civInfo) && .filter {
unit.civInfo.isAtWarWith(it.civInfo) &&
unit.civInfo.civName == it.foundingCiv && unit.civInfo.civName == it.foundingCiv &&
it.isInResistance() && it.isInResistance() &&
it.health < it.getMaxHealth()} //Most likely just been captured it.health < it.getMaxHealth()
} //Most likely just been captured
if (unit.type.isRanged()) // ranged units don't harm capturable cities, waste of a turn if (unit.type.isRanged()) // ranged units don't harm capturable cities, waste of a turn
@ -462,20 +473,20 @@ object UnitAutomation {
fun runAway(unit: MapUnit) { fun runAway(unit: MapUnit) {
val reachableTiles = unit.movement.getDistanceToTiles() val reachableTiles = unit.movement.getDistanceToTiles()
val enterableCity = reachableTiles.keys.firstOrNull { it.isCityCenter() && unit.movement.canMoveTo(it) } val enterableCity = reachableTiles.keys.firstOrNull { it.isCityCenter() && unit.movement.canMoveTo(it) }
if(enterableCity!=null) { if (enterableCity != null) {
unit.movement.moveToTile(enterableCity) unit.movement.moveToTile(enterableCity)
return return
} }
val tileFurthestFromEnemy = reachableTiles.keys.filter { unit.movement.canMoveTo(it) } val tileFurthestFromEnemy = reachableTiles.keys.filter { unit.movement.canMoveTo(it) }
.maxBy{ countDistanceToClosestEnemy(unit, it)} .maxBy { countDistanceToClosestEnemy(unit, it) }
if(tileFurthestFromEnemy==null) return // can't move anywhere! if (tileFurthestFromEnemy == null) return // can't move anywhere!
unit.movement.moveToTile(tileFurthestFromEnemy) unit.movement.moveToTile(tileFurthestFromEnemy)
} }
fun countDistanceToClosestEnemy(unit: MapUnit, tile: TileInfo): Int { fun countDistanceToClosestEnemy(unit: MapUnit, tile: TileInfo): Int {
for(i in 1..3) for (i in 1..3)
if(tile.getTilesAtDistance(i).any{containsEnemyMilitaryUnit(unit,it)}) if (tile.getTilesAtDistance(i).any { containsEnemyMilitaryUnit(unit, it) })
return i return i
return 4 return 4
} }