Simple Python Things

can be :p

if there is a quick python thread the pythonistas in charge should be The_J, Plat and myself (and baldyr if he ever returns) :p
 
Wouldnt it be easier to just rename this thread to quick python questions :lol:

Hey, this thread has another purpose :D.

Was just a random thought, because we have unrelated questions in Platyping's and my thread, so we could also create a general thread.

And what j_mie6 said :yup:.
 
hmm god-emperor would have to be a part of the team :lol: he may appear when you least expect him to but he literally knows everything about python and sdk in civ :p
 
:yup:

Nevertheless weems like it would have to be a moderator decision if ones made..and theres only one of those here :p

Oh, everyone is free to open a thread, so no need for me to decide anything :D.

And sure not necessary to put someone into charge of a thread, but it's just nice of it's clear that someone can answer questions in a thread, especially when not many people might be around who have the abilities.
 
I still limited to certain aspects of python :D
Have not bothered to explore the other parts which I am not interested in :mischief:

Was just a random thought, because we have unrelated questions in Platyping's and my thread, so we could also create a general thread.

He is talking about you, Jamie :lol:
 
ah yes but in my defense: it's platyping's python, not platyping's works with no questions :lol:
 
Plat, you will be happy to know I referenced you in my guide to the API (and basic introduction to python modding) :D:

Spoiler :

