As a French person I feel like it's my duty to explain strikes to you. - AdrienIer

Create an account  

 
Curious Civplayer - Mysteries of the DLL

(August 25th, 2020, 22:48)GeneralKilCavalry Wrote:
(August 25th, 2020, 19:12)sunrise089 Wrote: I have a question I think this thread might be perfect for: is the TBG map script at a given size altered by the number of players? In other words, is a Large size map different if there are 8 players in the game versus 10 or 12? I believe the answer is 'no' but a gamespy lobby argument has inspired me to find out from the experts. Thanks!

https://forums.civfanatics.com/threads/w...ns.478273/

You are correct - but a map with more players might slightly break the water %age - not sure about that one though.

Thank you! Would it make any difference if we play on ‘low’ sea level? I put ‘low’ in quotes because the map doesn’t have a sealevel adjustment, but if you select a different map which does, opt for low, and then switch back to TBG the setting will remain and there will be no water.
Reply

(August 5th, 2020, 22:03)Borsche Wrote: Something I would be interested in is governor tile preference lol. https://www.realmsbeyond.net/forums/show...p?tid=9679 its a very  embarassing experience but I ended up not popping out a warrior on the turn that would protect me from boak/gkc because they walked onto a hammers tile I was working. In the interturn it was reassigned to a different tile that left me a hammer short and the capital totally exposed. Something that would've been solved by logging in after boak/gkc and swapping tiles appropriately, but also something that wasn't anywhere close to my mind.

From my experience the governor heuristics are actually quite complex.  I've seen it smartly work a 2f plain grassland over a 1f copper mine because the city was at a small size and low on food surplus.  I've also seen it change between commerce and hammer tiles based upon the slider setting and the city's gold and science multipliers.  Certainly it often assigns things incorrectly based upon what we want (and this is to be expected since it has no concept of the bigger picture and your overall plan across multiple cities), but it also does a lot of things right and can surprise sometimes with decisions that you miss.
Reply

Back in PB49 Fintourist mentioned something that grabbed my attention:

(July 8th, 2020, 16:47)Fintourist Wrote: Speaking of bombarding, we are suffering here from a weird bug (I think it’s a bug). When catapults bombard against city walls they should reduce the defenses by 4 % each bombard. Right? Well that works exactly so, when the defending city has 20 %, 40 % or 80 % culture. However, when the city has 60 % culture, the cats only reduce the defenses by 3.7% or something like that. In essence, our 15 cats don’t get the defenses down to zero in 1 turn and for some reason 6 % defensive bonus remains.. No clue what causes that, but this forced us to move in one turn early as some of our cats have to bombard twice if we want to get rid of all defensive bonuses.  noidea

So How exactly is bombarding calculated?

To answer this we have to look in different source files. First in CvUnit.cpp and there in the bombard method. This is the interesting part there:


Code:
    int iBombardModifier = 0;
    if (!ignoreBuildingDefense())
    {
        iBombardModifier -= pBombardCity->getBuildingBombardDefense();
    }

    pBombardCity->changeDefenseModifier(-(bombardRate() * std::max(0, 100 + iBombardModifier)) / 100);
  • First we get the bombardModifier, which is the 50% reduction from Walls and the 25% reduction from Castles. Of course this only happens if we use a catapult or trebuchet to bombard.
  • We then call the changeDefenseModifer method of CvCity with the parameter bombardRate * reduction. This is the reduction we all expect so without Walls or Castles Cats do 8 and trebs do 16 bombard damage, with Walls 4 and 8 and lastly with Castle and Walls 2 and 4
Now we look at what is happening in CvCity:


Code:
void CvCity::changeDefenseModifier(int iChange)
{
    if (iChange != 0)
    {
        int iTotalDefense = getTotalDefense(false);

        if (iTotalDefense > 0)
        {
            changeDefenseDamage(-(GC.getMAX_CITY_DEFENSE_DAMAGE() * iChange) / iTotalDefense);
        }
    }
}

GetTotalDefense is the current maximum defense of the city. For example if you had the second border pop the city would be at 40% defense. Note that this is the maximum defense value and is not the reduced defense value due to bombard. For the purpose of a single bombardement this value is constant.

The only other interesting bit is the call of the changeDefenseDamage method with parameters: -(GC.getMAX_CITY_DEFENSE_DAMAGE() * iChange) / iTotalDefense
 
GC.getMAX_CITY_DEFENSE_DAMAGE() is a constant value set at 100
iChange is our previously caluclated bombard damage (8/4/2 for cats and 16/8/4 for trebs)
iTotalDefense is the previously mentioned GetTotalDefense

So if we assume a second border pop and a bombardement by a catapult we get the following calculation:

-(100 * 8) / 40 = -20

Now we go into the method changeDefenseDamage in CvCity:


Code:
void CvCity::changeDefenseDamage(int iChange)
{
    if (iChange != 0)
    {
        m_iDefenseDamage = range((m_iDefenseDamage + iChange), 0, GC.getMAX_CITY_DEFENSE_DAMAGE());

        if (iChange > 0)
        {
            setBombarded(true);
        }

        setInfoDirty(true);

        plot()->plotAction(PUF_makeInfoBarDirty);
    }
}

