Capitulation Mechanics

Gooblah

Heh...
Joined
Jun 5, 2007
Messages
4,282
Browsing through the files, I found the code for capitulation. My main concern is the way average power is calculated - literally, Total Power divided by the Number of Civs. Sounds good, but the presence of weak colonies or vassals makes an AI impossible to capitulate even if they have one city left and one unit, so long as that unit is an MA and a backwards colony is using Longbowmen. Okay, maybe a bit of exaggeration, but the point holds.

Anywho, I'm considering two methods to change this.
1) Drop the last three (variable, based on MapSize) Civs in terms of power.
2) Ignore colonies and vassals in the calculations.

Any tips on how to go about the first? It seems less drastic than the second, and more player-friendly.
 
I found an incredibly simple solution, but I'm having trouble figuring how to identify Worldsize.

I included CyMap.h in CvTeamAI.cpp, but I'm sure how to utilize the getWorldSize() function. any ideas? :confused:

Edit, never mind, i think i got it. Could someone review this code for me?

Code:
      int mapsize = CyMap::getWorldSize;
		int iAveragePower;
		if (mapsize == WORLDSIZE_DUEL || mapsize == WORLDSIZE_TINY)
		iAveragePower = iTotalPower / std::max(1, GC.getGameINLINE().countCivTeamsAlive());
		else if (mapsize == WORLDSIZE_SMALL || mapsize == WORLDSIZE_STANDARD)
		iAveragePower = iTotalPower / (std::max(1, GC.getGameINLINE().countCivTeamsAlive())-2);
		else if (mapsize == WORLDSIZE_LARGE || mapsize == WORLDSIZE_HUGE)
		iAveragePower = iTotalPower / (std::max(1, GC.getGameINLINE().countCivTeamsAlive())-3);

Note, in-file line 1 is on the same column as line 2.
 
You should bring this over to the better AI forums, especially if you have working code.
 
You should bring this over to the better AI forums, especially if you have working code.

Thanks! :) Sadly, I can't compile due my usage of a) Vista, b) VS 2009, and c) inability to find the libs folder for the program (and thus, can't insert the necessary libraries to make everything work). :(
 
Okayy....got compiling to work. :goodjob:

However, there is an error. Apparently, I can't call CyMap::getWorldSize like that, meaning that I'm making a foolish error. :mad: It's probably simple, so....hmm.

this is the current code:

Code:
int mapsize = CyMap::getWorldSize;

and it gets this error:
CvTeamAI.cpp|1594|error C3867: 'CyMap::getWorldSize': function call missing argument list; use '&CyMap::getWorldSize' to create a pointer to member||


Now, I tried adding the '&' symbol for the pointer, but i got another compiling error; apparently, getWorldSize() is a void, while my variable is an int. At the same time, I can't change my variable's type...rrgh. Any ideas? I ran through the CvMap and CyMap .cpp/.h to find otu if I was calling the function incorrectly...


Edit: WOOOOOOOOOOOOOOOOOOOOO! :goodjob: it compiled! fixed it after a few hours (really dumb mistake, messed with pointers/classes). Code I added/altered:

Code:
int iTotalPower = GC.getGameINLINE().countTotalCivPower();
		[B]int iAveragePower, worldsize;
		CvMap i;
		worldsize = i.getWorldSize();
		if (worldsize == WORLDSIZE_DUEL || worldsize == WORLDSIZE_TINY)
		iAveragePower = iTotalPower / std::max(1, GC.getGameINLINE().countCivTeamsAlive());
		else if (worldsize == WORLDSIZE_SMALL || worldsize == WORLDSIZE_STANDARD)
		iAveragePower = iTotalPower / (std::max(1, GC.getGameINLINE().countCivTeamsAlive())-2);
		else if (worldsize == WORLDSIZE_LARGE || worldsize == WORLDSIZE_HUGE)
		iAveragePower = iTotalPower / (std::max(1, GC.getGameINLINE().countCivTeamsAlive())-3);[/B]
		int iMasterPower = GET_TEAM(eTeam).getPower(false);
		int iVassalPower = (getPower(true) * (iPowerMultiplier + iPersonalityModifier / std::max(1, iMembers))) / 100;

Edit 2: Dammit! :mad: Another problem. Compiler couldn't input 'release.obj'. No idea what this is...any clues?
 
The problem with your code is that you're creating a new map and asking it for its size, which probably defaults to STANDARD. What you want to do instead is ask the game for the map that's being used and get the size from it.

Code:
int iMapSize = [B]GC.getMap().getWorldSize()[/B];
 
The problem with your code is that you're creating a new map and asking it for its size, which probably defaults to STANDARD. What you want to do instead is ask the game for the map that's being used and get the size from it.

Code:
int iMapSize = [B]GC.getMap().getWorldSize()[/B];

Oh. :D Thanks dude!

So, lemme get this straight:

GC references CvGlobals.h, right? so within the getmap() function of class CvGlobals, there's a getWorldSize()?

Edit: Weird, Codeblocks still can't compile....rrgh.
 
GC references CvGlobals.h, right?

Essentially, yes. Technically, GC is a preprocessor macro defined in CvGlobals.h that calls a global function that returns the single CvGlobals object for the system.

Code:
[B]GC[/B].getMap()

is turned into

Code:
[B]CvGlobals::getInstance()[/B].getMap()

by the C++ preprocessor using simple string replacement.

Within the getmap() function of class CvGlobals, there's a getWorldSize()?

Not exactly. CvGlobals::getMap() returns the CvMap that the game is using. CvMap::getWorldSize() returns the size of that map.

I would recommend reading a brief introduction to Object Oriented Programming or OOP. This will explain the difference between the function CvMap::getMap() and calling this function on an instance of CvMap as happens in the code above.
 
Yeah, I'm taking a course in C++ this semester, really useful.
:goodjob:

Anywho, started working on my next 'project', for Kevinman's World History Mod. Quick conversion of CvTeamAI.cpp to make voluntary vassalage an option only if the vassal's power is 55% or less of the prospective master's power. Pretty easy, I guess, but since I'm a bit of a noob, I wanted to get some advice...;).

