My personal thread: Clarification of certain game mechanics

It started off to be a pretty useless question without a single consequential deduction, but I had a lightning of genius (for once!) and finally was able to find out with ease (and almost no code heavy) the answer why the AI at occasions offer gold trades with smallest incrementation as 3 bumps instead of usual 5.

First, us, as human player, we are restricted UI regard with 10 gold bumps.

When the AI makes a counter offer (CvPlayerAI::AI_counterPropose()), instead of the human player, it seems it proposes incremential gold trades of 5. For instance, the human would have to give 20 gold for remaining tech while the AI may ask 15.

That seemed weird and buggy why the AI would occasionally offer gold trades in increments of 3.

Then, the moment of revelation happened while browsing this:

In CvPlayerAI.cpp
Spoiler :
int CvPlayerAI::AI_goldTradeValuePercent() const
{
int iValue = 2;
if (AI_isFinancialTrouble())
{
iValue += 1;
}
return 100 * iValue;

}


Basically, the AI values gold treasury more as it is struck by AI_financialTrouble() phenomenon.
For instance, behind the diplo table, normal circumstances make the AI values gold twice as it is presented, but when under financial crisis, 3 times as really presented in the UI.

Then, I know the human player suffers from some incremental substraction from global value:

DIPLOMACY_VALUE_REMAINDER
=10

that deletes trade like x1 to x9 to only accept x0 for a range of 0 to 99 for instance.
Suddenly I associated the number 10 and gold factors of real value, that is 2 and 3.

Well, 10/2= 5 increments (normal stance) and 10/3=3.333... ~ 3 (financial crisis).

I made a quick test game to test my theory and bam, it worked!

CONCLUSION

When you force the AI to counter propose and you see incrementations of 3 (e.g. x3 or x6 or x9), then you got a costless indicator of an AI suffering AI_financialTrouble() and this has some implications of course!

Another interesting thing I answered is gold is NOT worth one beaker at all. NEVER!
It's either 2 times a beaker or 3 times.
 
One of the most common action in civ4 is trading stuff around in the diplo table. Still, when doing so, there is not a instruction book indicating how the AI values stuff compared to other stuff.

For instance, you trade a technology and get full max worth in gold lump sum. But you don't get the exact amount as expected. Because there's a inner calculation beside what seen on the UI.

First, how stuff worths are calculated is defined in CvPlayerAI::AI_dealVal.

Considering there is no war between you and your AI partner, which would add another set of values defined in AI_endWarVal, here are the list of items and referred functions for item's worth in the AI eyes.

TRADE_TECHNOLOGIES: AI_techTradeVal()
TRADE_RESOURCES: AI_bonusTradeVal()
TRADE_CITIES: AI_cityTradeVal()
TRADE_GOLD: AI_goldTradeValuePercent()
TRADE_GOLD_PER_TURN: AI_goldPerTurnTradeVal()
TRADE_MAPS: AI_mapTradeVal()
TRADE_PEACE: AI_makePeaceTradeVal()
TRADE_WAR: AI_declareWarTradeVal()
TRADE_EMBARGO: AI_stopTradingTradeVal()
TRADE_CIVIC: AI_civicTradeVal()
TRADE_RELIGION: AI_religionTradeVal()

CvPlayerAI::AI_dealVal is kinda a crossroad of all possible trade items (even surrender value and vassal item, but those are not tradable with common stuff like techs or gold).

And this crossroad permits bug like this allowing different classes of objects being traded by overlooking the UI limitations (like disabling the other class of tradable object while choosing one).

TECHNOLOGIES

Let's start with technologies.
Important stuff to know is technologies' values are dependant on how far the AI has researched it, number of other AI's (and those who got that tech you want to tech trade) your trade partner knows, map size, gamespeed and a special 10% boost for certain techs (that are mainly those allowing military edge).

But in practice, for a given size and gamespeed, if you want to know how an AI gets it tech worth, just make a fast test game on noble because on noble our tech cost are normal while on higher levels, the tech costs are inflated (deflated for lower levels). Then, to know the hidden factor on how the AI put a worth note to a tech, just multiply the tech cost (after considering the factors just mentioned) by a factor of:

Code:
3/2

Yes, the AI values any tech with a 150% factor.

For instance, on emperor normal speed and standard size with a trade partner without any contact yet (and no 10% boost), Agriculture is still worth 78 :science: despite the tech tree shows differently. I trade it with Willem and he asked 52 :gold:.
He asked a conversion of 2/3 :gold: for each :science:. It's simply because he valued techs as 3/2 times better than each gold increment.

There is also the case of less gold for Agriculture, but that is not the tech value that directly affects it, but gold valued by the AI due to some financial crisis (AI_isFinancialTrouble), which simply elimimate the factor 3 and brings some modifications in that following formula: b=(3/2)g which gives now b=2*g, meaning each gold is valued half a beaker. Indeed, under financial crisis the AI values more gold and whines less for each coin gotten. Every penny is important.


GOLD LUMP SUM

Under work.
 
SYNOPSIS


One of the unbalance hated by the community is those free techs a vassal (peacevassal or a capitulee) get from a master. It is often cited as annoyingly help midget nations who peacevassaled to get up technologically, then break up vassal treaty and just did their parasite.
And what makes peacevassals more gamebreaking than war vassals (capitulee) is the fact relationships between those two (master and slave) are usually enough good to allow peacevassaling and near friendly stance where freebees go on senselessly.

Here how works free technologies given by a master to a vassal:
  • Free technologie mechanics work almost in the same way as a human gets a free one via AI generosity. That means the vassal gets one technology a turn. It is not an instantaneous process, but takes time.
  • Which tech is given is not determined by some hardcoded rules. It's a random process (RNG) where it gives a random number from 0 to 9999 to each tech and the one that gets the biggest number is the winner for the gift during that turn.
  • Not all technologies possessed by the master is available for gifts. Indeed, the master takes the same liberties as it was for another player. That means "We don't to start trading it just yet", "We don't like you enough!", "WFYABTA!", "We have our reasons!", "We'd rather win, thank you very much!", etc. are applied. Master attitude is an important factor to get him/her to take pity for its vassal. In a nutshell, if there is a possible denial for some techs, those are not chosen for gift until the denial is removed. FRIENDLY master to its vassal which happens often with peacevassaling makes the master to almost always give all his/her techs turn by turn.

STRATEGIC VALUE

Knowing which techs the vassal is going to get can be useful at moments.

Here are some cases:

  • If you know how well it goes between the vassal and the master and your know that tech you have is going to be given by the master to its vassal for FREE, then it's worth to get something out of it before the master spoils its vassal.
  • Since there are denials to what techs are given or not by the master to its vassal, then, you can perhaps try to spoil their relationships via changing one or another religion or master favorite civic (via the vassal ofc). If it is what makes the master to be FRIENDLY, it can be worth to avoid the master giving all its techs like an idiot.
  • About denials again, "We just don't want to start trading it just yet!" is about percentage of AI's having a tech the master has and if it is enough compared to its pre-defined personality (like Mansa needs 0% for tech trading=no monopoly) and that tech blocked by that denial is critical, then you may avoid trading it away lest to see the master allowing now its vassal to get it for free. For instance, let's suppose the master has feudalism but its vassal doesn't and won't get it because of "We just don't want to start trading it just yet!". If you retain yourself to trade feudalism a bit, you may perhaps capture more vassal's cities before longbows come into play.


IN IMAGES

In a worldbuilder game, I forced myself to be a vassal to some AI. Although the code has some difference when the human is a vassal, the mechanics remain the same.
It ended up I was under constant solicitation each turn for a tech, randomly chosen...so it means if a vassal accepts a tech, it counts towards WFYABTA for that vassal.

Spoiler :



Then, I attributed a tech not available in normal trade (refused via a denial) and removed all knowledge of tradable techs, so each turn, only the denied tech can be gifted. I never got it.

Spoiler :


That should be a decent proof of what the code gave me for interpretation.


CODE

In CvPlayerAI::AI_doDiplo()
Spoiler :
Code:
if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam() && GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()))
							{
								iBestValue = 0;
								eBestGiveTech = NO_TECH;

								for (iJ = 0; iJ < GC.getNumTechInfos(); iJ++)
								{
									if (GET_TEAM(getTeam()).AI_techTrade((TechTypes)iJ, GET_PLAYER((PlayerTypes)iI).getTeam()) == NO_DENIAL)
									{
										setTradeItem(&item, TRADE_TECHNOLOGIES, iJ);

										if (canTradeItem(((PlayerTypes)iI), item, true))
										{
											iValue = (1 + GC.getGameINLINE().getSorenRandNum(10000, "AI Vassal Tech gift"));

											if (iValue > iBestValue)
											{
												iBestValue = iValue;
												eBestGiveTech = ((TechTypes)iJ);
											}
										}
									}
								}

								if (eBestGiveTech != NO_TECH)
								{
									ourList.clear();
									theirList.clear();

									setTradeItem(&item, TRADE_TECHNOLOGIES, eBestGiveTech);
									ourList.insertAtEnd(item);

									if (GET_PLAYER((PlayerTypes)iI).isHuman())
									{
										if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
										{
											pDiplo = new CvDiploParameters(getID());
											FAssertMsg(pDiplo != NULL, "pDiplo must be valid");
											pDiplo->setDiploComment((DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GIVE_HELP"));
											pDiplo->setAIContact(true);
											pDiplo->setTheirOfferList(ourList);
											gDLL->beginDiplomacy(pDiplo, (PlayerTypes)iI);
											abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
										}
									}
									else
									{
										GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
									}
								}
							}
 
Excellent. My list of questions is almost all answered. No need for DanF5771 help after all.
 
Thanks for the great information about how the AI Master and AI Vassal relationship handles gifted Technologies from the Master.

I'm a bit curious about how you manipulated world builder to make the player a Vassal to an AI Master. That would never occur in a normal (non-world builder) game.

Again, excellent work!

Sun Tzu Wu
 
^
In the serie of worldbuilder buttoms, there's a handshake icon buttom called diplomacy IIRC. There you can force yourself to be a vassal of someone. Indeed, in a normal game, you cannot, but in a scenario, you can start a game being a vassal. Even a normal game without locked assets, you can pitch yourself into the warm arms of an AI master.

EDIT: Just got what you meant; nope, human vassalizing to an AI was blocked in the gameplay of a normal game to avoid possible abuses like getting free techs.
 
Thanks for the great information about how the AI Master and AI Vassal relationship handles gifted Technologies from the Master.

I'm a bit curious about how you manipulated world builder to make the player a Vassal to an AI Master. That would never occur in a normal (non-world builder) game.

Again, excellent work!

Sun Tzu Wu

I believe you go to the Diplomacy section where you can make (or break) vassalage.
 
SYNOPSIS​

"We demand emancipation!" is the raging shrieks of the population for being under a Labor class civic that they deem unjust, oppressing or simply backward.
It increases the unhappiness level by several unhappiness points according to the following paramaters (ordered from the most direct to the most generalized one):
  1. A base number: I compared Warlords and BTS; I saw no difference in coding. Some people ranted BTS increased emancipation unhappiness along BTS, but that seems to be an unfounded rant. I don't know if Vanilla version have publicized the code...
  2. Number of AI's still alive : indeed more AI's mean smaller incrementation of unhappiness per AI; more AI does not eqal to higher over unhappiness.
  3. Number of cities per AI under Emancipation: that is some kind of weigh that small nations have less impact than large scaled empires.
  4. Population in human player cities: Bigger populations mean more rabbid citizens overall.

MATHEMATICAL APPROACH​

Consider a base number called iCivicPercentAnger=400.
That is defined in CIV4CivicInfos.xml.

In CvPlayer, we find the function that increases the percentage of unhappiness according to number of AI's under Emancipation over all possible AI's STILL ALIVE and that is weighted with their city counts.

%CivicAnger = (Number of cities under Emancipation)/(Total Number of Cities in the world)

"Number of cities under Emancipation" is simply each AI under Emancipation and their weigh is their city counts.
"Total Number of Cities in the world" seems a pretty special parameter because it doesn't exclude barbarian cities in the count. Count them all if possible. And yes, that is possible to see barbarian cities during renaissance, especially if you play a Terra mapscript.

Now, to get the exact integer of unhappiness: Let it be named iUnhappiness.

iUnhappiness = [(%CivicAnger * (CityPopulation + Extra) )/ PERCENT_ANGER_DIVISOR]

CityPopulation is a common factor that affects many types of anger like motherland unhappiness, No military police anger, etc. That is why I consider that factor not proper to Emancipation, but a global factor in unhappiness.
PERCENT_ANGER_DIVISOR is some sort of constant defined in GlobalDefines.XML. Do not see beyond the fact it is worth 1000. That's it. :dunno:
Extra: I still need to define what it is. It can be some sort of additional parameter left for modders.
EDIT: Ok, I think I got the idea of what is that Extra parameter while browsing the code again. It seems to have to do with stacked unhappiness like from conscription, whipping, defying resolutions and stacking unhappy citizens.
Given the generality of that second formula for diverse forms of unhappiness, there is no extra for emancipation unhappiness.



SOURCE CODE​

Spoiler :

In CvPlayer.cpp
Code:
int CvPlayer::getCivicPercentAnger(CivicTypes eCivic, bool bIgnore) const
{
	int iCount;
	int iPossibleCount;
	int iI;

	if (GC.getCivicInfo(eCivic).getCivicPercentAnger() == 0)
	{
		return 0;
	}

	if (!bIgnore && (getCivics((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType())) == eCivic))
	{
		return 0;
	}

	iCount = 0;
	iPossibleCount = 0;

	for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).isAlive())
		{
			if (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam())
			{
				if (GET_PLAYER((PlayerTypes)iI).getCivics((CivicOptionTypes)(GC.getCivicInfo(eCivic).getCivicOptionType())) == eCivic)
				{
					iCount += GET_PLAYER((PlayerTypes)iI).getNumCities();
				}

				iPossibleCount += GET_PLAYER((PlayerTypes)iI).getNumCities();
			}
		}
	}

	if (iPossibleCount == 0)
	{
		return 0;
	}

	return ((GC.getCivicInfo(eCivic).getCivicPercentAnger() * iCount) / iPossibleCount);
}
In CvCity.cpp
Code:
int CvCity::unhappyLevel(int iExtra) const
{
	int iAngerPercent;
	int iUnhappiness;
	int iI;

	iUnhappiness = 0;

	if (!isNoUnhappiness())
	{
		iAngerPercent = 0;

		for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
		{
			iAngerPercent += GET_PLAYER(getOwnerINLINE()).getCivicPercentAnger((CivicTypes)iI);
		}

		iUnhappiness = ((iAngerPercent * (getPopulation() + iExtra)) / GC.getPERCENT_ANGER_DIVISOR());

		iUnhappiness -= std::min(0, getLargestCityHappiness());
		iUnhappiness -= std::min(0, getMilitaryHappiness());
		iUnhappiness -= std::min(0, getCurrentStateReligionHappiness());
		iUnhappiness -= std::min(0, getBuildingBadHappiness());
		iUnhappiness -= std::min(0, getExtraBuildingBadHappiness());
		iUnhappiness -= std::min(0, getFeatureBadHappiness());
		iUnhappiness -= std::min(0, getBonusBadHappiness());
		iUnhappiness -= std::min(0, getReligionBadHappiness());
		iUnhappiness -= std::min(0, getCommerceHappiness());
		iUnhappiness -= std::min(0, area()->getBuildingHappiness(getOwnerINLINE()));
		iUnhappiness -= std::min(0, GET_PLAYER(getOwnerINLINE()).getBuildingHappiness());
		iUnhappiness -= std::min(0, (getExtraHappiness() + GET_PLAYER(getOwnerINLINE()).getExtraHappiness()));
		iUnhappiness -= std::min(0, GC.getHandicapInfo(getHandicapType()).getHappyBonus());
		iUnhappiness += std::max(0, getVassalUnhappiness());
		iUnhappiness += std::max(0, getEspionageHappinessCounter());
	}

	return std::max(0, iUnhappiness);
}
In globalDefines.xml
Code:
<Define>
		<DefineName>PERCENT_ANGER_DIVISOR</DefineName>
		<iDefineIntVal>1000</iDefineIntVal>
	</Define>
In CIV4CivicInfos.xml
Code:
<CivicInfo>
			<CivicOptionType>CIVICOPTION_LABOR</CivicOptionType>
			<Type>CIVIC_EMANCIPATION</Type>
			<Description>TXT_KEY_CIVIC_EMANCIPATION</Description>
			<Civilopedia>TXT_KEY_CIVIC_EMANCIPATION_PEDIA</Civilopedia>
			<Strategy>TXT_KEY_CIVIC_EMANCIPATION_STRATEGY</Strategy>
			<Button>,Art/Interface/Buttons/Civics/Emancipation.dds,Art/Interface/Buttons/Civics_Civilizations_Religions_Atlas.dds,6,1</Button>
			<TechPrereq>TECH_DEMOCRACY</TechPrereq>
			<iAnarchyLength>1</iAnarchyLength>
			<Upkeep>UPKEEP_LOW</Upkeep>
			<iAIWeight>0</iAIWeight>
			<iGreatPeopleRateModifier>0</iGreatPeopleRateModifier>
			<iStateReligionGreatPeopleRateModifier>0</iStateReligionGreatPeopleRateModifier>
			<iDistanceMaintenanceModifier>0</iDistanceMaintenanceModifier>
			<iNumCitiesMaintenanceModifier>0</iNumCitiesMaintenanceModifier>
			<iExtraHealth>0</iExtraHealth>
			<iFreeExperience>0</iFreeExperience>
			<iWorkerSpeedModifier>0</iWorkerSpeedModifier>
			<iImprovementUpgradeRateModifier>100</iImprovementUpgradeRateModifier>
			<iMilitaryProductionModifier>0</iMilitaryProductionModifier>
			<iBaseFreeUnits>0</iBaseFreeUnits>
			<iBaseFreeMilitaryUnits>0</iBaseFreeMilitaryUnits>
			<iFreeUnitsPopulationPercent>0</iFreeUnitsPopulationPercent>
			<iFreeMilitaryUnitsPopulationPercent>0</iFreeMilitaryUnitsPopulationPercent>
			<iGoldPerUnit>0</iGoldPerUnit>
			<iGoldPerMilitaryUnit>0</iGoldPerMilitaryUnit>
			<iHappyPerMilitaryUnit>0</iHappyPerMilitaryUnit>
			<bMilitaryFoodProduction>0</bMilitaryFoodProduction>
			<iMaxConscript>0</iMaxConscript>
			<bNoUnhealthyPopulation>0</bNoUnhealthyPopulation>
			<bBuildingOnlyHealthy>0</bBuildingOnlyHealthy>
			<iLargestCityHappiness>0</iLargestCityHappiness>
			<iWarWearinessModifier>0</iWarWearinessModifier>
			<iFreeSpecialist>0</iFreeSpecialist>
			<iTradeRoutes>0</iTradeRoutes>
			<bNoForeignTrade>0</bNoForeignTrade>
			<iCivicPercentAnger>400</iCivicPercentAnger>
			<bStateReligion>0</bStateReligion>
			<bNoNonStateReligionSpread>0</bNoNonStateReligionSpread>
			<iStateReligionHappiness>0</iStateReligionHappiness>
			<iNonStateReligionHappiness>0</iNonStateReligionHappiness>
			<iStateReligionUnitProductionModifier>0</iStateReligionUnitProductionModifier>
			<iStateReligionBuildingProductionModifier>0</iStateReligionBuildingProductionModifier>
			<iStateReligionFreeExperience>0</iStateReligionFreeExperience>
			<YieldModifiers/>
			<CapitalYieldModifiers/>
			<TradeYieldModifiers/>
			<CommerceModifiers/>
			<CapitalCommerceModifiers/>
			<SpecialistExtraCommerces/>
			<Hurrys/>
			<SpecialBuildingNotRequireds/>
			<SpecialistValids/>
			<BuildingHappinessChanges/>
			<FeatureHappinessChanges/>
			<ImprovementYieldChanges/>
			<WeLoveTheKing/>
		</CivicInfo>

Didn't you forget to multiply %CivicAnger by iCivicPercentAnger?

For example on a map where half of the cities are in emancipation, a city of size 10 would get [1/2*400*10/1000]=2 unhappy citizens. This would seem realistic.

CvPlayer::getCivicPercentAnger returns with:

(GC.getCivicInfo(eCivic).getCivicPercentAnger() * iCount) / iPossibleCount
I guess that the red function call returns iCivicPercentAnger=400

I'm no code diver, but this code is far from optimal. I'm starting to understand why it takes ages to get to the next turn on bigger maps and 5 year old computers.

Oh, and great thread! :goodjob:

EDIT: so the actual number of emancipation unhapiness in a city of population POP is:

[#EmancipationCities/#CitiesOnTheMap*POP*0.4]
 
Dear Tachywaxon,

I come bearing questions regarding the Manhattan Project. When, where, and under what circumstances does the AI build this Project? It is the only "shiny" in the game (Great Wonders, National Wonders, Projects) that the AI does not seem to instinctively build with the avidity of a Warlord player.

Some possible factors:

  • Must the AI have access to Uranium in order to build the MP?
  • Does the iWonderConstructRand number affect the likelihood of building (or not building) the Project?
  • Does the iBasePeaceWeightRand of an AI influence matters?
  • If an AI is at war, or planning to go to war, does this increase the likelihood that they will commit to this? Or decrease?
And so on. I just finished a game in which I was warring with Ragnar and Suryavarman for most of the game. Both had Fission, while I did not, but neither built the project. Perhaps it is all just random.

I did search for further info, but could not find anything on the subject. That is not to say that this info does not exist.

With thanks. And happy holidays. :)

-Doshin

P.S. If you do not know and have no interest in the topic, please don't worry about it.
 
@Doshin

It's a real pleasure to answer questions and even more if it whips me to make the step to find that info.

Also, what you ask me forces to tread between what I can deal in the code of what I deal so-so. Yes, what the AI does in their cities is one of the two hardest facets (being the AI unit scripts and AI economic managements) to me.

In the SDK part of CIV hardcoding, there are four files I may attribute exclusively about the AI behaviours:

CvTeamAI.cpp
CvPlayerAI.cpp
CvUnitAI.cpp
CvCityAI.cpp


The two first are easy to look into because the lines of codes treat global behaviours like their own diplomatic decisions, global conjunture, reactions to human actions, etc.

The two last are complex because it treats a lot of possibilities and of course, AI units are the most complex given all the possible circumstances in which the unit can be.

Even DanF had problems occasionally about units in how to present in the most accurate way their behaviours. Not that they are that random because their behaviours are not that random! It's because it's hard to keep in track all the info and most are hidden to the human eyes.

Anyways, what you want me to look into is CvCityAI.cpp file

=====================================================
=====================================================

