Merge pull request #312 from ninjatao/auto_specialist

Allow auto assign population to specialists.
This commit is contained in:
yairm210 2018-12-10 10:52:57 +02:00 committed by GitHub
commit 4ea09a71b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 18 deletions

View File

@ -4,21 +4,39 @@ import com.badlogic.gdx.graphics.Color
import com.unciv.logic.battle.CityCombatant import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.CityInfo import com.unciv.logic.city.CityInfo
import com.unciv.logic.city.CityStats
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.gamebasics.unit.BaseUnit import com.unciv.models.gamebasics.unit.BaseUnit
import com.unciv.models.gamebasics.unit.UnitType import com.unciv.models.gamebasics.unit.UnitType
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats
import com.unciv.ui.utils.getRandom import com.unciv.ui.utils.getRandom
import kotlin.math.max import kotlin.math.max
import kotlin.math.sqrt import kotlin.math.sqrt
class Automation { class Automation {
internal fun rankTile(tile: TileInfo, civInfo: CivilizationInfo): Float { internal fun rankTile(tile: TileInfo?, civInfo: CivilizationInfo): Float {
if (tile == null) return 0.0f
val stats = tile.getTileStats(null, civInfo) val stats = tile.getTileStats(null, civInfo)
var rank = rankStatsValue(stats, civInfo)
if (tile.improvement == null) rank += 0.5f // improvement potential!
if (tile.hasViewableResource(civInfo)) rank += 1.0f
return rank
}
internal fun rankSpecialist(stats: Stats?, civInfo: CivilizationInfo): Float {
if (stats == null) return 0.0f
var rank = rankStatsValue(stats, civInfo)
rank += 0.3f //GPP bonus
return rank
}
fun rankStatsValue(stats: Stats, civInfo: CivilizationInfo): Float {
var rank = 0.0f var rank = 0.0f
if (stats.food <= 2) rank += stats.food if (stats.food <= 2) rank += (stats.food * 1.2f) //food get more value to kepp city growing
else rank += (2 + (stats.food - 2) / 2) // 1 point for each food up to 2, from there on half a point else rank += (2.4f + (stats.food - 2) / 2) // 1.2 point for each food up to 2, from there on half a point
if (civInfo.gold < 0 && civInfo.getStatsForNextTurn().gold <= 0) rank += stats.gold if (civInfo.gold < 0 && civInfo.getStatsForNextTurn().gold <= 0) rank += stats.gold
else rank += stats.gold / 2 else rank += stats.gold / 2
@ -26,8 +44,6 @@ class Automation {
rank += stats.production rank += stats.production
rank += stats.science rank += stats.science
rank += stats.culture rank += stats.culture
if (tile.improvement == null) rank += 0.5f // improvement potential!
if (tile.hasViewableResource(civInfo)) rank += 1.0f
return rank return rank
} }

View File

@ -117,6 +117,7 @@ class NextTurnAutomation{
private fun reassignWorkedTiles(civInfo: CivilizationInfo) { private fun reassignWorkedTiles(civInfo: CivilizationInfo) {
for (city in civInfo.cities) { for (city in civInfo.cities) {
city.workedTiles.clear() city.workedTiles.clear()
city.population.specialists.clear()
(0..city.population.population).forEach { city.population.autoAssignPopulation() } (0..city.population.population).forEach { city.population.autoAssignPopulation() }
Automation().chooseNextConstruction(city.cityConstructions) Automation().chooseNextConstruction(city.cityConstructions)
if (city.health < city.getMaxHealth()) if (city.health < city.getMaxHealth())

View File

@ -3,6 +3,7 @@ package com.unciv.logic.city
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.logic.automation.Automation import com.unciv.logic.automation.Automation
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.ui.utils.getRandom import com.unciv.ui.utils.getRandom
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -70,12 +71,36 @@ class PopulationManager {
internal fun autoAssignPopulation() { internal fun autoAssignPopulation() {
if(getFreePopulation()==0) return if(getFreePopulation()==0) return
val toWork: TileInfo? = cityInfo.getTiles()
//evaluate tiles
val bestTile: TileInfo? = cityInfo.getTiles()
.filter { it.arialDistanceTo(cityInfo.getCenterTile()) <= 3 } .filter { it.arialDistanceTo(cityInfo.getCenterTile()) <= 3 }
.filterNot { cityInfo.workedTiles.contains(it.position) || cityInfo.location==it.position} .filterNot { cityInfo.workedTiles.contains(it.position) || cityInfo.location==it.position}
.maxBy { Automation().rankTile(it,cityInfo.civInfo) } .maxBy { Automation().rankTile(it,cityInfo.civInfo) }
if (toWork != null) // This is when we've run out of tiles! val valueBestTile = Automation().rankTile(bestTile, cityInfo.civInfo)
cityInfo.workedTiles.add(toWork.position)
//evaluate specialists
val maxSpecialistsMap = getMaxSpecialists().toHashMap()
val policies = cityInfo.civInfo.policies.adoptedPolicies
val bestJob: Stat? = specialists.toHashMap()
.filter {maxSpecialistsMap.containsKey(it.key) && it.value < maxSpecialistsMap[it.key]!!}
.map {it.key}
.maxBy { Automation().rankSpecialist(cityInfo.cityStats.getStatsOfSpecialist(it, policies), cityInfo.civInfo) }
var valueBestSpecialist = 0f
if (bestJob != null) {
val specialistStats = cityInfo.cityStats.getStatsOfSpecialist(bestJob, policies)
valueBestSpecialist = Automation().rankSpecialist(specialistStats, cityInfo.civInfo)
}
//assign population
if (valueBestTile > valueBestSpecialist) {
if (bestTile != null)
cityInfo.workedTiles.add(bestTile.position)
} else {
if (bestJob != null) {
specialists.add(bestJob, 1f)
}
}
} }
fun unassignExtraPopulation() { fun unassignExtraPopulation() {
@ -87,16 +112,29 @@ class PopulationManager {
} }
while (getFreePopulation()<0) { while (getFreePopulation()<0) {
if(getNumberOfSpecialists()>0){ //evaluate tiles
val specialistTypeToUnassign = specialists.toHashMap().filter { it.value>0 }.map { it.key }.getRandom() val worstWorkedTile: TileInfo? = cityInfo.workedTiles
specialists.add(specialistTypeToUnassign,-1f) .asSequence()
} .map { cityInfo.tileMap[it] }
else { .minBy {Automation().rankTile(it, cityInfo.civInfo)}
val lowestRankedWorkedTile = cityInfo.workedTiles val valueWorstTile = Automation().rankTile(worstWorkedTile, cityInfo.civInfo)
.asSequence()
.map { cityInfo.tileMap[it] } //evaluate specialists
.minBy { Automation().rankTile(it, cityInfo.civInfo) }!! val policies = cityInfo.civInfo.policies.adoptedPolicies
cityInfo.workedTiles.remove(lowestRankedWorkedTile.position) val worstJob: Stat? = specialists.toHashMap()
.filter { it.value > 0 }
.map {it.key}
.minBy { Automation().rankSpecialist(cityInfo.cityStats.getStatsOfSpecialist(it, policies), cityInfo.civInfo) }
var valueWorstSpecialist = 0f
if (worstJob != null)
valueWorstSpecialist = Automation().rankSpecialist(cityInfo.cityStats.getStatsOfSpecialist(worstJob, policies), cityInfo.civInfo)
//un-assign population
if ((valueWorstTile < valueWorstSpecialist && worstWorkedTile != null)
|| worstJob == null) {
cityInfo.workedTiles.remove(worstWorkedTile!!.position)
} else {
specialists.add(worstJob!!, -1f)
} }
} }

View File

@ -15,6 +15,15 @@ open class Stats() {
setStats(hashMap) setStats(hashMap)
} }
fun clear() {
production = 0f
food = 0f
gold = 0f
science = 0f
culture = 0f
happiness = 0f
}
fun add(other: Stats) { fun add(other: Stats) {
// Doing this through the hashmap is nicer code but is SUPER INEFFICIENT! // Doing this through the hashmap is nicer code but is SUPER INEFFICIENT!
production += other.production production += other.production