Organized automation classes
This commit is contained in:
parent
5d4003ecbf
commit
4e699f917e
10 changed files with 120 additions and 110 deletions
|
@ -21,8 +21,8 @@ android {
|
|||
applicationId "com.unciv.game"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 45
|
||||
versionName "2.0.2"
|
||||
versionCode 46
|
||||
versionName "2.0.3"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.unciv.logic
|
||||
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.Notification
|
||||
|
|
95
core/src/com/unciv/logic/automation/Automation.kt
Normal file
95
core/src/com/unciv/logic/automation/Automation.kt
Normal file
|
@ -0,0 +1,95 @@
|
|||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.ui.utils.getRandom
|
||||
|
||||
class Automation {
|
||||
|
||||
|
||||
internal fun rankTile(tile: TileInfo, civInfo: CivilizationInfo): Float {
|
||||
val stats = tile.getTileStats(null, civInfo)
|
||||
var rank = 0.0f
|
||||
if (stats.food <= 2) rank += stats.food
|
||||
else rank += (2 + (stats.food - 2) / 2f) // 1 point for each food up to 2, from there on half a point
|
||||
rank += stats.gold / 2
|
||||
rank += stats.production
|
||||
rank += stats.science
|
||||
rank += stats.culture
|
||||
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
||||
if (tile.hasViewableResource(civInfo)) rank += 1.0f
|
||||
return rank
|
||||
}
|
||||
|
||||
fun automateCivMoves(civInfo: CivilizationInfo) {
|
||||
if (civInfo.tech.techsToResearch.isEmpty()) {
|
||||
val researchableTechs = GameBasics.Technologies.values.filter { civInfo.tech.canBeResearched(it.name) }
|
||||
val techToResearch = researchableTechs.minBy { it.cost }
|
||||
civInfo.tech.techsResearched.add(techToResearch!!.name)
|
||||
}
|
||||
|
||||
while(civInfo.policies.canAdoptPolicy()){
|
||||
val adoptablePolicies = GameBasics.PolicyBranches.values.flatMap { it.policies.union(listOf(it))}
|
||||
.filter { civInfo.policies.isAdoptable(it) }
|
||||
val policyToAdopt = adoptablePolicies.getRandom()
|
||||
civInfo.policies.adopt(policyToAdopt)
|
||||
}
|
||||
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
UnitAutomation().automateUnitMoves(unit)
|
||||
}
|
||||
|
||||
// train settler?
|
||||
if (civInfo.cities.any()
|
||||
&& civInfo.happiness > 2*civInfo.cities.size +5
|
||||
&& civInfo.getCivUnits().none { it.name == "Settler" }
|
||||
&& civInfo.cities.none { it.cityConstructions.currentConstruction == "Settler" }) {
|
||||
|
||||
val bestCity = civInfo.cities.maxBy { it.cityStats.currentCityStats.production }!!
|
||||
if(bestCity.cityConstructions.builtBuildings.size > 1) // 2 buildings or more, otherwisse focus on self first
|
||||
bestCity.cityConstructions.currentConstruction = "Settler"
|
||||
}
|
||||
|
||||
for (city in civInfo.cities) {
|
||||
if (city.health < city.getMaxHealth()) trainCombatUnit(city)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun trainCombatUnit(city: CityInfo) {
|
||||
city.cityConstructions.currentConstruction = "Archer" // when we have more units then we'll see.
|
||||
}
|
||||
|
||||
|
||||
fun chooseNextConstruction(cityConstructions: CityConstructions) {
|
||||
cityConstructions.run {
|
||||
val buildableNotWonders = getBuildableBuildings().filterNot { (getConstruction(it) as Building).isWonder }
|
||||
val buildableWonders = getBuildableBuildings().filter { (getConstruction(it) as Building).isWonder }
|
||||
|
||||
val civUnits = cityInfo.civInfo.getCivUnits()
|
||||
val militaryUnits = civUnits.filter { it.getBaseUnit().unitType != UnitType.Civilian }.size
|
||||
val workers = civUnits.filter { it.name == CityConstructions.Worker }.size
|
||||
val cities = cityInfo.civInfo.cities.size
|
||||
|
||||
when {
|
||||
!buildableNotWonders.isEmpty() -> currentConstruction = buildableNotWonders.first()
|
||||
militaryUnits==0 -> trainCombatUnit(cityInfo)
|
||||
workers==0 -> currentConstruction = CityConstructions.Worker
|
||||
militaryUnits<cities -> trainCombatUnit(cityInfo)
|
||||
workers<cities -> currentConstruction = CityConstructions.Worker
|
||||
buildableWonders.isNotEmpty() -> currentConstruction = buildableWonders.getRandom()
|
||||
else -> trainCombatUnit(cityInfo)
|
||||
}
|
||||
|
||||
if (cityInfo.civInfo == cityInfo.civInfo.gameInfo.getPlayerCivilization())
|
||||
cityInfo.civInfo.addNotification("Work has started on $currentConstruction", cityInfo.location)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,83 +1,15 @@
|
|||
package com.unciv.logic
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.UnCivGame
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.ui.utils.getRandom
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
|
||||
class Automation {
|
||||
|
||||
|
||||
internal fun rankTile(tile: TileInfo, civInfo: CivilizationInfo): Float {
|
||||
val stats = tile.getTileStats(null, civInfo)
|
||||
var rank = 0.0f
|
||||
if (stats.food <= 2) rank += stats.food
|
||||
else rank += (2 + (stats.food - 2) / 2f) // 1 point for each food up to 2, from there on half a point
|
||||
rank += stats.gold / 2
|
||||
rank += stats.production
|
||||
rank += stats.science
|
||||
rank += stats.culture
|
||||
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
||||
if (tile.hasViewableResource(civInfo)) rank += 1.0f
|
||||
return rank
|
||||
}
|
||||
|
||||
fun rankTileAsCityCenter(tileInfo: TileInfo, civInfo: CivilizationInfo, nearbyTileRankings: Map<TileInfo, Float>): Float {
|
||||
val bestTilesFromOuterLayer = tileInfo.tileMap.getTilesAtDistance(tileInfo.position,2)
|
||||
.sortedByDescending { nearbyTileRankings[it] }.take(2)
|
||||
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
|
||||
.sortedByDescending { nearbyTileRankings[it] }
|
||||
.take(5)
|
||||
return top5Tiles.map { nearbyTileRankings[it]!! }.sum()
|
||||
}
|
||||
|
||||
fun automateCivMoves(civInfo: CivilizationInfo) {
|
||||
if (civInfo.tech.techsToResearch.isEmpty()) {
|
||||
val researchableTechs = GameBasics.Technologies.values.filter { civInfo.tech.canBeResearched(it.name) }
|
||||
val techToResearch = researchableTechs.minBy { it.cost }
|
||||
civInfo.tech.techsResearched.add(techToResearch!!.name)
|
||||
}
|
||||
|
||||
while(civInfo.policies.canAdoptPolicy()){
|
||||
val adoptablePolicies = GameBasics.PolicyBranches.values.flatMap { it.policies.union(listOf(it))}
|
||||
.filter { civInfo.policies.isAdoptable(it) }
|
||||
val policyToAdopt = adoptablePolicies.getRandom()
|
||||
civInfo.policies.adopt(policyToAdopt)
|
||||
}
|
||||
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
automateUnitMoves(unit)
|
||||
}
|
||||
|
||||
// train settler?
|
||||
if (civInfo.cities.any()
|
||||
&& civInfo.happiness > 2*civInfo.cities.size +5
|
||||
&& civInfo.getCivUnits().none { it.name == "Settler" }
|
||||
&& civInfo.cities.none { it.cityConstructions.currentConstruction == "Settler" }) {
|
||||
|
||||
val bestCity = civInfo.cities.maxBy { it.cityStats.currentCityStats.production }!!
|
||||
if(bestCity.cityConstructions.builtBuildings.size > 1) // 2 buildings or more, otherwisse focus on self first
|
||||
bestCity.cityConstructions.currentConstruction = "Settler"
|
||||
}
|
||||
|
||||
for (city in civInfo.cities) {
|
||||
if (city.health < city.getMaxHealth()) trainCombatUnit(city)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun trainCombatUnit(city: CityInfo) {
|
||||
city.cityConstructions.currentConstruction = "Archer" // when we have more units then we'll see.
|
||||
}
|
||||
class UnitAutomation{
|
||||
|
||||
fun automateUnitMoves(unit: MapUnit) {
|
||||
|
||||
|
@ -171,18 +103,27 @@ class Automation {
|
|||
unit.moveToTile(distanceToTiles.keys.filter { it.unit == null }.toList().getRandom())
|
||||
}
|
||||
|
||||
private fun automateSettlerActions(unit: MapUnit) {
|
||||
val tileMap = unit.civInfo.gameInfo.tileMap
|
||||
|
||||
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>): Float {
|
||||
val bestTilesFromOuterLayer = tileInfo.tileMap.getTilesAtDistance(tileInfo.position,2)
|
||||
.sortedByDescending { nearbyTileRankings[it] }.take(2)
|
||||
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
|
||||
.sortedByDescending { nearbyTileRankings[it] }
|
||||
.take(5)
|
||||
return top5Tiles.map { nearbyTileRankings[it]!! }.sum()
|
||||
}
|
||||
|
||||
private fun automateSettlerActions(unit: MapUnit) {
|
||||
// find best city location within 5 tiles
|
||||
val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities }
|
||||
.flatMap { it.getCenterTile().getTilesInDistance(2) }
|
||||
|
||||
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
|
||||
val nearbyTileRankings = unit.getTile().getTilesInDistance(7).associateBy ( {it},{rankTile(it,unit.civInfo) })
|
||||
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
|
||||
.associateBy ( {it},{ Automation().rankTile(it,unit.civInfo) })
|
||||
val bestCityLocation = unit.getTile().getTilesInDistance(5)
|
||||
.minus(tilesNearCities)
|
||||
.maxBy { rankTileAsCityCenter(it, unit.civInfo, nearbyTileRankings) }!!
|
||||
.maxBy { rankTileAsCityCenter(it, nearbyTileRankings) }!!
|
||||
|
||||
if (unit.getTile() == bestCityLocation)
|
||||
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen!!).first { it.name == "Found city" }.action()
|
||||
|
@ -193,31 +134,4 @@ class Automation {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun chooseNextConstruction(cityConstructions: CityConstructions) {
|
||||
cityConstructions.run {
|
||||
val buildableNotWonders = getBuildableBuildings().filterNot { (getConstruction(it) as Building).isWonder }
|
||||
val buildableWonders = getBuildableBuildings().filter { (getConstruction(it) as Building).isWonder }
|
||||
|
||||
val civUnits = cityInfo.civInfo.getCivUnits()
|
||||
val militaryUnits = civUnits.filter { it.getBaseUnit().unitType != UnitType.Civilian }.size
|
||||
val workers = civUnits.filter { it.name == CityConstructions.Worker }.size
|
||||
val cities = cityInfo.civInfo.cities.size
|
||||
|
||||
when {
|
||||
!buildableNotWonders.isEmpty() -> currentConstruction = buildableNotWonders.first()
|
||||
militaryUnits==0 -> trainCombatUnit(cityInfo)
|
||||
workers==0 -> currentConstruction = CityConstructions.Worker
|
||||
militaryUnits<cities -> trainCombatUnit(cityInfo)
|
||||
workers<cities -> currentConstruction = CityConstructions.Worker
|
||||
buildableWonders.isNotEmpty() -> currentConstruction = buildableWonders.getRandom()
|
||||
else -> trainCombatUnit(cityInfo)
|
||||
}
|
||||
|
||||
if (cityInfo.civInfo == cityInfo.civInfo.gameInfo.getPlayerCivilization())
|
||||
cityInfo.civInfo.addNotification("Work has started on $currentConstruction", cityInfo.location)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.unciv.logic
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
|
@ -1,6 +1,6 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.Automation
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.stats.Stats
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.Automation
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.map.TileInfo
|
||||
|
||||
class CityExpansionManager {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.Automation
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.stats.Stats
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.unciv.logic.map
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.WorkerAutomation
|
||||
import com.unciv.logic.automation.WorkerAutomation
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.Unit
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.unciv.ui.worldscreen.unit
|
|||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.unciv.UnCivGame
|
||||
import com.unciv.logic.WorkerAutomation
|
||||
import com.unciv.logic.automation.WorkerAutomation
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.gamebasics.Building
|
||||
|
|
Loading…
Reference in a new issue