Adding new Resource Yields to Future Techs

Sorry, what do you mean by "Foo"?

Sorry, Foo and Bar are programming terms that mean "whatever" or "random variable name". They are placeholders. What I meant is that you should pick some other object, such as Player or City, and look in its source files, CyCity.h/cpp or CyPlayer.h/cpp. In this case, Foo stands for the name of the object that you picked.
 
Umm... gee, lots of options here...

So, I think my best bet is CyPlayer.h & .cpp, and CyPlayerInterface1.cpp. They've been changed thus:

Code:
[B]CyPlayer.h[/B]

int getYieldChange(YieldTypes eIndex);

Code:
[B]CyPlayer.cpp[/B]

int CyPlayer::getYieldChange(int /*YieldTypes*/ eIndex)
{
	return m_pPlayer ? m_pPlayer->getYieldChange(YieldTypes eIndex) : -1;
}

Code:
[B]CyPlayerInterface1.cpp[/B]

.def("getYieldChange", &CyPlayer::getYieldChange, "int (YieldTypes eIndex)")
 
Looks good to me. I was only thinking of the third code block when I said it would be 1 line. I forgot about the CyFoo ;) wrapper classes. You'll want to expose CvTechInfo::getYieldChange(int i) the same way.
 
On compile though, I get this error, referencing CyPlayer.cpp:
CyPlayer.cpp|1838|error C2511: 'int CyPlayer::getYieldChange(int)' : overloaded member function not found in 'CyPlayer'|

And wouldn't this, in CyInfoInterface1.cpp take care of exposing CvTechInfo::getYieldChange(int i)?
Code:
.def("getYieldChange", &CvTechInfo::getYieldChange, "int (int i)")

Or maybe I don't need CyPlayer.h & .cpp, just CyPlayerInterface1, same as I setup for CyInfoInterface1...?
 
I didn't look closely enough to your posted code. First, the type of the iIndex argument differs between CyPlayer.h and CyPlayer.cpp. In the .h, you have YieldTypes; in the .cpp you have int. The "int" is probably correct, but you should double-check this against some other function that takes a yield index.

Also, there's an error in the third block of code. Remove "YieldTypes" from the function call:

Code:
return m_pPlayer ? m_pPlayer->getYieldChange([s][COLOR="DarkOrange"]YieldTypes[/COLOR][/s] eIndex) : -1;

Or maybe I don't need CyPlayer.h & .cpp, just CyPlayerInterface1, same as I setup for CyInfoInterface1...?

You definitely need CyPlayer.h/cpp, and I believe you expose that function to Python rather than the one in CvPlayer.h/cpp. The CyFoo classes are there to wrap the CvFoo classes for Python. It's a common coding pattern (Facade and Proxy). As long as you are doing the same things that are done for ExtraHappiness, it should be okay.

The reason those aren't needed for the CvFooInfo classes (assuming you are right that they are not) is because every function on them is exposed.
 
BTW,

(int /*YieldTypes*/ eIndex)

Is the same as

(int eIndex)


the /* */ are comment blocks, meaning the code actually ignores anything inside of them. The information is just there so you know when reading the code what this int is meant to represent.
 
What's next? :cool:

Test. Test. Test. Play a test game for a little while (maybe assign YieldChanges to some early techs so you don't have to gift techs in WB) to make sure it works.

Then see what happens when you capture a city from an AI that has some YieldChanges and you have some YCs too. I expect that the city will revert to non-YC values. Verify this.

Then look over CvPlayer::acquireCity() (I think that's the right name) and see if you can figure out where to transfer the YCs from the new owner to the city. You will add them to the city the same way as you do in CvTeam::processTech(): CvCity::changeBaseYieldRate().
 
The information is just there so you know when reading the code what this int is meant to represent.

But don't remove it. Those comments are also used by the script that builds the Civ4 Python API HTML. If you ever want to see your API in HTML form, you'll be glad you kept it. ;)
 
That's great guys. Have already tested it. They're properly learnable, and add Yields as expected, no more weirdness as before, when it wasn't an array. They add bonuses when an AI learns them (gifted through WB at the moment), but as you expected EF, the bonuses don't stay on when a city changes hands. Although all the other cities for a civ will keep what's been earned thus far, the captured city gets cycled back to zero. Learning a bonus tech after the capture adds only the bonus for the newly learned tech (IOW, just +1).

So, I wll start with CvPlayer::acquireCity(), referencing CvTeam:: ProcessTech(): CvCity::changeBaseYieldRate() as I need to. I could probably just base my additions off ExtraHealth/Happiness, couldn't I? ... hmm, maybe not... ExtraHealth/Hapiness include a bunch of other things besides just Techs... hmm
 
For no particular reason other than to be near the last line posted below, I would put the code into CvCity::init(). The following is, as usual, uncompiled and untested:

Code:
	GET_PLAYER(getOwnerINLINE()).updateMaintenance();

[B]	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		changeBaseYieldRate((YieldTypes)iI, GET_PLAYER(getOwnerINLINE()).getYieldChange((YieldTypes)iI));
	}[/B]

	GC.getMapINLINE().updateWorkingCity();