The only interesting part here is:


Code:
m_iDefenseDamage = range((m_iDefenseDamage + iChange), 0, GC.getMAX_CITY_DEFENSE_DAMAGE());

Here we just add up the new damage to the previously applied damage and ensure that the value is between 0 and 100. Basically what this means is that cities have a (hidden) HP bar just like units for bombardement. If we go back to our previous example. We apply 20 damage everytime we bombard. So with 5 bombardements we reach the maximum damage of 100 and wouldn't you know it, we also only need 5 catapult bombardements to reduce a 40% city to zero.

Now the last part to the whole riddle is the actual conversion of this HP bar into the defense value. This happens in CvCity in the method getDefenseModifier:


Code:
int CvCity::getDefenseModifier(bool bIgnoreBuilding) const
{
    if (isOccupation())
    {
        return 0;
    }

    return ((getTotalDefense(bIgnoreBuilding) * (GC.getMAX_CITY_DEFENSE_DAMAGE() - getDefenseDamage())) / GC.getMAX_CITY_DEFENSE_DAMAGE());
}

We once again see values that we already know by now like getTotalDefense, GC.getMAX_CITY_DEFENSE_DAMAGE(). The new value getDefenseDamage() is our accumulated damage. So let's go back to our previous example and let's say we bombarded our 40% city with three catapults, which results in 60 damage. The formula now gives this:

(40 * (100 - 60)) / 100 = 16

which is exactly the value we see in-game.


Now going back all to Fintourists comment about 60% defense. Why exactly does this strange behaviour happen? Well that's because of the wonderful thing called integer calculation. And the first time we hit that problem is in the changeDefenseModifier method. If get back the formula from there:


Code:
changeDefenseDamage(-(GC.getMAX_CITY_DEFENSE_DAMAGE() * iChange) / iTotalDefense);

and put in a 60% culture defense and a catapult bombarding without any Walls, we get the following calculation:

-(100 * 8) / 60 = 13.333333

Now because of integer calculation this will not apply 13.3333 damage to the city, but rather 13. Basically we are missing 1 point of damage every third bombardement.

The next step we hit integer calculation problems is in our final calculation in getDefenseModifier:


Code:
return ((getTotalDefense(bIgnoreBuilding) * (GC.getMAX_CITY_DEFENSE_DAMAGE() - getDefenseDamage())) / GC.getMAX_CITY_DEFENSE_DAMAGE());

Here we quite often hit a non integer number which is exactly the moment, where Fintourist noticed the strange behavior around 60% culture defense. 60% culture defense is actually the only culture defense, where this quirk is noticable with one major exception. This exception is the wonder Chichen Itza, which increases the culture defense in every city by 25%. With this we can achieve defense values of:
  • 25%
  • 45%
  • 65%
  • 75%
  • 85%
  • 105%
  • 125%
With the exception of 25% every other culture defense provided by this wonder hits these strange integer calculation problems. What this means is that not only does Chichen Itza increase your culture defense, it also provides a tiny bombard defense bonus thanks to some quirky math.

Summary

I initially investigated this so that I could bugfix the behaviour, but now that I understand the whole calculation, I'm somewhat hesitant. The big misunderstanding in the reduction value of Walls and Castles is that one would assume that with Walls one reduces the defense by 4% instead of 8% with a catapult. In actuality you only decrease the damage you do to the cities HP. The fact that for the most time this also results in a same reduction of defense is just lucky. The other interesting factor in hear is the hidden bonus of Chichen Itza.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Thanks for digging into this, Charriu! thumbsup Interesting stuff. I have read that Civ IV mostly (entirely?) sticks to integer math for performance reasons. Most of the time this works well and is handled carefully enough that we do not notice 'rounding error' quirks in the code. But sometimes things like this bombardment issue pop up if you know the exact circumstances to look for.
Reply

Another question from the community, this time from Cornflakes

Cornflakes Wrote:@Charriu, would you be interested in looking up the actual mechanics of deciding the dice roll for great people? It is a true proportional split between number of points invested in each type, or does some additional weighting get factored in due to the way the code is written (such as with religions favoring earlier founded cities).

So Can you trust the odds for great people creation?

Once again we look into CvCity and here into the method doGreatPeople:


Code:
        int iTotalGreatPeopleUnitProgress = 0;
        for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
        {
            iTotalGreatPeopleUnitProgress += getGreatPeopleUnitProgress((UnitTypes)iI);
        }

        int iGreatPeopleUnitRand = GC.getGameINLINE().getSorenRandNum(iTotalGreatPeopleUnitProgress, "Great Person");

        UnitTypes eGreatPeopleUnit = NO_UNIT;
        for (int iI = 0; iI < GC.getNumUnitInfos(); iI++)
        {
            if (iGreatPeopleUnitRand < getGreatPeopleUnitProgress((UnitTypes)iI))
            {
                eGreatPeopleUnit = ((UnitTypes)iI);
                break;
            }
            else
            {
                iGreatPeopleUnitRand -= getGreatPeopleUnitProgress((UnitTypes)iI);
            }
        }

