45°38'N-13°47'E
Deity
Ok, I'm trying to analyze the code to hunt that "AI Diplo Declare War Trade" OOS; I suppose that this part of the code is the one where AI is trading other AI to make war (am I correct?); anyway here's the first part of the code:
So, going backwards from the bottom line: a random number is generated based on iDeclareWarTradeRand; this is taken directly from xml values in int iDeclareWarTradeRand = GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarTradeRand(); (most leaders have a value of 40, at least in AND). This iDeclareWarTradeRand is modified differently if iMinAtWarCounter has different values.
In turns, iMinAtWarCounter is calculated as above (or here below)
It shouldn't be that hard until now... either is iMinAtWarCounter or iAtWarCounter. Since iMinAtWarCounter = MAX_INT, the only variable is iAtWarCounter which depends on AI_getAtWarCounter and on AI_getWarPlan
Which leads me to
and
I'm not sure if the clues I'm following and my line of reasoning make sense until now and I'm not even sure how to go on from here. But while looking AI_getWarPlan entries in CvTeam.cpp, I've found the following function
There's a cached value which leads me to pay more attention to this function because I know caching is dangerous. It's true that at the end of the code there's a isHuman check for that cached value, if I'm not mistaken, but I'm not sure the OOS we've got involved human players. By the way, until now I've encountered OOS regarding humans only but can an OOS involve AI players only? Or should somehow a human player always be involved? Anyway, is that caching safe? Or should I place there a check like isNetworkMultiplayer (or something like that)?
Sorry for the long post, I'm writing down everything for reference and because it also helps me think.
If I can't come up with anything from that part of the code, I'll go up looking for other parts of AI_doDiplo, I suppose.
Code:
if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || !(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
{
PROFILE("CvPlayerAI::AI_doDiplo.DeclareWar");
int iDeclareWarTradeRand = GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarTradeRand();
int iMinAtWarCounter = MAX_INT;
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (atWar(((TeamTypes)iJ), getTeam()))
{
int iAtWarCounter = GET_TEAM(getTeam()).AI_getAtWarCounter((TeamTypes)iJ);
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iJ) == WARPLAN_DOGPILE)
{
iAtWarCounter *= 2;
iAtWarCounter += 5;
}
iMinAtWarCounter = std::min(iAtWarCounter, iMinAtWarCounter);
}
}
}
if (iMinAtWarCounter < 10)
{
iDeclareWarTradeRand *= iMinAtWarCounter;
iDeclareWarTradeRand /= 10;
iDeclareWarTradeRand ++;
}
if (iMinAtWarCounter < 4)
{
iDeclareWarTradeRand /= 4;
iDeclareWarTradeRand ++;
}
if (GC.getGameINLINE().getSorenRandNum(iDeclareWarTradeRand, "AI Diplo Declare War Trade") == 0)
{
So, going backwards from the bottom line: a random number is generated based on iDeclareWarTradeRand; this is taken directly from xml values in int iDeclareWarTradeRand = GC.getLeaderHeadInfo(getPersonalityType()).getDeclareWarTradeRand(); (most leaders have a value of 40, at least in AND). This iDeclareWarTradeRand is modified differently if iMinAtWarCounter has different values.
In turns, iMinAtWarCounter is calculated as above (or here below)
Code:
int iMinAtWarCounter = MAX_INT;
for (iJ = 0; iJ < MAX_CIV_TEAMS; iJ++)
{
if (GET_TEAM((TeamTypes)iJ).isAlive())
{
if (atWar(((TeamTypes)iJ), getTeam()))
{
int iAtWarCounter = GET_TEAM(getTeam()).AI_getAtWarCounter((TeamTypes)iJ);
if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iJ) == WARPLAN_DOGPILE)
{
iAtWarCounter *= 2;
iAtWarCounter += 5;
}
iMinAtWarCounter = std::min(iAtWarCounter, iMinAtWarCounter);
It shouldn't be that hard until now... either is iMinAtWarCounter or iAtWarCounter. Since iMinAtWarCounter = MAX_INT, the only variable is iAtWarCounter which depends on AI_getAtWarCounter and on AI_getWarPlan
Which leads me to
Code:
int CvTeamAI::AI_getAtWarCounter(TeamTypes eIndex) const
{
FAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
FAssertMsg(eIndex < MAX_TEAMS, "eIndex is expected to be within maximum bounds (invalid Index)");
return m_aiAtWarCounter[eIndex];
}
and
Code:
WarPlanTypes CvTeamAI::AI_getWarPlan(TeamTypes eIndex) const
{
FAssert(eIndex >= 0);
FAssert(eIndex < MAX_TEAMS);
FAssert(eIndex != getID() || m_aeWarPlan[eIndex] == NO_WARPLAN);
return m_aeWarPlan[eIndex];
}
I'm not sure if the clues I'm following and my line of reasoning make sense until now and I'm not even sure how to go on from here. But while looking AI_getWarPlan entries in CvTeam.cpp, I've found the following function
Code:
int CvTeamAI::AI_endWarVal(TeamTypes eTeam) const
{
PROFILE_FUNC();
int iValue;
FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
FAssertMsg(isAtWar(eTeam), "Current AI Team instance is expected to be at war with eTeam");
[COLOR="Red"] // Just evaluate once per turn and cache for the rest of that turn
if ( m_endWarValueCache[eTeam] != -1 )
{
return m_endWarValueCache[eTeam];
}[/COLOR]
iValue = 100;
iValue += (getNumCities() * 3);
iValue += (GET_TEAM(eTeam).getNumCities() * 3);
iValue += getTotalPopulation();
iValue += GET_TEAM(eTeam).getTotalPopulation();
iValue += (GET_TEAM(eTeam).AI_getWarSuccess(getID()) * 20);
int iOurPower = std::max(1, getPower(true));
int iTheirPower = std::max(1, GET_TEAM(eTeam).getDefensivePower());
iValue *= iTheirPower + 10;
iValue /= std::max(1, iOurPower + iTheirPower + 10);
WarPlanTypes eWarPlan = AI_getWarPlan(eTeam);
// if we are not human, do we want to continue war for strategic reasons?
// only check if our power is at least 120% of theirs
if (!isHuman() && iOurPower > ((120 * iTheirPower) / 100))
{
bool bDagger = false;
bool bAnyFinancialTrouble = false;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
{
if (GET_PLAYER((PlayerTypes)iI).AI_isDoStrategy(AI_STRATEGY_DAGGER))
{
bDagger = true;
}
if (GET_PLAYER((PlayerTypes)iI).AI_isFinancialTrouble())
{
bAnyFinancialTrouble = true;
}
}
}
}
// if dagger, value peace at 90% * power ratio
if (bDagger)
{
iValue *= 9 * iTheirPower;
iValue /= 10 * iOurPower;
}
// for now, we will always do the land mass check for domination
// if we have more than half the land, then value peace at 90% * land ratio
int iLandRatio = ((getTotalLand(true) * 100) / std::max(1, GET_TEAM(eTeam).getTotalLand(true)));
if (iLandRatio > 120)
{
iValue *= 9 * 100;
iValue /= 10 * iLandRatio;
}
// if in financial trouble, warmongers will continue the fight to make more money
if (bAnyFinancialTrouble)
{
switch (eWarPlan)
{
case WARPLAN_TOTAL:
// if we total warmonger, value peace at 70% * power ratio factor
if (bDagger || AI_maxWarRand() < 100)
{
iValue *= 7 * (5 * iTheirPower);
iValue /= 10 * (iOurPower + (4 * iTheirPower));
}
break;
case WARPLAN_LIMITED:
// if we limited warmonger, value peace at 70% * power ratio factor
if (AI_limitedWarRand() < 100)
{
iValue *= 7 * (5 * iTheirPower);
iValue /= 10 * (iOurPower + (4 * iTheirPower));
}
break;
case WARPLAN_DOGPILE:
// if we dogpile warmonger, value peace at 70% * power ratio factor
if (AI_dogpileWarRand() < 100)
{
iValue *= 7 * (5 * iTheirPower);
iValue /= 10 * (iOurPower + (4 * iTheirPower));
}
break;
}
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/19/10 jdog5000 */
/* */
/* War strategy AI, Victory Strategy AI */
/************************************************************************************************/
if( AI_isAnyMemberDoVictoryStrategy(AI_VICTORY_CULTURE4) )
{
iValue *= 4;
}
else if( AI_isAnyMemberDoVictoryStrategy(AI_VICTORY_CULTURE3) || AI_isAnyMemberDoVictoryStrategy(AI_VICTORY_SPACE4) )
{
iValue *= 2;
}
if ((!(isHuman()) && (eWarPlan == WARPLAN_TOTAL)) ||
(!(GET_TEAM(eTeam).isHuman()) && (GET_TEAM(eTeam).AI_getWarPlan(getID()) == WARPLAN_TOTAL)))
{
iValue *= 2;
}
else if ((!(isHuman()) && (eWarPlan == WARPLAN_DOGPILE) && (GET_TEAM(eTeam).getAtWarCount(true) > 1)) ||
(!(GET_TEAM(eTeam).isHuman()) && (GET_TEAM(eTeam).AI_getWarPlan(getID()) == WARPLAN_DOGPILE) && (getAtWarCount(true) > 1)))
{
iValue *= 3;
iValue /= 2;
}
// Do we have a big stack en route?
int iOurAttackers = 0;
for( int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++ )
{
if( GET_PLAYER((PlayerTypes)iPlayer).getTeam() == getID() )
{
iOurAttackers += GET_PLAYER((PlayerTypes)iPlayer).AI_enemyTargetMissions(eTeam);
}
}
int iTheirAttackers = 0;
CvArea* pLoopArea = NULL;
int iLoop;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
iTheirAttackers += countEnemyDangerByArea(pLoopArea, eTeam);
}
int iAttackerRatio = (100 * iOurAttackers) / std::max(1 + GC.getGameINLINE().getCurrentEra(), iTheirAttackers);
if( GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI) )
{
iValue *= 150;
iValue /= range(iAttackerRatio, 150, 900);
}
else
{
iValue *= 200;
iValue /= range(iAttackerRatio, 200, 600);
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
[COLOR="Red"] if (isHuman())
{
iValue = std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
}
m_endWarValueCache[eTeam] = iValue;[/COLOR]
return iValue;
}
There's a cached value which leads me to pay more attention to this function because I know caching is dangerous. It's true that at the end of the code there's a isHuman check for that cached value, if I'm not mistaken, but I'm not sure the OOS we've got involved human players. By the way, until now I've encountered OOS regarding humans only but can an OOS involve AI players only? Or should somehow a human player always be involved? Anyway, is that caching safe? Or should I place there a check like isNetworkMultiplayer (or something like that)?
Sorry for the long post, I'm writing down everything for reference and because it also helps me think.
