(AI Cities) Have you seen anything more perverse?

This is why I am always going early wars as I know the neighbour will have much better land!! Having more than one neighbour helps if the computer thinks coal, iron or other resources are good ones. How does the computer decide what a good start is? Does it rate zero food tiles like gold high? Or is it more weighted to food? Hmmm. I always bank that a normal start will have 5 resources albeit sometimes on a bad roll you have uranium or late games one like coal that are useless.
 
Last edited:
No, you're right.

In HandicapInfo.xml this parameter called "iStartingLocPercent".
It is 90% on Deity, 80% on immortal and 10% on settler, and this works in this way: right after a map generation, game estimates all starting positions, and gives to human position that worse than iStartingLocPercent% of them.
So, with 9 AI players on Deity you always will get the worse possible starting location ( based on game understanding what is good and what is bad of course ).
I'm pretty sure the game does not assign starts to the player like that. I mean with a little bit of patience (or Mapfinder) you can easily roll an awesome deity start without the AI all having stronger ones.
Where those percentages more likely might come into play would be be the start normalizer, which adds ressources, forests etc. to a player's start until its calculated value gets close enough to the best start on the map. Like maybe on deity it stops improving your bad starts earlier than say on noble, where it would still add ressources.
 
According to the code, that variable is only referenced once, when assigning starting plots.

Spoiler :
Code:
{
        iHumanSlot = range((((countCivPlayersAlive() - 1) * GC.getHandicapInfo(getHandicapType()).getStartingLocationPercent()) / 100), 0, (countCivPlayersAlive() - 1));

        for (iI = 0; iI < iHumanSlot; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                if (!(GET_PLAYER((PlayerTypes)iI).isHuman()))
                {
                    if (GET_PLAYER((PlayerTypes)iI).getStartingPlot() == NULL)
                    {
                        GET_PLAYER((PlayerTypes)iI).setStartingPlot(GET_PLAYER((PlayerTypes)iI).findStartingPlot(), true);
                        playerOrder.push_back(iI);
                    }
                }
            }
        }

This means the human player is pushed back in the initial assignment of the starting plots, so that the AI's are assigned first, and more are assigned before based on the # of players in the game.

As far as I can tell, the game does things in the following order:
assignStartingPlots(); <--- StartingLocationPercent Referenced Here
normalizeStartingPlots();
initFreeUnits();

Now it's possible I am misunderstanding something here, but that function is the only time it's referenced in the code as far as I could find.
 
Looked at it one more time ( last time I've looked at this many years ago... ).
So, this is from function assignStartingPlots(); :


Code:
    iHumanSlot = range((((countCivPlayersAlive() - 1) * GC.getHandicapInfo(getHandicapType()).getStartingLocationPercent()) / 100), 0, (countCivPlayersAlive() - 1));

[block1]
        for (iI = 0; iI < iHumanSlot; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                if (!(GET_PLAYER((PlayerTypes)iI).isHuman()))
                {
                    if (GET_PLAYER((PlayerTypes)iI).getStartingPlot() == NULL)
                    {
                        GET_PLAYER((PlayerTypes)iI).setStartingPlot(GET_PLAYER((PlayerTypes)iI).findStartingPlot(), true);
                        playerOrder.push_back(iI);
                    }
                }
            }
        }

[block2]
        for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                if (GET_PLAYER((PlayerTypes)iI).isHuman())
                {
                    if (GET_PLAYER((PlayerTypes)iI).getStartingPlot() == NULL)
                    {
                        GET_PLAYER((PlayerTypes)iI).setStartingPlot(GET_PLAYER((PlayerTypes)iI).findStartingPlot(), true);
                        playerOrder.push_back(iI);
                    }
                }
            }
        }

[block3]
        for (iI = 0; iI < MAX_CIV_PLAYERS; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                if (GET_PLAYER((PlayerTypes)iI).getStartingPlot() == NULL)
                {
                    GET_PLAYER((PlayerTypes)iI).setStartingPlot(GET_PLAYER((PlayerTypes)iI).findStartingPlot(), true);
                    playerOrder.push_back(iI);
                }
            }
        }

