Some suggestions on City AI governance

Killtech

Discutator
Joined
Apr 16, 2009
Messages
1,022
Location
Bonn, Germany
ok, i've checked several AI cities in a game and i have to say it's a disaster. the AI is totally unable to cope with the many many new buildings in RoM and AND and it is one source of the AIs complete failure at being a worthy opponent. when i just compare the total production modifiers of my and their down there's a horrific gap: their cities are about +15% to +40% at best while i'm at +115% in nearly every city! considering the poor choices of improvements for the city tiles my cities outproduce theirs by a factor 4 in average!! it is even much worse for gold and science.

ok, so i think the AIs city-governing behavior really needs to be fixed. i think i can draw a sketch how an efficient and competitive city AI should work:

first the decisions depend on the situation. thus it makes sense to categorize the different situations a city can be in in general. in a attempt to set categories for production i relate it on the time needed to produce a standard building for each epoch on snail speed (e.g. a market place in medieval).
1. Conquered City: (foreign culture is higher than yours)
2. Small Town: (size between 1 and 3)
3. Developing City: (builds a standard building in more than 10)
4. Core City: (none of the categories above)
the prior categories are always dominant to the lesser ones, i.e. if a city fits in both category 1 and 2 it's just of category one.

ok, now for the build order:
Conquered City:
these type of cities have a big problem with foreign culture and need to fix it as fast as possible. they very dependant of help form outside like caravans and gold rushing buildings. these towns usually cannot help the empire in anyway so all production is used to solve the culture problem.
1. monument and theater + sent urgent request for caravans
2. if(monastery can be build in less than 20 turns) build monastery
else if(city size is below 2) build +:food: building preferring most cheapest
else build production buildings preferring most cheapest
3. city walls
4. if(temple can be build in less than 20 turns) build temple
else if(city size is below 2) build +:food: building preferring most cheapest
else build production buildings preferring most cheapest
5. if(:thumbsdown: +1 >= :thumbsup:) build happiness building preferring most cheapest
6. grany and lighthouse
7. if(can build a culture granting world wonder in less than 50 turns) build wonder
else if(can build a culture granting national wonder in less than 40 turns) build nat wonder
8. if(can build a production building (+:hammers: or +x%) in less than 10 turns) build production building
8. any building granting (static) culture - prioritize most cheapest
9. any happiness building preferring most cheapest (suppress revolutions)
10. points 1-9 completed? ok, use Developing City behavior then

Small Town:
This cites cannot contribute anything to the empire in this state. they try to do everything to leave this status and grow because they are highly dependent on outside help (caravans, rushing buildings).
1. monument (or theater)
2. if(city growth is in less than 30 turns) build grainy
3. any +:food: building starting the with cheapest
4. lighthouse + sent urgent request for caravans
5. if(points 1-4 are done) behavior of Developing City

Developing City:
are good enough to produce on their own without help form outside. they cannot contribute much to the empire but sometimes do. however their priority is to maximize production
1. if(last production was not for the empire) {
if(empire need military very urgently) {
produce military } }
2. any production building granting static bonus (+:hammers) - or if possible granting at least +1 hammer (static or through modifiers) - starting with most cheapest
3. if(:thumbsdown: +1 >= :thumbsup:) build happiness building prefering most cheapest
4. if(last production was not for the empire) {
if(random> 0.6) { if(small or conquered cities around) produce caravan }
else if(world wonders are build nearby && can build caravan in less than 10 turn) build caravan
else if(empire needs workers) produce worker }
5. any +:food: building starting with the cheapest
6. if(random>0.7 && can build a world wonder in less then 50 turns) build world wonder
7. if(trade route adding building can be build in less then 25 turns) build it
8. courthouse then any +science or +gold building starting with the cheapest
9. if(random>0.4 && empire needs military urgently) build military
10. military buildings cheaper than monastery, then monasteries - state religion first
11. military buildings cheaper than temple, then temples - state religion first
12. continue with Core City behavior

