AI trade values: Gold vs. Gold-per-turn

Barathor

Emperor
Joined
May 7, 2011
Messages
1,202
Usually, getting money now is better than getting the same amount later. Yet, the AI doesn't believe that, and they’ll actually give you MORE lump sum gold than they will gold per turn.

In the past, I tinkered with the XML values for both gold and GPT trade value. They didn't seem to affect the AI the way I intended it to so I let it go and figured there was something else hard coded in the source files. Once the source files were released, I eventually revisited this and peeked into them. It turns out, that the AI applies an extra 40% value to their outgoing GPT deals. There’s also a comment there which states that it exists to combat exploits. Now one of my questions is: Which GPT exploit? I’m asking sincerely because I’m unaware of it, if it exists.

If anything, I would think the opposite true. That, if one wanted to, you would exploit the AI by siphoning out their gold total and gaining it for yourself. Especially in this game, where you can use lump sums of gold for so many useful things when needed (rushing most productions, research agreements, city-states, etc.). Even worse, as many know, you can trade items that are dependent on an active agreement per turn (like resources) for a lump sum of gold and then declare war to cancel the trade and keep the gold.

So, is that modifier which applies an extra 40% a relic leftover from Civ 4’s source file? My memory is a bit hazy regarding Civ 4, but I think one tactic was to milk the AI of its GPT to hamper its research and such via the slider (and there was no lump sum gold trading). It could of also existed to just help the AI from getting pounced on and traded with on a turn that it decided to nudge its slider down a notch and increase its GPT, so it all doesn't get taken away preventing their slider from going back up.

So, do you guys think that the extra 40% value of “exploit protection” applied to the AI's own GPT is unnecessary and maybe even backwards in Civ 5?

Is there any way to work around this possibly unneeded value modifier via Lua? I would hate to have to compile a whole new customized DLL just for this change. I like to keep things simple and modular, when it’s possible.

- - - - - - - -

1) I know there are exploits in this game involving lump sum trades, are there any for gold-per-turn trades like the source file states? Or is this just a relic left over from past Civ games?

2) Would removing this AI GPT value modifier, or applying another effect to counter it, at least be possible in Lua without having to compile a new DLL?

3) In Civ 5, where sums of gold are flexible and can be used for so many useful things, do you think having GPT at only 75% the value of lump sums of gold enough? What about 50%? Example: 240 gold would equal the “value” of 16 gold per turn. So, one could either trade for a lump sum or do a GPT trade where you’ll get that lump sum in 15 turns and then again in another 15 turns. Also, keep in mind, a trade agreement is the length of 6% of a game’s total turns.

4) Do you think lump sums of gold should have an AI value modifier applied to it to deter exploits? Personally, I would like to keep things “natural”, if possible, and hope that fairer gold-per-turn trades sway traders from lump sums more easily. Though, actually, probably the only way to deter lump sum trade exploits is to disable them, because no matter how high the AI values them and how little they offer, a player still gets gold for nothing once the trade is canceled.

- - - - - - - -

Anyway, I've been meaning to throw this out here for a time because I wanted to see what you guys think about it, the ones that care anyway. And also, how you guys think we could fix it if it’s in need of it. (And if it is in need of fixing, hopefully Firaxis is watching too and can maybe address it in a patch or something so it doesn’t have to be modded.)

The code is within CvDealAI.cpp (highlighted in red):

