Resolved #3663 - fixed settler automation bug

This commit is contained in:
Yair Morgenstern
2021-03-08 22:05:59 +02:00
parent 7eab616352
commit 87c9729e52
2 changed files with 35 additions and 25 deletions

View File

@ -20,8 +20,10 @@ object SpecificUnitAutomation {
fun automateWorkBoats(unit: MapUnit) {
val closestReachableResource = unit.civInfo.cities.asSequence()
.flatMap { city -> city.getWorkableTiles() }
.filter { hasWorkableSeaResource(it, unit.civInfo)
&& (unit.currentTile == it || unit.movement.canMoveTo(it)) }
.filter {
hasWorkableSeaResource(it, unit.civInfo)
&& (unit.currentTile == it || unit.movement.canMoveTo(it))
}
.sortedBy { it.aerialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movement.canReach(it) }
@ -64,13 +66,14 @@ object SpecificUnitAutomation {
val enemyCities = unit.civInfo.getKnownCivs()
.filter { unit.civInfo.getDiplomacyManager(it).hasModifier(DiplomaticModifiers.StealingTerritory) }
.flatMap { it.cities }.asSequence()
// find the suitable tiles (or their neigbours)
// find the suitable tiles (or their neighbours)
val tileToSteal = enemyCities.flatMap { it.getTiles() } // City tiles
.filter { it.neighbors.any { it.getOwner()!=unit.civInfo } } // Edge city tiles
.filter { it.neighbors.any { it.getOwner() != unit.civInfo } } // Edge city tiles
.flatMap { it.neighbors.asSequence() } // Neighbors of edge city tiles
.filter { it in unit.civInfo.viewableTiles // we can see them
&& it.neighbors.any { tile -> tile.getOwner() == unit.civInfo}// they are close to our borders
}
.filter {
it in unit.civInfo.viewableTiles // we can see them
&& it.neighbors.any { tile -> tile.getOwner() == unit.civInfo }// they are close to our borders
}
.sortedBy {
// get closest tiles
val distance = it.aerialDistanceTo(unit.currentTile)
@ -78,8 +81,9 @@ object SpecificUnitAutomation {
val owner = it.getOwner()
if (owner != null)
distance - WorkerAutomation(unit).getPriority(it, owner)
else distance }
.firstOrNull{ unit.movement.canReach(it) } // canReach is performance-heavy and always a last resort
else distance
}
.firstOrNull { unit.movement.canReach(it) } // canReach is performance-heavy and always a last resort
// if there is a good tile to steal - go there
if (tileToSteal != null) {
unit.movement.headTowards(tileToSteal)
@ -90,14 +94,16 @@ object SpecificUnitAutomation {
// try to build a citadel for defensive purposes
if (WorkerAutomation(unit).evaluateFortPlacement(unit.currentTile, unit.civInfo, true)) {
UnitActions.getImprovementConstructionActions(unit,unit.currentTile).firstOrNull()?.action?.invoke()
UnitActions.getImprovementConstructionActions(unit, unit.currentTile).firstOrNull()?.action?.invoke()
return
}
//if no unit to follow, take refuge in city or build citadel there.
val reachableTest : (TileInfo) -> Boolean = {it.civilianUnit == null &&
unit.movement.canMoveTo(it)
&& unit.movement.canReach(it)}
val reachableTest: (TileInfo) -> Boolean = {
it.civilianUnit == null &&
unit.movement.canMoveTo(it)
&& unit.movement.canReach(it)
}
val cityToGarrison = unit.civInfo.cities.asSequence().map { it.getCenterTile() }
.sortedBy { it.aerialDistanceTo(unit.currentTile) }
.firstOrNull { reachableTest(it) }
@ -105,12 +111,14 @@ object SpecificUnitAutomation {
if (cityToGarrison != null) {
// try to find a good place for citadel nearby
val potentialTilesNearCity = cityToGarrison.getTilesInDistanceRange(3..4)
val tileForCitadel = potentialTilesNearCity.firstOrNull { reachableTest(it) &&
WorkerAutomation(unit).evaluateFortPlacement(it, unit.civInfo, true) }
val tileForCitadel = potentialTilesNearCity.firstOrNull {
reachableTest(it) &&
WorkerAutomation(unit).evaluateFortPlacement(it, unit.civInfo, true)
}
if (tileForCitadel != null) {
unit.movement.headTowards(tileForCitadel)
if (unit.currentMovement > 0 && unit.currentTile == tileForCitadel)
UnitActions.getImprovementConstructionActions(unit,unit.currentTile).firstOrNull()?.action?.invoke()
UnitActions.getImprovementConstructionActions(unit, unit.currentTile).firstOrNull()?.action?.invoke()
} else
unit.movement.headTowards(cityToGarrison)
return
@ -169,13 +177,16 @@ object SpecificUnitAutomation {
.map { it.getTileResource() }.filter { it.resourceType == ResourceType.Luxury }
.distinct()
val citiesByRanking= possibleCityLocations
val citiesByRanking = possibleCityLocations
.map { Pair(it, rankTileAsCityCenter(it, nearbyTileRankings, luxuryResourcesInCivArea)) }
.sortedByDescending { it.second }.toList()
// It's possible that we'll see a tile "over the sea" that's better than the tiles close by, but that's not a reason to abandon the close tiles!
// Also this lead to some routing problems, see https://github.com/yairm210/Unciv/issues/3653
val bestCityLocation: TileInfo? = citiesByRanking.firstOrNull { unit.movement.getShortestPath(it.first).size < 4 }?.first
val bestCityLocation: TileInfo? = citiesByRanking.firstOrNull {
val pathSize = unit.movement.getShortestPath(it.first).size
return@firstOrNull pathSize in 1..3
}?.first
if (bestCityLocation == null) { // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
if (UnitAutomation.tryExplore(unit)) return // try to find new areas
@ -228,7 +239,7 @@ object SpecificUnitAutomation {
val chosenTile = applicableTiles.sortedByDescending { Automation.rankTile(it, unit.civInfo) }
.firstOrNull { unit.movement.canReach(it) }
if (chosenTile == null) continue // to another city
unit.movement.headTowards(chosenTile)
if (unit.currentTile == chosenTile)
UnitActions.getImprovementConstructionActions(unit, unit.currentTile).firstOrNull()?.action?.invoke()
@ -251,7 +262,7 @@ object SpecificUnitAutomation {
val citiesByNearbyAirUnits = pathsToCities.keys
.groupBy { key ->
key.getTilesInDistance(unit.getRange()*2)
key.getTilesInDistance(unit.getRange() * 2)
.count {
val firstAirUnit = it.airUnits.firstOrNull()
firstAirUnit != null && firstAirUnit.civInfo.isAtWarWith(unit.civInfo)
@ -301,9 +312,9 @@ object SpecificUnitAutomation {
// This really needs to be changed, to have better targetting for missiles
fun automateMissile(unit: MapUnit) {
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange()*2)
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange() * 2)
for(tile in tilesInRange) {
for (tile in tilesInRange) {
// For now AI will only use nukes against cities because in all honesty that's the best use for them.
if (tile.isCityCenter() && tile.getOwner()!!.isAtWarWith(unit.civInfo)) {
Battle.nuke(MapUnitCombatant(unit), tile)
@ -330,7 +341,7 @@ object SpecificUnitAutomation {
private fun tryRelocateToCitiesWithEnemyNearBy(unit: MapUnit): Boolean {
val immediatelyReachableCitiesAndCarriers = unit.currentTile
.getTilesInDistance(unit.getRange()*2).filter { unit.movement.canMoveTo(it) }
.getTilesInDistance(unit.getRange() * 2).filter { unit.movement.canMoveTo(it) }
for (city in immediatelyReachableCitiesAndCarriers) {
if (city.getTilesInDistance(unit.getRange())
@ -341,5 +352,4 @@ object SpecificUnitAutomation {
}
return false
}
}

View File

@ -22,7 +22,7 @@ class TileInfoTable(private val viewingCiv :CivilizationInfo) : Table(CameraStag
add(getStatsTable(tile))
add(tile.toString(viewingCiv).toLabel()).colspan(2).pad(10f)
// For debug only!
add(tile.position.toString().toLabel()).colspan(2).pad(10f)
// add(tile.position.toString().toLabel()).colspan(2).pad(10f)
}
pack()