Some weird condition structure...to my understanding.

Manco Capac

Friday,13 June,I Collapse
Joined
Mar 1, 2010
Messages
8,051
While reading CvPlayerAI::AI_baseBonusVal() which is a substep to understand worker behaviour when it comes to evaluate the most toppish in value for bonus resources, I was kinda stopped by a weird condition structure.

Code:
if (bIsWater && !isLimitedUnitClass((UnitClassTypes)(kLoopUnit.getUnitClassType())))
						{
							iTempValue *= std::min(iCoastalCityCount * 2, iCityCount);	// double coastal cities, cap at total cities
							iTempValue /= std::max(1, iCityCount);
						}

That !isLimitedUnitClass(), which is defined in CvGameCoreUtils.cpp:

Code:
bool isLimitedUnitClass(UnitClassTypes eUnitClass)
{
	return (isWorldUnitClass(eUnitClass) || isTeamUnitClass(eUnitClass) || isNationalUnitClass(eUnitClass));
}

So a limitedUnitClass is either a worldclassunit (a unit that can be built once in the whole world like a world wonder) or a teamunit (certain number of units permitted for a team) or a playerunit class (limited number of units for a player, which is a subset of a possible team).

!isLimitedUnitClass() is the...uh...contrary...okay.

What is the opposite first and then what is the use of that condition seriously?
Except for modding purposes, I think there is not a single worldunitclass or teamunitclass or playerunitclass. They are all -1.

I'm lost. :confused::crazyeye:
 
Moderator Action: Moved to the SDK forum.

The code makes not much sense for me. I can only guess it might have been a leftover from an earlier plan :dunno:.
But well, doesn't make any differences ingame. The opposite to a limited unit class is an unlimited unit class, and that's the default work boat.
 
That is interesting, The_J. So the if condtion allows all DOMAIN_SEA units except the workboat. Still, that doesn't really make sense in those code lines because the previous condition is:

Code:
if (iTempValue > 0)

The problem is to get some iTempValue, the previous steps were done accordingly to units enabled by bonuses (typical game is strat. resource; a particular mod, it can be anything depending of the mood of the modder).
Nonetheless, there is no bonus resource enabling workboats; they are resourceless.

Then iTempValue=0 and we completely pass the {}.

So why the the use of !IsLimitedUnitClass function? I have seriously no idea and I think the best attitude to adopt is I don't care. :lol:

Here's the relevant code section.


Spoiler :
Code:
for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
			{
				eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));

				if (eLoopUnit != NO_UNIT)
				{
					CvUnitInfo& kLoopUnit = GC.getUnitInfo(eLoopUnit);

					iTempValue = 0;

					if (kLoopUnit.getPrereqAndBonus() == eBonus)
					{
						iTempValue += 50;
					}

					for (iJ = 0; iJ < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iJ++)
					{
						if (kLoopUnit.getPrereqOrBonuses(iJ) == eBonus)
						{
							iTempValue += 40;
						}
					}

					iTempValue += kLoopUnit.getBonusProductionModifier(eBonus) / 10;

					if (iTempValue > 0)
					{
						bool bIsWater = (kLoopUnit.getDomainType() == DOMAIN_SEA);
						
						// if non-limited water unit, weight by coastal cities
						if (bIsWater && !isLimitedUnitClass((UnitClassTypes)(kLoopUnit.getUnitClassType())))
						{
							iTempValue *= std::min(iCoastalCityCount * 2, iCityCount);	// double coastal cities, cap at total cities
							iTempValue /= std::max(1, iCityCount);
						}

						if (canTrain(eLoopUnit))
						{
							// is it a water unit and no coastal cities or our coastal city cannot build because its obsolete
							if ((bIsWater && (pCoastalCity == NULL || (pCoastalCity->allUpgradesAvailable(eLoopUnit) != NO_UNIT))) ||
								// or our capital cannot build because its obsolete (we can already build all its upgrades)
								(pCapital != NULL && pCapital->allUpgradesAvailable(eLoopUnit) != NO_UNIT))
							{
								// its worthless
								iTempValue = 2;
							}
							// otherwise, value units we could build if we had this bonus double
							else
							{
								iTempValue *= 2;
							}
						}

						if (kLoopUnit.getPrereqAndTech() != NO_TECH)
						{
							iDiff = abs(GC.getTechInfo((TechTypes)(kLoopUnit.getPrereqAndTech())).getEra() - getCurrentEra());

							if (iDiff == 0)
							{
								iTempValue *= 3;
								iTempValue /= 2;
							}
							else
							{
								iTempValue /= iDiff;
							}
						}

						iValue += iTempValue;
					}
 
It checks all units and if they are enabled by that bonus, then iTempValue is greater than 0. If it is a ship, then its value is scaled by how many coastal cities you have compared to your total number of cities (it assumes that ships are more valuable if you have more coast). I have no idea why it does not do that scaling (which is a down scaling as the max scaling is 1) if it is a limited water unit (while there might not be one in standard Civ, there could be one defined with the XML).
 
Thanks AIAndy.

I think it does that down scaling (yes, that was bogging me too while making my own interpretation of the code) because if half or less of all cities are coastal, I guess it's suppose to tone down the importance of naval units in the value of a strategic resource. And island type of start will likely lead to an empire mostly >half citycount in coastal cities and thus it doesn't decrease the value of naval units enabled by the respective bonus, because most cities will benefit from the strategic resource.

Likely, it is in the same thought process as in a vote, it is 50% plus one vote to count majority.
 
Ok, I re-looked the IsLimitedUnitClass and I think I got the idea finally. Not really the purpose of that condition, but what it is.

As said, IsLimitedUnitClass function verifies if a unit is limited by either ( || : OR ) class that limits units. A unit can be limited globally (like world wonders), can be limited by team (player 1 can get X number of such and the other gets Y and X+Y must equal maxed out at defined number) or can be limited by player ID.

For an unmodded game, there is no such units limited by worldscale or teamscale, but they are units limited by player ID. Missionaries (maxed out at 3) and Executives (maxed out at 5).

All other units show that typical :

Code:
<Type>UNITCLASS_TYPICAL_UNIT</Type>
			<Description>TXT_KEY_TYPICAL_UNIT</Description>
			<iMaxGlobalInstances>-1</iMaxGlobalInstances>
			<iMaxTeamInstances>-1</iMaxTeamInstances>
			<iMaxPlayerInstances>-1</iMaxPlayerInstances>
			<iInstanceCostModifier>0</iInstanceCostModifier>
			<DefaultUnit>UNIT_TYPICAL_UNIT</DefaultUnit>

Thus, !IsLimitedUnitClass() returns a unit that does not fit to single limitation (worldscale, teamscale or playerscale).

For naval units, that is basically all of them and not only the workboat.

Any use in the concerned lines of code? Nope or not really.
Leftover code? Possibly or just meat for modders I suppose.

Can Someone confirm the logics of my reasoning. Please. I can't know if no one gives me a nod.
 
Again stuck to something that makes no sense to me.

Take a quick look here:

Code:
RouteTypes eBestRoute = getBestRoute();
			for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
			{
				RouteTypes eRoute = (RouteTypes)(GC.getBuildInfo((BuildTypes)iI).getRoute());

				if (eRoute != NO_ROUTE)
				{
					iTempValue = 0;
					if (GC.getRouteInfo(eRoute).getPrereqBonus() == eBonus)
					{
						iTempValue += 80;
					}
					for (iJ = 0; iJ < GC.getNUM_ROUTE_PREREQ_OR_BONUSES(); iJ++)
					{
						if (GC.getRouteInfo(eRoute).getPrereqOrBonus(iJ) == eBonus)
						{
							iTempValue += 40;
						}
					}
					if ((eBestRoute != NO_ROUTE) && (GC.getRouteInfo(getBestRoute()).getValue() <= GC.getRouteInfo(eRoute).getValue()))
					{
						iValue += iTempValue;
					}
					else
					{
						iValue += iTempValue / 2;
					}
				}
			}

What poses me problem is that :
GC.getRouteInfo(eRoute).getPrereqBonus() == eBonus
For a certain bonus resource (assume COAL or OIL), if the prereq resource enables a typeRoute (assume the only normal game case: RAILROAD), then add the to previous iTempValue a value of 80.

Ok, that seems fine. What about the next :

GC.getRouteInfo(eRoute).getPrereqOrBonus(iJ) == eBonus

That seems repetitive redundant.

It basically takes info from CIV4RouteInfos.xml:

Code:
<RouteInfo>
			<Type>ROUTE_RAILROAD</Type>
			<Description>TXT_KEY_ROUTE_RAILROAD</Description>
			<iValue>2</iValue>
			<iAdvancedStartCost>18</iAdvancedStartCost>
			<iAdvancedStartCostIncrease>0</iAdvancedStartCostIncrease>
			<iMovement>20</iMovement>
			<iFlatMovement>6</iFlatMovement>
			<BonusType>NONE</BonusType>
			<PrereqOrBonuses>
				<PrereqOrBonus>
					<BonusType>BONUS_COAL</BonusType>
				</PrereqOrBonus>
				<PrereqOrBonus>
					<BonusType>BONUS_OIL</BonusType>
				</PrereqOrBonus>
			</PrereqOrBonuses>
			<Yields/>
			<TechMovementChanges/>
			<Button>Art/Interface/Buttons/Builds/BuildRailroad.dds</Button>
		</RouteInfo>

I clearly recognize PrereqOrBonuses where if eBonus (assume COAL or OIL), then a +40 to iTempValue.

Now comes the node of my problem:

What was the first condition?
That GC.getRouteInfo(eRoute).getPrereqBonus() == eBonus.

:crazyeye::eek:
 
It seems the one bonus for <BonusType> allows an AND contruction, whereas the other bonuses are an OR construction.
Means with the current setup you need coal or oil for railroads, but you could also make it require both (but not more than 2).
They should have maybe named it PrereqAndBonus, or something like that.


Can Someone confirm the logics of my reasoning. Please. I can't know if no one gives me a nod.

That's also my interpretation :yup:.
 
Thanks for the nod.

So basically, in that line:

Code:
if (GC.getRouteInfo(eRoute).getPrereqBonus() == eBonus)
					{
						iTempValue += 80;
					}

PrereqBonus() is in fact a PrereqAndBonus().
And since there is only the railroad needing resource(s), then there is not a single PrereqBonus() in the normal game, right?
Thus, that iTempValue+=80 is scrap until I put some TypeRoute needing two required resources.
 
A quick one:

Assume a subroutine function:

Func(int iArg1, int iArg2, int iArg3)

{
return Bla_bla_bla;
}

That function is called in the main code, but returns two arguments only.

iArg=1 and iArg=2.

Do I assume iArg3 takes by default -1?
 
Thus, that iTempValue+=80 is scrap until I put some TypeRoute needing two required resources.

My interpretation :yup:.
(but I'm not a C++ coder)

A quick one:

Assume a subroutine function:

Func(int iArg1, int iArg2, int iArg3)

{
return Bla_bla_bla;
}

That function is called in the main code, but returns two arguments only.

iArg=1 and iArg=2.

Do I assume iArg3 takes by default -1?

uuhh...err...um...a function can return less arguments than are given as input, that is normal.
If you mean if it could be called with less than the 3 defined arguments: That's also possible, but not sure how it's done in C++. But it would have to be mentioned somewhere what the default value is, so no guessing, that would be written down somewhere in the function header, I think.
 
I didn't know there was so much of a difference between python, C++ and other type of modders.
I'm not any of them for instance. Just a regular player learning by backtracking method ( superficial understanding to deeper).

Here an example and btw, I don't mind if you return you can't understand. ;)


Spoiler :
Code:
int CvPlayerAI::AI_bonusVal(BonusTypes eBonus, int iChange) const
{
	int iValue = 0;
	int iBonusCount = getNumAvailableBonuses(eBonus);
	if ((iChange == 0) || ((iChange == 1) && (iBonusCount == 0)) || ((iChange == -1) && (iBonusCount == 1)))
	{
		//This is assuming the none-to-one or one-to-none case.
		iValue += AI_baseBonusVal(eBonus);
		iValue += AI_corporationBonusVal(eBonus);
	}
	else
	{
		//This is basically the marginal value of an additional instance of a bonus.
		iValue += AI_baseBonusVal(eBonus) / 5;
		iValue += AI_corporationBonusVal(eBonus);		
	}
	return iValue;
}

In the conditional structure:

if ((iChange == 0) || ((iChange == 1) && (iBonusCount == 0)) || ((iChange == -1) && (iBonusCount == 1))) {bla_bla_bla}

it needs from the subroutine both iChange and iBonusCount.

iBonusCount is simply returning the number of instances of eBonus.
iChange, I don't try to understand (because local variables often have no definition, just links) but I saw a call elsewhere with no iChange.
That's a bummer.

Here it is:

iValue = GET_PLAYER(getOwnerINLINE()).AI_bonusVal(eNonObsoleteBonus);

eBonus is all non-obsoleted resources, but no argument for iChange.

Looking back to:

if ((iChange == 0) || ((iChange == 1) && (iBonusCount == 0)) || ((iChange == -1) && (iBonusCount == 1))) {bla_bla_bla}

I assumed iChange by default is -1.

I'll see in CvPlayer.h . Thanks for pointing that out.

EDIT: You don't mind if I put some threads outside SDK/Python forums.
You see, it seems a dead place while more people see outside this subforum.
 
Code:
int AI_bonusVal(BonusTypes eBonus, int iChange = 1) const;

Yup. You were right! It is defined by default in the header. :):):)

