How does the AI choose civics?

Munch

Benevolent Despot
Joined
May 25, 2006
Messages
2,081
Hi,

I'm interested in modding civics, either just changing them or adding new ones. Thing is, I don't understand how the AI decides which civics to run at present.

For instance, currently Theocracy is a 'war' civic. When an AI switches to theocracy does it do so because "theocracy" is synonymous with "war", or because it thinks "+2 exp would be very handy in my military buildup"?

This is an important distinction because if say theocracy was modded to do something irrelevant towards military, would the AI still treat it as a war civic or would it assess the merits of the civic (bonuses granted, upkeep required, other bonuses sacrificed by not running say pacifism, ...)?

My second question is whether there is any coded behaviour along the lines of "everyone is running emancipation and it's hurting me, i'd better switch". Again, if so, does this take the form of 'hard-coded' (probably a misuse of the term) favouritism towards emancipation in such circumstances, or a reasoned argument about the merits of switching out of serfdom or whatever. If I was to add a new labour civic that had a greater happiness penalty for foreigners who aren't running it (let's say, civic: automation), would the AI understand this straight away?

Thanks in advance for any help :)
 
bump.

This is something I need more information about as well. Can anyone help? Please post anything you know.
 
Hmmm, very interesting question. Sadly, I can't help much except tell you that while I hope the AI can make Civic choices based on what the civics actually do, I doubt it.

What I can tell you is that one way they make civic choices is based on their favorite civic (something that was added in BTS or Warlords I believe). As far as I can tell, as soon as they have access to their favorite civic, they switch to it and hold on for dear life (and of course try to get me to adopt it too...good luck, buddy). The only way you can make them change that one is by sufficiently crushing their civilization to the point where they will agree to switch civics.

While this information is probably not new to you or particularly useful in regards to overall AI reaction to your new civic, it is probably worth keeping in mind; after all, maybe it should be the favorite civic of a few Civs? :)
 
The AI chooses civics for what it needs. It will occasionally adopt a non-favorite civic, but this is usually when unhappiness forces it to, so not often. It chooses civics based on their effects, like happiness, bonuses, and experienceand weighs them against any negatives (?). Warlike AIs will choose exp-boosting more often, while peacemongers and techwhores will choose financial ones.
 
The AI chooses civics for what it needs. It will occasionally adopt a non-favorite civic, but this is usually when unhappiness forces it to, so not often. It chooses civics based on their effects, like happiness, bonuses, and experienceand weighs them against any negatives (?). Warlike AIs will choose exp-boosting more often, while peacemongers and techwhores will choose financial ones.

Thanks!

This is how it is defined, based on the bonuses? So if I totally change civics around, warlike AIs will choose any civics that have experience boosting bonuses? Do you know of any place where this is described in more detail, like which traits lean towards which bonuses specifically? And/or which civ file(s) this is defined in?

Also, what influences the AI to decide to make changes at all? Will increased revolution times reduce the likelihood of an AI civ making a particular switch?
 
The AI chooses civics for what it needs. It will occasionally adopt a non-favorite civic, but this is usually when unhappiness forces it to, so not often. It chooses civics based on their effects, like happiness, bonuses, and experienceand weighs them against any negatives (?). Warlike AIs will choose exp-boosting more often, while peacemongers and techwhores will choose financial ones.

This seems too good to be true! Are you sure that the AI is smart enough to - at the very least - consider which out of a bunch of positive bonuses it wants? I would appreciate it if you or anyone could demonstrate how this is coded in xml or python files. :)
 
Thanks!

This is how it is defined, based on the bonuses? So if I totally change civics around, warlike AIs will choose any civics that have experience boosting bonuses? Do you know of any place where this is described in more detail, like which traits lean towards which bonuses specifically? And/or which civ file(s) this is defined in?

Also, what influences the AI to decide to make changes at all? Will increased revolution times reduce the likelihood of an AI civ making a particular switch?

