Resource icon

C3X: EXE Mod including Bug Fixes, Stack Bombard, and Much More Release 17C

Flintlock, do you know, if this listing will set the behavior for that produced individual AI unit during the complete game, per example will that produced offensive AI infantry unit be used offensively during the complete game when being produced, or will the AI be able to change that AI strategy during the game (per example let the produced offensive infantry act with the defensive AI strategy when needed) ?
As far as I know the AI can't change units' strategies after they've been built. The way it works under the hood is that each unit type can only have one AI strategy. If you assign multiple strategies to one type in the editor then that type gets copied multiple times and each copy is assigned only one strategy. So to change a unit's strategy you'd have to change its type, which is an unusual thing to do though it would be possible. I don't see any indication in the code that that ever happens, for example the AI maintains a count of how many units it has for each strategy and the only time that count is ever decremented is when a unit is destroyed.
How do you view the AI production value ranking?
Make sure the enable_ai_production_ranking option is active then press P while viewing an AI city.
 
As far as I know the AI can't change units' strategies after they've been built. The way it works under the hood is that each unit type can only have one AI strategy. If you assign multiple strategies to one type in the editor then that type gets copied multiple times and each copy is assigned only one strategy. So to change a unit's strategy you'd have to change its type, which is an unusual thing to do though it would be possible. I don't see any indication in the code that that ever happens, for example the AI maintains a count of how many units it has for each strategy and the only time that count is ever decremented is when a unit is destroyed.
Than it could make sense for each unit with a double strategy, instead of creating one unit with a double strategy to create two units with a single strategy, where that strategy is even more enforced, p.e. create an offensive and a defensive infantry, both using the same animations. :think:
 
Just flagging up something I noticed and mentioned in the "play as barbarians" thread below. With "enable_city_capture_by_barbarians=true" It looks like you pay maintenance for spawned barbs as well as any you manage to create in your cities.

You might decide that as this is a highly experimental feature (even more so actually playing as them yourself!) it's fine to leave as is, but just thought I'd mention it. Attached a thrown together save for easy testing as well.
 

Attachments

  • BarbTest1.zip
    33.8 KB · Views: 4
Flintlock... while it is true that the AI does not change Unit strategies that have multiple strategies assigned to them, If units with multiple strategies are Pre-placed in a Game, the AI does change the unit strategies as wanted... or a better way to state this is the AI will use multiple unit strategies for any unit that has them if the unit is Pre-placed.
This is because the AI programing to select only one unit strategy and not change it is bypassed due to not building the unit itself... therefore, the AI uses the multiple strategies set for the unit.

I have wondered if the Auto-produced units could be programed to use multiple strategies since the AI does not directly build the units and select the unit's strategy, although programing seems to omit multiple unit strategies with Auto-produced units too.
 