Globally, how the AI decides what to do for each of its cities depends of
the big function called CvCityAI::AI_chooseProduction().
In this long enumeration of conditional statements read in order downward, the AI will look at the situation for the city and see what is "best" to do right now.

For instance, for non-finished builds, if the present build has three turns left, then the AI will not scrap it for something else. Upon any circumstances!

Or, after conquering a city, that city, after the transitory revolt period, will not build units but mainly a granary, lighthouse, a forge, etc. In a specific order.

What makes the AI random in their choices, I think, is the circumstances that change from random elements. For instance, a war has been declared (random), the AI will start a defensive war and build the repective hardcoded units. Or their scouts have been killed (random) and they value their explorations insufficient, thus making new scouts or military units to do the job. This is what I think it is happening in the random department of their city builds.

Now, if we look into AI_chooseProduction(), when it comes to projects, it is half-way down the list of possible builds.

There are probably other random effects, but the small piece of code about projects is:

Code:
if (!bDanger && (iProductionRank <= ((kPlayer.getNumCities() / 5) + 1)))
	{
		if (AI_chooseProject())
		{
			return;
		}
	}

After ranking cities after their production capacities (and that's why the capital is often chosen for wonders and projects), one fifth of the upper side of all most productive cities are eligible for welcoming a project. Others are disqualified for efficiency in the project race.
Also, that chosen "lambda" city has to be free of dangers. Yes, if one endangers that city, then never we'll see that city choosing any project.

About what's the meaning of jeopardizing a city, see in the spoilers:
Spoiler :


Code:
bool CvCityAI::AI_isDanger()
{
	return GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 2, false);
}

Hereup is the code that deals and alarms that city is under pressure. Without entering details of AI_getPlotDanger(), the second argument is the range of jeopardizing units.

So basically, a unit in a city's BFC plus the corners is definition of endangering a city.


Now, let's take a look into AI_chooseProject():

Code:
bool CvCityAI::AI_chooseProject()
{
	ProjectTypes eBestProject;

	eBestProject = AI_bestProject();

	if (eBestProject != NO_PROJECT)
	{
		pushOrder(ORDER_CREATE, eBestProject, -1, false, false, false);
		return true;
	}

	return false;
}

It is basically the green light to start a project if there is a remaining project available.
Otherwise, it returns "false" by default.
AI_bestProject() defines what the AI considers the best project at this moment.

Spoiler :
Code:
ProjectTypes CvCityAI::AI_bestProject()
{
	ProjectTypes eBestProject;
	int iProductionRank;
	int iTurnsLeft;
	int iValue;
	int iBestValue;
	int iI;

	iProductionRank = findYieldRateRank(YIELD_PRODUCTION);

	iBestValue = 0;
	eBestProject = NO_PROJECT;

	for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
	{
		if (canCreate((ProjectTypes)iI))
		{
			iValue = AI_projectValue((ProjectTypes)iI);

			if ((GC.getProjectInfo((ProjectTypes)iI).getEveryoneSpecialUnit() != NO_SPECIALUNIT) ||
				  (GC.getProjectInfo((ProjectTypes)iI).getEveryoneSpecialBuilding() != NO_SPECIALBUILDING) ||
				  GC.getProjectInfo((ProjectTypes)iI).isAllowsNukes())
			{
				if (GC.getGameINLINE().getSorenRandNum(100, "Project Everyone") == 0)
				{
					iValue++;
				}
			}

			if (iValue > 0)
			{
				iValue += getProjectProduction((ProjectTypes)iI);

				iTurnsLeft = getProductionTurnsLeft(((ProjectTypes)iI), 0);

				if ((iTurnsLeft <= GC.getGameINLINE().AI_turnsPercent(10, GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getCreatePercent())) || !(GET_TEAM(getTeam()).isHuman()))
				{
					if ((iTurnsLeft <= GC.getGameINLINE().AI_turnsPercent(20, GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getCreatePercent())) || (iProductionRank <= std::max(3, (GET_PLAYER(getOwnerINLINE()).getNumCities() / 2))))
					{
						if (iProductionRank == 1)
						{
							iValue += iTurnsLeft;
						}
						else
						{
							FAssert((MAX_INT / 1000) > iValue);
							iValue *= 1000;
							iValue /= std::max(1, (iTurnsLeft + 10));
						}

						iValue = std::max(1, iValue);

						if (iValue > iBestValue)
						{
							iBestValue = iValue;
							eBestProject = ((ProjectTypes)iI);
						}
					}
				}
			}
		}
	}

	return eBestProject;
}

At first, for each project, it gives a intrinsic project value defined in AI_projectValue(). I'll return on that particular side later.

Here is a short list of deciding elements that weigh the balance towards a particular project:

  • For world projects like the Internet or the Manhattan Project, the RNG gives one chance out of 100 to increase its value by one increment.
  • If the AI_projectValue() gives a non-zero value to a project, then the value of projects in certain cities, especially those ranked high in production, are inflated according to the remaining turns (gamespeed adjusted) before completing the project.
    Given they are differently values and the remaining turns needed to complete such project is depending also of certain strategic resources, thus yes, some projects are favored according to the circumstance.
    HERE to see the projects cost. And if the AI has certain strategic resources but not all, this will favor one (multiples?) project over the others.
  • For a certain reason, the most productive city in the empire has a particular way to value its possible projects list from which to choose the best: the value is increased by an added value worth the remaining turns, which means faster is done the project, less big is the added value. It seems, it is hardcoded that the most productive city should get the most expensive project (even more without any bonus modifier) while other cities allowed (according to the production ranking) take its projects according the fastest done.

Finally, we end on the last note about intrinsic value of each project available in the unmodded BTS: AI_projectValue()

Spoiler :
Code:
int CvCityAI::AI_projectValue(ProjectTypes eProject)
{
	int iValue;
	int iI;

	iValue = 0;

	if (GC.getProjectInfo(eProject).getNukeInterception() > 0)
	{
		if (GC.getGameINLINE().canTrainNukes())
		{
			iValue += (GC.getProjectInfo(eProject).getNukeInterception() / 10);
		}
	}

	if (GC.getProjectInfo(eProject).getTechShare() > 0)
	{
		if (GC.getProjectInfo(eProject).getTechShare() < GET_TEAM(getTeam()).getHasMetCivCount(true))
		{
			iValue += (20 / GC.getProjectInfo(eProject).getTechShare());
		}
	}

	for (iI = 0; iI < GC.getNumVictoryInfos(); iI++)
	{
		if (GC.getGameINLINE().isVictoryValid((VictoryTypes)iI))
		{
			iValue += (std::max(0, (GC.getProjectInfo(eProject).getVictoryThreshold(iI) - GET_TEAM(getTeam()).getProjectCount(eProject))) * 20);
		}
	}

	for (iI = 0; iI < GC.getNumProjectInfos(); iI++)
	{
		iValue += (std::max(0, (GC.getProjectInfo((ProjectTypes)iI).getProjectsNeeded(eProject) - GET_TEAM(getTeam()).getProjectCount(eProject))) * 10);
	}

	return iValue;
}


In that function, four statements are presented.

  1. The first is simply the SDI and its value is fixed to 75 (yeah, the same value as the percentage of interception) divided by 10. By rounding down, it gives 7 and that's the SDI intrinsic value.
  2. The second is indeed the Internet. It is basically given a value for a certain AI if that AI has met at least 3 AI's (duh :) ). It's intrinsic value is 20 divided by 2, which is 10.
  3. The two last are about the SS parts.

What do we see from this little pre-defined list: no special value for the Manhattan Project. Nothing that permits it to stand out from the rest.

============================================================
============================================================

Conclusions

Must the AI have access to Uranium in order to build the MP?

