• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

[PYTHON] AI create Project - How's this code?

smeagolheart

Monarch
Joined
Sep 4, 2007
Messages
924
Location
Phoenix, AZ
How's this look for AI to create the project?

Code:
	def AI_chooseProduction(self,argsList):
		pCity = argsList[0]

## Renaissance Start ##
		iOwner = pCity.getOwner( )
		pPlayer = gc.getPlayer( pCity.getOwner( ) )	
		iProject = gc.getInfoTypeForString("PROJECT_RENAISSANCE")
		iTech = gc.getInfoTypeForString('TECH_RENAISSANCE')
		tTech = gc.getInfoTypeForString('TECH_THEOLOGY')

		if ( gc.getTeam(pPlayer.getTeam()).isHasTech(iTech) == false ):
			if ( gc.getTeam(pPlayer.getTeam()).isHasTech(tTech) == true ):
				if pCity.canCreate( iProject, 0, 0 ):
					gc.getMap( ).plot( pCity.getX( ), pCity.getY( ) ).getPlotCity( ).pushOrder( OrderTypes.ORDER_CREATE, iProject, -1, False, False, False, True )
					return True	

## Renaissance End ##
		return False

It worked in my test mod but that was not a full blown game with a lot of things going on. Any possible issues you think?

This is from this thread: renaissance project
 
I think your code will choose the first city it tries to build the project, instead one of the top production cities.
 
You could try and insert debug messages (print command followed by any string or other value) in PythonDbg.log to follow what is happening with your code. Do you want some examples of how to do this? (Also, enable the debug log in the .ini file.)
 