This looks more complicated then expected, which is often a sign that something is up. Let's take the example of 15 points into Great Prophet, 35 into Great Artist and 50 into Great Scientist and see what the algorithm does:

  1. First we add up all the individual GreatPerson points in the first for loop. In our example this would be 15+35+50=100. Now the reason why they are doing that is, that if I stand at 99/100 GPP And add 3 more to any single Great Person I and up with 102 points for the creation algorithm and not 100.
  2. We generate a random number between 0 and the totalGreatPeoplePoints we just calculated in 1. In our example let's assume we roll the number 49.
  3. In the next for loop we iterate through all the unit types. This is done because every Great Person is a single unit type. That way we iterate across every single great person type.
  4. In this loop we check if the rolled random number is smaller then the current Great Person points. For this it is important to know in which order the Great Persons are checked and that would be Prophet->Artist->Scientist->Merchant->Engineer. If the number is smaller, then this is the Great Person to be created. If it's not then we subtract the current Great Person points from the randomly generated number and continue with the loop.
Now let's look at our example and how it behaves in the 4th step.

  1. First we check the Great Prophet. We have 15 points into that type, which is smaller then our random number of 49. Therefore we subtract  49-15=34. 34 is our new number to check against.
  2. Next we check against the Great Artist. We have 35 points into that, which is bigger then our random number of 34. Great we create a Great Artist and stop the loop.
  3. Great Scientist points are no longer checked, because we already found our Great Person to be created.
Summary


Looking at this process my first intention was: "Oh the order in which the Great Persons are checked is important and changes the odds". But looking at some examples proved that this is not the case. In fact this time the presented odds are correct and reliable. So you can trust the game with Great Person creation.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Note that that function isn't checking Great Person POINTS. It's checking Great Person UNIT progress, which is different.

Unit progress comes 1 per turn per source. If you have a national wonder producing 1 Great Artist point per turn and an engineer specialist producing 3 Great Engineer points, the result is 50/50 equal probabilities of GA vs GE. I believe this ignores GPP multipliers too.

This is how No Espionage creates the weird typeless points from spy sources. The main GPP counter is just one number and doesn't have its points subdivided per type. The spy sources increase that number but don't increase any of the per-type progress counters.
Reply

Ha I knew I was missing something there.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Can you look up how spy defense works with friendly spies and security bureaus?
Reply

Alright, well here's one that I'm curious about. BGPB13 spoilers (not that it really matters lol GKC you can read this if you want, its really weird)

I went to fire off a 2-man GA with a scientist and a prophet. I also had 2 engineers and they were all (stupidly) on the same tile. I selected the scientist and the prophet, and hit the GA button... and it ate one of my engineers. What the hell is going on here? How is the game selecting the GPs for a GA when there's multiple on the same tile?

From testing, I could have avoided this just by moving the prophet + scientist to a different tile, but its weird when they're all on the same tile. I'm not sure GAs were ever stress tested - from my own testing if you were to hit the GA with a ton of GPs on the same tile, it will actually trigger multiple golden ages. It'll eat up enough GP to trigger as many GAs as possible.

This is definitely something that wouldn't come up a bunch, but I'm playing the Nebuchadnezzar IND/PHI combo in BGPB13 so I just happened to have 4 GPs available. Thank god I didn't have 5 and just burned them all at once.
Reply

(September 6th, 2020, 22:32)Borsche Wrote: Alright, well here's one that I'm curious about. BGPB13 spoilers (not that it really matters lol GKC you can read this if you want, its really weird)

I went to fire off a 2-man GA with a scientist and a prophet. I also had 2 engineers and they were all (stupidly) on the same tile. I selected the scientist and the prophet, and hit the GA button... and it ate one of my engineers. What the hell is going on here? How is the game selecting the GPs for a GA when there's multiple on the same tile?

From testing, I could have avoided this just by moving the prophet + scientist to a different tile, but its weird when they're all on the same tile. I'm not sure GAs were ever stress tested - from my own testing if you were to hit the GA with a ton of GPs on the same tile, it will actually trigger multiple golden ages. It'll eat up enough GP to trigger as many GAs as possible.

This is definitely something that wouldn't come up a bunch, but I'm playing the Nebuchadnezzar IND/PHI combo in BGPB13 so I just happened to have 4 GPs available. Thank god I didn't have 5 and just burned them all at once.

from what i was told, firing off a golden age ( despite selection certain great people for whatever stupid reason ) uses up the NEAREST to the capital great people.
"Superdeath seems to have acquired a rep for aggression somehow. [Image: noidea.gif] In this game that's going to help us because he's going to go to the negotiating table with twitchy eyes and slightly too wide a grin and terrify the neighbors into favorable border agreements, one-sided tech deals and staggered NAPs."
-Old Harry. PB48.
Reply



Forum Jump: