Organized automation classes

This commit is contained in:
Yair Morgenstern 2018-05-06 11:48:01 +03:00
parent 5d4003ecbf
commit 4e699f917e
10 changed files with 120 additions and 110 deletions

View file

@ -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 {

View file

@ -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

View 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)
}
}
}

View file

@ -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)
}
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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