It does help as it reduces the number of turns for completing the project and that increases its value for a chosen preferred project amongst all possible projects.
It's not a necessity from what I see, but it greatly helps.


Does the iWonderConstructRand number affect the likelihood of building (or not building) the Project?

No, it applies to wonders, not projects. As far as I know.

Does the iBasePeaceWeightRand of an AI influence matters?

AFAIK, this random element randomizes the peaceweight of an AI to an extent of 3 both sides (more peaceweight or less). It has to do with the fixed diplomatic web settled at the beginning of a game. For instance, peaceweights will define how many negmods or plusmods some AI's will have with particular other AI's. This will settle an Alex resenting throughout the whole game Gandhi and vice-versa. And other games, they resent each other a bit less or more. This element of randomization cannot be determined easily and is not presented in the UI of BUFFY nor BUG I think.


If an AI is at war, or planning to go to war, does this increase the likelihood that they will commit to this? Or decrease?

Will certainly decrease. When going for war, the AI will allocate their cities for unit pumping for 10 turns (I think it is 10 turns IIRC) WHEOOH.
Under state of wars, some productive cities can be endangered and that stumps from choosing any projects for starting. Also, if the AI has a flavour for unit pumping, then those cities will roll more unit builds. I think the AI cannot see the prospects of Manhattan Project as a mean to crush its opponent. The AI crunches numbers and that's all!
 
Didn't you forget to multiply %CivicAnger by iCivicPercentAnger?

For example on a map where half of the cities are in emancipation, a city of size 10 would get [1/2*400*10/1000]=2 unhappy citizens. This would seem realistic.

CvPlayer::getCivicPercentAnger returns with:

(GC.getCivicInfo(eCivic).getCivicPercentAnger() * iCount) / iPossibleCount
I guess that the red function call returns iCivicPercentAnger=400

I'm no code diver, but this code is far from optimal. I'm starting to understand why it takes ages to get to the next turn on bigger maps and 5 year old computers.

Oh, and great thread! :goodjob:

EDIT: so the actual number of emancipation unhapiness in a city of population POP is:

[#EmancipationCities/#CitiesOnTheMap*POP*0.4]

This is no exaggeration, but finding errors in my semi-articles is the highest form of respect to me. :D
That means someone actually reads my $hit. :king:

Now, let's see as Emancipation unhappiness is a useful mechanics occasionally for some games played with strong micro.

And yes! I completely forgot that constant.

Yes, on bigger maps, counting each turn the city numbers can be a long stacked process if there is other functions like that. Anyways, it is known that the original BTS is full of holes. According to Karadoc, his mod called K-mod is the most updated in terms of a good AI and fast process between turns. Also good and consistent UI functions.
 
Yes, on bigger maps, counting each turn the city numbers can be a long stacked process if there is other functions like that. Anyways, it is known that the original BTS is full of holes. According to Karadoc, his mod called K-mod is the most updated in terms of a good AI and fast process between turns. Also good and consistent UI functions.

That would be counting them each turn for each player. This alone is not too much (not noticeable to humans), but this coding style screams for some optimization. I'm very interested in K-mod, porblem is, I find it difficult enough to beat this AI on Immortal. Maybe when I can beat Deity, I will change to K-mod.

I wish there was some kind of optimization & UI mod that fixes stack selection bugs, using multithreading and better algorithms to have an overall faster game and better experience, without altering game mechanics.

This should be part of BUG, or called BUG Improved Gameplay, BIG :D

Or we should persuade Karadoc to try and separate K-mod's UI improvements into a separate mod. (I'm guessing that speed improvements have been done by completely rewriting parts of the AI, so separating that might be a huge task.)
 
I haven't been able to find the SDK dealing with exact mechanics of war success definitions
CvUnit::updateCombat is responsible for war success updates. Cargo unit are killed via CvUnit::kill, which is the same as disband, hence they do no affect war success.
 
@Tachywaxon

Many thanks for your extremely detailed and informative response. :D What most struck me me about your answer is that it seems that no individual AI is more or less likely to build a Project than any other. I suppose this makes good sense when one thinks about the Apollo Program... Ramesses is no more likely to go for a space win than Shaka, for instance, provided that they are both at tech parity. It had just never occurred to me before that this would apply to (say) The Internet.

If you don't mind my asking, what is the AI_projectValue() assigned to the MP? You note that the SDI is given 7, and the Internet 10.

To expand the topic a little, it seems to me that two factors conspire to limit the number of times the MP appears in an average game of Civ.

(1) Fission seems to be a low priority tech for most AI.
(2) The UN vote will often ban Nuclear Weapons before the Project itself is built.

I have nothing to justify assertion #1, but (speaking anecdotally) it seems that the AI tends to head towards Mass Media and Rocketry before Fission. The Nuclear ban vote thus often anticipates the MP being built. Perhaps once the Apollo Program is built, the AI also gets tied up in building spaceship parts (are these counted as projects?). Or the iTurnsLeft starts to diminish the chances of its being built.

Regarding point #2, here an AI's peace weight (or at least Personality type) does seem to influence things:

Spoiler :

Code:
if (GC.getVoteInfo(eVote).isNoNukes())
{
	int iVoteBanThreshold = 0;
	iVoteBanThreshold += GET_TEAM(getTeam()).getNukeInterception() / 3;
	iVoteBanThreshold += GC.getLeaderHeadInfo(getPersonalityType()).getBuildUnitProb();
	iVoteBanThreshold *= std::max(1, GC.getLeaderHeadInfo(getPersonalityType()).getWarmongerRespect());
	if (GC.getGameINLINE().isOption(GAMEOPTION_AGGRESSIVE_AI))
	{
		iVoteBanThreshold *= 2;
	}

	bool bAnyHasSdi = false;
	for (iI = 0; iI < MAX_TEAMS; iI++)
	{
		if (GET_TEAM((TeamTypes)iI).isAlive() && iI != getTeam())
		{
			if (GET_TEAM((TeamTypes)iI).getNukeInterception() > 0)
			{
				bAnyHasSdi = true;
				break;
			}
		}
	}

	if (!bAnyHasSdi && GET_TEAM(getTeam()).getNukeInterception() > 0 && GET_TEAM(getTeam()).getNumNukeUnits() > 0)
	{
		iVoteBanThreshold *= 2;
	}

	bValid = (GC.getGameINLINE().getSorenRandNum(100, "AI nuke ban vote") > iVoteBanThreshold);
	
	if (AI_isDoStrategy(AI_STRATEGY_OWABWNW))
	{
		bValid = false;
	}
	else if ((GET_TEAM(getTeam()).getNumNukeUnits() / std::max(1, GET_TEAM(getTeam()).getNumMembers())) < (GC.getGameINLINE().countTotalNukeUnits() / std::max(1, GC.getGameINLINE().countCivPlayersAlive())))
	{
		bValid = false;
	}
	if (!bValid && AI_getNumTrainAIUnits(UNITAI_ICBM) > 0)
	{
		if (GC.getGame().getSorenRandNum(AI_isDoStrategy(AI_STRATEGY_OWABWNW) ? 2 : 3, "AI Erratic Defiance (No Nukes)") == 0)
		{
			bDefy = true;
		}
	}
}

I can only parse this to the extent that bestsss once summarized: that unit prob, war monger respect, and SDI/Nuke availability impact things. I don't know how they impact things, though I suspect (s)he does or did.

I feel obliged to acknowledge my source, since it appears that bestsss just posted before me for the first time in nearly three(!) years.

Thanks again! And apologies if I said anything wholly incorrect or misleading.

Edit: I'm also thrilled to learn that an "OWABWNW" strategy exists. Who'd've thought?
 
@tachywaxon