Core City:
these cities are the strength of an empire. they provide help for all other cities and concentrate on the empires needs.
1. if(empire needs military urgently) produce military
2. if random>0.3) { if(small cities or conquered cities around || world wonder building cities) build caravan }
3. any production building starting with most cheapest
4. if(:thumbsdown: + 2 >= :thumbsup:) build happiness building prefering most cheapest
5. military buildings - starting with best xp bonus
6. if(last production was not for the empire) {
if(random>0.5 && empire needs military) produce military
else if(empire has holy city) build missionary }
6. if(can build a world wonder in less then 40 turns) build world wonder
7. if(trade route adding building can be build in less then 25 turns) build it
8. courthouse then any +science or +gold building starting with the cheapest6.
9. national wonders of strategic importance
10. any trade rout adding building
11. if(random>0.6) world wonder
12. if(random>0.4) national wonder
13. if(this citiy posses a religion another city in the empire lacks && religous civics fit) build missionary
14. if(small towns or conquered cities or world wonder building anywhere in empire) build caravan
15. if(health building costs no maintenance) build it
16. if(empire has sufficient military) produce science for x turns
 
I agree and like your layout. Unfortunately Firaxis's code for Building Values is a 2k behemoth that is hard to understand and harder to fix. It needs major re-writing. The best way to re-write it would be to make it more modular, so that instead of one giant function it's 10-20 small functions that are for specific factors, like Health, happiness, production, etc...

That way it would be much easier to skip over functions (like health) for small cities, but not for powerful ones, or vice versa. If anyone would like to help with the actual code, PM me. I'm only going to be modularizing the code at this point, the AI will make exactly the same decisions, but it will be easier to fix it after this.
 
I agree and like your layout. Unfortunately Firaxis's code for Building Values is a 2k behemoth that is hard to understand and harder to fix. It needs major re-writing. The best way to re-write it would be to make it more modular, so that instead of one giant function it's 10-20 small functions that are for specific factors, like Health, happiness, production, etc...

That way it would be much easier to skip over functions (like health) for small cities, but not for powerful ones, or vice versa. If anyone would like to help with the actual code, PM me. I'm only going to be modularizing the code at this point, the AI will make exactly the same decisions, but it will be easier to fix it after this.
Is this all in the SDK?
 
I agree and like your layout. Unfortunately Firaxis's code for Building Values is a 2k behemoth that is hard to understand and harder to fix. It needs major re-writing. The best way to re-write it would be to make it more modular, so that instead of one giant function it's 10-20 small functions that are for specific factors, like Health, happiness, production, etc...

That way it would be much easier to skip over functions (like health) for small cities, but not for powerful ones, or vice versa. If anyone would like to help with the actual code, PM me. I'm only going to be modularizing the code at this point, the AI will make exactly the same decisions, but it will be easier to fix it after this.

wow, over 2100 lines of code for choosing production, and yet making very poor choices. i've just looked up the code. modulizing this is a good idea. just how? if it wound be up to me to write this function anew i'd make it just a function to determinate the situation and call a specific situation-script for each case. the situations would be just something like i described in my first post e.g. 'small town'. btw the 'if(kPlayer.isRebel())' on top level in AI_chooseProductionfrom revolutions is just something of that thinking - just needed to be packed in a separate function.

i'm a bit short on time right now - not even time to play much. but i think i could help if you need something specific.
 
wow, over 2100 lines of code for choosing production, and yet making very poor choices. i've just looked up the code. modulizing this is a good idea. just how? if it wound be up to me to write this function anew i'd make it just a function to determinate the situation and call a specific situation-script for each case. the situations would be just something like i described in my first post e.g. 'small town'. btw the 'if(kPlayer.isRebel())' on top level in AI_chooseProductionfrom revolutions is just something of that thinking - just needed to be packed in a separate function.

i'm a bit short on time right now - not even time to play much. but i think i could help if you need something specific.

Well, I've cut about 700 lines out and divided up 8 new individualized value functions already:

Code:
    int AI_buildingHappinessValue(BuildingTypes eBuilding);
    int AI_buildingHealthValue(BuildingTypes eBuilding);
    int AI_buildingRevolutionValue(BuildingTypes eBuilding);
    int AI_buildingExperienceValue(BuildingTypes eBuilding, bool bDomainSea = false);
    int AI_buildingUnitValue(BuildingTypes eBuilding);
    int AI_buildingMaitenanceValue(BuildingTypes eBuilding);
    int AI_buildingSpecialistValue(BuildingTypes eBuilding);
    int AI_buildingTradeValue(BuildingTypes eBuilding);