So, found the necessary function. Edited it to add a quick if statement comparing iTheirPower and iOurPower:

Code:
if ( ((55 * iOurPower)/100) <= iTheirPower)

then added (after all the for loop jazz) an else statement that returned DENIAL_ATTITUDE:

Code:
else
	{
		return DENIAL_ATTITUDE;
	}

Pretty straightforward. My problem is that I'm not sure if DENIAL_ATTITUDE is correct...I assumed so, since that's what's used in map trading when it refers to a rejection from another AI who's not their worst enemy. I think it looks right, and I'll play around with it...

Edit: nvm, realized I hadn't defined iOurPower/iTheirPower. Done.
 
Two suggestions for ya:

First, on game logic, how about using DENIAL_POWER_US for "We're doing fine on our own." DENIAL_ATTITUDE is for when they don't like you enough; you don't need to be their worst enemy for this one, by the way.

Second, on coding style, I would reverse your if test and move the "return" up to it:

Code:
if (55 * iOurPower /100 > iTheirPower)
{
	return DENIAL_POWER_US;
}

I think this is better because the result of not being powerful enough (returning a denial) is right next to the test that causes it. This is sometimes called "early return" in that you bail out of the function as soon as you determine that's what you want to do. Some people don't like having multiple exits from a function, and that can be confusing if there are multiple places in a long function.

However, I prefer to have effects near their causes. This way you don't have to read through the entire function to find out what happens in the "else" clause, nor do you see the else clause and have to scroll up to see what the if-test is.

A minor advantage is that it removes one level of indentation for everything that follows. While this is fairly trivial, as the function grows and gets more complicated, each level of nesting requires keeping a larger mental context which just makes it more difficult to understand at a glance what the function does.

Finally, note that I removed all but the outermost parentheses. This really is trivial, but if you memorize the few rules of operator precedence you will save yourself a lot of () typing and matching in the long run. :goodjob: In this case, * and / have the same precedence, so they are evaluated left-to-right, and the comparison operators have lower precedence, so > is evaluated last.

While the presence of () has no bearing on the compiler in this case, it forces me to match them to know what the expression does. Leaving them out allows me to read it more quickly by depending on the rules of operator precedence.
 
Two suggestions for ya:

First, on game logic, how about using DENIAL_POWER_US for "We're doing fine on our own." DENIAL_ATTITUDE is for when they don't like you enough; you don't need to be their worst enemy for this one, by the way.

Second, on coding style, I would reverse your if test and move the "return" up to it:

Code:
if (55 * iOurPower /100 > iTheirPower)
{
	return DENIAL_POWER_US;
}

I think this is better because the result of not being powerful enough (returning a denial) is right next to the test that causes it. This is sometimes called "early return" in that you bail out of the function as soon as you determine that's what you want to do. Some people don't like having multiple exits from a function, and that can be confusing if there are multiple places in a long function.

Oh yeahhhh.....the program checks through each code block one at a time right? So if your object meets condition 1, then it'll go for that, even if it could have met condition 2.

However, I prefer to have effects near their causes. This way you don't have to read through the entire function to find out what happens in the "else" clause, nor do you see the else clause and have to scroll up to see what the if-test is.

So, program checks the 'if' statement. if it doesn't meet the condition, it ignores it and heads to the next line of code - in this case a for loop.

Finally, note that I removed all but the outermost parentheses. This really is trivial, but if you memorize the few rules of operator precedence you will save yourself a lot of () typing and matching in the long run. :goodjob: In this case, * and / have the same precedence, so they are evaluated left-to-right, and the comparison operators have lower precedence, so > is evaluated last.

While the presence of () has no bearing on the compiler in this case, it forces me to match them to know what the expression does. Leaving them out allows me to read it more quickly by depending on the rules of operator precedence.

This I know, but IMO it's easier. Well, thanks EmperorFool. :goodjob:
 
Given that the code inside the if test exits the function with "return", there's no need to put the rest of the code following it inside an "else" block.
 
Top Bottom