So calling the functions just paste over the default values. :goodjob:
 
I've been looking some stuff in my free times and made my own interpretation of things.

Yes, it has mainly to do with SDK/C++ but perhaps with a flavour of python.


It all started with my general problem about member variables that end to be dead ends in my C++ readings.

First, I've read member variables were created to create a sense of indepedance of a function's element, so we can attribute some values to it without being completely tied to the function. Then, the member variable is reattached to the function.

After that, I was thinking about some values directly from an actual game and python exposed parts in the SDK. And python is the dynamic part of the game while the SDK/DLL is the rigid parts.

I looked for the Cy*****.cpp files and saw many member variables and methinks it gives definitive values to some of those aforementioned dead ends.

So, if I understand well, member variables are extraction of some functions (or classes occasionally) that python programming can take effect onto it. Those are the sensible parts of SDK to python.

Am I right?

Yes, it is a messy to express such inner workings, but I am still looking C++ tutorials and without any courses of any sort, it is messy learning and I trudge.

If some kind soul could enlighten me please. :please:
 
I'm not sure if you are using the terminology the way I think you are, but...

Any variable is just a place to put data. Member variables are just data. The data associated with the class it is a member of. It has nothing, directly, to do with functions. It has nothing, literally, to do with Python. You'd have them if there was no Python. You need data. For example, a unit has a strength value. This is data. It has to be kept somewhere. That somewhere is in a variable attached to a class. The data from the XML is loaded into instances of the *Info classes. Somewhere between some and all of the data in an info class is copied into the the actual instances used in the game - if you build a unit in the game it causes a CvUnit class object to be created and in the process of being created it copies a bunch of data from the CvUnitInfo class for the desired unit type. It's just data. Most of that data has functions you use to access it (read it or write it) but that is largely not necessary, in most cases the data could be accessed directly. For reasons I mostly won't go into, using functions to get/set/modify the data are often a good idea - one thing I will mention is that it allows you the opportunity to do a "sanity check" to make sure someone is not trying to read or write non-existent data (like an element off the end of an array, or before the beginning) or set a variable to an invalid value. I mention that because you can see that Civ frequently does this, a lot of it via Assert statements.

Anything that Python should be able to see has to be specifically exposed to the Python. That is what the Py*.h and Py*.cpp files are for. Those files give Python access to specific functions that exist in the DLL (as far as I know, it does not make any of the member variables directly available - it only directly exposes functions, some of which let you access data and some of which "do things").
 
Member variables are just data.


First, thanks for the extended post, always a mine of info for me. :)

Yeah, looking more and more the SDK and more and more I see those variables just as data, sometimes tied to a function.

For instance, take circumnavigation example.

Code:
bool CvGame::isCircumnavigated() const
{
	return m_bCircumnavigated;
}

My lasting problem is even if m_bCircumnavigated is just data, how the game recognize this variable is tied to either circumnavigation has been done or not.
And there are plenty of those member variables I can tie with a certain class.
 
This question is easy, so please try answer it. It'll take 15 seconds.

In my civ4 folder game, I see two types of Assets folder containing their own XML files.
There is one outside Warlord folder and BTS folder and one subfolder Assets for each expansion pack.
I also noticed some XML files are lacking in particular versions like CIV4UpkeepInfo.xml does not exist in BTS folder nor in Warlord one. Does it mean there were no updates on these XML files and thus just refer the general Assets folder outside BTS and Warlord folders?
 
Any variable is just a place to put data (after reading more and more code). Member variables are just data.

