Slight performance improvements for AI

This commit is contained in:
Yair Morgenstern
2018-08-16 09:13:34 +03:00
parent 1affaa38c2
commit b4fece29e0
15 changed files with 63 additions and 64 deletions

View File

@ -106,7 +106,7 @@ class Automation {
unit.promotions.addPromotion(availablePromotions.getRandom().name)
}
val unitType = unit.getBaseUnit().unitType
val unitType = unit.baseUnit().unitType
if(unitType.isRanged()) rangedUnits.add(unit)
else if(unitType.isMelee()) meleeUnits.add(unit)
else civilianUnits.add(unit)
@ -165,7 +165,7 @@ class Automation {
val buildableWonders = getBuildableBuildings().filter { it.isWonder }
val civUnits = cityInfo.civInfo.getCivUnits()
val militaryUnits = civUnits.filter { it.getBaseUnit().unitType != UnitType.Civilian }.size
val militaryUnits = civUnits.filter { it.baseUnit().unitType != UnitType.Civilian }.size
val workers = civUnits.filter { it.name == CityConstructions.Worker }.size
val cities = cityInfo.civInfo.cities.size
@ -204,7 +204,7 @@ class Automation {
fun evaluteCombatStrength(civInfo: CivilizationInfo): Int {
// Since units become exponentially stronger per combat strength increase, we square em all
fun square(x:Int) = x*x
val unitStrength = civInfo.getCivUnits().map { square(max(it.getBaseUnit().strength, it.getBaseUnit().rangedStrength)) }.sum()
val unitStrength = civInfo.getCivUnits().map { square(max(it.baseUnit().strength, it.baseUnit().rangedStrength)) }.sum()
val cityStrength = civInfo.cities.map { square(CityCombatant(it).getCityStrength()) }.sum()
return (sqrt(unitStrength.toDouble()) /*+ sqrt(cityStrength.toDouble())*/).toInt()
}

View File

@ -36,20 +36,21 @@ class UnitAutomation{
if (tryUpgradeUnit(unit, unitActions)) return
// Accompany settlers
if (tryAccompanySettler(unit)) return
val unitDistanceToTiles = unit.getDistanceToTiles()
if (tryAccompanySettler(unit,unitDistanceToTiles)) return
if (unit.health < 50) {
healUnit(unit)
healUnit(unit,unitDistanceToTiles)
return
} // do nothing but heal
// if there is an attackable unit in the vicinity, attack!
if (tryAttackNearbyEnemy(unit)) return
if (tryAttackNearbyEnemy(unit,unitDistanceToTiles)) return
if (tryGarrisoningUnit(unit)) return
if (unit.health < 80) {
healUnit(unit)
healUnit(unit, unitDistanceToTiles)
return
} // do nothing but heal until 80 health
@ -57,7 +58,7 @@ class UnitAutomation{
if (tryAdvanceTowardsCloseEnemy(unit)) return
if (unit.health < 100) {
healUnit(unit)
healUnit(unit, unitDistanceToTiles)
return
}
@ -65,7 +66,7 @@ class UnitAutomation{
if (tryHeadTowardsEnemyCity(unit)) return
// else, go to a random space
randomWalk(unit)
randomWalk(unit,unitDistanceToTiles)
// if both failed, then... there aren't any reachable tiles. Which is possible.
}
@ -79,8 +80,8 @@ class UnitAutomation{
}
}
fun healUnit(unit:MapUnit) {
val tilesInDistance = unit.getDistanceToTiles().keys.filter { unit.canMoveTo(it) }
fun healUnit(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.canMoveTo(it) }
val unitTile = unit.getTile()
val tilesByHealingRate = tilesInDistance.groupBy { rankTileForHealing(it,unit) }
@ -103,11 +104,10 @@ class UnitAutomation{
class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo)
fun getAttackableEnemies(unit: MapUnit): ArrayList<AttackableTile> {
fun getAttackableEnemies(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): ArrayList<AttackableTile> {
val tilesWithEnemies = unit.civInfo.getViewableTiles()
.filter { containsAttackableEnemy(it,unit.civInfo) }
val distanceToTiles = unit.getDistanceToTiles()
val rangeOfAttack = unit.getRange()
val attackableTiles = ArrayList<AttackableTile>()
@ -116,7 +116,7 @@ class UnitAutomation{
// 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!
// Silly floats, basically
val tilesToAttackFrom = distanceToTiles.filter { unit.currentMovement - it.value > 0.1 }
val tilesToAttackFrom = unitDistanceToTiles.filter { unit.currentMovement - it.value > 0.1 }
.map { it.key }
.filter { unit.canMoveTo(it) || it==unit.getTile() }
for(reachableTile in tilesToAttackFrom){ // tiles we'll still have energy after we reach there
@ -131,7 +131,7 @@ class UnitAutomation{
private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean {
var closeEnemies = unit.getTile().getTilesInDistance(5)
.filter{ containsAttackableEnemy(it, unit.civInfo) && unit.movementAlgs().canReach(it)}
if(unit.getBaseUnit().unitType.isRanged())
if(unit.baseUnit().unitType.isRanged())
closeEnemies = closeEnemies.filterNot { it.isCityCenter() && it.getCity()!!.health==1 }
val closestEnemy = closeEnemies.minBy { it.arialDistanceTo(unit.getTile()) }
@ -143,8 +143,8 @@ class UnitAutomation{
return false
}
private fun tryAccompanySettler(unit: MapUnit): Boolean {
val closeTileWithSettler = unit.getDistanceToTiles().keys.firstOrNull {
private fun tryAccompanySettler(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
val closeTileWithSettler = unitDistanceToTiles.keys.firstOrNull {
it.civilianUnit != null && it.civilianUnit!!.name == "Settler"
}
if (closeTileWithSettler != null && unit.canMoveTo(closeTileWithSettler)) {
@ -155,10 +155,10 @@ class UnitAutomation{
}
private fun tryUpgradeUnit(unit: MapUnit, unitActions: List<UnitAction>): Boolean {
if (unit.getBaseUnit().upgradesTo != null) {
val upgradedUnit = GameBasics.Units[unit.getBaseUnit().upgradesTo!!]!!
if (unit.baseUnit().upgradesTo != null) {
val upgradedUnit = GameBasics.Units[unit.baseUnit().upgradesTo!!]!!
if (upgradedUnit.isBuildable(unit.civInfo)) {
val goldCostOfUpgrade = (upgradedUnit.cost - unit.getBaseUnit().cost) * 2 + 10
val goldCostOfUpgrade = (upgradedUnit.cost - unit.baseUnit().cost) * 2 + 10
val upgradeAction = unitActions.firstOrNull { it.name.startsWith("Upgrade to") }
if (upgradeAction != null && unit.civInfo.gold > goldCostOfUpgrade) {
upgradeAction.action()
@ -173,7 +173,7 @@ class UnitAutomation{
if(unit.civInfo.cities.isEmpty()) return false
var enemyCities = unit.civInfo.exploredTiles.map { unit.civInfo.gameInfo.tileMap[it] }
.filter { it.isCityCenter() && it.getOwner() != unit.civInfo }
if(unit.getBaseUnit().unitType.isRanged())
if(unit.baseUnit().unitType.isRanged())
enemyCities = enemyCities.filterNot { it.getCity()!!.health==1 }
val closestReachableEnemyCity = enemyCities
@ -188,8 +188,8 @@ class UnitAutomation{
return false
}
private fun tryAttackNearbyEnemy(unit: MapUnit): Boolean {
val attackableEnemies = getAttackableEnemies(unit)
private fun tryAttackNearbyEnemy(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
val attackableEnemies = getAttackableEnemies(unit,unitDistanceToTiles)
// Only take enemies we can fight without dying
.filter {
BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit),
@ -204,7 +204,7 @@ class UnitAutomation{
val cityWithHealthLeft = cityTilesToAttack.filter { it.tileToAttack.getCity()!!.health != 1 } // don't want ranged units to attack defeated cities
.minBy { it.tileToAttack.getCity()!!.health }
if (unit.getBaseUnit().unitType.isMelee() && capturableCity!=null)
if (unit.baseUnit().unitType.isMelee() && capturableCity!=null)
enemyTileToAttack = capturableCity // enter it quickly, top priority!
else if (nonCityTilesToAttack.isNotEmpty()) // second priority, units
@ -225,7 +225,7 @@ class UnitAutomation{
}
private fun tryGarrisoningUnit(unit: MapUnit): Boolean {
if(unit.getBaseUnit().unitType.isMelee()) return false // don't garrison melee units, they're not that good at it
if(unit.baseUnit().unitType.isMelee()) return false // don't garrison melee units, they're not that good at it
val reachableCitiesWithoutUnits = unit.civInfo.cities.filter {
val centerTile = it.getCenterTile()
unit.canMoveTo(centerTile)
@ -258,8 +258,8 @@ class UnitAutomation{
return false
}
private fun randomWalk(unit: MapUnit) {
val reachableTiles = unit.getDistanceToTiles()
private fun randomWalk(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
val reachableTiles = unitDistanceToTiles
.filter { unit.canMoveTo(it.key) }
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value == unit.currentMovement }
if (reachableTilesMaxWalkingDistance.any()) unit.moveToTile(reachableTilesMaxWalkingDistance.toList().getRandom().first)
@ -295,7 +295,7 @@ class UnitAutomation{
if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
{
randomWalk(unit)
randomWalk(unit, unit.getDistanceToTiles())
return
}

View File

@ -43,7 +43,7 @@ class CityCombatant(val city: CityInfo) : ICombatant {
// Garrisoned unit gives up to 20% of strength to city, health-dependant
if(cityTile.militaryUnit!=null)
strength += cityTile.militaryUnit!!.getBaseUnit().strength * cityTile.militaryUnit!!.health/100f
strength += cityTile.militaryUnit!!.baseUnit().strength * cityTile.militaryUnit!!.health/100f
strength += city.cityConstructions.getBuiltBuildings().sumBy{ it.cityStrength }

View File

@ -18,16 +18,16 @@ class MapUnitCombatant(val unit: MapUnit) : ICombatant {
}
override fun getAttackingStrength(defender: ICombatant): Int { // todo remove defender
if (isRanged()) return unit.getBaseUnit().rangedStrength
else return unit.getBaseUnit().strength
if (isRanged()) return unit.baseUnit().rangedStrength
else return unit.baseUnit().strength
}
override fun getDefendingStrength(attacker: ICombatant): Int { // todo remove attacker
return unit.getBaseUnit().strength
return unit.baseUnit().strength
}
override fun getUnitType(): UnitType {
return unit.getBaseUnit().unitType
return unit.baseUnit().unitType
}
override fun toString(): String {

View File

@ -199,7 +199,7 @@ class CivilizationInfo {
// disband units until there are none left OR the gold values are normal
if(!isBarbarianCivilization() && gold < -100 && nextTurnStats.gold.toInt() < 0) {
for (i in 1 until (gold / -100)) {
var civMilitaryUnits = getCivUnits().filter { it.getBaseUnit().unitType != UnitType.Civilian }
var civMilitaryUnits = getCivUnits().filter { it.baseUnit().unitType != UnitType.Civilian }
if (civMilitaryUnits.isNotEmpty()) {
val unitToDisband = civMilitaryUnits.first()
unitToDisband.removeFromTile()

View File

@ -21,11 +21,8 @@ class MapUnit {
var attacksThisTurn = 0
var promotions = UnitPromotions()
init{
promotions.unit=this
}
fun getBaseUnit(): BaseUnit = GameBasics.Units[name]!!
@Transient lateinit var baseUnit: BaseUnit
fun baseUnit(): BaseUnit = baseUnit
fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement
@Transient
@ -144,7 +141,7 @@ class MapUnit {
fun getSpecialAbilities(): MutableList<String> {
val abilities = mutableListOf<String>()
val baseUnit = getBaseUnit()
val baseUnit = baseUnit()
if(baseUnit.uniques!=null) abilities.addAll(baseUnit.uniques!!)
abilities.addAll(promotions.promotions.map { GameBasics.UnitPromotions[it]!!.effect })
return abilities
@ -179,13 +176,13 @@ class MapUnit {
}
fun removeFromTile(){
if (getBaseUnit().unitType== UnitType.Civilian) getTile().civilianUnit=null
if (baseUnit().unitType== UnitType.Civilian) getTile().civilianUnit=null
else getTile().militaryUnit=null
}
fun putInTile(tile:TileInfo){
if(!canMoveTo(tile)) throw Exception("I can't go there!")
if(getBaseUnit().unitType== UnitType.Civilian)
if(baseUnit().unitType== UnitType.Civilian)
tile.civilianUnit=this
else tile.militaryUnit=this
currentTile = tile
@ -199,7 +196,7 @@ class MapUnit {
if(tileOwner!=null && tileOwner.civName!=owner
&& (tile.isCityCenter() || !civInfo.canEnterTiles(tileOwner))) return false
if (getBaseUnit().unitType== UnitType.Civilian)
if (baseUnit().unitType== UnitType.Civilian)
return tile.civilianUnit==null && (tile.militaryUnit==null || tile.militaryUnit!!.owner==owner)
else return tile.militaryUnit==null && (tile.civilianUnit==null || tile.civilianUnit!!.owner==owner)
}
@ -220,11 +217,12 @@ class MapUnit {
fun setTransients(){
promotions.unit=this
baseUnit=GameBasics.Units[name]!!
}
fun getRange(): Int {
if(getBaseUnit().unitType.isMelee()) return 1
var range = getBaseUnit().range
if(baseUnit().unitType.isMelee()) return 1
var range = baseUnit().range
if(hasUnique("+1 Range")) range++
return range
}

View File

@ -20,7 +20,7 @@ class UnitPromotions{
fun getAvailablePromotions(): List<Promotion> {
return GameBasics.UnitPromotions.values
.filter { unit.getBaseUnit().unitType.toString() in it.unitTypes && it.name !in promotions }
.filter { unit.baseUnit().unitType.toString() in it.unitTypes && it.name !in promotions }
.filter { it.prerequisites.isEmpty() || it.prerequisites.any { p->p in promotions } }
}
}

View File

@ -78,6 +78,7 @@ class BaseUnit : INamed, IConstruction, ICivilopedia {
unit.name = name
unit.maxMovement = movement
unit.currentMovement = movement.toFloat()
unit.setTransients()
return unit
}

View File

@ -209,7 +209,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
table.add("Closest city".tr())
table.row()
for(unit in civInfo.getCivUnits()){
val baseUnit = unit.getBaseUnit()
val baseUnit = unit.baseUnit()
table.add(unit.name.tr())
if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add()
if(baseUnit.rangedStrength>0) table.add(baseUnit.rangedStrength.toString()) else table.add()

View File

@ -26,7 +26,7 @@ class PromotionPickerScreen(mapUnit: MapUnit) : PickerScreen() {
val availablePromotionsGroup = VerticalGroup()
availablePromotionsGroup.space(10f)
val unitType = mapUnit.getBaseUnit().unitType
val unitType = mapUnit.baseUnit().unitType
val promotionsForUnitType = GameBasics.UnitPromotions.values.filter { it.unitTypes.contains(unitType.toString()) }
val unitAvailablePromotions = mapUnit.promotions.getAvailablePromotions()
for (promotion in promotionsForUnitType) {

View File

@ -21,7 +21,7 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
val whiteHalo = if(unit.isFortified()) ImageGetter.getImage("OtherIcons/Shield.png")
else ImageGetter.getImage("OtherIcons/Circle.png")
whiteHalo.setSize(30f,30f)
val unitImage = if(unit.getBaseUnit().unitType== UnitType.Civilian) civilianUnitImage!!
val unitImage = if(unit.baseUnit().unitType== UnitType.Civilian) civilianUnitImage!!
else militaryUnitImage!!
whiteHalo.center(unitImage)
unitImage.addActor(whiteHalo)

View File

@ -106,10 +106,10 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
if(unit.canMoveTo(tile))
tileGroups[tile]!!.showCircle(colorFromRGB(0, 120, 215))
val unitType = unit.getBaseUnit().unitType
val unitType = unit.baseUnit().unitType
val attackableTiles: List<TileInfo> = when{
unitType==UnitType.Civilian -> unit.getDistanceToTiles().keys.toList()
else -> UnitAutomation().getAttackableEnemies(unit).map { it.tileToAttack }
else -> UnitAutomation().getAttackableEnemies(unit, unit.getDistanceToTiles()).map { it.tileToAttack }
}
@ -117,7 +117,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
it.getUnits().isNotEmpty()
&& it.getUnits().first().owner != unit.owner
&& (playerViewableTiles.contains(it) || UnCivGame.Current.viewEntireMapForDebug)}) {
if(unit.getBaseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle()
if(unit.baseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle()
else {
tileGroups[tile]!!.showCircle(colorFromRGB(237, 41, 57))
tileGroups[tile]!!.showCrosshair()

View File

@ -31,7 +31,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
fun update() {
val unitTable = worldScreen.bottomBar.unitTable
if (unitTable.selectedUnit == null
|| unitTable.selectedUnit!!.getBaseUnit().unitType == UnitType.Civilian){
|| unitTable.selectedUnit!!.baseUnit().unitType == UnitType.Civilian){
hide()
return
} // no attacker
@ -121,7 +121,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
attacker.unit.getDistanceToTiles()
val attackableEnemy = UnitAutomation().getAttackableEnemies(attacker.unit)
val attackableEnemy = UnitAutomation().getAttackableEnemies(attacker.unit, attacker.unit.getDistanceToTiles())
.firstOrNull{ it.tileToAttack == defender.getTile()}
if(attackableEnemy==null || !attacker.unit.canAttack()) attackButton.disable()

View File

@ -47,26 +47,26 @@ class UnitActions {
},true)
}
if(unit.getBaseUnit().unitType!= UnitType.Civilian
if(unit.baseUnit().unitType!= UnitType.Civilian
&& !unit.hasUnique("No defensive terrain bonus") && !unit.isFortified()) {
actionList += UnitAction("Fortify", { unit.action = "Fortify 0" }, unit.currentMovement != 0f)
}
if(unit.getBaseUnit().unitType!= UnitType.Civilian && unit.promotions.canBePromoted()){
if(unit.baseUnit().unitType!= UnitType.Civilian && unit.promotions.canBePromoted()){
actionList += UnitAction("Promote",
{UnCivGame.Current.screen = PromotionPickerScreen(unit)},
unit.currentMovement != 0f)
}
if(unit.getBaseUnit().upgradesTo!=null && tile.getOwner()==unit.civInfo) {
var upgradedUnit = unit.getBaseUnit().getUpgradeUnit(unit.civInfo)
if(unit.baseUnit().upgradesTo!=null && tile.getOwner()==unit.civInfo) {
var upgradedUnit = unit.baseUnit().getUpgradeUnit(unit.civInfo)
// Go up the upgrade tree until you find the first one which isn't obsolete
while (upgradedUnit.obsoleteTech!=null && unit.civInfo.tech.isResearched(upgradedUnit.obsoleteTech!!))
upgradedUnit = upgradedUnit.getUpgradeUnit(unit.civInfo)
if (upgradedUnit.isBuildable(unit.civInfo)) {
val goldCostOfUpgrade = (upgradedUnit.cost - unit.getBaseUnit().cost) * 2 + 10
val goldCostOfUpgrade = (upgradedUnit.cost - unit.baseUnit().cost) * 2 + 10
actionList += UnitAction("Upgrade to [${upgradedUnit.name}] ([$goldCostOfUpgrade] gold)",
{
unit.civInfo.gold -= goldCostOfUpgrade

View File

@ -62,13 +62,13 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
unitNameLabel.setText(nameLabelText)
var unitLabelText = "Movement".tr()+": " + unit.getMovementString()
if (unit.getBaseUnit().unitType != UnitType.Civilian)
unitLabelText += "\n"+"Strength".tr()+": " + unit.getBaseUnit().strength
if (unit.baseUnit().unitType != UnitType.Civilian)
unitLabelText += "\n"+"Strength".tr()+": " + unit.baseUnit().strength
if (unit.getBaseUnit().rangedStrength!=0)
unitLabelText += "\n"+"Ranged strength".tr()+": "+unit.getBaseUnit().rangedStrength
if (unit.baseUnit().rangedStrength!=0)
unitLabelText += "\n"+"Ranged strength".tr()+": "+unit.baseUnit().rangedStrength
if (unit.getBaseUnit().unitType != UnitType.Civilian)
if (unit.baseUnit().unitType != UnitType.Civilian)
unitLabelText += "\n"+"XP".tr()+": "+unit.promotions.XP+"/"+unit.promotions.xpForNextPromotion()
if(unit.isFortified() && unit.getFortificationTurns()>0)