No, unless someone can solve this by simply looking at the code (I can't seem to, at least) you need to debug it yourself. So if you add a print statement anytime the code is executed, then you can follow it in the debug log. And if you add additional print statements inside the code you can see what is actually happening. Like:
Code:
print ("city name", pCity.getName())
This would be useful information when testing, not?

Or:
Code:
		if ( gc.getTeam(pPlayer.getTeam()).isHasTech(iTech) == false ):
			if ( gc.getTeam(pPlayer.getTeam()).isHasTech(tTech) == true ):
				if pCity.canCreate( iProject, 0, 0 ):
					[B]print "all conditions cleared!"[/B]
					gc.getMap( ).plot( pCity.getX( ), pCity.getY( ) ).getPlotCity( ).pushOrder( OrderTypes.ORDER_CREATE, iProject, -1, False, False, False, True )
					return True
By the way, this is not entirely necessary:
Code:
gc.getMap( ).plot( pCity.getX( ), pCity.getY( ) ).getPlotCity( )
Because you already have the CyCity instance you need assigned to the name pCity. You can just use that directly:
Code:
pCity.pushOrder( OrderTypes.ORDER_CREATE, iProject, -1, False, False, False, True )
Just out of curiosity: Are you creating this code yourself (the interesting use of white space inside the code suggest this) or are you still basically copy-pasting stuff?
 
In my test, AI had two cities, one the capital and the other a brand new city. AI tried to build it in the capital

That's because the capital is the 1st city so it selects the project.
If the capital is a lousy production city, and there are other much better production cities, it will probably still build this project in your capital.

Again, that's what I *think* will happen. to actually know it I need to debug the code.

I don't know which code you're basing your code on, but looking in the SDK (BTS 3.19) at CvCityAI::AI_chooseProduction():

There are a few conditions before calling the python function (e.g. are we about to finish current production, are we training the first defender of this city), that if any of which is true - the python function is not called.

Assuming it is called - your code will select this city (assuming it's possible) to build the project.

The AI in the SDK, before building a project, checks that the city is one of the top production cities:

Code:
// This goes over all the cities and checks the production rank of each, 
// returning this city's rate (1 will be the top production city)
iProductionRank = findYieldRateRank(YIELD_PRODUCTION);

...

if (!bDanger && (iProductionRank <= ((kPlayer.getNumCities() / 5) + 1)))
{
	if (AI_chooseProject())
	{
                // A project was selected for build
                return;
	}
}

Your code ignores this (it just doesn't reach this code).
 
That's because the capital is the 1st city so it selects the project.
If the capital is a lousy production city, and there are other much better production cities, it will probably still build this project in your capital.

Again, that's what I *think* will happen. to actually know it I need to debug the code.

I don't think so.

That function is run every time a city finishes what it was working on and wants to build something new. A high production city typically builds a lot more things so calls this function more often. This would result in the project having a tendency to be built in a high production city more often than in a low production city. But not always.

Even with that being the case, it would be a good idea to make sure the city has above average production so as to avoid building it in some tiny tundra city with 3 production (finally, after a century or three, it finishes it's first build and it is the very turn on which the project can be started; what now? You want me to build this huge project? Sure thing. It'll only take about 200 turns (or whatever).). Something like the method shown would probably be a good way to go.
 
I don't think so.

That function is run every time a city finishes what it was working on and wants to build something new.

Actually, this not the only time this function is run. It is also run whenever the production flag is dirty, which can be (at minimum of 10 turns interval) whenever you research a new tech, for example.
So after getting theology is a probable time for all the cities to recheck their production.
 
This seems to work

Code:
	def AI_chooseProduction(self,argsList):
		pCity = argsList[0]

## Renaissance Start ##
		iOwner = pCity.getOwner( )
		pPlayer = gc.getPlayer( pCity.getOwner( ) )	
		iProject = gc.getInfoTypeForString("PROJECT_RENAISSANCE")
		iTech = gc.getInfoTypeForString('TECH_RENAISSANCE')
		tTech = gc.getInfoTypeForString('TECH_THEOLOGY')

		if ( gc.getTeam(pPlayer.getTeam()).isHasTech(iTech) == false ):
			if ( gc.getTeam(pPlayer.getTeam()).isHasTech(tTech) == true ):
				## go over the cities and checks production rank, returning this city's rate (1 will be the top production city)
				iProductionRank = pCity.findYieldRateRank(1)
				topProd = ((pPlayer.getNumCities() / 5) + 1)
				
				if iProductionRank <= topProd:
					if pCity.canCreate( iProject, 0, 0 ):
						gc.getMap( ).plot( pCity.getX( ), pCity.getY( ) ).getPlotCity( ).pushOrder( OrderTypes.ORDER_CREATE, iProject, -1, False, False, False, True )
						return True	                ## project was selected for build					
## Renaissance End ##
		return False

There is no equivalent to the "bDanger" in that code though so I am somewhat afraid the AI will change production regardless of whether "Genghis is knocking down the gate" unless that is somehow not an issue. Is canCreate anything to worry about? I looked it up in cyplayer and there really isn't much there.

Code:
bool CyPlayer::canCreate(int /*ProjectTypes*/ eProject, bool bContinue, bool bTestVisible)
{
	return m_pPlayer ? m_pPlayer->canCreate((ProjectTypes)eProject, bContinue, bTestVisible) : false;
 
The code looks good.
canCreate won't cause you any problems (look at CvPlayer.cpp, instead of CyPlayer.cpp, for the actual implementation).

bDanger is actually CvCityAI::AI_isDanger, which you can add to CyCity to expose to python (assuming you already compile the DLL).

Try adding this to CyCity.h:

Code:
bool AI_isDanger();

This to CyCity.cpp:

Code:
bool CyCity::AI_isDanger()
{
	return m_pCity ? m_pCity->AI_isDanger() : false;
}
And this to CyCityInterface1.cpp:

Code:
.def("AI_isDanger", &CyCity::AI_isDanger, "bool () - is city in danger?")

(I haven't compiled the code, so there might be errors).
 
Actually, this not the only time this function is run. It is also run whenever the production flag is dirty, which can be (at minimum of 10 turns interval) whenever you research a new tech, for example.
So after getting theology is a probable time for all the cities to recheck their production.
Interesting. From the behavior of this in Final Frontier (Plus or not) I knew it was called from time to time other than when the last thing was done, but I had no idea why or, especially, that finishing a tech could do it (although that does make sense - the tech could have some new thing that is better to produce than what you were producing). I probably should have looked at the code...
 
The code looks good.
canCreate won't cause you any problems (look at CvPlayer.cpp, instead of CyPlayer.cpp, for the actual implementation).

bDanger is actually CvCityAI::AI_isDanger, which you can add to CyCity to expose to python (assuming you already compile the DLL).

Try adding this to CyCity.h:

Code:
bool AI_isDanger();

This to CyCity.cpp:

Code:
bool CyCity::AI_isDanger()
{
	return m_pCity ? m_pCity->AI_isDanger() : false;
}
And this to CyCityInterface1.cpp:

Code:
.def("AI_isDanger", &CyCity::AI_isDanger, "bool () - is city in danger?")

(I haven't compiled the code, so there might be errors).

I don't have a complete SDK instance set aside for this so it will take a long time to do to set up the proper files to test. I wonder is this is the best way and most value added to get the end result.
 
Back
Top Bottom