This will make it so we can do If (smallCity) checks much easier, without having to understand or re-write everything. At the very least, it's easier to understand...
 
Well, I've cut about 700 lines out and divided up 8 new individualized value functions already:

Code:
    int AI_buildingHappinessValue(BuildingTypes eBuilding);
    int AI_buildingHealthValue(BuildingTypes eBuilding);
    int AI_buildingRevolutionValue(BuildingTypes eBuilding);
    int AI_buildingExperienceValue(BuildingTypes eBuilding, bool bDomainSea = false);
    int AI_buildingUnitValue(BuildingTypes eBuilding);
    int AI_buildingMaitenanceValue(BuildingTypes eBuilding);
    int AI_buildingSpecialistValue(BuildingTypes eBuilding);
    int AI_buildingTradeValue(BuildingTypes eBuilding);

This will make it so we can do If (smallCity) checks much easier, without having to understand or re-write everything. At the very least, it's easier to understand...
:goodjob: will this cause any speed ups/downs?
 
Technically, it will slow the game down, but very very little. All that's new is a few variable assignments. So maybe, at most, 1 extra ms. :p

Okay, I have the monolithic function down to 1300 lines of code, from 2200+. It calls 19 different functions which make it much easier to read and debug. I still have more pruning to do, I doubt I will have much time tomorrow, but maybe Thursday.

Killtech, could you draw up a good way for the AI to figure out which City category they fall into? Also, keep in mind that the city functions are even more complex than your layout, since there is code for the AI to try cultural victories, or attempt to make a GPP farm. You might want to make some kind of exceptional case handling.
 
Conquered City: Under 50% of pop is conquerers

Small Town: under x population in each era so Ancient would be under 4, Classical 8, Medieval 12... needs play-test

how is that?
 
Conquered City: Under 50% of pop is conquerers

Small Town: under x population in each era so Ancient would be under 4, Classical 8, Medieval 12... needs play-test

how is that?

Nationality can be a bad way of telling if the city is conquered or not, because of IDW. You could just have lost a lot of battles near a border city... Which is why I would prefer a slightly more complex method of determination.
 
I implemented the Developing City and Big Gold City good into the AI logic. At first I moved it too close to the beginning of the AI selections, and the AI made a granary and warrior's hut before attempting to build their first warrior. :lol:

Not exactly optimal. So I moved it below the basic defense code and the worker selections, but before a lot of the unit rushing code. Watching a game with the changes, the AI's capital focused on getting a worker and two workboats first (a good idea, there was some good sea resources to be exploited.) Then they went for a granary, then a lighthouse. I can only assume this was the developing city code. The result was that the capital was a size 5 city and the AI was researching Bronze Working before it finally started building it's first settler. It got it's settler out and placed a new city on a nice river site.

Now, the second new city had two defenders that the capital sent, so all was good, so it began making a monument right away. Can't tell if this is me or the regular logic, since Monuments are usually built by the AI anyway.

A quick tangent. While I was adding the code, I found a (what I believe to be a bug) in the Early Wonder Selection code for cities. It divided the Chance of building a wonder by 5, then added 7:

iWonderChance /= 5;
iWonderChance += 7;

I assume that they meant to multiply it by seven (to increase the overall chance by 40%) instead of reducing it and adding an arbitrary amount. After changing that, the AI have been much more wonder-happy. Around the time the AI started researching Bronze working, Stonehenge was built. 8 turns later, the Oracle. Now, my AI's size 6 capital just started working on the Art of War while it's researching Iron working. It seems to be doing a much better job in that respect now.

Overall Summary:

The AI's cities are becoming more developed but their empires are expanding slower. I'm not sure if this is good or bad. Opinions? Good, Bad, Indifferent?
 
That sounds more like me - stay put and develop your cities, whilst building a couple of Wonders instead of army spamming :D
 
The AI's cities are becoming more developed but their empires are expanding slower. I'm not sure if this is good or bad. Opinions? Good, Bad, Indifferent?
Good, especially if playing with revolutions on.
 
Killtech, could you draw up a good way for the AI to figure out which City category they fall into? Also, keep in mind that the city functions are even more complex than your layout, since there is code for the AI to try cultural victories, or attempt to make a GPP farm. You might want to make some kind of exceptional case handling.

ok. in principle civ_kings proposal isn't that far away from what it should be. but indeed i failed to include a spacial script for the game start situation - the case where your best city is a small town. this is needed to force the AI to build military if it has nearly none at the start.