I assume its ingrained in the LH's personality. My justification for the bonuses is that in the endgame, most civs have similar civic choices. I'm not sure what will make them swithc, as they usually switch at the first opportunity. But iv'e seen Shaka runnig theocracy when everyone else has FR.
 
Here are the tags from the LH file regarding civics, that are not self-explanatory (to me anyway):

<iFavoriteCivicAttitudeChange>
<iFavoriteCivicAttitudeDivisor>
<iFavoriteCivicAttitudeChangeLimit>

So, you can get some ideaa of what these do from the names, but it would be sweet if someone who understands them more would share what they know. The civ4 wiki has them listed as "unknown effect".

These also seem to apply only to the favorite civic, and are only about relationships with over civs, not picking their own. There does not appear to be anything else in that file regarding civics.

I'm trying to figure out how the AI decides to changes civics, when it does not involve the favorite civic.
 
I would appreciate it if you or anyone could demonstrate how this is coded in xml or python files. :)


What I am looking for as well. :)
 
Here are the tags from the LH file regarding civics, that are not self-explanatory (to me anyway):

<iFavoriteCivicAttitudeChange>
<iFavoriteCivicAttitudeDivisor>
<iFavoriteCivicAttitudeChangeLimit>

So, you can get some ideaa of what these do from the names, but it would be sweet if someone who understands them more would share what they know. The civ4 wiki has them listed as "unknown effect".

These also seem to apply only to the favorite civic, and are only about relationships with over civs, not picking their own. There does not appear to be anything else in that file regarding civics.

I'm trying to figure out how the AI decides to changes civics, when it does not involve the favorite civic.

Unfortunately I believe you are correct about the behaviour of these parameters. There are very similar parameters that I have messed with pertaining to the negative aspect of differing religions in diplomacy: the limit I believe is the most personality change that can be produced by the situation, and the change and divisor refer to how quickly relationships sour (or grow stronger in the case of sharing a favourite civic).

If there's nothing else about civics in there I very much doubt the civic choosing behaviour has anything to do with this file, unless some of the variables are called by functions in python somewhere when weighing up civic options.
 
This is how it's defined in the SDK files:

Code:
int CvPlayerAI::AI_civicValue(CivicTypes eCivic)
{
	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();
    // Unofficial Patch Start
    // * Modified AI civic valuation to more accurately consider state religion preference.
	ReligionTypes eBestReligion = AI_bestReligion();
	if (eBestReligion == NO_RELIGION)
	{
		eBestReligion = getStateReligion();
	}
	iHighestReligionCount = ((eBestReligion == NO_RELIGION) ? 0 : getHasReligionCount(eBestReligion));
	// Unofficial Patch End
	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;
}
It's rather complex calculation so I'm not sure if this helps you in any way... Basically I think it goes like this: for certain civic category if the returned iValue is higher for civic than what the AI civ currently has active, it will change to this new civic.
 
It's rather complex calculation so I'm not sure if this helps you in any way... Basically I think it goes like this: for certain civic category if the returned iValue is higher for civic than what the AI civ currently has active, it will change to this new civic.

That's fascinating, thanks very much. I haven't read the whole thing but it appears that the iValue for each civic considers the merit of that civic's bonuses with respect to many factors about the AI's civ. I'm glad to know that civic choice is as informed as this, and it should follow that if I swapped the behaviour of two civics, the AI would know the difference. I appreciate the help! :goodjob:
 
stuff like Flavors and AIweight, in the appropriate XML's help the AI's decide what to do as well. Civics have AIweight, but not flavors for some reason... other stuff like techs and units have flavors though.
 
stuff like Flavors and AIweight, in the appropriate XML's help the AI's decide what to do as well. Civics have AIweight, but not flavors for some reason... other stuff like techs and units have flavors though.

I didn't know that about civics, but I have played with the weighted favourite flavours of various AIs and the flavours of some techs. For instance, I thought it was strange that the AI don't regard Robotics as a military tech despite it giving you Mechanised Infantry.
 
Thanks a lot, zappara.
 
Back
Top Bottom