This is done with the use of a comment (which starts with # everything past that on the same line is ignored!)
Code:
[COLOR="orange"]def[/COLOR] [COLOR="blue"]countWorkingTilesForTypeInCity[/COLOR](pCity, eTerrain):
[COLOR="green"]        """
        Counts the number of tiles that the city pCity (CyCity) is working of eTerrain (TerrainType)
        """[/COLOR]
        i = 0 [COLOR="Red"]#declare variable to hold the number of tiles[/COLOR]
        [COLOR="orange"]for[/COLOR] x [COLOR="orange"]in[/COLOR] xrange(21): [COLOR="Red"]#cycle through numbers 0 to 20 and perform below operations on them[/COLOR]
                pPlot = pCity.getCityIndexPlot(x) [COLOR="red"]#accesses the city plot x (where x is cycled through 0 to 20)[/COLOR]
                [COLOR="orange"]if[/COLOR] (pPlot.isBeingWorked() [COLOR="orange"]and[/COLOR] pCity.canWork(pPlot)) [COLOR="orange"]or[/COLOR] pPlot.isCity(): [COLOR="red"]#if the plot is worked (by that city!) or the plot itself is a city[/COLOR]
                        [COLOR="orange"]if[/COLOR] pPlot.getTerrainType() == eTerrain: [COLOR="red"]#and the terrain matched the terrain specified[/COLOR]
                                i += 1 [COLOR="red"]#make i equal to i + 1[/COLOR]
        [COLOR="orange"]return[/COLOR] i [COLOR="red"]#returns number of working types of type eTerrain[/COLOR]

[COLOR="orange"]def[/COLOR] [COLOR="blue"]countWorkingTilesYieldInCity[/COLOR](pCity, iYield):
       [COLOR="Green"] """
        Counts the total Yield of type specified by iYield (food, hammers, commerce) of the tiles worked by the city
        """[/COLOR]
        i = 0 [COLOR="red"]#declare variable to hold the total yield[/COLOR]
        [COLOR="orange"]for[/COLOR] x [COLOR="orange"]in[/COLOR] xrange(21): [COLOR="Red"]#cycle through numbers 0 to 20 and perform below operations on them[/COLOR]
                pPlot = pCity.getCityIndexPlot(x) [COLOR="red"]#accesses the city plot x (where x is cycled through 0 to 20)[/COLOR]
                [COLOR="orange"]if[/COLOR] (pPlot.isBeingWorked() [COLOR="orange"]and[/COLOR] pCity.canWork(pPlot)) [COLOR="orange"]or[/COLOR] pPlot.isCity(): [COLOR="red"]#if the plot is worked (by that city!) or the plot itself is a city[/COLOR]
                        i += pPlot.getYield(iYield) [COLOR="red"]#then get the yield of the tile and increase i by that much[/COLOR]
        [COLOR="orange"]return[/COLOR] i [COLOR="red"]#return the total yield of type eYield from the city[/COLOR]
 
[COLOR="orange"]def[/COLOR] [COLOR="blue"]egyptPenalty[/COLOR]():
[COLOR="Green"]        """
        In order to balance Egypt's power 0.5 of a hammer is removed for every plains tile worked. Rounded up.
        """[/COLOR]
        pEgypt = pointer("Egypt", CyPlayer) [COLOR="red"]#Get a pointer for Egpyt (this is using Baldyr's incredible CivPlayer utility[/COLOR]
        (loopCity, iter) = pEgypt.firstCity(False) [COLOR="red"]#get egypt's first city[/COLOR]
        [COLOR="Orange"]while[/COLOR] (loopCity): [COLOR="red"]#while there is actually a city[/COLOR]
                pCity = loopCity [COLOR="Red"]#assign pCity to the loopCity value[/COLOR]
                iCurrentPlains = countWorkingTilesForTypeInCity(pCity, eEgyptianTerrain) [COLOR="red"]#use function to calculate number of plains worked by city[/COLOR]
                iCurrentCityYield = countWorkingTilesYieldInCity(pCity, eProduction) [COLOR="red"]#use function to calucate the total hammer yield in city[/COLOR]
                iProductionForPlains = gc.getTerrainInfo(eEgyptianTerrain).getYield(eProduction) [COLOR="red"]#how many hammers is a plains tile worth?[/COLOR]
                iDifference = iCurrentCityYield - int(round(iCurrentPlains * float(iProductionForPlains)/2)) [COLOR="red"]#times the number of plains by the hammers they are worth and half it. then remove it from city's yield[/COLOR]
                pCity.setBaseYieldRate(eProduction, iDifference) [COLOR="red"]#make the city's yield for hammers equal to the new value iDifference[/COLOR]
                (loopCity, iter) = pEgypt.nextCity(iter, False) [COLOR="red"]#move on to the next city[/COLOR]
Muuuuuch better. Now we can better understand what is happening here. Note the triple quotation marks: that is a docstring and allows functions and methods to be given a description. If any more experienced python programmers are reading and think why did he not use:
Code:
for iCity in xrange(pEgpyt.getNumCities()):
	pCity = pEgypt.getCity(iCity)
Here is a note from fellow pythonista Platyping on that problem when I made it ages ago:
[that code] is a bad idea. When cities are conquered, there will still be a empty slot left behind, resulting in those cities in the last few entries skipped
. Be careful when iterating over cities!!! He also elaborates on the cause:
Example:
I have 5 cities, A to E
City 0 refers to A
City 4 refers to E
numCities = 5

Now C is conquered
City 0 = A
City 1 = B
City 2 = None
City 3 = D
City 4 = E
numCities = 4

So using for loop, you will skip City E now.
However, building a new city F will fill up the empty slot.
Thanks to Platyping there for uncovering that flaw in the system!


thought I ought to finish it soon so it can go into our quick questions thread as a link :D
 
Yikes, I rather you don't mention me, then I get less messages :D
Anyway, I was not the one who discovered that method.

I discovered that flaw, and consult the great The_J (Ask him for questions when you have python doubts:D) about it.
Then he told me yeah that was discovered quite sometime ago, but no one bothered to update any of the old python works since the original makers like tsentom or GIR are all inactive.
Since I pretty much learn my python by studying old codes, so I pretty much picked up the bad habits of old codes, and slowly discover them...
Then he provided me with that solution.
 
Speaking of which, I do have one question :D
Any idea how canHaveBonus is supposed to work?

I tried one simple test:
Code:
	def onEndPlayerTurn(self, argsList):
		'Called at the end of a players turn'
		iGameTurn, iPlayer = argsList
	
## King Richard's Crusade Start ##
		pPlayer = gc.getPlayer(iPlayer)
		if pPlayer.isHuman():
			pCity = pPlayer.getCapitalCity()
			Dye = gc.getInfoTypeForString("BONUS_DYE")
			for i in range(21):
				pPlot = pCity.getCityIndexPlot(i)
				if pPlot.canHaveBonus(Dye, true):
					pPlot.setBonusType(Dye)
## Machu Picchu End ##
to see how it is meant to work, but whether i use pPlot.canHaveBonus(Dye, true) or pPlot.canHaveBonus(Dye, false), nothing happens.

Of course, if I delete that line, the whole capital is flooded with dyes in all plots, so nothing wrong with the other lines
 
I'm sure nobody is gonna message you because I said you discovered that :lol: but I will put in there to post questions in the help thread, that way we all can answer them :D
 
number 1: Xrange :lol:

other than that I guess it is meant to check if the bonus can go onto the plot the boolean arguement is all about whether it should depend on the possition of the plot in the world. (maybe if some mod decides banana's can only spawn on the equator?). So either the plots it's iterating over can't acutally have dyes, it's broken, or the XML is lying again. I will look into the SDK and see what it says it does!

Spoiler :

Code:
bool CvPlot::canHaveBonus(BonusTypes eBonus, bool bIgnoreLatitude) const
{
	FAssertMsg(getTerrainType() != NO_TERRAIN, "TerrainType is not assigned a valid value");

	if (eBonus == NO_BONUS)
	{
		return true;
	}

	if (getBonusType() != NO_BONUS)
	{
		return false;
	}

	if (isPeak())
	{
		return false;
	}

	if (getFeatureType() != NO_FEATURE)
	{
		if (!(GC.getBonusInfo(eBonus).isFeature(getFeatureType())))
		{
			return false;
		}

		if (!(GC.getBonusInfo(eBonus).isFeatureTerrain(getTerrainType())))
		{
			return false;
		}
	}
	else
	{
		if (!(GC.getBonusInfo(eBonus).isTerrain(getTerrainType())))
		{
			return false;
		}
	}

	if (isHills())
	{
		if (!(GC.getBonusInfo(eBonus).isHills()))
		{
			return false;
		}
	}
	else if (isFlatlands())
	{
		if (!(GC.getBonusInfo(eBonus).isFlatlands()))
		{
			return false;
		}
	}

	if (GC.getBonusInfo(eBonus).isNoRiverSide())
	{
		if (isRiverSide())
		{
			return false;
		}
	}

	if (GC.getBonusInfo(eBonus).getMinAreaSize() != -1)
	{
		if (area()->getNumTiles() < GC.getBonusInfo(eBonus).getMinAreaSize())
		{
			return false;
		}
	}

	if (!bIgnoreLatitude)
	{
		if (getLatitude() > GC.getBonusInfo(eBonus).getMaxLatitude())
		{
			return false;
		}

		if (getLatitude() < GC.getBonusInfo(eBonus).getMinLatitude())
		{
			return false;
		}
	}

	if (!isPotentialCityWork())
	{
		return false;
	}

	return true;
}


let's see if we can work it out from that, really it seems to be a validator. Ie it will return true if the bonus was iron and the plot was grassland hills with no bonus already present (simmilar to what I was going to write in my optimiser.)
 
After more trials and errors, I guess I can estimate what it does :D
It does take care of the XML tags like forests, no river side etc, on top of obvious ones like peaks and water.
But it also checks to see whether there is already a bonus present...

Dye is a bad choice, since looking at the XML tags, it can only spawn in jungles on grassland.

Using cows make it easier to understand :D

But looking at this screenshot, I still dun understand why there is one empty grassland plot...
 
resource you can't see yet, horses, uranium, iron etc. Open up worldbuilder :p
 
Indeed, not very helpful code then :D
See, this one is indeed quick python question thread :D
 
haha for the end of my guide I just did a walkthrough writing a function that returns the number of cows present in city radiuses :lol:

Spoiler :

Code:
def countCowsInEveryCityRadius():
    """
    Counts all the cow bonuses that are in the BFC of every city in the world (sorted by civs).
    Note that the civ checks are not really nessicary but they are used to highlight the for loop!
    """

    iNumCows = 0 #Create a variable to store the number of cows
    gc = CyGlobalContext() #create our CyGlobalContext instance to use for methods later
    Game = CyGame() #create our CyGame instance to use for methods later
    eCow = gc.getInfoTypeForString("BONUS_COW") #create an enum to represent the cows :D
    for iPlayer in xrange(Game.countCivPlayersEverAlive()): #we could have used countCivPlayersAlive() but I wanted to highlight the next line
        pPlayer = gc.getPlayer(iPlayer) #get a CyPlayer instance of the player who has index iPlayer
        if not pPlayer.isAlive(): #if the player is dead
            continue #then skip the player and move on to the next one, he clearly doesn't have any cities!
        (loopCity, iter) = pPlayer.firstCity(False) #assign loopCity to the first city owned by the player! If you look in the API the False arguement here represents whether it should be reversed (ie take the last city)
        while (loopCity): #while there is actually a city to go onto!
            for iIndex in xrange(21): #why 21? Well there is a total of 21 plots in a city's BFC!
                pPlot = loopCity.getCityIndexPlot(iIndex) #get the plot at iIndex and assign it to pPlot (CyPlot)!
                if pPlot.getBonusType(-1) == eCow or pPlot.getBonusType(pPlayer.getTeam()) == eCow: #does the plot have an unowned cow or a cow owned by the city's owner, pPlayer? the -1 here represents noteam as stated in the TeamTypes in the API
                    iNumCows += 1 #add one to the num of cows.
            (loopCity, iter) = pPlayer.nextCity(iter, False) #move onto the next City
    return iNumCows #return the number of cows!


because you always use cows to explain things I have started to do it aswell :lol:
 
Quick question:

is CyCity.happyLevel() going to return the amount of :) or the amount of :) - :mad:?
 
Out of boredom and curiousity, did a simple test, added population to city based on happylevel for a couple of turns.
Amount of Happiness
 
Back
Top Bottom