to handle this we'd need another category. First we need something like a 'Best City' which is only determined by having best production of all cities in the empire. Then we can define every city a ‘Main City’ which production is at least 66% of ‘best City’s or hammer difference is not larger than 3. Of course the ‘best city’ is always a ‘main city’. this category would handle only the most urgent problems:

Main City:
1. if(a city in the area has no defenders) build defensive military
2. if(random<0.4) { if(empire need military very urgently) build military }
else { if(city size>=3 && empire can affort another city (i.e. assumed maintenance of new town will still allow science spending of above 60%)) build settler
3. if(no workers) build worker
4. if(point 1-3 was not hit) continue with next category

With this we can let &#8216;small town&#8217; concentrate purely on their own needs never considering to build military (these assume military will be produced by a better developed city &#8211; or if none available a &#8216;main city&#8217; which can be in fact itself). I&#8217;ve added a minimal expansion policy but the conditions should prevent a too strong expansion. A minimal worker count should be standard too.
As for the &#8216;small town&#8217; category determination the population size is the main criteria because this cities profit from growth more than from any other improvement: there is a huge jump form 2 worked tiles to 3 &#8211; especially in production, and that&#8217;s what&#8217;s the most important. However if we have a town that has surprisingly high production for it&#8217;s size we could put it into &#8216;developing city&#8217;.
Condition for small town:
Population size <= 4 && production is below 10 :hammers: && there is none production building available that would at least grant effectively one hammer and would be buildable in less than 20 turns.

As for the &#8216;conquered city&#8217;: here the problem is basically that from the high foreign nationality the rev-instability will go up signifincantly. Furthermore the low culture implies in many cases that the borders are not in your favor thus you cannot work many tiles. So indeed the criteria for this is more complicated because it depends on whether revolution mod is on and fixed borders.
So I&#8217;d propose the following for Conquered City:
1. (Foreign Nationality >= 35% (rev-mod on) || Foreign Nationality >= 50% (rev-mod off) ) && majority of city tiles are not in players possession
2. (Foreign Nationality >= 45% (rev-mod on) || Foreign Nationality >= 70% (rev-mod off) ) && 75% of city tiles are in players possession
3. (Foreign Nationality >= 50% (rev-mod on) || Foreign Nationality >= 80% (rev-mod off) )
However I&#8217;m not quite sure on the concrete numbers.

Ok, now the 3 categories &#8216;main city&#8217;, &#8216;small town&#8217; and &#8216;conquered city&#8217; should not need any further exception handling. Cities which hit these conditions cannot contribute much for the empire except trying to become more independent and thus more important.

So all the special cases handling occurs in &#8216;developing cities&#8217; and &#8216;core cities&#8217; &#8211; the most of it in the &#8216;core cities&#8217;. all victory related (culture, space ship, religion) issues should be handled only in 'core cities'. but leader character preference should be done in both, 'developing and core cities'.
to be more specific: a agricultural fixed leader could prefer a cheap +:food: building over a +:hammers: building - so we get a mixing between food building production and industry. core to such decisions would be an float efficiency value: (expected :hammers:or:food:or:commerce: gain)/(turns needed to produce that building). so if even more specific: let's say a buling has 'Eh' hammer efficiency, another has 'Ef' for food then we have furthermore a dafault preference for :hammers: factor Fh = 2, food factor Ff = 1 and a leader modifier Mh, Mf than the decision process might look loke this: if(Eh*(Fh+Mh) > Ef*(Ff*Mf)) build industry; else build food building;.

in addition one could think of further city categories for special purposes later on (after the default categories work well enough). such as a unique 'military production city' which concentrates on building military buildings first, settling great generals (this would even be the criteria: has most GG in it) and producing specific national and world wonders that help military - then produce military unit of course (best case: city can support whole empire with troops, other cities can concentrate on other issues). commerce, trade, culture buildings, health come in last here.

other city specializations could be: 'navy city', 'support (caravan) city', 'expansion (settler+workers) city', 'religion spread city',... all thse specialization categories would come between 'developing city' and 'core city' and need to be limited in number so not every metropolis becomes a 'support city'. and if all specialization are assigned a city will just do the standard 'core city' script.
 
Back
Top Bottom