Code:
int CvDealAI::GetGPTforForValueExchange(int iGPTorValue, bool bNumGPTFromValue, int iNumTurns, bool bFromMe, PlayerTypes eOtherPlayer, bool bUseEvenValue, bool bRoundUp)
{
	CvAssertMsg(GetPlayer()->GetID() != eOtherPlayer, "DEAL_AI: Trying to check value of GPT with oneself.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.");

	int iPreCalculationDurationMultiplier;
	int iMultiplier;
	int iDivisor;
	int iPostCalculationDurationDivider;

	// We passed in Value, we want to know how much GPT we get for it
	if(bNumGPTFromValue)
	{
		iPreCalculationDurationMultiplier = 1;
		iMultiplier = 100;
		iDivisor = /*80*/ GC.getEACH_GOLD_PER_TURN_VALUE_PERCENT();
		iPostCalculationDurationDivider = iNumTurns;	// Divide value by number of turns to get GPT

		// Example: want amount of GPT for 100 value.
		// 100v * 1 = 100
		// 100 * 100 / 80 = 125
		// 125 / 20 turns = 6.25GPT
	}
	// We passed in an amount of GPT, we want to know how much it's worth
	else
	{
		iPreCalculationDurationMultiplier = iNumTurns;	// Multiply GPT by number of turns to get value
		iMultiplier = /*80*/ GC.getEACH_GOLD_PER_TURN_VALUE_PERCENT();
		iDivisor = 100;
		iPostCalculationDurationDivider = 1;

		// Example: want value for 6 GPT
		// 6GPT * 20 turns = 120
		// 120 * 80 / 100 = 96
		// 96 / 1 = 96v
	}

	// Convert based on the rules above
	int iReturnValue = iGPTorValue * iPreCalculationDurationMultiplier;
	iReturnValue *= iMultiplier;

	// While we have a big number shall we apply some modifiers to it?
	if(bFromMe)
	{
		[COLOR="Red"][B]// AI values it's GPT more highly because it's easy to exploit this
		iReturnValue *= 140;
		iReturnValue /= 100;[/B][/COLOR]

		int iModifier;

		// Approach is important
		switch(GetPlayer()->GetDiplomacyAI()->GetMajorCivApproach(eOtherPlayer, /*bHideTrueFeelings*/ true))
		{
		case MAJOR_CIV_APPROACH_HOSTILE:
			iModifier = 150;
			break;
		case MAJOR_CIV_APPROACH_GUARDED:
			iModifier = 110;
			break;
		case MAJOR_CIV_APPROACH_AFRAID:
			iModifier = 100;
			break;
		case MAJOR_CIV_APPROACH_FRIENDLY:
			iModifier = 100;
			break;
		case MAJOR_CIV_APPROACH_NEUTRAL:
			iModifier = 100;
			break;
		default:
			CvAssertMsg(false, "DEAL_AI: AI player has no valid Approach for Gold valuation.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.")
			iModifier = 100;
			break;
		}

		// See whether we should multiply or divide
		if(!bNumGPTFromValue)
		{
			iReturnValue *= iModifier;
			iReturnValue /= 100;
		}
		else
		{
			iReturnValue *= 100;
			iReturnValue /= iModifier;
		}

		// Opinion also matters
		switch(GetPlayer()->GetDiplomacyAI()->GetMajorCivOpinion(eOtherPlayer))
		{
		case MAJOR_CIV_OPINION_ALLY:
			iModifier = 100;
			break;
		case MAJOR_CIV_OPINION_FRIEND:
			iModifier = 100;
			break;
		case MAJOR_CIV_OPINION_FAVORABLE:
			iModifier = 100;
			break;
		case MAJOR_CIV_OPINION_NEUTRAL:
			iModifier = 100;
			break;
		case MAJOR_CIV_OPINION_COMPETITOR:
			iModifier = 115;
			break;
		case MAJOR_CIV_OPINION_ENEMY:
			iModifier = 140;
			break;
		case MAJOR_CIV_OPINION_UNFORGIVABLE:
			iModifier = 200;
			break;
		default:
			CvAssertMsg(false, "DEAL_AI: AI player has no valid Opinion for Gold valuation.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.")
			iModifier = 100;
			break;
		}

		// See whether we should multiply or divide
		if(!bNumGPTFromValue)
		{
			iReturnValue *= iModifier;
			iReturnValue /= 100;
		}
		else
		{
			iReturnValue *= 100;
			iReturnValue /= iModifier;
		}
	}

	// Sometimes we want to round up.  Let's say a the AI offers a deal to the human.  We have to ensure that the human can also offer that deal back and the AI will accept (and vice versa)
	if(bRoundUp)
	{
		iReturnValue += 99;
	}

	iReturnValue /= iDivisor;

	iReturnValue /= iPostCalculationDurationDivider;

	// Are we trying to find the middle point between what we think this item is worth and what another player thinks it's worth?
	if(bUseEvenValue)
	{
		iReturnValue += GET_PLAYER(eOtherPlayer).GetDealAI()->GetGPTforForValueExchange(iGPTorValue, bNumGPTFromValue, iNumTurns, !bFromMe, GetPlayer()->GetID(), /*bUseEvenValue*/ false, bRoundUp);

		iReturnValue /= 2;
	}

	return iReturnValue;
}
 
Top Bottom