The AI will change a Unit's strategy as it sees fit (and I'll get the shorthand version of AI build choices together, one of these decades.) Example: the AI will re-flag a 1-9-1 Unit, flagged Offensive, to Defensive, almost certainly. (If you're impatient for my summary of AI Flag Preferences and Combat Values, you can start Here, at the very beginning.)
 
Big update time. R17 Preview 1 is finally ready, download it from GitHub here. What was holding it up was that I was tinkering with the code for calculating trade networks, and ended up replacing most of it from the base game. I had a feeling that the base game's implementation was way too slow, that calculating the trade network is not that complicated, and especially that it shouldn't have to blow up in the late game. My feeling was right! I was able to get my reimplementation of the trade network to run ~35x faster than the original in an extreme test case. It's enough to make maps like Monstrosity playable all the way to the end.

My extreme test case is a late industrial era Monstrosity game with many wars going on. Previously (*) trade net calculations between turns would take 894 sec out of a total turn time of 958 sec. My custom trade net code runs in only 24.9 sec, reducing the total turn time to 93.4 sec. This is an extreme case since ~90% of the turn time is spent computing trade networks, usually it's less than that. I also measured times on an earlier save from that same game, in the early industrial era without many wars. In that more typical case, trade net calculations previously took 86 sec of an overall 153 sec turn. The custom code reduces that to just 3.85 sec, bringing the overall turn time down to 74 sec.

(*) Aside: "previously" here means with C3X R16, so including the improvement loop optimization. Out of curiosity, I turned that off and found it increased the trade net time to 943 sec and overall turn time to 1008 sec. So optimizing the improv. loops is worth about 5% in that case. That is not surprising since the Monstrosity scenario doesn't add any city improvements and the trade net calculations take so long they dwarf everything else. Optimizing the trade net makes the improv. loop optimization look better. With the trade net optimized and the improv. loops not, the trade net takes 50.0 sec of an overall 117 sec turn time, so the improv. loop optimization further reduces that by about 20%.

As for what the optimizations entail, by far the biggest improvement comes from preserving pathfinding info across jobs. Basically, when computing sea trade, the game loops over all pairs of cities with harbors that are not already connected by roads and tries to find a path between them. It processes the pairs independently, clearing the pathfinder's internal state before each search. That's extremely wasteful. For example, trying to path from city A to city B is done by crawling outward from A building up a set of reachable tiles in the direction of B (Wikipedia has a good illustration). Either you eventually reach B, and so there is a path, or you run out of reachable tiles, and can conclude no path exists. If next you want to check for a path between cities A and C, you can get a big head start by reusing the results from the search from A to B, since those results already include many tiles reachable from A. In the extreme case where there is no path from A to B, you've already done the work of searching for a path from A to C. That's because the only way to conclude there is no path is to visit every tile reachable from A, so either you've already visited C and there is a path A->C or you haven't and there isn't.

In fact, you can do even better than that. Imagine you've searched for paths connecting A->B, A->C, A->D, etc., building up a set of reachable tiles along the way. Next you're asked if there's a path B->C. If B is in the set of tiles reachable from A, you can again reuse the pathfinder state since A and B are in the same set of reachable tiles.

Reusing the pathfinder state is not the only optimization I've done. Since I reimplemented the pathfinder logic for the trade network entirely I had the opportunity to make many improvements. Two more notable ones:
  1. The game's data structures make very poor use of the CPU cache. The game was written in a typical unoptimized object-oriented fashion meaning all the data that makes up a tile has been piled into a tile object and then the game map is an array of tile objects. The problem with that is when the CPU checks, for example, the terrain of a tile, it pulls into its cache many unrelated parts of the tile object which it will not use. That stuff gets pulled in simply because it's what close in memory. If soon after the CPU needs to check the terrain of the surrounding tiles, as it does while pathfinding, that data will probably not already be in the cache. It would be faster to store the terrain data separately and arrange it so that data for nearby tiles is kept nearby in memory. The replacement logic can't replace the original tile or map layout but what it can do is stash the relevant tile data in a cache-efficient structure after it's accessed once, so all later accesses to the same tile's data are done in a cache-efficient manner.
  2. The logic for checking sea trade accessibility at a tile level has been simplified so it only runs once per tile. The base game is inefficient here in that it checks accessibility between tiles, not per-tile, even though when you boil down the rules they only matter per-tile. For example, say you want to know if a sea route can pass between tiles T and U, and then later between tiles T and V. The base game will make four checks here, two for tiles T & U, and another for tiles T & V. However, you only need to make three checks here, one for each tile, since a sea route can pass between any two adjacent accessible tiles.

    By the way, you might be wondering if that's really true, if any two adjacent, passable sea tiles can have a sea trade route passed between them. What about tiles separated by an isthmus? For example, imagine four tiles in a diamond shape, where the left and right tiles are land and the top and bottom are water. In that case, you can't pass naval units between the top and bottom tiles (unless you've activated the C3X land-sea intersections option) but you actually can pass a sea trade route between them. That's an odd little aspect of the game rules, and I'm not sure why the game is programmed that way. It may have been done for performance reasons, or maybe the devs simply never thought of it.
In terms of final performance, it's the reusing of the pathfinder's state that's responsible for almost all of the speed up. The above two improvements I mentioned only speed up trade net calculations by about 30%. I also replaced the calculation of the road network and managed to speed that up by about 2x. That sounds good but it really doesn't matter since road network computations take up 0.1% or less of the overall turn time.

To get reliable numbers for the turn times, I added the ability to measure them in-game, the option is named measure_turn_times (duh). If activated, you'll get a popup at the end of every interturn reporting how long it took, including specifically how long it took to recalculate the trade networks. Some of you might find this interesting, for example if you have a very slow save and want to know how much of the slowdown is due to the trade network.

There's a lot more in R17 Preview 1 than that. It also contains many extensions to zone of control and defensive bombard. Thanks to Vaughn for commissioning those. Here's the full list of changes:
  • Optimize computation of trade networks
    • For details, see the info text file in the Trade Net X folder
  • Option to measure turn times
  • Zone of control changes
    • Allow land-to-sea and sea-to-land attacks, only using bombard stat
    • May be lethal
    • May be exerted by air units
    • Show attack animation even when attacker is not at the top of its stack
  • Defensive bombard changes
    • May be lethal
    • May be performed by air units
    • Invisible, undetected units may be made immune
    • May be performed multiple times per turn with blitz
    • Naval units in a city may perform defensive bombard vs land attackers
  • Allow precision strikes to target tile improvements
  • Option not to end a unit's turn after it bombards a barricade
  • Option to allow bombardment of other improvements on a tile with an occupied airfield
  • Option to boost OCN increase from forbidden palaces in non-communal governments
  • Fix graphical issues when running on Wine
  • Option to allow defensive retreat on water tiles
  • Overhaul implementation of shared visibility for simplicity and correctness
  • Display total city count (disabled by default, appears on demographics screen)
Here's a little preview of what defensive bombard and zone of control can now do. I could have posted this version a day sooner if I hadn't taken the time to make these, so be sure to appreciate them:

zoc-land-vs-sea.gif

Land units can exert ZoC over passing sea units & vice versa.

zoc-air-vs-sea.gif

Air units can exert ZoC.

db-aerial.gif

And they can perform defensive bombardment as well.

db-aerial-lethal.gif

Defensive bombard & ZoC can be made lethal.

That's it! Please report any bugs if you find them. I'm 99% sure that my reimplementation of the trade network calculations will give exactly the same results as the original game, but it's always possible I missed something.
 
That's fascinating! Sounds like an absolutely revolutionary change for big maps! Since you're also doing a lot of bombardement changes, this anecdote is fitting: I always found it funny how you could immediately tell whether a tile improvement bombardement would be successful or not in the late game on big maps, as a failed attempt would immediately play the animation and text, while a successful shot would freeze the game for a few seconds while the game recalculated the trade routes, before then showing the animation and text. I'm guessing this will also be a thing of the past now, which is nice. :D
The bombardement features are also really cool! Really opening up so many options to make units more distinct from one another.

Thanks a lot for all your work on C3X! :)
 
This is awesome news. I was always wondering why my large maps showed to a crawl between turns, or why I dreaded building harbors/airports in cities captured on campaign. I added a "Fighter Weapons School" wonder to provide airports in all cities just to avoid it.

The addition of the ZOC changes is exciting as well. R16 allowed me to finally make F-16/MiG-29/etc. Wild Weasels to kill all the self-propelled anti-aircraft guns & self-propelled air defense systems I added. From what I've seen, with R17 I can truly make the A-10/Su-25/etc. units into CAS aircraft as well. I can't wait to see this update in action.
 
Oh, while on the topic of defensive bombard, allow me one more question: Do bombardement units with a ROF >1 still do [ROF] many attack rolls, but have their damage capped at the 1 HP, or do they also just roll 1 time?
 
Oh, while on the topic of defensive bombard, allow me one more question: Do bombardement units with a ROF >1 still do [ROF] many attack rolls, but have their damage capped at the 1 HP, or do they also just roll 1 time?
This is just the question I was preparing to post. :)