How do I understand it: first we find variable iHumanSlot. For example, if there are 18 CIVS, and we are playing at Deity it will be 17 * 0.9 = 15.3 rounded down = 15. For Settler it will be 17 * 0.1 = 1.7 or rounded 1.

This means that first we are finding starting locations for 15 AIs ( block1), then for human ( block2 ) and then for AI's left.
Next thing is - how works findStartingPlot?
I've had impression before that it just finds the best available plot on the map. And that's why who chooses first - gets advantage, who chooses last ( for example, human on Deity ) - gets poor starting point.

Let's look at this place in function findStartingPlot:

Code:
for(int iPass = 0; iPass < GC.getMapINLINE().maxPlotDistance(); iPass++)
    {
        CvPlot *pBestPlot = NULL;
        int iBestValue = 0;
      
        for (iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
        {
            pLoopPlot = GC.getMapINLINE().plotByIndexINLINE(iI);

            if ((iBestArea == -1) || (pLoopPlot->getArea() == iBestArea))
            {
                //the distance factor is now done inside foundValue
                iValue = pLoopPlot->getFoundValue(getID());

                if (bRandomize && iValue > 0)
                {
                    iValue += GC.getGameINLINE().getSorenRandNum(10000, "Randomize Starting Location");
                }

                if (iValue > iBestValue)
                {
                    bValid = true;

                    if (bValid)
                    {
                        iBestValue = iValue;
                        pBestPlot = pLoopPlot;
                    }
                }
            }
        }

        if (pBestPlot != NULL)
        {
            return pBestPlot;
        }

So this looks like we check all plots on the map ( if area is not set, and in random maps it looks like it is not set ), and calculate for all of them getFoundValue , and take the best value.
But what I've forgot on this years that there is a randomization process in it: we add random number from 0 to 9999 to the calculated value for each plot.
For example if we found plot1 with value 5000, and plot2 with value 7500, but random adding in first case was 6234 and in second case - 1117, then game still choose plot1 as the best one.
And this actually leave probability even to the first chooser to get a poor place. But still probably who choose close to last places ( as for example, human player on deity ) will have too small chance to get the best possible starting plot, at least in game with many AI opponents. Even if randomization process spoil choose for one or two AI's, and they miss the 3-golds start, it could not spoil it for all of them!

Next is to look - what is getFoundValue, how it estimates specific plot?
This is really big and complicated function as it is used during the whole game to estimate value of the plot too, so it take into account everything, even culture and units... Even if we try to look only at parameters that taken into account when bStartLocation is true ( i.e. this called for estimate this plot as a starting location ) it is too complicated.
So, I can say, for example some insights like:

"You get 600 points if plot is coastal. And additionally 1000 if this the only plot on this area ( on this continent/island ).
You get 200 points if it is on the hill. 40 points if it connected to the river and additional 100 points for freshWater. "

I can say that it takes into account resources, water, that plots with 2 food definitely better than 1 food etc. It penalties for deserts or water plots. Or plots that impassable to reach as mountains or other continent plots. It takes into account not only the plots within radius 2, but also estimates with a smaller weight plots within radius 5 etc. etc.
Also it is, of course, prevents to set starting location too close to the existing ones - otherwise everybody would just start in the same, the richest area.

But other things are too hard too say without deep analysis: this function looks like a total mess.
I even can't say this additional 5000 ( in average ) scores for randomization - is it many? few? Does it makes choose of starting location totally random, or, counterwise, has very small effect?
Hard to say.
 
Last edited:
I'm pretty sure the game does not assign starts to the player like that. I mean with a little bit of patience (or Mapfinder) you can easily roll an awesome deity start without the AI all having stronger ones.
Where those percentages more likely might come into play would be be the start normalizer, which adds ressources, forests etc. to a player's start until its calculated value gets close enough to the best start on the map. Like maybe on deity it stops improving your bad starts earlier than say on noble, where it would still add ressources.

If you look at my previous comment - there is a randomization process that adds random number between 0 and 9999 every time engine estimates the starting plot. So, this is probably explains your games with mapFinder: with big enough number of tries, you can achieve that all deity opponents will choose poor starting point because of this randomization, and leave the best starting point to you. This is probably the easier to achieve the lesser opponents you have.
 
Back
Top Bottom