Putting it here will cover

  • Founding
  • Conquering
  • Gifting
 
EF, you're a genius!! That works perfectly!!! Capturing or gifting a city now updates the Yield bonuses to whatever the receiving civ should have, and founding always worked anyways. :goodjob: :thanx:

OK, next then. I need to teach the AI what to do with these. So, seeing as how so far my edits were in CvInfos, CvPlayer, CvTeam, & CvCity, I should probably proceed with the AI copies of CvPlayer/Team/City, right?

I think though, that this is going to be an order of magnitude more difficult than what we've accomplished so far... :dunno: :scared:

EDIT: umm... ya, I haven't got a clue what I'm doing...
 
EF, you're a genius!!

Shhh, I'm trying to keep that on the D/L. :D

Founding always worked anyways. :goodjob: :thanx:

It did? Hmm, I'll have to ponder that some day.

OK, next then. I need to teach the AI what to do with these.

Just to be clear, the AI will receive these benefits. But you probably know that and are seeking to teach the AI the value of these benefits, right? On that end, I know as much as you. :) I'd guess this is in CvPlayerAI.cpp. Somewhere in the code is a formula for calculating the value of a tech, but I have never looked at that code.

I'm hoping xienwolf's FfH experience might help. You certainly jumped into the deep end of the pool on this one!
 
Shhh, I'm trying to keep that on the D/L. :D
You're secret 's safe with me! ;)

It did? Hmm, I'll have to ponder that some day.
Ya, it always worked... Maybe there's some tiny bit of code that gets run through when one is founding a city that doesn't get checked when you capture one? Doesn't sound like that makes sense, but...

Just to be clear, the AI will receive these benefits...
Ya, that part works. I'm trying to make sure it knows what to do about it. At the endgame, it doesn't matter too much, 'cause they're aren't any other options for it study. But at the same time, it might just sit there forever researching a Food tech, without ever switching to Prod or Commerce. My bigger concern is, what if there's an early tech that has a bonus assigned to it? At this time, I don't think the AI would be able to recognize the added benefit...

You certainly jumped into the deep end of the pool on this one!
Deep end of pool? I feel like I walked off the edge of the continental shelf! :lol:

I guess we'll just wait for xienwolf's next reply then, so that he can give me some pointers. In the meantime, I'll see if I can get my added Yields to display. Python's somewhat easier thankfully!
 
Correct, the AI won't know anything about these bonuses. I'd love to know more about how the AI makes these kinds of decisions. The only "AI" code I've ever done is for simple turn-based games like tic-tac-toe (woot!), checkers and chess.

In those games you have a board-evaluation function that calculates a single value for a player's position and use a min-max algorithm, checking all possible moves in a huge move tree. That becomes suboptimal in chess as there are far too many possible moves to check, and chess is an easy game. The AI for Civ must be more strategy-based for some decisions, but I have no idea really.

I was shocked at how easy the min-max algorithm was, and the same could happen here.
 
The reason founding worked and transfer didn't probably is that when you capture/gift a city it creates a brand new one for the new leader, then copies data over from the old city to the new one for the new leader to have a "duplicate" so anything run during the founding process is run per player, and things which might be different from one player to another require modification of the transfer process.


Teaching the AI isn't actually that hard. Deciding what to teach it is the trick.

Everything you want to do for the AI will be done within:

Code:
TechTypes CvPlayerAI::AI_bestTech(int iMaxPathLength, bool bIgnoreCost, bool bAsync, TechTypes eIgnoreTech, AdvisorTypes eIgnoreAdvisor)

This function is where the AI decides precisely what it wants to research. Numbers tend to be fairly high on this function, so keep in mind that there is about a 2000 point gap between techs where the AI considers they are worth "about the same" as each other. So the defining "THIS IS ALWAYS THE BETTER CHOICE" value is in excess of 4,000.

You want to scroll down to the line reading:
Code:
iValue += kTeam.getResearchProgress((TechTypes)iI);

Everything up to this point you can consider to be "administration" and is required no matter which tech you attach these fields to. Just look at how each of the "chunks" of code look from here in on the function and I think you'll understand what to do REAL quickly.

Things which might be useful to know about though:

The AI has set values for each yield depending on personality and strategy. Incorporating those will be important, so look at this section of the code:

Code:
								for (iJ = 0; iJ < GC.getNumImprovementInfos(); iJ++)
								{
									for (iK = 0; iK < NUM_YIELD_TYPES; iK++)
									{
										iTempValue = 0;

										iTempValue += (GC.getImprovementInfo((ImprovementTypes)iJ).getTechYieldChanges(iI, iK) * getImprovementCount((ImprovementTypes)iJ) * 50);

										iTempValue *= AI_yieldWeight((YieldTypes)iK);
										iTempValue /= 100;

										iValue += iTempValue;
									}
								}

And it shows you a good approach to using those weights.

Also from the section on deciding about gaining access to new improvements they teach the computer that food is vital, and that if your finances are shot, commerce rules.