Now, it settled in my mind, it is really data. But let assumes the naming of that member variable is hard to follow and I can't figure out what does it mean: how am I going to track back where the data is taken?
 
This question is easy, so please try answer it. It'll take 15 seconds.

In my civ4 folder game, I see two types of Assets folder containing their own XML files.
There is one outside Warlord folder and BTS folder and one subfolder Assets for each expansion pack.
I also noticed some XML files are lacking in particular versions like CIV4UpkeepInfo.xml does not exist in BTS folder nor in Warlord one. Does it mean there were no updates on these XML files and thus just refer the general Assets folder outside BTS and Warlord folders?

Assumign a BtS mod:
Any file not in the mod uses the file from BtS.
If it is not in BtS it uses the file from Warlords.
If it is not in Warlords it uses the file in regular Civ4.

Well, actually it starts at Custom Assets if the mod is not set to disallow that in the mod's .ini file.
 
Now, it settled in my mind, it is really data. But let assumes the naming of that member variable is hard to follow and I can't figure out what does it mean: how am I going to track back where the data is taken?

You can do a search for the variable name and find out where the data comes from and what it is being used for. If the variable name is not helpful, perhaps the name of the XML tag it is loaded from (assuming it is loaded and not generated some other way) will help. But in general the name of a variable does not really have anything to do with what it is used for - the computer doesn't care what you call it, only what you do with it. Therefore the only way to really know what it is for is to do that search and look at everything it is used for.

For example, say the InfoThing class has a variable m_iData. This is probably not a useful name, all variables hold data so it tells you pretty much nothing other than the "i" probably means it is an integer but you can tell that by how it is declared anyway. So you search for m_iData, focusing initially on the file where InfoThing is declared. It probably has an InfoThing::getData function that returns the value of m_iData and also an InfoThing::setData function that sets it, and maybe an InfoThing::changeData function as well. (The variable might not appear anywhere else other than perhaps the initialization code for InfoThing.) So search for setData and see where it is being called. If the variable is loaded from some data in an XML file then the XML parsing code for InfoThing must set it, so what tag in what XML file is the source? If it is not loaded, it must be used to hold some value that is somehow calculated from other things. What are those things? After seeing where the data in that variable comes from, you'd check to see where getData is called to see how it is being used.
 
Back
Top Bottom