In the editors there is the option to set the Rate of Fire (ROF) to higher values than 1 and in old mods like RAR this was done a lot of times, but the programming always cut it down to a ROF of 1, meaning maximal one hitpoint can be taken away from a unit with defensive bombardment. This is a pity, as the setting of higher ROF values is available in the Firaxis and Quintillus editors and can be very useful, especially for anti-tank guns and land-unit helicopters.

On the other side, deathly defensive bombardment for land units can be simply set in the editor and, as far as I remember, is working in theory, but in reality in the game can only work, if a land unit with only its last HP available is attacking (cause of the 1 HP damage limit for defensive bombardment).
So it can be, that this option is not needed in your mod.

Defensive Bombardment.jpg


The Trade Net X sounds very promising, so even with R16 I noticed a considerable speeding up of the interturn phases in my test games. As for Trade Net X an installation of Windows SDK seems to be needed, according to the Microsoft instructions, an additional 4 GB must be free on the harddrive - what I don´t have available at present.

Flintlock, a big thank you very much for all your efforts in creating and sharing this fantastic mod! :thanx:
 
Last edited:
What was holding it up was that I was tinkering with the code for calculating trade networks, and ended up replacing most of it from the base game. I had a feeling that the base game's implementation was way too slow, that calculating the trade network is not that complicated, and especially that it shouldn't have to blow up in the late game. My feeling was right! I was able to get my reimplementation of the trade network to run ~35x faster than the original in an extreme test case. It's enough to make maps like Monstrosity playable all the way to the end.
This is a wonderful improvement and game changing for a lots of scenarios I have in mind. Faster times between turns is wonderful!
 