Code:
														// food bonuses are more valueble
														if (iL == YIELD_FOOD)
														{
															iTempValue *= 2;
														}
														// otherwise, devalue the bonus slightly
														else if (iL == YIELD_COMMERCE && bFinancialTrouble)
														{
															iTempValue *= 4;
															iTempValue /= 3;
														}
														else
														{
															iTempValue *= 3;
															iTempValue /= 4;
														}

Really that is about all you need to know I think. Don't forget to multiply by the number of cities the player owns somewhere, since this bonus is applied per city. Someone with 20 cities will value such a tech FAR more than someone with a single city.
 
Guys, how does this look?

Code:
if (GC.getTechInfo((TechTypes)iI).getYieldChange(YIELD_FOOD))
{
	if (iPercentOfDomination < 75)
	{
	iValue += (getNumCities() * 200);
	}
	else
	{
	iValue += (getNumCities() * 80);
	}
}

if (GC.getTechInfo((TechTypes)iI).getYieldChange(YIELD_PRODUCTION))
{
	iValue += (getNumCities() + ((atWar) ? 50 : 15) * 5);
}

if (GC.getTechInfo((TechTypes)iI).getYieldChange(YIELD_COMMERCE))
{
	iValue += (getNumCities() + ((bFinancialTrouble) ? 45 : 14) * 5);
}

I wanted to keep the effects on decision-making subtle, so I didn't use values that were too big. As it is, Production trumps Commerce, and Food trumps Production. Except in financial stress, when Commerce wins, or War, when Prod trumps all.

What do you think? Good decision process? Room for improvement? Thanks again! :)


EDIT: Found an glitch, might be minor. The following error:
CvPlayerAI.cpp|3351|warning C4551: function call missing argument list|
is produced by the YIELD_PRODUCTION iValue statement. I think it's referring to the use of "atWar". DOn't know what to do about it...
 
EF, some things are confusing me about RawYields.py...

What're the all CAPS items in each self._addYield declaration, and where are they defined? I've got my addition setup like this:
Code:
def getTechYieldChange((self, eYield, iValue):
	self._addYield(eYield, TECHNOLOGIES, iValue)
which of course didn't work, or I wouldn't be writing this. ;)
 
Guys, how does this look?

That looks like a reasonable start. One thing odd I noticed is that the first one multiplies by getNumCities() while the other two add it. Is that intentional?

I think it's referring to the use of "atWar".

Is the variable called bAtWar()? If you are trying to call the function CvPlayerAI::atWar()--assuming there is one--, you need to add ():

Code:
iValue += (getNumCities() + ((atWar[B][COLOR="YellowGreen"]()[/COLOR][/B]) ? 50 : 15) * 5);

What're the all CAPS items in each self._addYield declaration, and where are they defined?

They are numeric constants defined at the top of the file, one per line item. You need to add it to the list at the top and increment the NUM_TYPES value:

Code:
NUM_TYPES = [B][COLOR="DarkOrange"]13[/COLOR][/B]
(
	...
	FOREIGN_OVERSEAS_TRADE,
	
	[B][COLOR="YellowGreen"]TECHNOLOGIES,[/COLOR][/B]
	BUILDINGS,
	...
) = range(NUM_TYPES)

Add a new label to the list in the same order you inserted TECHNOLOGIES:

Code:
# Labels
LABEL_KEYS = ("TXT_KEY_CONCEPT_WORKED_TILES",
			  ...
			  "TXT_KEY_CONCEPT_TECHNOLOGIES",
			  ...
)

Don't forget to create that <TEXT> entry in one of your CIV4GameText XML files or use one of the ones in the original game such as "TXT_KEY_PEDIA_CATEGORY_TECH".

You'll then need a function to capture it and call that function from processCity():

Code:
	def processCity(self, pCity):
		"""
		Calculates the yields for the given city's tiles, specialists, corporations and multipliers.
		The building and trade yields are calculated by CvMainInterface.
		"""
		self.calculateTiles(pCity)
		[B][COLOR="YellowGreen"]self.calculateTechnologies(pCity)[/COLOR][/B]
		self.calculateSpecialists(pCity)
		self.calculateCorporations(pCity)
		self.calculateModifiers(pCity)

[B][COLOR="YellowGreen"]	def calculateTechnologies(self, pCity):
		pPlayer = gc.getPlayer(pCity.getOwner())
		for eYield in range(YieldTypes.NUM_YIELD_TYPES):
			iValue = pPlayer.getYieldChange(eYield)
			self.addTechnology(eYield, iValue)

	def addTechnology(self, eYield, iValue):
		self._addYield(eYield, TECHNOLOGIES, iValue)[/COLOR][/B]

Finally, add your line item to the list in the loop in fillTable():

Code:
for eType in (DOMESTIC_TRADE, FOREIGN_TRADE, FOREIGN_OVERSEAS_TRADE, [B][COLOR="YellowGreen"]TECHNOLOGIES, [/COLOR][/B]BUILDINGS, CORPORATIONS, SPECIALISTS):

Again, put it in order wherever you want it.
 
Top Bottom