AI trading uses gold inflation (#10370)

* Fixed cities being sold for at most 1000 gold

* Replaced evaluateBuy/SellCost with evaluateBuy/SellCostWithInflation

* Fixed goldInflation being multiplied instead of divided

* Increased inflation modifier, lowering it's impact

* Changed inflation values to be lower

* Moved return value to be inline again (it was moved for testing)
This commit is contained in:
Oskar Niesen
2023-10-29 11:58:59 -05:00
committed by GitHub
parent bd7db9b21a
commit cb661182ec
2 changed files with 33 additions and 8 deletions

View File

@ -175,7 +175,7 @@ object NextTurnAutomation {
if (offer.type == TradeType.Treaty) if (offer.type == TradeType.Treaty)
continue // Don't try to counter with a defensive pact or research pact continue // Don't try to counter with a defensive pact or research pact
val value = evaluation.evaluateBuyCost(offer, civInfo, otherCiv) val value = evaluation.evaluateBuyCostWithInflation(offer, civInfo, otherCiv)
if (value > 0) if (value > 0)
potentialAsks[offer] = value potentialAsks[offer] = value
} }
@ -204,7 +204,7 @@ object NextTurnAutomation {
// Remove 1 amount as long as doing so does not change the price // Remove 1 amount as long as doing so does not change the price
val originalValue = counterofferAsks[ask]!! val originalValue = counterofferAsks[ask]!!
while (ask.amount > 1 while (ask.amount > 1
&& originalValue == evaluation.evaluateBuyCost( && originalValue == evaluation.evaluateBuyCostWithInflation(
TradeOffer(ask.name, ask.type, ask.amount - 1, ask.duration), TradeOffer(ask.name, ask.type, ask.amount - 1, ask.duration),
civInfo, otherCiv) ) { civInfo, otherCiv) ) {
ask.amount-- ask.amount--
@ -216,7 +216,7 @@ object NextTurnAutomation {
for (goldAsk in counterofferAsks.keys for (goldAsk in counterofferAsks.keys
.filter { it.type == TradeType.Gold_Per_Turn || it.type == TradeType.Gold } .filter { it.type == TradeType.Gold_Per_Turn || it.type == TradeType.Gold }
.sortedByDescending { it.type.ordinal }) { // Do GPT first .sortedByDescending { it.type.ordinal }) { // Do GPT first
val valueOfOne = evaluation.evaluateBuyCost(TradeOffer(goldAsk.name, goldAsk.type, 1, goldAsk.duration), civInfo, otherCiv) val valueOfOne = evaluation.evaluateBuyCostWithInflation(TradeOffer(goldAsk.name, goldAsk.type, 1, goldAsk.duration), civInfo, otherCiv)
val amountCanBeRemoved = deltaInOurFavor / valueOfOne val amountCanBeRemoved = deltaInOurFavor / valueOfOne
if (amountCanBeRemoved >= goldAsk.amount) { if (amountCanBeRemoved >= goldAsk.amount) {
deltaInOurFavor -= counterofferAsks[goldAsk]!! deltaInOurFavor -= counterofferAsks[goldAsk]!!
@ -236,7 +236,7 @@ object NextTurnAutomation {
.sortedByDescending { it.type.ordinal }) { .sortedByDescending { it.type.ordinal }) {
if (tradeLogic.currentTrade.theirOffers.none { it.type == ourGold.type } && if (tradeLogic.currentTrade.theirOffers.none { it.type == ourGold.type } &&
counterofferAsks.keys.none { it.type == ourGold.type } ) { counterofferAsks.keys.none { it.type == ourGold.type } ) {
val valueOfOne = evaluation.evaluateSellCost(TradeOffer(ourGold.name, ourGold.type, 1, ourGold.duration), civInfo, otherCiv) val valueOfOne = evaluation.evaluateSellCostWithInflation(TradeOffer(ourGold.name, ourGold.type, 1, ourGold.duration), civInfo, otherCiv)
val amountToGive = min(deltaInOurFavor / valueOfOne, ourGold.amount) val amountToGive = min(deltaInOurFavor / valueOfOne, ourGold.amount)
deltaInOurFavor -= amountToGive * valueOfOne deltaInOurFavor -= amountToGive * valueOfOne
if (amountToGive > 0) { if (amountToGive > 0) {

View File

@ -6,7 +6,6 @@ import com.unciv.logic.automation.ThreatLevel
import com.unciv.logic.automation.civilization.NextTurnAutomation import com.unciv.logic.automation.civilization.NextTurnAutomation
import com.unciv.logic.city.City import com.unciv.logic.city.City
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.ModOptionsConstants
@ -73,9 +72,9 @@ class TradeEvaluation {
val sumOfTheirOffers = trade.theirOffers.asSequence() val sumOfTheirOffers = trade.theirOffers.asSequence()
.filter { it.type != TradeType.Treaty } // since treaties should only be evaluated once for 2 sides .filter { it.type != TradeType.Treaty } // since treaties should only be evaluated once for 2 sides
.map { evaluateBuyCost(it, evaluator, tradePartner) }.sum() .map { evaluateBuyCostWithInflation(it, evaluator, tradePartner) }.sum()
var sumOfOurOffers = trade.ourOffers.sumOf { evaluateSellCost(it, evaluator, tradePartner) } var sumOfOurOffers = trade.ourOffers.sumOf { evaluateSellCostWithInflation(it, evaluator, tradePartner) }
val relationshipLevel = evaluator.getDiplomacyManager(tradePartner).relationshipIgnoreAfraid() val relationshipLevel = evaluator.getDiplomacyManager(tradePartner).relationshipIgnoreAfraid()
// If we're making a peace treaty, don't try to up the bargain for people you don't like. // If we're making a peace treaty, don't try to up the bargain for people you don't like.
@ -95,6 +94,12 @@ class TradeEvaluation {
return sumOfTheirOffers - sumOfOurOffers return sumOfTheirOffers - sumOfOurOffers
} }
fun evaluateBuyCostWithInflation(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
if (offer.type != TradeType.Gold && offer.type != TradeType.Gold_Per_Turn)
return (evaluateBuyCost(offer, civInfo, tradePartner) / getGoldInflation(civInfo)).toInt()
return evaluateBuyCost(offer, civInfo, tradePartner)
}
fun evaluateBuyCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int { fun evaluateBuyCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
when (offer.type) { when (offer.type) {
TradeType.Gold -> return offer.amount TradeType.Gold -> return offer.amount
@ -198,6 +203,12 @@ class TradeEvaluation {
} }
fun evaluateSellCostWithInflation(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
if (offer.type != TradeType.Gold && offer.type != TradeType.Gold_Per_Turn)
return (evaluateSellCost(offer, civInfo, tradePartner) / getGoldInflation(civInfo)).toInt()
return evaluateSellCost(offer, civInfo, tradePartner)
}
fun evaluateSellCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int { fun evaluateSellCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
when (offer.type) { when (offer.type) {
TradeType.Gold -> return offer.amount TradeType.Gold -> return offer.amount
@ -275,7 +286,7 @@ class TradeEvaluation {
val stats = city.cityStats.currentCityStats val stats = city.cityStats.currentCityStats
val sumOfStats = val sumOfStats =
stats.culture + stats.gold + stats.science + stats.production + stats.happiness + stats.food + distanceBonus stats.culture + stats.gold + stats.science + stats.production + stats.happiness + stats.food + distanceBonus
return min(sumOfStats.toInt() * 100, 1000) return (sumOfStats.toInt() * 100).coerceAtLeast(1000)
} }
TradeType.Agreement -> { TradeType.Agreement -> {
if (offer.name == Constants.openBorders) { if (offer.name == Constants.openBorders) {
@ -292,6 +303,20 @@ class TradeEvaluation {
} }
} }
/**
* This returns how much one gold is worth now in comparison to starting out the game
* Gold is worth less as the civilization has a higher income
*/
fun getGoldInflation(civInfo: Civilization): Double {
val modifier = 1000.0
val goldPerTurn = civInfo.stats.statsForNextTurn.gold.toDouble()
// To visualise the function, plug this into a 2d graphing calculator \frac{1000}{x^{1.2}+1.11*1000}
// Goes from 1 at GPT = 0 to .834 at GPT = 100, .296 at GPT = 1000 and 0.116 at GPT = 10000
// The current value of gold will never go below 10% or the .1f that it is set to
// So this does not scale off to infinity
return modifier / (goldPerTurn.pow(1.2).coerceAtLeast(0.0) + (1.11f * modifier)) + .1f
}
/** This code returns a positive value if the city is significantly far away from the capital /** This code returns a positive value if the city is significantly far away from the capital
* and given how this method is used this ends up making such cities more expensive. That's how * and given how this method is used this ends up making such cities more expensive. That's how
* I found it. I'm not sure it makes sense. One might also find arguments why cities closer to * I found it. I'm not sure it makes sense. One might also find arguments why cities closer to