did you happen to find the code which directs what kind of tiles the citizen governor assigns when set to build wealth? It has to be something very specific since it seems like it ignores the usual switches to emphasize hammers etc. and just goes on emphasize growth usually (preferring 2F grassland tiles over anything with only 1F and less).

I remember me looking around the cpp's for an hour or two very unsuccessfully, so if you could direct me into couple of methods/classes (i looked into the most obvious ones, but nothing really stood out to me as saying "this part of code is specifically when building wealth")...
 
@tachywaxon

did you happen to find the code which directs what kind of tiles the citizen governor assigns when set to build wealth? It has to be something very specific since it seems like it ignores the usual switches to emphasize hammers etc. and just goes on emphasize growth usually (preferring 2F grassland tiles over anything with only 1F and less).

I remember me looking around the cpp's for an hour or two very unsuccessfully, so if you could direct me into couple of methods/classes (i looked into the most obvious ones, but nothing really stood out to me as saying "this part of code is specifically when building wealth")...

I should take care of other stuff in line before, but you caught me interested with this challenge. It is because I'm not a real code diver. I'm a dilettante lacking scholarship of a programmer.

Still, I tested first in a game what you meant about building wealth is switching plots assignment (because I almost never play long term with Citizen Automation) and I noted like you did that building wealth (which is accounted as a "process" in code terms) immediately swapped hammer rich plots to mediocre food plots. I haven't pinpoint yet the exact reason, but I have a hunch for now (can't continue for today).

Anyways, I haven't delved too far in that function yet, but that is likely the one you are looking for: AI_yieldValue(****)