I'm intrigued by that monstrosity map, surely it's so big you'll hit the city cap long before you cover it and will end up conducting wars (or peace) with enemies that are very far away. It'll be fun to try with the performance upgrade that's for sure.
 
Oh, while on the topic of defensive bombard, allow me one more question: Do bombardement units with a ROF >1 still do [ROF] many attack rolls, but have their damage capped at the 1 HP, or do they also just roll 1 time?
They roll only once. Defensive bombard doesn't take rate of fire into account at all. This is something I'd like to change, both for DB and ZoC.
On the other side, deathly defensive bombardment for land units can be simply set in the editor and, as far as I remember, is working in theory, but in reality in the game can only work, if a land unit with only its last HP available is attacking (cause of the 1 HP damage limit for defensive bombardment).
So it can be, that this option is not needed in your mod.
In the base game, defensive bombard can do at most one damage and will never activate against an attacker with one HP, so it can never outright kill an attacker. That option basically allows defensive bombard to activate against 1 HP attackers but under the hood it's more complicated than that. Simply allowing DB vs 1 HP attackers creates situations where the attacker takes damage to DB, leaving it with zero HP, but then the combat continues as normal. If the attacker takes no more damage during combat, it can actually win, all the while having zero HP. So it's necessary to modify combat after DB happens to ensure the zero HP unit dies as it should. The point is, the option is definitely not redundant. By the way, that option doesn't override the lethal bombard setting. A unit can only kill by DB if both that option is activated and it has the lethal bombard ability.
As for Trade Net X an installation of Windows SDK seems to be needed, according to the Microsoft instructions, an additional 4 GB must be free on the harddrive - what I don´t have available at present.
You don't need to install Windows SDK to use Trade Net X, only to compile it, i.e. to produce TradeNetX.dll from the source code. That DLL is already included so there's no reason to compile it unless you want to modify the source code or something like that.
Does it technically mean that 1 Spearman and, say 10 Artilleries, may shrug off 11 attackers easily?
Not yet since defensive bombard still does at most one damage. Again, that's something I'd like to change, and it should be pretty easy for DB (ZoC less so). Even then, artillery doesn't have lethal bombard so it wouldn't be able to kill all the attackers. If you had 1 spearman and 10 bombers attacked by 11 regular warriors, and each bomber can do DB hitting three times, then yeah the warriors wouldn't stand a chance.
 
Flintlock, thank you very much for the clarification about lethal defensive bombardment and the Trade Net X. :)
 
Big update time. R17 Preview 1 is finally ready, download it from GitHub here. What was holding it up was that I was tinkering with the code for calculating trade networks, and ended up replacing most of it from the base game. I had a feeling that the base game's implementation was way too slow, that calculating the trade network is not that complicated, and especially that it shouldn't have to blow up in the late game. My feeling was right! I was able to get my reimplementation of the trade network to run ~35x faster than the original in an extreme test case. It's enough to make maps like Monstrosity playable all the way to the end.
This sounds great! I'm really looking forward to Release 17 coming out. Playing those extra large maps will be much more manageable now.

Since you are addressing a lot of Zone of Control and defensive bombard issues in this release, would it be possible to fix Coastal Fortresses too? Originally the CF follows the ZoC rule: If a ship already adjacent to the Fortress moves to another adjacent tile, then the CF will (attempt to) shoot at it. This means that a CF inside a bay (where there is only 1 coastal tile adjacent) will never fire at enemy ships. Furthermore, the AI tends to move their warships in from deeper waters, bombard, then pull their ship straight out to sea again. Which doesn't allow for the ZoC rule to trigger, and renders the CF's attack mostly useless. In my opinion it would be better and more logical if the CF would shoot immediately, every time an enemy ship enters a tile within reach. Another small issue is that the sound that plays when the CF fires is quite faint, it would be nice if it was more obvious.
 
Last edited:
Top Bottom