1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

AI trade valuation quest

Discussion in 'Civ4 - Strategy & Tips' started by Ghpstage, Dec 20, 2013.

  1. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    While its probably overly ambitious, being off work and bored for a while I thought about a pie in the sky idea I mentioned in Tachys game mechanics thread, that is figuring out how AI value everything in regards to trade. While parts have been ripped into before I don't think something of this scale has been done.

    So, I thought, in order to not clog up Tachys thread anymore I may aswell make a thread specifically about it, hopefully roping in some people to help, perhaps by looking through code themselves, spotting my mistakes, testing, commenting on how to improve the presentation, suggesting where I should look next, or ways I can test. General comments, criticisms and related questions welcomed of course.

    The stated purpose for this thread is to find and collect all the coding information needed to put numbers to trade deals, and hopefully explain just why the AI thinks corn for iron trades are a good deal if coming from an AI :lol:
    If theres anything else of interest related to these I will probably have a look at those too.
    Ideally this thread will eventually become a decent reference for trade matters.


    Thread Layout
    This thread is being made while much of the code-diving is being done rather than as a compilation of existing information, as a result the order of posting is going to reflect the order of functions I dive, which is likely to be illogical from a straight reading perspective. The plan is to link to other posts (not all from this thread or by me) where necessary to try and make it useable.
    I know my presentation at this point is poor, part of this is because is due to it being a work in progress, but also partly due to me being pretty bad at that kind of thing. This should improve when I need to use this thread myself in order to test my claims, I will try to make a summary of useful points and equations at the start of each post when that section has run its course and i'm convinced of its validity.

    I'm trying to colour code things for the most part, but I need to redo some things.
    Red will be for the primary two functions that govern overall trade, Blue for individual component value functions, yellow for components of components, green for other functions of interest. But red unbolded text is also used for some notes.

    Most of the code involved is found in either CvPlayerAI.cpp or CvTeamAI.cpp, some things cross into CvCityAI.cpp and other places, at some point i'll get around to sourcing each code extract.
    By its very nature this is going to be extremely code heavy, if anyones going to find it useful it'll be the beancounter types. Perhaps some useful generalities useful to anybody will come out of this, but i'm not going to promise anything.


    General Assumption of the Trade Table
    As far as I know right now, the functions evaluate each side of the trade table from the Buyers perspective (as in both parties are 'buying' the others offer), giving a numerical value for each side.

    There are two different minimum acceptable values to the AI, equal or greater than an AIs offer, and equal or greater than (1.1 * AIs offer).
    offer =>AI Offer is the norm for tradings, while offer =>1.1 * AIs offer apprently only applies to resources/gpt trade renewals.

    In general the human is going to give an AI more than the trade is worth, but fortunately the human does get some compensation for these unequal trades to AIs in the form of +diplo points for "fair trade", which has already been looked into by Tachywaxon link.
    Tachy also covered the mechanics of another trade related diplo factor, "You have shared your technological discoveries with us"

    The two parent functions governing all human-AI trades are;
    CvPlayerAI::AI_considerOffer
    Which is ultimately where the AI decides what trades with a human are acceptable, it also governs values for tribute and begging deals

    CvPlayerAI::AI_dealVal
    This is the utimate value function, all it does is add up the total value of everything on offer, the only part that acts slightly differently is AI_goldTradeValuePercent which is divided by 100 (giving 2 or 3) before being added. I'll spoiler the code for completion below,​
    Spoiler :
    Code:
    int CvPlayerAI::AI_dealVal(PlayerTypes ePlayer, const CLinkList<TradeData>* pList, bool bIgnoreAnnual, int iChange) const
    {
    	CLLNode<TradeData>* pNode;
    	CvCity* pCity;
    	int iValue;
    
    	FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
    
    	iValue = 0;
    
    	if (atWar(getTeam(), GET_PLAYER(ePlayer).getTeam()))
    	{
    		iValue += GET_TEAM(getTeam()).AI_endWarVal(GET_PLAYER(ePlayer).getTeam());
    	}
    
    	for (pNode = pList->head(); pNode; pNode = pList->next(pNode))
    	{
    		FAssertMsg(!(pNode->m_data.m_bHidden), "(pNode->m_data.m_bHidden) did not return false as expected");
    
    		switch (pNode->m_data.m_eItemType)
    		{
    		case TRADE_TECHNOLOGIES:
    			iValue += GET_TEAM(getTeam()).AI_techTradeVal((TechTypes)(pNode->m_data.m_iData), GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_RESOURCES:
    			if (!bIgnoreAnnual)
    			{
    				iValue += AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), ePlayer, iChange);
    			}
    			break;
    		case TRADE_CITIES:
    			pCity = GET_PLAYER(ePlayer).getCity(pNode->m_data.m_iData);
    			if (pCity != NULL)
    			{
    				iValue += AI_cityTradeVal(pCity);
    			}
    			break;
    		case TRADE_GOLD:
    			iValue += (pNode->m_data.m_iData * AI_goldTradeValuePercent()) / 100;
    			break;
    		case TRADE_GOLD_PER_TURN:
    			if (!bIgnoreAnnual)
    			{
    				iValue += AI_goldPerTurnTradeVal(pNode->m_data.m_iData);
    			}
    			break;
    		case TRADE_MAPS:
    			iValue += GET_TEAM(getTeam()).AI_mapTradeVal(GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_SURRENDER:
    			if (!bIgnoreAnnual)
    			{
    				iValue += GET_TEAM(getTeam()).AI_surrenderTradeVal(GET_PLAYER(ePlayer).getTeam());
    			}
    			break;
    		case TRADE_VASSAL:
    			if (!bIgnoreAnnual)
    			{
    				iValue += GET_TEAM(getTeam()).AI_vassalTradeVal(GET_PLAYER(ePlayer).getTeam());
    			}
    			break;
    		case TRADE_OPEN_BORDERS:
    			iValue += GET_TEAM(getTeam()).AI_openBordersTradeVal(GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_DEFENSIVE_PACT:
    			iValue += GET_TEAM(getTeam()).AI_defensivePactTradeVal(GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_PEACE:
    			iValue += GET_TEAM(getTeam()).AI_makePeaceTradeVal(((TeamTypes)(pNode->m_data.m_iData)), GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_WAR:
    			iValue += GET_TEAM(getTeam()).AI_declareWarTradeVal(((TeamTypes)(pNode->m_data.m_iData)), GET_PLAYER(ePlayer).getTeam());
    			break;
    		case TRADE_EMBARGO:
    			iValue += AI_stopTradingTradeVal(((TeamTypes)(pNode->m_data.m_iData)), ePlayer);
    			break;
    		case TRADE_CIVIC:
    			iValue += AI_civicTradeVal(((CivicTypes)(pNode->m_data.m_iData)), ePlayer);
    			break;
    		case TRADE_RELIGION:
    			iValue += AI_religionTradeVal(((ReligionTypes)(pNode->m_data.m_iData)), ePlayer);
    			break;
    		}
    	}
    
    	return iValue;
    }


    AI_techTradeVal
    AI_bonusTradeVal
    AI_cityTradeVal
    AI_goldTradeValuePercent
    AI_goldPerTurnTradeVal
    AI_mapTradeVal
    AI_makePeaceTradeVal
    AI_declareWarTradeVal
    AI_stopTradingTradeVal
    AI_civicTradeVal
    AI_religionTradeVal

    AI_endWarVal



    The four below are also in the dealval function, for trade table purposes we already know that you cannot trade anything for them in any way shape or form so their value must be equal. Open Borders and Defensive Pacts do have small values, (total city count of both teams for Open Borders, multiply that by 3 for Defensive Pacts) that could hypothetically trip diplomatic thresholds, but for reasonable use they are so small as to be safely ignored.
    Strangely AI_Vassaltradeval may aswell not exist at all, as it just calls up AI_surrenderTradeVal which is 0....
    This may be worth knowing in its own right, as it means that capitulating an AI doesn't eat into your war spoils
    .
    AI_surrenderTradeVal
    AI_vassalTradeVal
    AI_openBordersTradeVal
    AI_defensivePactTradeVal

    Some, if not all of the others have been done by others in the past, unfortunately I have't been able to find them so i've had to redo the work myself.

    Theres a couple of other related functions I may check out later if I get success with the main part, most to do with when the I does something based on the same value used in the tradeval functions. So far i'm thinking about these,

    CvPlayerAI::AI_commerceWeight
    This gives weights to the different types of commerce depending on AI strategy. It is called up in CvPlayerAI::AI_civicVal
    CvPlayerAI::AI_doPeace
    CvPlayerAI::AI_counterPropose
    This is what the AI goes through when you click "What would make this deal work?" Its another huge function.
    CvPlayerAI::AI_doReligion
    Controls AI state religion changes
    CvPlayerAI::AI_doCivics
    Controls AI civic swaps
    CvPlayerAI::AI_isFinancialTrouble
    Defines when an AI is in Financial trouble

    There are quite a few places where the code asks if the AI is running various strategies, for now at least i'm not going to touch that function with a barge pole, and if I ever do it'll get its own thread.


    To Do list

    Unlinked functions
    Testing for everything other than gold, GPT, map and tech values
    AI_cityTradeval - awaiting AI_bonusTradeval
    AI_declareWarTradeVal - Waiting to know what EstimateEndTurn is
    AI_religionTradeVal - need to figure out end of AI_bestReligion
    AI_endWarVal - quite a few bits and pieces to finish off
    Proofreading to see if the summaries match the post after editing
    AI_stopTradingTradeVal - deal value calcs
    AI_civicvalue
    Yellow links list
    Tidying everything up
     
  2. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    Summary for quick reference

    Below is code and Workings
    First few, all been done by other people in the past
    AI_goldTradeValuePercent
    Code:
    int CvPlayerAI::AI_goldTradeValuePercent() const
    {
    int iValue = 2;
    if (AI_isFinancialTrouble())
    {
    iValue += 1;
    }
    return 100 * iValue;
    } 
    Golds value is,
    TradeValue = 200, or 300 IF the AI is in Financial Trouble

    In the Dealval AI_goldTradeValuePercent is divided by 100 to give 2 or 3 value points per gold, which will be familiar quite a few people.

    AI_goldPerTurnTradeVal
    Code:
    int CvPlayerAI::AI_goldPerTurnTradeVal(int iGoldPerTurn) const
    {
    int iValue = iGoldPerTurn * GC.getDefineINT("PEACE_TREATY_LENGTH");
    iValue *= AI_goldTradeValuePercent();
    iValue /= 100;
    
    return iValue;
    } 
    GPTs value
    TradeValue = (GPT*10) multiplied by 2, or 3 IF seller is in Financial Trouble.
    Due to its impact on the values of gold and GPT, when an AI is in Financial Trouble you buy things from them on the cheap, but you make a loss selling things to them.

    AI_mapTradeVal
    Code:
    int CvTeamAI::AI_mapTradeVal(TeamTypes eTeam) const
    {
    	CvPlot* pLoopPlot;
    	int iValue;
    	int iI;
    
    	FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
    
    	iValue = 0;
    
    	for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
    	{
    		pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
    
    		if (!(pLoopPlot->isRevealed(getID(), false)) && pLoopPlot->isRevealed(eTeam, false))
    		{
    			if (pLoopPlot->isWater())
    			{
    				iValue++;
    			}
    			else
    			{
    				iValue += 5;
    			}
    		}
    	}
    
    	iValue /= 10;
    
    	if (GET_TEAM(eTeam).isVassal(getID()))
    	{
    		iValue /= 2;
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    A value is applied only to tiles that the buyer has not yet uncovered, remembering this,
    TradeValue = 5 * (Number of land tiles + Number of water tiles)


    The value rounds down to the nearest 10

    The DIPLOMACY_VALUE_REMAINDER bit at the end is from GlobalDefines xml and has a value of 10. It gives a minimum value of 10 points, which when converted to gold in most cases is 10/2=5 which is the typical minimum the AI asks for in most if not all trades.
     
  3. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    Summary for quick reference
    *Needs testing but is most probably right*

    Code and working below

    AI_makePeaceTradeVal
    This refers to bribing an AI (seller) to stop a war against someone else (target)
    Code:
    int CvTeamAI::AI_makePeaceTradeVal(TeamTypes ePeaceTeam, TeamTypes eTeam) const
    {
    	int iModifier;
    	int iValue;
    
    	FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
    	FAssertMsg(ePeaceTeam != getID(), "shouldn't call this function on ourselves");
    	FAssertMsg(GET_TEAM(ePeaceTeam).isAlive(), "GET_TEAM(ePeaceTeam).isAlive is expected to be true");
    	FAssertMsg(atWar(ePeaceTeam, eTeam), "eTeam should be at war with ePeaceTeam");
    
    	iValue = (50 + GC.getGameINLINE().getGameTurn());
    	iValue += ((GET_TEAM(eTeam).getNumCities() + GET_TEAM(ePeaceTeam).getNumCities()) * 8);
    
    	iModifier = 0;
    
    	switch ((GET_TEAM(eTeam).AI_getAttitude(ePeaceTeam) + GET_TEAM(ePeaceTeam).AI_getAttitude(eTeam)) / 2)
    	{
    	case ATTITUDE_FURIOUS:
    		iModifier += 400;
    		break;
    
    	case ATTITUDE_ANNOYED:
    		iModifier += 200;
    		break;
    
    	case ATTITUDE_CAUTIOUS:
    		iModifier += 100;
    		break;
    
    	case ATTITUDE_PLEASED:
    		iModifier += 50;
    		break;
    
    	case ATTITUDE_FRIENDLY:
    		break;
    
    	default:
    		FAssert(false);
    		break;
    	}
    
    	iValue *= std::max(0, (iModifier + 100));
    	iValue /= 100;
    
    	iValue *= 40;
    	iValue /= (GET_TEAM(eTeam).AI_getAtWarCounter(ePeaceTeam) + 10);
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    50 + gameturn + (sellers citycount + targets citycount * 8) = ivalue

    This is all multiplied by a modifier based on the average diplomatic opinion they have of each other, and just like when you have a vassal its the average of diplo levels not points, might take a while to work out if theres a lot of vassals in play :lol:

    But anyway, that average value gives the modifier value,
    Furious = 400
    Annoyed = 200
    Cautious = 100
    Pleased = 50
    Friendly = 0
    Though being at war its obviously more likely to be at the upper end.

    Next its ivalue * ((modifier+100)/100) = ivalue(2)

    This can be simplified to ivalue * modifier if you take the below instead.
    Furious = 5
    Annoyed = 3
    Cautious = 2
    Pleased = 1.5
    Friendly = 1

    Then,
    ivalue(2) * 40 / (length of war in turns + 10)

    Same minimum value of DIPLOMACY_VALUE_REMAINDER (10) as applies to maps
     
  4. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    Do not worry. I sure know people really want not clog up my threads. Learnt it the harsh way with my last triple write-ups.

    Good luck with the project. It's one of the most useful concept about trade.
     
  5. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    Summary

    Code and working below

    AI_techTradeVal
    Code:
    int CvTeamAI::AI_techTradeVal(TechTypes eTech, TeamTypes eTeam) const
    { / 
    	FAssert(eTeam != getID());
    	int iKnownCount;
    	int iPossibleKnownCount;
    	int iCost;
    	int iValue;
    	int iI;
    
    	iCost = std::max(0, (getResearchCost(eTech) - getResearchProgress(eTech)));
    
    	iValue = ((iCost * 3) / 2);
    
    	iKnownCount = 0;
    	iPossibleKnownCount = 0;
    
    	for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
    	{
    		if (GET_TEAM((TeamTypes)iI).isAlive())
    		{
    			if (iI != getID())
    			{
    				if (isHasMet((TeamTypes)iI))
    				{
    					if (GET_TEAM((TeamTypes)iI).isHasTech(eTech))
    					{
    						iKnownCount++;
    					}
    
    					iPossibleKnownCount++;
    				}
    			}
    		}
    	}
    
    	iValue += (((iCost / 2) * (iPossibleKnownCount - iKnownCount)) / iPossibleKnownCount);
    
    	iValue *= std::max(0, (GC.getTechInfo(eTech).getAITradeModifier() + 100));
    	iValue /= 100;
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    The only part of this i'm not entirely sure about is the beginning
    (getResearchCost(eTech) - getResearchProgress(eTech)
    While its obviously cost - investment on incomplete techs, i'm not sure how it reacts to different difficulty levels as the ResearchCost part is the noble difficuly (xml) cost.
    Unfortunately I don't know where 'ResearchProgress' is defined so i'm left to speculate that its modified to suit that.

    My guess for how to get this is
    But this will need testing


    The rest starts
    ivalue = 3 / 2 * icost (icost being what the above gives)

    ivalue(2) = (ivalue + icost / 2) * (Total number civs in game - number of civs with the tech ) / total number of civs in game
    The above line will just be ivalue(2) = ivalue when you know all AIs.

    and finally,
    ivalue(2) * (AITradeModifier + 100) /100



    Lol I just thought it didn't really belong there without either being completed, or specifically asked about :p
    Thanks :goodjob:

    EDIT - The list of techs with AITradeModifier value of 10 kindly provided by Tachy in the post below, the value for all other techs is 0
    As Tachy mentioned theres also a link in that post into where Fair Trade diplo points, and Tachys version of tech trade values.
     
  6. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    ^
    In the "Fair Trades Explained", I dabbled into tech values.

    Hence, here is the list of techs with 10%. Yes, they tend to be military.



    Feudalism
    Guild
    Military Tradition
    Ecology (Military-wise?)
    Electricity
    Fission
    Flight
    Advanced Flight
    Composites
    Stealth
    Genetics
    Fiber Optics SMF-28e
    Fusion
    Archery
    HBR
    Machinery
    Gunpower
    Rifling
    Steel
    Assembly Line
    Railroad
    Artillery
    Industrialism
    Rocketry
    Satellites
    Robotics


    Will you mind if I lurk around? I'll tone down my typical @$$holeries. Promises. :mischief:
     
  7. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    You should say in the intro those functions are calculated for both the AI and you. Then, it is substracted and the remainder (for whoever is the winner) needs to be paid to conclude the deal.

    For deal objects that is the same for both like peace deals, it is there is becomes blurry. Otherwise, for techs, gold, etc. It is rather giving itself.
     
  8. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    *Waiting for testing*

    AI_cityTradeVal
    While this can't be traded in the normal sense, it can be 'purchased' using war success and whatever else matters in AI_endwarval
    Code:
    int CvPlayerAI::AI_cityTradeVal(CvCity* pCity) const
    {
    	CvPlot* pLoopPlot;
    	int iValue;
    	int iI;
    
    	FAssert(pCity->getOwnerINLINE() != getID());
    
    	iValue = 300;
    
    	iValue += (pCity->getPopulation() * 50);
    
    	iValue += (pCity->getCultureLevel() * 200);
    
    	iValue += (((((pCity->getPopulation() * 50) + GC.getGameINLINE().getElapsedGameTurns() + 100) * 4) * pCity->plot()->calculateCulturePercent(pCity->getOwnerINLINE())) / 100);
    
    	for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
    	{
    		pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
    
    		if (pLoopPlot != NULL)
    		{
    			if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
    			{
    				iValue += (AI_bonusVal(pLoopPlot->getBonusType(getTeam())) * 10);
    			}
    		}
    	}
    
    	if (!(pCity->isEverOwned(getID())))
    	{
    		iValue *= 3;
    		iValue /= 2;
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    300 + (50 * city pop) + (200* city cuture level) = ivalue

    ivalue(2) = ivalue + (((50 * city pop) + game turn number + 100) * 4) * (% of city culture owner has / 100))

    IF city has resources then
    ivalue(3) = ivalue 2 + (AI_bonusVal for all resources) * 10
    IF not then ivalue(3) = ivalue(2)

    IF city was ever owned by the recipient (i.e. returning a captured city) then,
    ivalue(3) * 3 / 2 = Final value
    IF not, then
    ivalue(3) = Final value

    Typical that this would call up AI_bonusVal.....
    Which calls up some of the most horrific looking functions in the whole of CvPlayerAI.cpp. Think i'll leave that one for now :lol:



    Thanks,
    Ha.
    Of course feel free to lurk all you like.

    Will get around to that sometime tomorrow, tiredness is finally starting to kick in :coffee:
     
  9. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    I often let the AI purchased my cities. I sometimes purchased theirs. It's fun doing that...and abusive.

    It just needs fairy fingers.

    I seriously SERIOUSLY counsel to leave the demon alone. For sanity's sake. It's an unnerving function. I did try that one (up to 60% I think) and got tired. Still got it on some papers somewhere....if I didn't burn it yet.
     
  10. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    Ok, come to think, if I had chance to retrieve those papers, will you be interested?

    Maybe we could join our forces (along DanF5771) to destroy the beast. :)
     
  11. TheMeInTeam

    TheMeInTeam Top Logic

    Joined:
    Jan 26, 2008
    Messages:
    25,318
    much reward from thread
    so amaze
    wow

    Thanks for make, will maybe lead to abuse AI

    Can help low skill player defeat detity.

    (Seriously though, following with interest :)).
     
  12. Manco Capac

    Manco Capac Friday,13 June,I Collapse

    Joined:
    Mar 1, 2010
    Messages:
    8,051
    Did you let your shiba inu takes control of your control? Wait a min...you have a shiba inu?
     
  13. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    I have had a bit of a crack at it before so I know how longwinded and nasty it is, but without a challenge this wouldn't be as interesting. Besides, its needed for 3 of the trade values so this project is already a failure if it isn't even attempted!
    This would probably be for the best, especially as my own code reading, and especially my presentation of it isn't really up to scratch yet :shifty:



    AI_declareWarTradeVal
    Code:
    int CvTeamAI::AI_declareWarTradeVal(TeamTypes eWarTeam, TeamTypes eTeam) const
    {
    	PROFILE_FUNC();
    
    	int iModifier;
    	int iValue;
    
    	FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
    	FAssertMsg(eWarTeam != getID(), "shouldn't call this function on ourselves");
    	FAssertMsg(GET_TEAM(eWarTeam).isAlive(), "GET_TEAM(eWarTeam).isAlive is expected to be true");
    	FAssertMsg(!atWar(eWarTeam, eTeam), "eTeam should be at peace with eWarTeam");
    
    	iValue = 0;
    	iValue += (GET_TEAM(eWarTeam).getNumCities() * 10);
    	iValue += (GET_TEAM(eWarTeam).getTotalPopulation(true) * 2);
    
    	iModifier = 0;
    
    	switch (GET_TEAM(eTeam).AI_getAttitude(eWarTeam))
    	{
    	case ATTITUDE_FURIOUS:
    		break;
    
    	case ATTITUDE_ANNOYED:
    		iModifier += 25;
    		break;
    
    	case ATTITUDE_CAUTIOUS:
    		iModifier += 50;
    		break;
    
    	case ATTITUDE_PLEASED:
    		iModifier += 150;
    		break;
    
    	case ATTITUDE_FRIENDLY:
    		iModifier += 400;
    		break;
    
    	default:
    		FAssert(false);
    		break;
    	}
    
    	iValue *= std::max(0, (iModifier + 100));
    	iValue /= 100;
    
    	int iTheirPower = GET_TEAM(eTeam).getPower(true);
    	int iWarTeamPower = GET_TEAM(eWarTeam).getPower(true);
    
    	iValue *= 50 + ((100 * iWarTeamPower) / (iTheirPower + iWarTeamPower + 1));
    	iValue /= 100;
    
    	if (!(GET_TEAM(eTeam).AI_isAllyLandTarget(eWarTeam)))
    	{
    		iValue *= 2;
    	}
    
    	if (!isAtWar(eWarTeam))
    	{
    		iValue *= 3;
    	}
    	else
    	{
    		iValue *= 150;
    		iValue /= 100 + ((50 * std::min(100, (100 * AI_getWarSuccess(eWarTeam)) / (8 + getTotalPopulation(false)))) / 100);
    	}
    	
    	iValue += (GET_TEAM(eTeam).getNumCities() * 20);
    	iValue += (GET_TEAM(eTeam).getTotalPopulation(true) * 15);
    	
    	if (isAtWar(eWarTeam))
    	{
    		switch (GET_TEAM(eTeam).AI_getAttitude(getID()))
    		{
    		case ATTITUDE_FURIOUS:
    		case ATTITUDE_ANNOYED:
    		case ATTITUDE_CAUTIOUS:
    			iValue *= 100;
    			break;
    
    		case ATTITUDE_PLEASED:
    			iValue *= std::max(75, 100 - getAtWarCount(true) * 10);
    			break;
    
    		case ATTITUDE_FRIENDLY:
    			iValue *= std::max(50, 100 - getAtWarCount(true) * 20);
    			break;
    
    		default:
    			FAssert(false);
    			break;
    		}
    		iValue /= 100;
    	}
    	
    	iValue += GET_TEAM(eWarTeam).getNumNukeUnits() * 250;//Don't want to get nuked
    	iValue += GET_TEAM(eTeam).getNumNukeUnits() * 150;//Don't want to use nukes on another's behalf
    
    	if (GET_TEAM(eWarTeam).getAtWarCount(false) == 0)
    	{
    		iValue *= 2;
    	
    		for (int iI = 0; iI < MAX_CIV_TEAMS; iI++)
    		{
    			if (GET_TEAM((TeamTypes)iI).isAlive())
    			{
    				if (iI != getID() && iI != eWarTeam && iI != eTeam)
    				{
    					if (GET_TEAM(eWarTeam).isDefensivePact((TeamTypes)iI))
    					{
    						iValue += (GET_TEAM((TeamTypes)iI).getNumCities() * 30);
    						iValue += (GET_TEAM((TeamTypes)iI).getTotalPopulation(true) * 20);
    					}
    				}
    			}
    		}
    	}
    
    	iValue *= 60 + (140 * GC.getGameINLINE().getGameTurn()) / std::max(1, GC.getGameINLINE().getEstimateEndTurn());
    	iValue /= 100;
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    This is a long one with a lot of IF statements

    ivalue = ((targets city count * 10) + (targets total pop * 2)) * Attitude Modifier

    Attitude modifier determined by what the bribed party thinks of the target
    ivalue(2) takes power into account and whether target is a land target or not

    ivalue(2) = 50 + ((100 * targets power) / ( targets power + bribed AIs power + 1))
    IF target is landtarget then multiply all by 2.

    The next part asks if the targets already at war, and if so who with

    IF target isn't already at war
    ivalue(3) = ivalue(2) * 3

    IF it is at war then,
    ivalue(3) = ivalue(2) * (150 / (100 + (50 * larger of 100 : 100 * targets war success) / (8 + world total pop))/100

    Otherwise,
    ivalue(3) = ivalue(2) + (bribed AIs city count * 20) + (bribed AIs pop * 15)


    IF target is at war AND the bribed AIs opinion of the targets enemies is Pleased OR Friendy then
    ivalue(4) = ivalue(3) * Attitude modifier
    This time the attitude modifier is determined by what the bribed AI thinks of the targets war enemies. This just makes it cheaper to bribe to help friends.
    It isn't clear what happens if the target is at war with multiple enemies here, my guess is that if you have multiple it just multiplies all of them sequentially i.e. for 1 Pleased and one Friendly and minimums in use;
    ivalue(4) = ivalue(3) * 0.5 * 0.75
    Obviously will have to test this one


    IF the target is not at war, or bribed AIs opinion of targets enemies is Cauious or lower then
    ivalue(4) = ivalue(3)

    Now some considerations for nukes
    ivalue(5) = ivalue(4) + (targets nuke count * 250) + (bribed AIs nuke count * 150)
    Until taking a fresh look at the DenialTypes I always thought it was impossible to bribe an AI into a war against a nuclear power under any circumstances.... but it is allowed if the nuclear power is already involved in a war.

    IF the target isn't already at war and doesn't have a defensive pact
    ivalue(6) = ivalue(5) * 2
    IF target isn't at war AND has a defensive pact,
    ivalue(6) = (ivalue(5) * 2) + (target teams city count * 30) + (targets total pop * 20)
    This seems insane as even a 1 city vassal would call this up and seriously inflate the cost. Perhaps there is a potential positive impact from keeping a tiny vassal.... depending of course on how the AI-AI trades consider deals to be 'fair'.

    Last bit finally
    ivalue(7) = ivalue(6) * (60 + (140 * gameturn / minimum of 1 : EstimateEndTurn) / 100)
    Unfortunately I don't yet know what the very last term means exactly :sad:. Added to todo list
    Value is rounded down as usual, and while the mimimum cost is there, its not going to be possible to hit this :p
     
  14. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    *waiting for testing*


    CvPlayerAI::AI_religionTradeVal
    This controls the cost of deals asking a party to change religions, which can only be bartered for if the AI is selling it, and only to the religion the human is running, the human can only be requested by AIs to change for free!

    This function is a bit of a pain as it involves two functions,
    CvPlayerAI::AI_bestReligion
    CvPlayerAI::AI_religionValue


    Both of which are also used in,
    CvPlayerAI::AI_doReligion
    which controls the whens and whats of changes in AI state religion.

    First the final function
    AI_religionTradeVal
    Code:
    int CvPlayerAI::AI_religionTradeVal(ReligionTypes eReligion, PlayerTypes ePlayer) const
    {
    	ReligionTypes eBestReligion;
    	int iValue;
    
    	iValue = (3 * (getTotalPopulation() + GET_PLAYER(ePlayer).getTotalPopulation())); // XXX
    
    	eBestReligion = [COLOR="DarkOrange"]GET_PLAYER(ePlayer).AI_bestReligion()[/COLOR];
    
    	if (eBestReligion != NO_RELIGION)
    	{
    		if (eBestReligion != eReligion)
    		{
    			iValue += std::max(0, (GET_PLAYER(ePlayer).AI_religionValue(eBestReligion) - [COLOR="DarkOrange"]GET_PLAYER(ePlayer).AI_religionValue(eReligion)))[/COLOR];
    		}
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
    	{
    		iValue /= 2;
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    ivalue = 3 * (world total pop + offering civs total pop)

    ivalue(2) = ivalue(1) + AI_religionVal of the offering civs best religion - AI_religionVal of proposed religion
    Basically, add the value of the difference between the religions

    IF offering civ is a vassal of buying civ then
    AI_religionTradeVal = ivalue(2) / 2
    IF not, then AI_religionTradeval = ivalue(2)


    CvPlayerAI::AI_bestReligion
    This just determines which of the 8 religions (including No State) is 'best'
    It uses the AIs favourite religion which can be found in the leaderhead.xml file
    Code:
    ReligionTypes CvPlayerAI::AI_bestReligion() const
    {
    	ReligionTypes eBestReligion;
    	int iValue;
    	int iBestValue;
    	int iI;
    
    	iBestValue = 0;
    	eBestReligion = NO_RELIGION;
    
    	ReligionTypes eFavorite = (ReligionTypes)GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion();
    
    	for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
    	{
    		if (canDoReligion((ReligionTypes)iI))
    		{
    			iValue = [COLOR="DarkOrange"]AI_religionValue((ReligionTypes)iI)[/COLOR];
    
    			if (getStateReligion() == ((ReligionTypes)iI))
    			{
    				iValue *= 4;
    				iValue /= 3;
    			}
    
    			if (eFavorite == iI)
    			{
    				iValue *= 5;
    				iValue /= 4;
    			}
    
    			if (iValue > iBestValue)
    			{
    				iBestValue = iValue;
    				eBestReligion = ((ReligionTypes)iI);
    			}
    		}
    	}
    
    	if ((NO_RELIGION == eBestReligion) || AI_isDoStrategy(AI_STRATEGY_MISSIONARY))
    	{
    		return eBestReligion;
    	}
    	
    	int iBestCount = getHasReligionCount(eBestReligion);
    	int iSpreadPercent = (iBestCount * 100) / std::max(1, getNumCities());
    	int iPurityPercent = (iBestCount * 100) / std::max(1, countTotalHasReligion());
    	if (iPurityPercent < 49)
    	{
    		if (iSpreadPercent > ((eBestReligion == eFavorite) ? 65 : 75))
    		{
    			if (iPurityPercent > ((eBestReligion == eFavorite) ? 25 : 32))
    			{
    				return eBestReligion;
    			}
    		}
    		return NO_RELIGION;
    	}
    	
    	return eBestReligion;
    }
    As default No State Religion starts as best

    ivalue = religionValue

    IF state religion then
    ivalue(2) = ivalue * 4 / 3
    IF not then ivalue(2) = ivalue

    IF favourite religion
    ivalue(3) = ivalue(2) * 5 / 4
    IF not then ivalue(3) = ivalue(2)

    Of note here is that the AIs favourite religion does have an impact, increasing the religions value by 25%,
    and that there is a bias toward sticking in the current religion, increasing its value by 33%.
    While we already know shrines and/holy cities have a massive impact that usually overwhelms these, it may well be worth considering for AIs that don't have holy cities!

    It cycles through all available religions evaluating them all and then calls back the highest value as the AI_bestReligion

    While you might expect that that wud be the end of it the AI calls up some weirdness.
    Code:
    	{
    		return eBestReligion;
    	}
    	
    	int iBestCount = getHasReligionCount(eBestReligion);
    	int iSpreadPercent = (iBestCount * 100) / std::max(1, getNumCities());
    	int iPurityPercent = (iBestCount * 100) / std::max(1, countTotalHasReligion())
    IF the AI_bestReligion is No State Religion OR The AI is in the Missionary Strategy then, it considers two new value, ispreadpercent and ipuritypercent. This bit has me confused right now....

    It uses the BestReligion gained earlier and,
    Defines iBestCount as the number of cities with the BestReligion

    iSpreadPercent is simply the percentage of the offering civs owned cities that have the bestReligion
    iSpreadPurity I think this is the iBestCount / total nuber of religions in cities owned by the selling player.

    Then theres this...
    Code:
    	if (iPurityPercent < 49)
    	{
    		if (iSpreadPercent > ((eBestReligion == eFavorite) ? 65 : 75))
    		{
    			if (iPurityPercent > ((eBestReligion == eFavorite) ? 25 : 32))
    			{
    				return eBestReligion;
    			}
    		}
    		return NO_RELIGION;
    	}
    	
    	return eBestReligion;
    I'm going to have to throw my hands up here and say what the hell is that!?
    The 65 OR 75 and 25 OR 32 values don't appear to have any purpose whatsoever :confused:
    I would have though it would go through different religions to look for the best, but to me all this says is
    BestReligion = BestReligion :confused:

    But, IF the purity precent is less than 49% it goes through this, fortunatey all of this only happen if the AI is doing its Missionary Strategy OR No State Religion is AI_bestReligion

    And the final part needed is how the AI put an initial value to religions,
    AI_religionValue
    Code:
    int CvPlayerAI::AI_religionValue(ReligionTypes eReligion) const
    {
    	if (getHasReligionCount(eReligion) == 0)
    	{
    		return 0;
    	}
    
    	int iValue = GC.getGameINLINE().countReligionLevels(eReligion);
    
    	int iLoop;
    	CvCity* pLoopCity;
    	for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
    	{
    		if (pLoopCity->isHasReligion(eReligion))
    		{
    			iValue += pLoopCity->getPopulation();
    		}
    	}
    
    	CvCity* pHolyCity = GC.getGameINLINE().getHolyCity(eReligion);
    	if (pHolyCity != NULL)
    	{
    		bool bOurHolyCity = pHolyCity->getOwnerINLINE() == getID();
    		bool bOurTeamHolyCity = pHolyCity->getTeam() == getTeam();
    
    		if (bOurHolyCity || bOurTeamHolyCity)
    	{
    			int iCommerceCount = 0;
    
    			for (int iI = 0; iI < GC.getNumBuildingInfos(); iI++)
    		{
    			if (pHolyCity->getNumActiveBuilding((BuildingTypes)iI) > 0)
    			{
    					for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
    				{
    					if (GC.getBuildingInfo((BuildingTypes)iI).getGlobalReligionCommerce() == eReligion)
    					{
    						iCommerceCount += GC.getReligionInfo(eReligion).getGlobalReligionCommerce((CommerceTypes)iJ) * pHolyCity->getNumActiveBuilding((BuildingTypes)iI);
    					}
    				}
    			}
    		}
    
    			if (bOurHolyCity)
    		{
    			iValue *= (3 + iCommerceCount);
    			iValue /= 2;
    		}
    			else if (bOurTeamHolyCity)
    		{
    			iValue *= (4 + iCommerceCount);
    			iValue /= 3;
    		}
    	}
    	}
    
    	return iValue;
    }
    It does this to get values for all religions, which are used in AI_bestReligion. If the civ doesn't have the religion in any city it gets a 0 at the first line and ends there.

    This is actually pretty short, though i'm not 100% certain if the city count is only for the civs cities, or a global count though a quick test strongly suggested the former.

    The Holy factor depends on the civ, or one of its teammates owning the holy city, and how much shrine gold they are getting.

    IF civs holy city,
    Holy factor = (3 + shrine gold*2) / 2

    IF teams holy city
    Holy factor = (4 + shrine gold*2) / 3

    This means that even without a shrine, a holy city does cause a significant bias, 3 / 2 fr own holy city and 4 / 3 for team.
     
  15. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    *waiting for testing*

    CvPlayerAI::AI_doReligion
    Code:
    void CvPlayerAI::AI_doReligion()
    {
    	ReligionTypes eBestReligion;
    
    	FAssertMsg(!isHuman(), "isHuman did not return false as expected");
    
    	if (AI_getReligionTimer() > 0)
    	{
    		AI_changeReligionTimer(-1);
    		return;
    	}
    
    	if (!canChangeReligion())
    	{
    		return;
    	}
    
    	FAssertMsg(AI_getReligionTimer() == 0, "AI Religion timer is expected to be 0");
    
    	eBestReligion = AI_bestReligion();
    
    	if (eBestReligion == NO_RELIGION)
    	{
    		eBestReligion = getStateReligion();
    	}
    
    	if (canConvert(eBestReligion))
    	{
    		convert(eBestReligion);
    		AI_setReligionTimer((getMaxAnarchyTurns() == 0) ? (GC.getDefineINT("MIN_CONVERSION_TURNS") * 2) : RELIGION_CHANGE_DELAY);
    	}
    }
    All this really says is that an AI will swap to its best religion when a religion counter reaches zero.
    The religion counter is set at the end of the function
    Which means if Spiritual 10 turns, if not 15

    Strangely there doesn't seem to be a AI_setReligionTimer reset that follows the trading of a religion change in CvPlayerAI.
    It may be elsewhere, but I IRC the AI routinely swap back very quickly, so its likely it just doesn't happen.
     
  16. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    CvTeamAI::AI_endWarVal
    Spoiler :
    Code:
    int CvTeamAI::AI_endWarVal(TeamTypes eTeam) const
    {
    	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");
    
    	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 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;
    
    			}
    		}
    	}
    
    
    	// XXX count units in enemy territory...
    
    	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;
    	}
    
    	if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
    	{
    		int iOurEndangeredCities = 0;
    		int iTheirEndangeredCities = 0;
    		
    		for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++)
    		{
    			CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
    			
    			if (kPlayer.getTeam() == eTeam)
    			{
    				int iLoop;
    				CvCity* pTheirLoopCity;
    				
    				for (pTheirLoopCity = kPlayer.firstCity(&iLoop); pTheirLoopCity != NULL; pTheirLoopCity = kPlayer.nextCity(&iLoop))
    				{
    					if (pTheirLoopCity->AI_isDanger())
    					{
    						iTheirEndangeredCities++;
    					}
    				}
    			}
    
    			if (kPlayer.getTeam() == getID())
    			{
    				int iLoop;
    				CvCity* pOurLoopCity;
    				
    				for (pOurLoopCity = kPlayer.firstCity(&iLoop); pOurLoopCity != NULL; pOurLoopCity = kPlayer.nextCity(&iLoop))
    				{
    					if (pOurLoopCity->AI_isDanger())
    					{
    						iOurEndangeredCities++;
    					}
    				}
    			}
    		}
    
    		if (iTheirEndangeredCities > iOurEndangeredCities)
    		{
    			iValue /= 3;
    		}
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    This is made up of 3 parts, a base value, a modifier depending on AI war strategy which isn't used for the humans side (anti human bias!), and an endangered city check which is only made if the Aggresive AIs option is turned on. As an equation it'd be
    Base * AIFactor * Aggfactor

    To find the net value for who owes what to make peace you have to find the endwarval for both parties and subtract one from the other,

    The code for what i've called the base value is found here
    Code:
    	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);
    I'm writing this from the perspective of the buyer. In practice peace deals can only be offered by both teams at the same time, and only one team will be able to trade anything extra for it.
    The cost of peace will depend on the difference in AI_endWarVal between both parties.

    (100 + (our city count + their team city count) * 3 + (our total pop + their teams total pop) * 3 + 20 * their team war success) * (their power + 10) / (our team power + their team power + 10)

    As this is pretty much the only factor involved when calculating human war success the main point of interest is the warsuccess bit, for each point of war success we get, the value increases by
    20 * (their power + 10) / (our team power + their team power + 10).
    While 20 per war success is the hypothetical max, its never going to be worth that much as its multpilied by the percentage of our power against the total power in play, which will always be less than 1. Also, war success is more valuable the more power you have.

    The AIFactor doesn't make much sense in a lot of places to me....
    Code:
    	WarPlanTypes eWarPlan = AI_getWarPlan(eTeam);
    
    	[COLOR="SandyBrown"]// if we 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;
    					}
    				}
    			}
    		}[/COLOR]
    		
    		// 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;
    
    			}
    		}
    	}
    
    
    	// XXX count units in enemy territory...
    
    	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;
    	}
    The first part in brown looks out of place to me, If it really says what I think it does then I would have thought it would belong in DenialTypes,
    IF our team power is 1.2 * their team power AND Dagger strategy OR in financial trouble then continue the war....

    Otherwise
    IF Dagger strat
    0.9 * their power / our power

    Then theres another bit that i'm going to have to look more deeply into, TotalLand.
    I'd have thought that if they wanted to achieve what they set out in the // note lines then it would be easiest done if TotalLand was a simple tile count, and land ratio was
    GlobalTotaLand / theirTotalLand, which IF > 50 would trigger some modified equation, but its obviously nothing like this....

    To carry on, another confusing bit

    IF in financial trouble
    swap warplans?
    The picks are random unless the AI started in Dagger, which forces it into TotalWar
    Then, still IF Financial trouble
    IF warplan is TotalWar OR LimitedWar OR Dogpile then,
    * 0.7 * ((5 * their power) / (our power * (4 * their power)))


    IF plan = Total War
    *2

    IF plan = Dogpile
    *3 / 2
    These last 2 bits are the probable cause of the seriously inflated values the AI has of how its doing in wars....

    Last is the AggFactor which only applies if AggAI option is turned on
    Code:
    	if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
    	{
    		int iOurEndangeredCities = 0;
    		int iTheirEndangeredCities = 0;
    		
    		for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++)
    		{
    			CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
    			
    			if (kPlayer.getTeam() == eTeam)
    			{
    				int iLoop;
    				CvCity* pTheirLoopCity;
    				
    				for (pTheirLoopCity = kPlayer.firstCity(&iLoop); pTheirLoopCity != NULL; pTheirLoopCity = kPlayer.nextCity(&iLoop))
    				{
    					if (pTheirLoopCity->AI_isDanger())
    					{
    						iTheirEndangeredCities++;
    					}
    				}
    			}
    
    			if (kPlayer.getTeam() == getID())
    			{
    				int iLoop;
    				CvCity* pOurLoopCity;
    				
    				for (pOurLoopCity = kPlayer.firstCity(&iLoop); pOurLoopCity != NULL; pOurLoopCity = kPlayer.nextCity(&iLoop))
    				{
    					if (pOurLoopCity->AI_isDanger())
    					{
    						iOurEndangeredCities++;
    					}
    				}
    			}
    		}
    
    		if (iTheirEndangeredCities > iOurEndangeredCities)
    		{
    			iValue /= 3;
    		}
    	}
    It counts up the number of our and their cities that are in danger, and,
    IF theres more of their cities in danger than ours then
    AggFactor = / 3
    Seems odd that this isn't considered in normal game rules too as having your cities under threat is a pretty good indication that your losing. Though I wouldn't be suprised if 'Endangered' means just one unit within 4 tiles or something....

    After a quick look at how danger is determined it does seem like it could be abused to make huge war profits, or make peace very cheap, as it appears that any unit in moving range of a city will cause that city to be in danger......
     
  17. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    Summary
    CvPlayerAI::AI_considerOffer
    Spoiler :
    CvPlayerAI::AI_considerOffer
    Code:
    bool CvPlayerAI::AI_considerOffer(PlayerTypes ePlayer, const CLinkList<TradeData>* pTheirList, const CLinkList<TradeData>* pOurList, int iChange) const
    {
    	CLLNode<TradeData>* pNode;
    	int iThreshold;
    
    	FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
    
    	if (AI_goldDeal(pTheirList) && AI_goldDeal(pOurList))
    	{
    		return false;
    	}
    
    	if (iChange > -1)
    	{
    		for (pNode = pOurList->head(); pNode; pNode = pOurList->next(pNode))
    		{
    			if (getTradeDenial(ePlayer, pNode->m_data) != NO_DENIAL)
    			{
    				return false;
    			}
    		}
    	}
    
    	if (GET_PLAYER(ePlayer).getTeam() == getTeam())
    	{
    		return true;
    	}
    
    	if ((pOurList->getLength() == 0) && (pTheirList->getLength() > 0))
    	{
    		return true;
    	}
    
    	int iOurValue = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList, false, iChange);
    	int iTheirValue = AI_dealVal(ePlayer, pTheirList, false, iChange);
    
    	if (iOurValue > 0 && 0 == pTheirList->getLength() && 0 == iTheirValue)
    	{
    		if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && CvDeal::isVassalTributeDeal(pOurList))
    		{
    			if (AI_getAttitude(ePlayer, false) <= GC.getLeaderHeadInfo(getPersonalityType()).getVassalRefuseAttitudeThreshold()
    				&& GET_TEAM(getTeam()).getAtWarCount(true) == 0
    				&& GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount() == 0)
    			{
    				iOurValue *= (GET_TEAM(getTeam()).getPower(false) + 10);
    				iOurValue /= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 10);
    			}
    			else
    			{
    				return true;
    			}
    		}
    		else
    		{
    			if (AI_getAttitude(ePlayer) < ATTITUDE_PLEASED)
    			{
    				if (GET_TEAM(getTeam()).getPower(false) > ((GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) * 4) / 3))
    				{
    					return false;
    				}
    			}
    
    			if (AI_getMemoryCount(ePlayer, MEMORY_MADE_DEMAND_RECENT) > 0)
    			{
    				return false;
    			}
    		}
    
    		iThreshold = (GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 50);
    
    		iThreshold *= 2;
    
    		if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_isLandTarget(getTeam()))
    		{
    			iThreshold *= 3;
    		}
    
    		iThreshold *= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 100);
    		iThreshold /= (GET_TEAM(getTeam()).getPower(false) + 100);
    
    		iThreshold -= GET_PLAYER(ePlayer).AI_getPeacetimeGrantValue(getID());
    
    		return (iOurValue < iThreshold);
    	}
    
    	if (iChange < 0)
    	{
    		return (iTheirValue * 110 >= iOurValue * 100);
    	}
    
    	return (iTheirValue >= iOurValue);
    }
    First this checks if the trade is allowed and that the AIs aren't denying the trade for whatever reason (DenialTypes).

    The acceptable values for trades are in here
    IF the iChange value is less than 0 then they expect 110% of what they offer, otherwise they want an equal or better trade from their perspective.
    According to Tachys research, the 110% inflating factor applies when renewing resource trades, meaning they want more as time goes on.


    In the case of begs and demands
    Code:
    	int iOurValue = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList, false, iChange);
    	int iTheirValue = AI_dealVal(ePlayer, pTheirList, false, iChange);
    
    	if (iOurValue > 0 && 0 == pTheirList->getLength() && 0 == iTheirValue)
    	{
    		if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && CvDeal::isVassalTributeDeal(pOurList))
    		{
    			if (AI_getAttitude(ePlayer, false) <= GC.getLeaderHeadInfo(getPersonalityType()).getVassalRefuseAttitudeThreshold()
    				&& GET_TEAM(getTeam()).getAtWarCount(true) == 0
    				&& GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount() == 0)
    			{
    				iOurValue *= (GET_TEAM(getTeam()).getPower(false) + 10);
    				iOurValue /= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 10);
    			}
    			else
    			{
    				return true;
    			}
    		}
    		else
    		{
    			if (AI_getAttitude(ePlayer) < ATTITUDE_PLEASED)
    			{
    				if (GET_TEAM(getTeam()).getPower(false) > ((GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) * 4) / 3))
    				{
    					return false;
    				}
    			}
    
    			if (AI_getMemoryCount(ePlayer, MEMORY_MADE_DEMAND_RECENT) > 0)
    			{
    				return false;
    			}
    		}
    
    		iThreshold = (GET_TEAM(getTeam()).AI_getHasMetCounter(GET_PLAYER(ePlayer).getTeam()) + 50);
    
    		iThreshold *= 2;
    
    		if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_isLandTarget(getTeam()))
    		{
    			iThreshold *= 3;
    		}
    
    		iThreshold *= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 100);
    		iThreshold /= (GET_TEAM(getTeam()).getPower(false) + 100);
    
    		iThreshold -= GET_PLAYER(ePlayer).AI_getPeacetimeGrantValue(getID());
    
    		return (iOurValue < iThreshold);
    A value for allowable gbegs/demands (grants) is generated,

    ivalue = (Turns you have known the civ + 50) * 2
    IF landtarget then
    ivalue(2) = ivalue*3

    The final grant value = (ivalue(2) * givers power / beggars power) - total grants previously recieved.


    Its common knowledge but if pleased or better then its a beg, and the only refusal conditions are not exceeding the iThreshold value, and the AI not remembering the last request.

    Demands, when cautious or below however, need to meet those two conditions, AND are automatically turned down IF
    Their power > 4 / 3 * our power

    And theres another bit within that,
    Code:
    if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && CvDeal::isVassalTributeDeal(pOurList))
    		{
    			if (AI_getAttitude(ePlayer, false) <= GC.getLeaderHeadInfo(getPersonalityType()).getVassalRefuseAttitudeThreshold()
    				&& GET_TEAM(getTeam()).getAtWarCount(true) == 0
    				&& GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount() == 0)
    			{
    				iOurValue *= (GET_TEAM(getTeam()).getPower(false) + 10);
    				iOurValue /= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 10);
    			}
    			else
    			{
    				return true;
    Has been covered by Tachy, and tells us that;
    and that otherwise it gets treated like other begs with power scaling.
     
  18. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    CvPlayerAI::AI_stopTradingTradeVal
    Spoiler :
    Code:
    int CvPlayerAI::AI_stopTradingTradeVal(TeamTypes eTradeTeam, PlayerTypes ePlayer) const
    {
    	CvDeal* pLoopDeal;
    	int iModifier;
    	int iValue;
    	int iLoop;
    
    	FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");
    	FAssertMsg(GET_PLAYER(ePlayer).getTeam() != getTeam(), "shouldn't call this function on ourselves");
    	FAssertMsg(eTradeTeam != getTeam(), "shouldn't call this function on ourselves");
    	FAssertMsg(GET_TEAM(eTradeTeam).isAlive(), "GET_TEAM(eWarTeam).isAlive is expected to be true");
    	FAssertMsg(!atWar(eTradeTeam, GET_PLAYER(ePlayer).getTeam()), "eTeam should be at peace with eWarTeam");
    
    	iValue = (50 + (GC.getGameINLINE().getGameTurn() / 2));
    	iValue += (GET_TEAM(eTradeTeam).getNumCities() * 5);
    
    	iModifier = 0;
    
    	switch (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getAttitude(eTradeTeam))
    	{
    	case ATTITUDE_FURIOUS:
    		break;
    
    	case ATTITUDE_ANNOYED:
    		iModifier += 25;
    		break;
    
    	case ATTITUDE_CAUTIOUS:
    		iModifier += 50;
    		break;
    
    	case ATTITUDE_PLEASED:
    		iModifier += 100;
    		break;
    
    	case ATTITUDE_FRIENDLY:
    		iModifier += 200;
    		break;
    
    	default:
    		FAssert(false);
    		break;
    	}
    
    	iValue *= std::max(0, (iModifier + 100));
    	iValue /= 100;
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isOpenBorders(eTradeTeam))
    	{
    		iValue *= 2;
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isDefensivePact(eTradeTeam))
    	{
    		iValue *= 3;
    	}
    
    	for(pLoopDeal = GC.getGameINLINE().firstDeal(&iLoop); pLoopDeal != NULL; pLoopDeal = GC.getGameINLINE().nextDeal(&iLoop))
    	{
    		if (pLoopDeal->isCancelable(getID()) && !(pLoopDeal->isPeaceDeal()))
    		{
    			if (GET_PLAYER(pLoopDeal->getFirstPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
    			{
    				if (pLoopDeal->getLengthSecondTrades() > 0)
    				{
    					iValue += (GET_PLAYER(pLoopDeal->getFirstPlayer()).AI_dealVal(pLoopDeal->getSecondPlayer(), pLoopDeal->getSecondTrades()) * ((pLoopDeal->getLengthFirstTrades() == 0) ? 2 : 1));
    				}
    			}
    
    			if (GET_PLAYER(pLoopDeal->getSecondPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
    			{
    				if (pLoopDeal->getLengthFirstTrades() > 0)
    				{
    					iValue += (GET_PLAYER(pLoopDeal->getSecondPlayer()).AI_dealVal(pLoopDeal->getFirstPlayer(), pLoopDeal->getFirstTrades()) * ((pLoopDeal->getLengthSecondTrades() == 0) ? 2 : 1));
    				}
    			}
    		}
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
    	{
    		iValue /= 2;
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }

    First bit
    Code:
    	iValue = (50 + (GC.getGameINLINE().getGameTurn() / 2));
    	iValue += (GET_TEAM(eTradeTeam).getNumCities() * 5);
    
    	iModifier = 0;
    
    	switch (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).AI_getAttitude(eTradeTeam))
    	{
    	case ATTITUDE_FURIOUS:
    		break;
    
    	case ATTITUDE_ANNOYED:
    		iModifier += 25;
    		break;
    
    	case ATTITUDE_CAUTIOUS:
    		iModifier += 50;
    		break;
    
    	case ATTITUDE_PLEASED:
    		iModifier += 100;
    		break;
    
    	case ATTITUDE_FRIENDLY:
    		iModifier += 200;
    		break;
    
    	default:
    		FAssert(false);
    		break;
    	}
    
    	iValue *= std::max(0, (iModifier + 100));
    	iValue /= 100;
    	
            if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isOpenBorders(eTradeTeam))
    	{
    		iValue *= 2;
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isDefensivePact(eTradeTeam))
    	{
    		iValue *= 3;
    From buyers (our) perspective buying from an AI (them/their) against another (target)

    50 + (gameturn / 2) + their cities * 5 * Attitude Modifier * OBf * DPf
    The Attitude Modifier depends on what they think of the target
    OBf is
    IF they have Open Border with target then 2, otherwise 1

    DPf is
    IF they have a Defensive packed with target then 3, otherwise 1
    We'll call the current value, ivalue


    Next block
    Code:
    for(pLoopDeal = GC.getGameINLINE().firstDeal(&iLoop); pLoopDeal != NULL; pLoopDeal = GC.getGameINLINE().nextDeal(&iLoop))
    	{
    		if (pLoopDeal->isCancelable(getID()) && !(pLoopDeal->isPeaceDeal()))
    		{
    			if (GET_PLAYER(pLoopDeal->getFirstPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
    			{
    				if (pLoopDeal->getLengthSecondTrades() > 0)
    				{
    					iValue += (GET_PLAYER(pLoopDeal->getFirstPlayer()).AI_dealVal(pLoopDeal->getSecondPlayer(), pLoopDeal->getSecondTrades()) * ((pLoopDeal->getLengthFirstTrades() == 0) ? 2 : 1));
    				}
    			}
    
    			if (GET_PLAYER(pLoopDeal->getSecondPlayer()).getTeam() == GET_PLAYER(ePlayer).getTeam())
    			{
    				if (pLoopDeal->getLengthFirstTrades() > 0)
    				{
    					iValue += (GET_PLAYER(pLoopDeal->getSecondPlayer()).AI_dealVal(pLoopDeal->getFirstPlayer(), pLoopDeal->getFirstTrades()) * ((pLoopDeal->getLengthSecondTrades() == 0) ? 2 : 1));
    				}
    			}
    		}
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
    	{
    		iValue /= 2;
    	}
    Most of this is involved in the values of existing trade deals which I can't fully decipher yet so i'll just call them V1 and V2, so now its,
    iValue +V1 + V2

    IF they are our vassal then
    (iValue +V1 + V2) / 2
    Then round down to nearest 10



    Still a fair few bits and pieces to fix and add, the two remaining untouched functions
    AI_bonusTradeVal
    AI_civicTradeVal

    are long and horrible....
    I'll probably go for the civics first as for the most part its a summation of a long series of mostly simple factors, a number of which aren't used in the vanilla game. It might be best in terms of usability and sanity to break it up to calculations for the individual civics
     
  19. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    *under construction*

    The initial function isn't too bad, nor is the bestCivic function it calls, but civicVal is a monster that calls up even more nasties. I'm going to break this into a few chunks, for now the plan is that the first post will cover
    AI_civicTradeVal
    AI_bestCivic
    and set the stage for what needs to be done in
    AI_civicVal
    Including its code and some of the other functions it calls up,
    AI_Happinessweight
    AI_Healthweight
    AI_Commerceweight


    And from there link to posts containing the different civic groups, Government, Legal, Labour, Economic, Religion. It'll be better for comparison this way, and easier to actually do but it will stop it being immediately useful in many mods.

    Its likely that even more will be called up, and even without more to add, these alone may take up enough space to justify a second post just for them.....
    AI_doCivics has to go somewhere too.

    CvPlayerAI::AI_civicTradeVal
    Code:
    int CvPlayerAI::AI_civicTradeVal(CivicTypes eCivic, PlayerTypes ePlayer) const
    {
    	CivicTypes eBestCivic;
    	int iValue;
    
    	iValue = (2 * (getTotalPopulation() + GET_PLAYER(ePlayer).getTotalPopulation())); // XXX
    
    	eBestCivic = GET_PLAYER(ePlayer).AI_bestCivic((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType()));
    
    	if (eBestCivic != NO_CIVIC)
    	{
    		if (eBestCivic != eCivic)
    		{
    			iValue += std::max(0, ((GET_PLAYER(ePlayer).AI_civicValue(eBestCivic) - GET_PLAYER(ePlayer).AI_civicValue(eCivic)) * 2));
    		}
    	}
    
    	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()))
    	{
    		iValue /= 2;
    	}
    
    	iValue -= (iValue % GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    
    	if (isHuman())
    	{
    		return std::max(iValue, GC.getDefineINT("DIPLOMACY_VALUE_REMAINDER"));
    	}
    	else
    	{
    		return iValue;
    	}
    }
    2 * (their pop + our pop) + (AI_civicValue of bestCivic - AI_civicValue of proposed civic * 2)
    Value gets halved if its a vassal changing civics for its master
    Rounded down to nearest 10.
    IF a human is buying from an AI a minimum value of 10 is applied.


    Code:
    CivicTypes CvPlayerAI::AI_bestCivic(CivicOptionTypes eCivicOption) const
    {
    	CivicTypes eBestCivic;
    	int iValue;
    	int iBestValue;
    	int iI;
    
    	iBestValue = MIN_INT;
    	eBestCivic = NO_CIVIC;
    
    	for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    	{
    		if (GC.getCivicInfo((CivicTypes)iI).getCivicOptionType() == eCivicOption)
    		{
    			if (canDoCivics((CivicTypes)iI))
    			{
    				iValue = AI_civicValue((CivicTypes)iI);
    
    				if (isCivic((CivicTypes)iI))
    				{
    					if (getMaxAnarchyTurns() > 0)
    					{
    						iValue *= 6;
    						iValue /= 5;
    					}
    					else
    					{
    						iValue *= 16;
    						iValue /= 15;
    					}
    				}
    
    				if (iValue > iBestValue)
    				{
    					iBestValue = iValue;
    					eBestCivic = ((CivicTypes)iI);
    				}
    			}
    		}
    	}
    
    	return eBestCivic;
    }
    Determination of which Civic is best.
    Unlike AI_bestReligion this does take account of Spiritual, and doesn't take account of favourite civics which is suprising.

    The only thing this function does is give inertia to current civics, probably to prevent the AI jumping back and forth repeatedly
    IF current civic AND IF Spiritual
    AI_civicvalue * 6 / 5

    IF not Spiritual
    AI_civicvalue * 15 / 16
    Highest value becomes eBestCivic

    Now comes the bit where things start getting nasty.
    In the spoilers is the entirity of AI_civicValue, as mentioned above theres no point in attacking it all at once, especially as more than 95% of it is irrelevant for any individual civic, and more than half of it is irrelevant in vanilla.
    Spoiler :
    Code:
    int CvPlayerAI::AI_civicValue(CivicTypes eCivic) const
    {
    	PROFILE_FUNC();
    
    	bool bWarPlan;
    	int iConnectedForeignCities;
    	int iTotalReligonCount;
    	int iHighestReligionCount;
    	int iWarmongerPercent;
    	int iHappiness;
    	int iValue;
    	int iTempValue;
    	int iI, iJ;
    
    	bool bCultureVictory3 = AI_isDoStrategy(AI_STRATEGY_CULTURE3);
    	bool bCultureVictory2 = AI_isDoStrategy(AI_STRATEGY_CULTURE2);
    
    	FAssertMsg(eCivic < GC.getNumCivicInfos(), "eCivic is expected to be within maximum bounds (invalid Index)");
    	FAssertMsg(eCivic >= 0, "eCivic is expected to be non-negative (invalid Index)");
    
    	CvCivicInfo& kCivic = GC.getCivicInfo(eCivic);
    
    	bWarPlan = (GET_TEAM(getTeam()).getAnyWarPlanCount(true) > 0);
    
    	iConnectedForeignCities = countPotentialForeignTradeCitiesConnected();
    	iTotalReligonCount = countTotalHasReligion();
    	ReligionTypes eBestReligion = AI_bestReligion();
    	if (eBestReligion == NO_RELIGION)
    	{
    		eBestReligion = getStateReligion();
    	}
    	iHighestReligionCount = ((eBestReligion == NO_RELIGION) ? 0 : getHasReligionCount(eBestReligion));
    	iWarmongerPercent = 25000 / std::max(100, (100 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarRand())); 
    
    	iValue = (getNumCities() * 6);
    
    	iValue += (GC.getCivicInfo(eCivic).getAIWeight() * getNumCities());
    
    	iValue += (getCivicPercentAnger(eCivic) / 10);
    
    	iValue += -(GC.getCivicInfo(eCivic).getAnarchyLength() * getNumCities());
    
    	iValue += -(getSingleCivicUpkeep(eCivic, true));
    
    	iValue += ((kCivic.getGreatPeopleRateModifier() * getNumCities()) / 10);
    	iValue += ((kCivic.getGreatGeneralRateModifier() * getNumMilitaryUnits()) / 50);
    	iValue += ((kCivic.getDomesticGreatGeneralRateModifier() * getNumMilitaryUnits()) / 100);
    	iValue += -((kCivic.getDistanceMaintenanceModifier() * std::max(0, (getNumCities() - 3))) / 8);
    	iValue += -((kCivic.getNumCitiesMaintenanceModifier() * std::max(0, (getNumCities() - 3))) / 8);
    	iValue += (kCivic.getFreeExperience() * getNumCities() * (bWarPlan ? 8 : 5) * iWarmongerPercent) / 100; 
    	iValue += ((kCivic.getWorkerSpeedModifier() * AI_getNumAIUnits(UNITAI_WORKER)) / 15);
    	iValue += ((kCivic.getImprovementUpgradeRateModifier() * getNumCities()) / 50);
    	iValue += (kCivic.getMilitaryProductionModifier() * getNumCities() * iWarmongerPercent) / (bWarPlan ? 300 : 500 ); 
    	iValue += (kCivic.getBaseFreeUnits() / 2);
    	iValue += (kCivic.getBaseFreeMilitaryUnits() / 3);
    	iValue += ((kCivic.getFreeUnitsPopulationPercent() * getTotalPopulation()) / 200);
    	iValue += ((kCivic.getFreeMilitaryUnitsPopulationPercent() * getTotalPopulation()) / 300);
    	iValue += -(kCivic.getGoldPerUnit() * getNumUnits());
    	iValue += -(kCivic.getGoldPerMilitaryUnit() * getNumMilitaryUnits() * iWarmongerPercent) / 200;
    
    	//iValue += ((kCivic.isMilitaryFoodProduction()) ? 0 : 0);
    	iValue += (getWorldSizeMaxConscript(eCivic) * ((bWarPlan) ? (20 + getNumCities()) : ((8 + getNumCities()) / 2)));
    	iValue += ((kCivic.isNoUnhealthyPopulation()) ? (getTotalPopulation() / 3) : 0);
    	if (bWarPlan)
    	{
    		iValue += ((kCivic.getExpInBorderModifier() * getNumMilitaryUnits()) / 200);
    	}
    	iValue += ((kCivic.isBuildingOnlyHealthy()) ? (getNumCities() * 3) : 0);
    	iValue += -((kCivic.getWarWearinessModifier() * getNumCities()) / ((bWarPlan) ? 10 : 50));
    	iValue += (kCivic.getFreeSpecialist() * getNumCities() * 12);
    	iValue += ((kCivic.getTradeRoutes() * std::max(0, iConnectedForeignCities - getNumCities() * 3) * 6) + (getNumCities() * 2)); 
    	iValue += -((kCivic.isNoForeignTrade()) ? (iConnectedForeignCities * 3) : 0);
    	if (kCivic.isNoCorporations())
    	{
    		iValue -= countHeadquarters() * (40 + 3 * getNumCities());
    	}
    	if (kCivic.isNoForeignCorporations())
    	{
    		for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
    		{
    			if (!GET_TEAM(getTeam()).hasHeadquarters((CorporationTypes)iCorp))
    			{
    				iValue += countCorporations((CorporationTypes)iCorp) * 3;
    			}
    		}
    	}
    	if (kCivic.getCorporationMaintenanceModifier() != 0)
    	{
    		int iCorpCount = 0;
    		int iHQCount = 0;
    		for (int iCorp = 0; iCorp < GC.getNumCorporationInfos(); ++iCorp)
    		{
    			if (GET_TEAM(getTeam()).hasHeadquarters((CorporationTypes)iCorp))
    			{
    				iHQCount++;
    			}
    			iCorpCount += countCorporations((CorporationTypes)iCorp);
    		}
    		iValue += (-kCivic.getCorporationMaintenanceModifier() * (iHQCount * (25 + getNumCities() * 2) + iCorpCount * 7)) / 25;
    
    	}
    
    	if (kCivic.getCivicPercentAnger() != 0)
    	{
    		int iNumOtherCities = GC.getGameINLINE().getNumCities() - getNumCities();
    		iValue += (30 * getNumCities() * getCivicPercentAnger(eCivic, true)) / kCivic.getCivicPercentAnger();
    		
    		int iTargetGameTurn = 2 * getNumCities() * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent();
    		iTargetGameTurn /= GC.getGame().countCivPlayersEverAlive();
    		iTargetGameTurn += GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getGrowthPercent() * 30;
    		
    		iTargetGameTurn /= 100;
    		iTargetGameTurn = std::max(10, iTargetGameTurn);
    		
    		int iElapsedTurns = GC.getGame().getElapsedGameTurns();
    
    		if (iElapsedTurns > iTargetGameTurn)
    		{
    			iValue += (std::min(iTargetGameTurn, iElapsedTurns - iTargetGameTurn) * (iNumOtherCities * kCivic.getCivicPercentAnger())) / (15 * iTargetGameTurn);
    		}
    	}
    
    	if (kCivic.getExtraHealth() != 0)
    	{
    		iValue += (getNumCities() * 6 * AI_getHealthWeight(isCivic(eCivic) ? -kCivic.getExtraHealth() : kCivic.getExtraHealth(), 1)) / 100;
    	}
    			
    	iTempValue = kCivic.getHappyPerMilitaryUnit() * 3;
    	if (iTempValue != 0)
    	{
    		iValue += (getNumCities() * 9 * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
    	}
    		
    	iTempValue = kCivic.getLargestCityHappiness();
    	if (iTempValue != 0)
    	{
    		iValue += (12 * std::min(getNumCities(), GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()) * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
    	}
    	
    	if (kCivic.getWarWearinessModifier() != 0)
    	{
    		int iAngerPercent = getWarWearinessPercentAnger();
    		int iPopulation = 3 + (getTotalPopulation() / std::max(1, getNumCities()));
    
    		int iTempValue = (-kCivic.getWarWearinessModifier() * iAngerPercent * iPopulation) / (GC.getPERCENT_ANGER_DIVISOR() * 100);
    		if (iTempValue != 0)
    		{
    			iValue += (11 * getNumCities() * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
    		}
    	}
    	
    	iValue += (kCivic.getNonStateReligionHappiness() * (iTotalReligonCount - iHighestReligionCount) * 5);
    
    	if (kCivic.isStateReligion())
    	{
    		if (iHighestReligionCount > 0)
    		{
    			iValue += iHighestReligionCount;
    
    			iValue += ((kCivic.isNoNonStateReligionSpread()) ? ((getNumCities() - iHighestReligionCount) * 2) : 0);
    			iValue += (kCivic.getStateReligionHappiness() * iHighestReligionCount * 4);
    			iValue += ((kCivic.getStateReligionGreatPeopleRateModifier() * iHighestReligionCount) / 20);
    			iValue += (kCivic.getStateReligionGreatPeopleRateModifier() / 4);
    			iValue += ((kCivic.getStateReligionUnitProductionModifier() * iHighestReligionCount) / 4);
    			iValue += ((kCivic.getStateReligionBuildingProductionModifier() * iHighestReligionCount) / 3);
    			iValue += (kCivic.getStateReligionFreeExperience() * iHighestReligionCount * ((bWarPlan) ? 6 : 2));
    		}
    	}
    
    	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
    	{
    		iTempValue = 0;
    
    		iTempValue += ((kCivic.getYieldModifier(iI) * getNumCities()) / 2);
    		iTempValue += ((kCivic.getCapitalYieldModifier(iI) * 3) / 4);
    		CvCity* pCapital = getCapitalCity(); 
    		if (pCapital) 
    		{
    			iTempValue += ((kCivic.getCapitalYieldModifier(iI) * pCapital->getBaseYieldRate((YieldTypes)iI)) / 80); 
    		}
    		iTempValue += ((kCivic.getTradeYieldModifier(iI) * getNumCities()) / 11);
    
    		for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
    		{
    			iTempValue += (AI_averageYieldMultiplier((YieldTypes)iI) * (kCivic.getImprovementYieldChanges(iJ, iI) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities() * 2))) / 100;
    		}
    
    		if (iI == YIELD_FOOD) 
    		{ 
    			iTempValue *= 3; 
    		} 
    		else if (iI == YIELD_PRODUCTION) 
    		{ 
    			iTempValue *= ((AI_avoidScience()) ? 6 : 2); 
    		} 
    		else if (iI == YIELD_COMMERCE) 
    		{ 
    			iTempValue *= ((AI_avoidScience()) ? 1 : 2); 
    		} 
    
    		iValue += iTempValue;
    	}
    
    	for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
    	{
    		iTempValue = 0;
    
    		iTempValue += ((kCivic.getCommerceModifier(iI) * getNumCities()) / 3);
    		iTempValue += (kCivic.getCapitalCommerceModifier(iI) / 2);
    		if (iI == COMMERCE_ESPIONAGE)
    		{
    			iTempValue *= AI_getEspionageWeight();
    			iTempValue /= 500;
    		}
    		iTempValue += ((kCivic.getSpecialistExtraCommerce(iI) * getTotalPopulation()) / 15);
    
    		iTempValue *= AI_commerceWeight((CommerceTypes)iI);
    
    		if ((iI == COMMERCE_CULTURE) && bCultureVictory2)
    		{
    		    iTempValue *= 2;
    		    if (bCultureVictory3)
    		    {
    		        iTempValue *= 2;		        
    		    }
    		}
    		iTempValue /= 100;
    
    		iValue += iTempValue;
    	}
    
    	for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
    	{
    		if (kCivic.getBuildingHappinessChanges(iI) != 0)
    		{
    			iValue += (kCivic.getBuildingHappinessChanges(iI) * getBuildingClassCount((BuildingClassTypes)iI) * 3);
    		}
    	}
    
    	for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
    	{
    		iHappiness = kCivic.getFeatureHappinessChanges(iI);
    
    		if (iHappiness != 0)
    		{
    			iValue += (iHappiness * countCityFeatures((FeatureTypes)iI) * 5);
    		}
    	}
    
    	for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
    	{
    		if (kCivic.isHurry(iI))
    		{
    			iTempValue = 0;
    
    			if (GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction() > 0)
    			{
    				iTempValue += ((((AI_avoidScience()) ? 50 : 25) * getNumCities()) / GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction());
    			}
    			iTempValue += (GC.getHurryInfo((HurryTypes)iI).getProductionPerPopulation() * getNumCities() * (bWarPlan ? 2 : 1)) / 5;
    			iValue += iTempValue;
    		}
    	}
    
    	for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
    	{
    		if (kCivic.isSpecialBuildingNotRequired(iI))
    		{
    			iValue += ((getNumCities() / 2) + 1); // XXX
    		}
    	}
    
    	for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++) 
    	{ 
    		iTempValue = 0; 
    		if (kCivic.isSpecialistValid(iI)) 
    		{ 
    			iTempValue += ((getNumCities() *  (bCultureVictory3 ? 10 : 1)) + 6);
    		} 
    		iValue += (iTempValue / 2); 
    	} 
    
    	if (GC.getLeaderHeadInfo(getPersonalityType()).getFavoriteCivic() == eCivic)
    	{
    		if (!kCivic.isStateReligion() || iHighestReligionCount > 0)
    		{
    			iValue *= 5; 
    			iValue /= 4; 
    			iValue += 6 * getNumCities();
    			iValue += 20; 
    		}
    	}
    
    	if (AI_isDoStrategy(AI_STRATEGY_CULTURE2) && (GC.getCivicInfo(eCivic).isNoNonStateReligionSpread()))
    	{
    	    iValue /= 10;	    
    	}
    
    	return iValue;
    }
    Further functions are called up in it that have importance

    AI_getHappinessWeight for civics involving happiness changes
    Code:
    int CvPlayerAI::AI_getHappinessWeight(int iHappy, int iExtraPop) const
    {
    	int iWorstHappy = 0;
    	int iBestHappy = 0;
    	int iTotalUnhappy = 0;
    	int iTotalHappy = 0;
    	int iLoop;
    	CvCity* pLoopCity;
    	int iCount = 0;
    	
    	if (0 == iHappy)
    	{
    		iHappy = 1;
    	}
    	int iValue = 0;
    	for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
    	{
    		int iCityHappy = pLoopCity->happyLevel() - pLoopCity->unhappyLevel(iExtraPop);
    		
    		iCityHappy -= std::max(0, pLoopCity->getCommerceHappiness());
    		int iHappyNow = iCityHappy;
    		int iHappyThen = iCityHappy + iHappy;
    		
    		//Integration
    		int iTempValue = (((100 * iHappyThen - 10 * iHappyThen * iHappyThen)) - (100 * iHappyNow - 10 * iHappyNow * iHappyNow));
    		if (iHappy > 0)
    		{
    			iValue += std::max(0, iTempValue);
    		}
    		else
    		{
    			iValue += std::max(0, -iTempValue);
    		}
    		
    		iCount++;
    		if (iCount > 6)
    		{
    			break;
    		}
    	}
    	
    	return (0 == iCount) ? 50 * iHappy : iValue / iCount;
    }

    AI_getHealthweight for civics involving heath changes
    Code:
    int CvPlayerAI::AI_getHealthWeight(int iHealth, int iExtraPop) const
    {
    	int iWorstHealth = 0;
    	int iBestHealth = 0;
    	int iTotalUnhappy = 0;
    	int iTotalHealth = 0;
    	int iLoop;
    	CvCity* pLoopCity;
    	int iCount = 0;
    	
    	if (0 == iHealth)
    	{
    		iHealth = 1;
    	}
    	int iValue = 0;
    	for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
    	{
    		int iCityHealth = pLoopCity->goodHealth() - pLoopCity->badHealth(false, iExtraPop);
    		
    		int iHealthNow = iCityHealth;
    		int iHealthThen = iCityHealth + iHealth;
    		
    		//Integration
    		int iTempValue = (((100 * iHealthThen - 6 * iHealthThen * iHealthThen)) - (100 * iHealthNow - 6 * iHealthNow * iHealthNow));
    		if (iHealth > 0)
    		{
    			iValue += std::max(0, iTempValue);
    		}
    		else
    		{
    			iValue += std::max(0, -iTempValue);
    		}
    		iCount++;
    		if (iCount > 6)
    		{
    			break;
    		}
    	}
    	
    	return (0 == iCount) ? 50 : iValue / iCount;
    }

    I'll link to evaluations of each civic group below
    Government
    Legal
    Labour
    Economic
    Religion
     
  20. Ghpstage

    Ghpstage Deity

    Joined:
    Jan 15, 2009
    Messages:
    2,944
    Location:
    Bristol, England
    *Problems with AI_Happinessweight slowing this down*

    Part of this is common to all civics, and makes up the entirety of Despotism

    SIZE="3"]Despotism[/SIZE]
    Code:
    	iValue = (getNumCities() * 6);
    
    
    	iValue += -(GC.getCivicInfo(eCivic).getAnarchyLength() * getNumCities());
    
    	iValue += -(getSingleCivicUpkeep(eCivic, true));
    I'm not going to repeat this code for every civic, so unless theres a change in anarchy length somewhere along the line I will just stick in the forumula without explanation. The bracketed (low) refers to the civic upkeep type which will govern the differences in the single civic upkeep cost

    Citycount is the number of cities they own
    Where, Single Civic Upkeep is the civic upkeep for the civic being bought from them


    Hereditary Rule
    Code:
    	iTempValue = kCivic.getHappyPerMilitaryUnit() * 3;
    	if (iTempValue != 0)
    	{
    		iValue += (getNumCities() * 9 * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
    	}
    What I get from this is,
    Not sure about the meaning of
    AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)

    AI_GetHappinessWeight is a seperate function I will have to attack, but the query at the end looks like it will flip the final value of the latter bracket negative if already in HR :s


    Representation
    Code:
    	iTempValue = kCivic.getLargestCityHappiness();
    	if (iTempValue != 0)
    	{
    		iValue += (12 * std::min(getNumCities(), GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities()) * AI_getHappinessWeight(isCivic(eCivic) ? -iTempValue : iTempValue, 1)) / 100;
    	}
    	for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
    	{
    		iTempValue = 0;
    
    		iTempValue += ((kCivic.getCommerceModifier(iI) * getNumCities()) / 3);
    		iTempValue += (kCivic.getCapitalCommerceModifier(iI) / 2);
    		if (iI == COMMERCE_ESPIONAGE)
    		{
    			iTempValue *= AI_getEspionageWeight();
    			iTempValue /= 500;
    		}
    		iTempValue += ((kCivic.getSpecialistExtraCommerce(iI) * getTotalPopulation()) / 15);
    
    		iTempValue *= AI_commerceWeight((CommerceTypes)iI);
    
    		if ((iI == COMMERCE_CULTURE) && bCultureVictory2)
    		{
    		    iTempValue *= 2;
    		    if (bCultureVictory3)
    		    {
    		        iTempValue *= 2;		        
    		    }
    		}
    		iTempValue /= 100;
    
    		iValue += iTempValue;
    	}
    (min of citycount, number of cities that get :)) is almost always going to be the number of cities that get :) unless the civ is a battered vassal or tiny colony, it's the happiness you get from Rep adjusted for map size and is 3 on standard.

    AI_commerceweight:science: I plan to cover eventually, if I do i'll put a link here


    Police State
    Code:
    	iValue += (kCivic.getMilitaryProductionModifier() * getNumCities() * iWarmongerPercent) / (bWarPlan ? 300 : 500 ); 
    
    	iValue += -((kCivic.getWarWearinessModifier() * getNumCities()) / ((bWarPlan) ? 10 : 50));
    iwarmongerpercent is
    Code:
    	iWarmongerPercent = 25000 / std::max(100, (100 + GC.getLeaderHeadInfo(getPersonalityType()).getMaxWarRand())); 
    iWarmongerPercent = 25000 / 100 + MaxWarRand
    MaxWarRand is from the leaderhead XML, I know someone (DanF5771?) already made a list of all the leaderhead XML values, i'll link it when I dig it up.


    Universal Suffrage
    Code:
    	for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
    	{
    		if (kCivic.isHurry(iI))
    		{
    			iTempValue = 0;
    
    			if (GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction() > 0)
    			{
    				iTempValue += ((((AI_avoidScience()) ? 50 : 25) * getNumCities()) / GC.getHurryInfo((HurryTypes)iI).getGoldPerProduction());
    			}
    			iTempValue += (GC.getHurryInfo((HurryTypes)iI).getProductionPerPopulation() * getNumCities() * (bWarPlan ? 2 : 1)) / 5;
    			iValue += iTempValue;
    		}
    	}
    
    		for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
    		{
    			iTempValue += (AI_averageYieldMultiplier((YieldTypes)iI) * (kCivic.getImprovementYieldChanges(iJ, iI) * (getImprovementCount((ImprovementTypes)iJ) + getNumCities() * 2))) / 100;
    		}
    
    		if (iI == YIELD_FOOD) 
    		{ 
    			iTempValue *= 3; 
    		} 
    		else if (iI == YIELD_PRODUCTION) 
    		{ 
    			iTempValue *= ((AI_avoidScience()) ? 6 : 2); 
    		} 
    		else if (iI == YIELD_COMMERCE) 
    		{ 
    			iTempValue *= ((AI_avoidScience()) ? 1 : 2); 
    		} 
    
    		iValue += iTempValue;
    	}
    AVG yield multipliers is an odd one as theres no query on the type of yield, it may well mean
    (total of all :hammers:, and if in Burea :commerce: modifiers) / citycount
    Needs further investigation if noone else can help on this.

    I don't now if those AI yield weightings are general in the code, or just specific to valueing civics,
    but F = 3, H = 2, C = 2 is pretty questionable, and F = 3, H = 6, C = 1 when avoiding science really raises an eyebrow
     

Share This Page