Spoiler :
Code:
int CvCityAI::AI_yieldValue(short* piYields, short* piCommerceYields, bool bAvoidGrowth, bool bRemove, bool bIgnoreFood, bool bIgnoreGrowth, bool bIgnoreStarvation, bool bWorkerOptimization)
{
	const int iBaseProductionValue = 15;
	const int iBaseCommerceValue = 7;
	
	const int iMaxFoodValue = (3 * iBaseProductionValue) - 1;

	int aiYields[NUM_YIELD_TYPES];
	int aiCommerceYieldsTimes100[NUM_COMMERCE_TYPES];

	int iExtraProductionModifier = 0;
	int iBaseProductionModifier = 100;
	
	bool bEmphasizeFood = AI_isEmphasizeYield(YIELD_FOOD);
	bool bFoodIsProduction = isFoodProduction();
	bool bCanPopRush = GET_PLAYER(getOwnerINLINE()).canPopRush();
	
	for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
	{
		aiCommerceYieldsTimes100[iJ] = 0;
	}

	for (int iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		if (piYields[iI] == 0)
		{
			aiYields[iI] = 0;
		}
		else
		{
			// Get yield for city after adding/removing the citizen in question
			int iOldCityYield = getBaseYieldRate((YieldTypes)iI);
			int iNewCityYield = (bRemove ? (iOldCityYield - piYields[iI]) : (iOldCityYield + piYields[iI]));
			int iModifier = getBaseYieldRateModifier((YieldTypes)iI);
			if (iI == YIELD_PRODUCTION)
			{
				iBaseProductionModifier = iModifier;
				iExtraProductionModifier = getProductionModifier();
				iModifier += iExtraProductionModifier;
			}

			iNewCityYield = (iNewCityYield * iModifier) / 100;
			iOldCityYield = (iOldCityYield * iModifier) / 100;

			// The yield of the citizen in question is the difference of total yields
			// to account for rounding of modifiers
			aiYields[iI] = (bRemove ? (iOldCityYield - iNewCityYield) : (iNewCityYield - iOldCityYield));
		}
	}
			
	for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
	{
		int iModifier = getTotalCommerceRateModifier((CommerceTypes)iJ);
				    
		int iCommerceTimes100 = aiYields[YIELD_COMMERCE] * GET_PLAYER(getOwnerINLINE()).getCommercePercent((CommerceTypes)iJ);
		if (piCommerceYields != NULL)
		{
			iCommerceTimes100 += piCommerceYields[iJ] * 100;
		}
		aiCommerceYieldsTimes100[iJ] += (iCommerceTimes100 * iModifier) / 100;
	}

	if (isProductionProcess() && !bWorkerOptimization)
	{
		for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
		{
			aiCommerceYieldsTimes100[iJ] += GC.getProcessInfo(getProductionProcess()).getProductionToCommerceModifier(iJ) * aiYields[YIELD_PRODUCTION];
		}

		aiYields[YIELD_PRODUCTION] = 0;
	}
	
	// should not really use this much, but making it accurate
	aiYields[YIELD_COMMERCE] = 0;
	for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
	{
		aiYields[YIELD_COMMERCE] += aiCommerceYieldsTimes100[iJ] / 100;
	}

	int iValue = 0;
	int iSlaveryValue = 0;
	
	int iFoodGrowthValue = 0;
	int iFoodGPPValue = 0;
	
	if (!bIgnoreFood && aiYields[YIELD_FOOD] != 0)
	{
		// tiny food factor, to ensure that even when we don't want to grow, 
		// we still prefer more food if everything else is equal
		iValue += (aiYields[YIELD_FOOD] * 1);

		int iFoodPerTurn = (foodDifference(false) - ((bRemove) ? aiYields[YIELD_FOOD] : 0));
		int iFoodLevel = getFood();
		int iFoodToGrow = growthThreshold();
		int iHealthLevel = goodHealth() - badHealth(/*bNoAngry*/ false, 0);
		int iHappinessLevel = (isNoUnhappiness() ? std::max(3, iHealthLevel + 5) : happyLevel() - unhappyLevel(0));
		int iPopulation = getPopulation();
		int	iExtraPopulationThatCanWork = std::min(iPopulation - range(-iHappinessLevel, 0, iPopulation) + std::min(0, extraFreeSpecialists()) , NUM_CITY_PLOTS) - getWorkingPopulation() + ((bRemove) ? 1 : 0);
		int iConsumtionPerPop = GC.getFOOD_CONSUMPTION_PER_POPULATION();

		int iAdjustedFoodDifference = (getYieldRate(YIELD_FOOD) + std::min(0, iHealthLevel)) - ((iPopulation + std::min(0, iHappinessLevel) - ((bRemove) ? 1 : 0)) * iConsumtionPerPop);
		
		// if we not human, allow us to starve to half full if avoiding growth
		if (!bIgnoreStarvation)
		{
			int iStarvingAllowance = 0;
			if (bAvoidGrowth && !isHuman())
			{
				iStarvingAllowance = std::max(0, (iFoodLevel - std::max(1, ((9 * iFoodToGrow) / 10))));
			}
			
			if ((iStarvingAllowance < 1) && (iFoodLevel > ((iFoodToGrow * 75) / 100)))
			{
				iStarvingAllowance = 1;
			}
			
			// if still starving
			if ((iFoodPerTurn + iStarvingAllowance) < 0)
			{
				// if working plots all like this one will save us from starving
				if (std::max(0, iExtraPopulationThatCanWork * aiYields[YIELD_FOOD]) >= -iFoodPerTurn)
				{
					// if this is high food, then we want to pick it first, this will allow us to pick some great non-food later
					int iHighFoodThreshold = std::min(getBestYieldAvailable(YIELD_FOOD), iConsumtionPerPop + 1);				
					if (iFoodPerTurn <= (AI_isEmphasizeGreatPeople() ? 0 : -iHighFoodThreshold) && aiYields[YIELD_FOOD] >= iHighFoodThreshold)
					{
						// value all the food that will contribute to not starving
						iValue += 2048 * std::min(aiYields[YIELD_FOOD], -iFoodPerTurn);
					}
					else
					{
						// give a huge boost to this plot, but not based on how much food it has
						// ie, if working a bunch of 1f 7h plots will stop us from starving, then do not force working unimproved 2f plot
						iValue += 2048;
					}
				}
				else
				{
					// value food high(32), but not forced
					iValue += 32 * std::min(aiYields[YIELD_FOOD], -iFoodPerTurn);
				}
			}
		}
		
		// if food isn't production, then adjust for growth
		if (bWorkerOptimization || !bFoodIsProduction)
		{
			int iPopToGrow = 0;
			if (!bAvoidGrowth)
			{
				// only do relative checks on food if we want to grow AND we not emph food
				// the emp food case will just give a big boost to all food under all circumstances
				if (bWorkerOptimization || (!bIgnoreGrowth))// && !bEmphasizeFood))
				{
					// also avail: iFoodLevel, iFoodToGrow
					
					// adjust iFoodPerTurn assuming that we work plots all equal to iConsumtionPerPop
					// that way it is our guesstimate of how much excess food we will have
					iFoodPerTurn += (iExtraPopulationThatCanWork * iConsumtionPerPop);
					
					// we have less than 10 extra happy, do some checks to see if we can increase it
					if (iHappinessLevel < 10)
					{
						// if we have anger becase no military, do not count it, on the assumption that it will 
						// be remedied soon, and that we still want to grow
						if (getMilitaryHappinessUnits() == 0)
						{
							if (GET_PLAYER(getOwnerINLINE()).getNumCities() > 2)
							{
								iHappinessLevel += ((GC.getDefineINT("NO_MILITARY_PERCENT_ANGER") * (iPopulation + 1)) / GC.getPERCENT_ANGER_DIVISOR());
							}
						}

						// currently we can at most increase happy by 2 in the following checks
						const int kMaxHappyIncrease = 2;

						// if happy is large enough so that it will be over zero after we do the checks
						int iNewFoodPerTurn = iFoodPerTurn + aiYields[YIELD_FOOD] - iConsumtionPerPop;
						if ((iHappinessLevel + kMaxHappyIncrease) > 0 && iNewFoodPerTurn > 0)
						{
							int iApproxTurnsToGrow = (iNewFoodPerTurn > 0) ? ((iFoodToGrow - iFoodLevel) / iNewFoodPerTurn) : MAX_INT;

							// do we have hurry anger?
							int iHurryAngerTimer = getHurryAngerTimer();
							if (iHurryAngerTimer > 0)
							{
								int iTurnsUntilAngerIsReduced = iHurryAngerTimer % flatHurryAngerLength();
								
								// angry population is bad but if we'll recover by the time we grow...
								if (iTurnsUntilAngerIsReduced <= iApproxTurnsToGrow)
								{
									iHappinessLevel++;
								}
							}

							// do we have conscript anger?
							int iConscriptAngerTimer = getConscriptAngerTimer();
							if (iConscriptAngerTimer > 0)
							{
								int iTurnsUntilAngerIsReduced = iConscriptAngerTimer % flatConscriptAngerLength();
								
								// angry population is bad but if we'll recover by the time we grow...
								if (iTurnsUntilAngerIsReduced <= iApproxTurnsToGrow)
								{
									iHappinessLevel++;
								}
							}

							// do we have defy resolution anger?
							int iDefyResolutionAngerTimer = getDefyResolutionAngerTimer();
							if (iDefyResolutionAngerTimer > 0)
							{
								int iTurnsUntilAngerIsReduced = iDefyResolutionAngerTimer % flatDefyResolutionAngerLength();

								// angry population is bad but if we'll recover by the time we grow...
								if (iTurnsUntilAngerIsReduced <= iApproxTurnsToGrow)
								{
									iHappinessLevel++;
								}
							}
						}
					}
					
					if (bEmphasizeFood)
					{
						//If we are emphasize food, pay less heed to caps.
						iHealthLevel += 5;
						iHappinessLevel += 2;
					}

					bool bBarFull = (iFoodLevel + iFoodPerTurn /*+ aiYields[YIELD_FOOD]*/ > ((90 * iFoodToGrow) / 100));

					int iPopToGrow = std::max(0, iHappinessLevel);
					int iGoodTiles = AI_countGoodTiles(iHealthLevel > 0, true, 50, true);
					iGoodTiles += AI_countGoodSpecialists(iHealthLevel > 0);
					iGoodTiles += bBarFull ? 0 : 1;

					if (!bEmphasizeFood)
					{
						iPopToGrow = std::min(iPopToGrow, iGoodTiles + ((bRemove) ? 1 : 0));
					}

					// if we have growth pontential, fill food bar to 85%
					bool bFillingBar = false;
					if (iPopToGrow == 0 && iHappinessLevel >= 0 && iGoodTiles >= 0 && iHealthLevel >= 0)
					{
						if (!bBarFull)
						{
							if (AI_specialYieldMultiplier(YIELD_PRODUCTION) < 50)
							{
								bFillingBar = true;
							}
						}
					}
					
					if (getPopulation() < 3)
					{
						iPopToGrow = std::max(iPopToGrow, 3 - getPopulation());
						iPopToGrow += 2;
					}

					// if we want to grow
					if (iPopToGrow > 0 || bFillingBar)
					{
						
						// will multiply this by factors
						iFoodGrowthValue = aiYields[YIELD_FOOD];
						if (iHealthLevel < (bFillingBar ? 0 : 1))
						{
							iFoodGrowthValue--;
						}

						// (range 1-25) - we want to grow more if we have a lot of growth to do
						// factor goes up like this: 0:1, 1:8, 2:9, 3:10, 4:11, 5:13, 6:14, 7:15, 8:16, 9:17, ... 17:25
						int iFactorPopToGrow;

						if (iPopToGrow < 1 || bFillingBar)
							iFactorPopToGrow = 20 - (10 * (iFoodLevel + iFoodPerTurn + aiYields[YIELD_FOOD])) / iFoodToGrow;
						else if (iPopToGrow < 7)
							iFactorPopToGrow = 17 + 3 * iPopToGrow;
						else
							iFactorPopToGrow = 41;
						
						iFoodGrowthValue *= iFactorPopToGrow;
						
						//If we already grow somewhat fast, devalue further food
						//Remember growth acceleration is not dependent on food eaten per 
						//pop, 4f twice as fast as 2f twice as fast as 1f...
						int iHighGrowthThreshold = 2 + std::max(std::max(0, 5 - getPopulation()), (iPopToGrow + 1) / 2);
						if (bEmphasizeFood)
						{
							iHighGrowthThreshold *= 2;
						}
						
						if (iFoodPerTurn > iHighGrowthThreshold)
						{
							iFoodGrowthValue *= 25 + ((75 * iHighGrowthThreshold) / iFoodPerTurn);
							iFoodGrowthValue /= 100;
						}
					}
				}
			
				//very high food override
				if ((isHuman()) && ((iPopToGrow > 0) || bCanPopRush))
				{
					//very high food override
					int iTempValue = std::max(0, 30 * aiYields[YIELD_FOOD] - 15 * iConsumtionPerPop);
					iTempValue *= std::max(0, 3 * iConsumtionPerPop - iAdjustedFoodDifference);
					iTempValue /= 3 * iConsumtionPerPop;
					if (iHappinessLevel < 0)
					{
						iTempValue *= 2;
						iTempValue /= 1 + 2 * -iHappinessLevel;
					}
					iFoodGrowthValue += iTempValue;
				}
				//Slavery Override
				if (bCanPopRush && (iHappinessLevel > 0))
				{
					iSlaveryValue = 30 * 14 * std::max(0, aiYields[YIELD_FOOD] - ((iHealthLevel < 0) ? 1 : 0));
					iSlaveryValue /= std::max(10, (growthThreshold() * (100 - getMaxFoodKeptPercent())));
					
					iSlaveryValue *= 100;
					iSlaveryValue /= getHurryCostModifier(true);
					
					iSlaveryValue *= iConsumtionPerPop * 2;
					iSlaveryValue /= iConsumtionPerPop * 2 + std::max(0, iAdjustedFoodDifference);
				}
				
				//Great People Override
				if ((iExtraPopulationThatCanWork > 1) && AI_isEmphasizeGreatPeople())
				{
					int iAdjust = iConsumtionPerPop;
					if (iFoodPerTurn == 0)
					{
						iAdjust -= 1;
					}
					iFoodGPPValue += std::max(0, aiYields[YIELD_FOOD] - iAdjust) * std::max(0, (12 + 5 * std::min(0, iHappinessLevel)));
				}
			}
		}
	}
	
	
	int iProductionValue = 0;
	int iCommerceValue = 0;
	int iFoodValue = std::min(iFoodGrowthValue, iMaxFoodValue * aiYields[YIELD_FOOD]);
	// if food is production, the count it
	int adjustedYIELD_PRODUCTION = (((bFoodIsProduction) ? aiYields[YIELD_FOOD] : 0) + aiYields[YIELD_PRODUCTION]);
			
	// value production medium(15)
	iProductionValue += (adjustedYIELD_PRODUCTION * iBaseProductionValue);
	if (!isProduction() && !isHuman())
	{
		iProductionValue /= 2;
	}	
	// value commerce low(6)

	for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
	{
		if (aiCommerceYieldsTimes100[iI] != 0)
		{
			int iCommerceWeight = GET_PLAYER(getOwnerINLINE()).AI_commerceWeight((CommerceTypes)iI);
			if (AI_isEmphasizeCommerce((CommerceTypes)iI))
			{
				iCommerceWeight *= 200;
				iCommerceWeight /= 100;
			}
			if (iI == COMMERCE_CULTURE)
			{
				if (getCultureLevel() <= (CultureLevelTypes) 1)
				{
					iCommerceValue += (15 * aiCommerceYieldsTimes100[iI]) / 100;
				}
			}
			iCommerceValue += (iCommerceWeight * (aiCommerceYieldsTimes100[iI] * iBaseCommerceValue) * GET_PLAYER(getOwnerINLINE()).AI_averageCommerceExchange((CommerceTypes)iI)) / 1000000;
		}
	}
/*
	if (!bWorkerOptimization && bEmphasizeFood)
	{
		if (!bFoodIsProduction)
		{
			// value food extremely high(180)
			iFoodValue *= 125;
			iFoodValue /= 100;
		}
	}
	
	if (!bWorkerOptimization && AI_isEmphasizeYield(YIELD_PRODUCTION))
	{
		// value production high(80)
		iProductionValue += (adjustedYIELD_PRODUCTION * 80);
	}
*/
	//Slavery translation
	if ((iSlaveryValue > 0) && (iSlaveryValue > iFoodValue))
	{
		//treat the food component as production
		iFoodValue = 0;
	}
	else
	{
		//treat it as just food
		iSlaveryValue = 0;
	}
	
	iFoodValue += iFoodGPPValue;
/*
	if (!bWorkerOptimization && AI_isEmphasizeYield(YIELD_COMMERCE))
	{
		for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
		{
			iCommerceValue += ((iCommerceYields[iI] * 40) * GET_PLAYER(getOwnerINLINE()).AI_averageCommerceExchange((CommerceTypes)iI)) / 100;
		}
	}

	for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
	{
		if (!bWorkerOptimization && AI_isEmphasizeCommerce((CommerceTypes) iJ))
		{
			// value the part of our commerce that goes to our emphasis medium (40)
			iCommerceValue += (iCommerceYields[iJ] * 40);
		}
	}
*/
	//Lets have some fun with the multipliers, this basically bluntens the impact of
	//massive bonuses.....
	
	//normalize the production... this allows the system to account for rounding
	//and such while preventing an "out to lunch smoking weed" scenario with
	//unusually high transient production modifiers.
	//Other yields don't have transient bonuses in quite the same way.

	if (AI_isEmphasizeYield(YIELD_PRODUCTION))
	{
		iProductionValue *= 130;
		iProductionValue /= 100;
		
		if (isFoodProduction())
		{
			iFoodValue *= 130;
			iFoodValue /= 100;
		}
		
		if (!AI_isEmphasizeYield(YIELD_COMMERCE))
		{
			iCommerceValue *= 60;
			iCommerceValue /= 100;
		}
		if (!AI_isEmphasizeYield(YIELD_FOOD))
		{
			iFoodValue *= 75;
			iFoodValue /= 100;
		}
	}
	if (AI_isEmphasizeYield(YIELD_FOOD))
	{
		if (!isFoodProduction())
		{
			iFoodValue *= 130;
			iFoodValue /= 100;
			iSlaveryValue *= 130;
			iSlaveryValue /= 100;
		}
	}
	if (AI_isEmphasizeYield(YIELD_COMMERCE))
	{
		iCommerceValue *= 130;
		iCommerceValue /= 100;
		if (!AI_isEmphasizeYield(YIELD_PRODUCTION))
		{
			iProductionValue *= 75;
			iProductionValue /= 100;
		}
		if (!AI_isEmphasizeYield(YIELD_FOOD))
		{
			//Don't supress twice.
			if (!AI_isEmphasizeYield(YIELD_PRODUCTION))
			{
				iFoodValue *= 80;
				iFoodValue /= 100;
			}
		}
	}
		

	if (isFoodProduction())
	{
		iProductionValue *= 100 + (bWorkerOptimization ? 0 : AI_specialYieldMultiplier(YIELD_PRODUCTION));
		iProductionValue /= 100;		
	}
	else
	{
		iProductionValue *= iBaseProductionModifier;
		iProductionValue /= (iBaseProductionModifier + iExtraProductionModifier);
		
		iProductionValue += iSlaveryValue;
		iProductionValue *= (100 + (bWorkerOptimization ? 0 : AI_specialYieldMultiplier(YIELD_PRODUCTION)));
		
		iProductionValue /= GET_PLAYER(getOwnerINLINE()).AI_averageYieldMultiplier(YIELD_PRODUCTION);
	}
	
	iValue += iProductionValue;
	
	
	iCommerceValue *= (100 + (bWorkerOptimization ? 0 : AI_specialYieldMultiplier(YIELD_COMMERCE)));
	iCommerceValue /= GET_PLAYER(getOwnerINLINE()).AI_averageYieldMultiplier(YIELD_COMMERCE);
	iValue += iCommerceValue;
//	
	iFoodValue *= 100;
	iFoodValue /= GET_PLAYER(getOwnerINLINE()).AI_averageYieldMultiplier(YIELD_FOOD);
	iValue += iFoodValue;

	
	return iValue;
}

This section, I think treats the value of hammers when "processing" them into commerce, which could be research, gold or culture. Doesn't matter I think.

Code:
if (isProductionProcess() && !bWorkerOptimization)
	{
		for (int iJ = 0; iJ < NUM_COMMERCE_TYPES; iJ++)
		{
			aiCommerceYieldsTimes100[iJ] += GC.getProcessInfo(getProductionProcess()).getProductionToCommerceModifier(iJ) * aiYields[YIELD_PRODUCTION];
		}

		aiYields[YIELD_PRODUCTION] = 0;
	}

Each hammer value is decreased in value I suppose (I haven't looked how pure hammers are valued yet) because of the processing. That makes probably food in comparison much stronger in value than a single :food: plus hammers.

The citizen Automation uses the same rules as the AI would.

Then, AI_yieldValue(****) goes in AI_plotValue(****), which will determine which plots are best under circumstances (and processing hammers into wealth is one).
 
ah interesting...didn't came to mind that the game would manipulate yields based on production, but you probably are on the right track...will look into it evening with netbeans to have some syntax highlighting and better formatting.
 
Top Bottom