mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-04 06:33:32 +07:00
Force ranking, bullying improvements (#5102)
* Proper demand tribute thresholds * Proper calculation for Force rankings * debug strings * use new force ranking for AI threatAssessment * use new force ranking for AI threatAssessment, pt 2
This commit is contained in:
parent
486e2a7a8a
commit
2ca42a705f
@ -187,7 +187,7 @@ The unique luxury is one of: =
|
||||
|
||||
Demand Tribute =
|
||||
Tribute Willingness =
|
||||
>0 to take gold, >30 and size 4 city for worker =
|
||||
At least 0 to take gold, at least 30 and size 4 city for worker =
|
||||
Major Civ =
|
||||
No Cities =
|
||||
Base value =
|
||||
|
@ -8,6 +8,7 @@ import com.unciv.models.ruleset.VictoryType
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.victoryscreen.RankingType
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -99,17 +100,9 @@ object Automation {
|
||||
return chosenUnit.name
|
||||
}
|
||||
|
||||
fun evaluateCombatStrength(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.baseUnit().strength, it.baseUnit().rangedStrength)) }.sum()
|
||||
return sqrt(unitStrength.toDouble()).toInt() + 1 //avoid 0, because we divide by the result
|
||||
}
|
||||
|
||||
fun threatAssessment(assessor: CivilizationInfo, assessed: CivilizationInfo): ThreatLevel {
|
||||
val powerLevelComparison =
|
||||
evaluateCombatStrength(assessed) / evaluateCombatStrength(assessor).toFloat()
|
||||
assessed.getStatForRanking(RankingType.Force) / assessor.getStatForRanking(RankingType.Force).toFloat()
|
||||
return when {
|
||||
powerLevelComparison > 2 -> ThreatLevel.VeryHigh
|
||||
powerLevelComparison > 1.5f -> ThreatLevel.High
|
||||
|
@ -23,6 +23,7 @@ import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.pickerscreens.BeliefContainer
|
||||
import com.unciv.ui.victoryscreen.RankingType
|
||||
import kotlin.math.min
|
||||
|
||||
object NextTurnAutomation {
|
||||
@ -185,6 +186,9 @@ object NextTurnAutomation {
|
||||
if (distance < 20)
|
||||
value -= (20 - distance) / 4
|
||||
}
|
||||
else if (civInfo.victoryType() == VictoryType.Diplomatic) {
|
||||
value += 5 // Generally be friendly
|
||||
}
|
||||
if (civInfo.gold < 100) {
|
||||
// Consider bullying for cash
|
||||
value -= 5
|
||||
@ -229,7 +233,7 @@ object NextTurnAutomation {
|
||||
if(diplomacyManager.relationshipLevel() < RelationshipLevel.Friend
|
||||
&& diplomacyManager.diplomaticStatus == DiplomaticStatus.Peace
|
||||
&& valueCityStateAlliance(civInfo, state) <= 0
|
||||
&& state.getTributeWillingness(civInfo) > 0) {
|
||||
&& state.getTributeWillingness(civInfo) >= 0) {
|
||||
if (state.getTributeWillingness(civInfo, demandingWorker = true) > 0)
|
||||
state.tributeWorker(civInfo)
|
||||
else
|
||||
@ -493,12 +497,12 @@ object NextTurnAutomation {
|
||||
}
|
||||
|
||||
private fun motivationToAttack(civInfo: CivilizationInfo, otherCiv: CivilizationInfo): Int {
|
||||
val ourCombatStrength = Automation.evaluateCombatStrength(civInfo).toFloat()
|
||||
var theirCombatStrength = Automation.evaluateCombatStrength(otherCiv)
|
||||
val ourCombatStrength = civInfo.getStatForRanking(RankingType.Force).toFloat()
|
||||
var theirCombatStrength = otherCiv.getStatForRanking(RankingType.Force).toFloat()
|
||||
|
||||
//for city-states, also consider there protectors
|
||||
if(otherCiv.isCityState() and otherCiv.getProtectorCivs().isNotEmpty()) {
|
||||
theirCombatStrength += otherCiv.getProtectorCivs().sumOf{Automation.evaluateCombatStrength(it)}
|
||||
theirCombatStrength += otherCiv.getProtectorCivs().sumOf{it.getStatForRanking(RankingType.Force)}
|
||||
}
|
||||
|
||||
if (theirCombatStrength > ourCombatStrength) return 0
|
||||
|
@ -231,15 +231,15 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
if (civInfo.getDiplomacyManager(demandingCiv).influence < -30)
|
||||
modifiers["Influence below -30"] = -300
|
||||
|
||||
// Slight optimization, we don't do the expensive stuff if we have no chance of getting a positive result
|
||||
if (!requireWholeList && modifiers.values.sum() <= -200)
|
||||
// Slight optimization, we don't do the expensive stuff if we have no chance of getting a >= 0 result
|
||||
if (!requireWholeList && modifiers.values.sum() < -200)
|
||||
return modifiers
|
||||
|
||||
val forceRank = civInfo.gameInfo.getAliveMajorCivs().sortedByDescending { it.getStatForRanking(
|
||||
RankingType.Force) }.indexOf(demandingCiv)
|
||||
modifiers["Military Rank"] = 100 - ((100 / civInfo.gameInfo.gameParameters.players.size) * forceRank)
|
||||
|
||||
if (!requireWholeList && modifiers.values.sum() <= -100)
|
||||
if (!requireWholeList && modifiers.values.sum() < -100)
|
||||
return modifiers
|
||||
|
||||
val bullyRange = max(5, civInfo.gameInfo.tileMap.tileMatrix.size / 10) // Longer range for larger maps
|
||||
|
@ -30,15 +30,13 @@ import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.getPlaceholderParameters
|
||||
import com.unciv.models.translations.getPlaceholderText
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.toPercent
|
||||
import com.unciv.ui.victoryscreen.RankingType
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.LinkedHashMap
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.*
|
||||
|
||||
class CivilizationInfo {
|
||||
|
||||
@ -94,6 +92,9 @@ class CivilizationInfo {
|
||||
@Transient
|
||||
val cityStateFunctions = CityStateFunctions(this)
|
||||
|
||||
@Transient
|
||||
private var cachedMilitaryMight = -1
|
||||
|
||||
var playerType = PlayerType.AI
|
||||
|
||||
/** Used in online multiplayer for human players */
|
||||
@ -497,13 +498,33 @@ class CivilizationInfo {
|
||||
RankingType.Production -> statsForNextTurn.production.roundToInt()
|
||||
RankingType.Gold -> gold
|
||||
RankingType.Territory -> cities.sumBy { it.tiles.size }
|
||||
RankingType.Force -> units.sumBy { it.baseUnit.strength }
|
||||
RankingType.Force -> getMilitaryMight()
|
||||
RankingType.Happiness -> getHappiness()
|
||||
RankingType.Technologies -> tech.researchedTechnologies.size
|
||||
RankingType.Culture -> policies.adoptedPolicies.count { !Policy.isBranchCompleteByName(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMilitaryMight(): Int {
|
||||
if (cachedMilitaryMight < 0)
|
||||
cachedMilitaryMight = calculateMilitaryMight()
|
||||
return cachedMilitaryMight
|
||||
}
|
||||
|
||||
private fun calculateMilitaryMight(): Int {
|
||||
var sum = 0
|
||||
for (unit in units) {
|
||||
sum += if (unit.baseUnit.isWaterUnit())
|
||||
unit.getForceEvaluation() / 2 // Really don't value water units highly
|
||||
else
|
||||
unit.getForceEvaluation()
|
||||
}
|
||||
val goldBonus = sqrt(gold.toFloat()).toPercent() // 2f if gold == 10000
|
||||
sum = (sum * min(goldBonus, 2f)).toInt() // 2f is max bonus
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
|
||||
fun getGreatPeople(): HashSet<BaseUnit> {
|
||||
val greatPeople = gameInfo.ruleSet.units.values.asSequence()
|
||||
@ -653,6 +674,8 @@ class CivilizationInfo {
|
||||
diplomacy.values.toList().forEach { it.nextTurn() } // we copy the diplomacy values so if it changes in-loop we won't crash
|
||||
updateAllyCivForCityState()
|
||||
updateHasActiveGreatWall()
|
||||
|
||||
cachedMilitaryMight = -1 // Reset so we don't use a value from a previous turn
|
||||
}
|
||||
|
||||
private fun startTurnFlags() {
|
||||
|
@ -9,6 +9,7 @@ import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.ui.victoryscreen.RankingType
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -259,12 +260,12 @@ class TradeEvaluation {
|
||||
}
|
||||
|
||||
fun evaluatePeaceCostForThem(ourCivilization: CivilizationInfo, otherCivilization: CivilizationInfo): Int {
|
||||
val ourCombatStrength = Automation.evaluateCombatStrength(ourCivilization)
|
||||
val theirCombatStrength = Automation.evaluateCombatStrength(otherCivilization)
|
||||
val ourCombatStrength = ourCivilization.getStatForRanking(RankingType.Force)
|
||||
val theirCombatStrength = otherCivilization.getStatForRanking(RankingType.Force)
|
||||
if (ourCombatStrength*1.5f >= theirCombatStrength && theirCombatStrength * 1.5f >= ourCombatStrength)
|
||||
return 0 // we're roughly equal, there's no huge power imbalance
|
||||
if (ourCombatStrength == 0) return -1000
|
||||
if (theirCombatStrength == 0) return 1000 // Chumps got no cities or units
|
||||
if (theirCombatStrength == 0) return 1000 // Chumps got no combat units
|
||||
if (ourCombatStrength > theirCombatStrength) {
|
||||
val absoluteAdvantage = ourCombatStrength - theirCombatStrength
|
||||
val percentageAdvantage = absoluteAdvantage / theirCombatStrength.toFloat()
|
||||
|
@ -431,17 +431,17 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
|
||||
diplomacyTable.addSeparator()
|
||||
diplomacyTable.add("Tribute Willingness".toLabel()).row()
|
||||
diplomacyTable.add(">0 to take gold, >30 and size 4 city for worker".toLabel()).row()
|
||||
val modifierTable = Table()
|
||||
val tributeModifiers = otherCiv.getTributeModifiers(viewingCiv, requireWholeList = true)
|
||||
for (item in tributeModifiers) {
|
||||
val color = if (item.value > 0) Color.GREEN else Color.RED
|
||||
val color = if (item.value >= 0) Color.GREEN else Color.RED
|
||||
modifierTable.add(item.key.toLabel(color))
|
||||
modifierTable.add(item.value.toString().toLabel(color)).row()
|
||||
}
|
||||
modifierTable.add("Sum:".toLabel())
|
||||
modifierTable.add(tributeModifiers.values.sum().toLabel()).row()
|
||||
diplomacyTable.add(modifierTable).row()
|
||||
diplomacyTable.add("At least 0 to take gold, at least 30 and size 4 city for worker".toLabel()).row()
|
||||
diplomacyTable.addSeparator()
|
||||
|
||||
val demandGoldButton = "Take [${otherCiv.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
|
||||
@ -451,7 +451,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
||||
}
|
||||
diplomacyTable.add(demandGoldButton).row()
|
||||
if (otherCiv.getTributeWillingness(viewingCiv, demandingWorker = false) <= 0) demandGoldButton.disable()
|
||||
if (otherCiv.getTributeWillingness(viewingCiv, demandingWorker = false) < 0) demandGoldButton.disable()
|
||||
|
||||
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
|
||||
demandWorkerButton.onClick {
|
||||
@ -460,7 +460,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
||||
}
|
||||
diplomacyTable.add(demandWorkerButton).row()
|
||||
if (otherCiv.getTributeWillingness(viewingCiv, demandingWorker = true) <= 0) demandWorkerButton.disable()
|
||||
if (otherCiv.getTributeWillingness(viewingCiv, demandingWorker = true) < 0) demandWorkerButton.disable()
|
||||
|
||||
val backButton = "Back".toTextButton()
|
||||
backButton.onClick {
|
||||
|
Loading…
Reference in New Issue
Block a user