Mini-engine progress

It's good to see that the obstacles seem to get smaller by time. It's no longer graphics, just 3D graphics, and that doesn't need to be done from scratch. I hope that this tendency continues 🙂

A very far off question:
Does it seem possible to train Civ4 AI with machine learning? I know StarCraft2 does it and IIRC FreeCiv too.

I just asked ChatGPT about it and gave a very interesting summary. To my surprise it specifically mentioned Civilization 😅
Spoiler If you are interested... :
Yes! Some strategy games have experimented with AI that learns and improves by playing against human players. While they may not use large language models (LLMs) specifically, they often leverage machine learning, neural networks, or reinforcement learning. Here are some notable examples:

1. AlphaStar (StarCraft II - DeepMind)

Developed by DeepMind, AlphaStar was trained using reinforcement learning and supervised learning from human matches.

It reached Grandmaster level in StarCraft II by learning from human replays and playing against itself.


2. OpenAI Five (Dota 2)

A machine learning AI that trained by playing millions of matches against itself.

It surpassed top human players and even defeated professional teams.


3. Tesseract AI (Company of Heroes 3)

Uses machine learning to analyze human strategies and improve AI behavior dynamically.

The AI adapts to playstyles rather than using static pre-programmed strategies.


4. Total War: Arena & Creative Assembly Experiments

Creative Assembly has experimented with reinforcement learning for Total War AI.

AI can learn from past battles and adjust tactics over time.


5. Mini-AlphaGo (Go & Turn-Based Games)

Inspired by AlphaGo, some AI in turn-based games like Go or Chess continues to evolve by learning from human play.


6. AI in Online Strategy Games

Games like Clash of Clans and other competitive mobile strategy games use AI that adjusts defensive strategies based on previous attacks.

Some MOBA bots (like in League of Legends) use AI to learn player movements and improve over time.


Challenges of Learning AIs in Strategy Games

Balance Issues: AI that learns too fast can become unbeatable or exploit mechanics.

Computational Cost: Running complex learning algorithms in real-time is demanding.

Unpredictability: AI that adapts too much might lead to broken or frustrating gameplay.


Future of AI in Strategy Games

We might see AI generals in RTS games that develop unique tactics based on past games.

AI could personalize difficulty in 4X games like Civilization based on your playstyle.

Future strategy games could feature AI that mimics human-like strategic thinking instead of following pre-set patterns.


 
Does it seem possible to train Civ4 AI with machine learning?
Since the AI is in the DLL, the answer is yes. The question is how much work it would be. We can set up interprocess communication hooks to let an external process gain access to the game data and send commands like move unit X to plot Y. It would likely be a massive undertaking to get it up and running through and that's before even starting to make an external AI to act on it. It's possible that there are other ways to do it too, but placing the AI in another process means we get rid of all compiler restrictions, which may be of benefit in case of AI as they seem to need the newest stuff.

In short it's possible in theory, but it's not something I would expect to see soon. It could be a project of the same size as making a new exe.
 
Bad news. I investigated Sugar Bomb Engine as it's an open source engine based on GameBryo. Looking through the source I found this.
PHP:
    else if(extension.Icmp("nif") == 0)
    {
        mpSys->Warning("idRenderModelStatic::InitFromFile: NIF models are not yet supported!: \'%s\'", name.c_str());
        //loaded = LoadNIF( name );
        loaded = false;
        reloadable = true;
    }
Any other ideas where we might be able to find information on how to decode the nif files?
 
Bad news. I investigated Sugar Bomb Engine as it's an open source engine based on GameBryo. Looking through the source I found this.
PHP:
    else if(extension.Icmp("nif") == 0)
    {
        mpSys->Warning("idRenderModelStatic::InitFromFile: NIF models are not yet supported!: \'%s\'", name.c_str());
        //loaded = LoadNIF( name );
        loaded = false;
        reloadable = true;
    }
Any other ideas where we might be able to find information on how to decode the nif files?


Nif Tools perhaps? I can't speak to anything related to editing them, but from other games running the same engine, this seems to be the standard tools to edit them. But that's just my impression from an evening's googling.
 
Nif Tools perhaps? I can't speak to anything related to editing them, but from other games running the same engine, this seems to be the standard tools to edit them. But that's just my impression from an evening's googling.
I'm no expert but as I understand what needed is not an EDITOR but an ENGINE.
Maybe this isn't the best illustration but... Thinking of a car what we need is not someone who can paint fancy flames or skulls or butterflies on the chassis of our car. Rather we need someone who can build the whole chassis (or rather already has a complete spare one) into which we can put our new motor.
 
I'm no expert but as I understand what needed is not an EDITOR but an ENGINE.
What we need is code, which can convert NIF into something understood by Godot (or some other engine). Having the source code for some editor, which can read/write the NIF format is a whole lot better than no code at all. It's not copy paste, but I never imagined we would find code, which is completely copy paste.
 
lurker's comment:
I don't think I've seen anyone do a 'Civ like' in it yet but am keeping an eye out for one as I'm sure there's already something out there not discovered yet
I see you haven't been hanging out in the Civ3 C&C space a whole lot recently. There has been a slow-and-not-all-that-steady (but moving again) effort in that direction, using Godot, over the past three-plus years. The announcement thread is here and the latest release thread is here.

I was involved with it a good amount for the first year-plus. Although WildWeazel had used Unity a bit, none of us were game programmers, just regular programmers. But Godot was a pleasure to work with, even in its prior iteration (Godot 3). We chose to use C# because it was the common language that those of us who started it shared, and with Civ III as our base goal, there were no DLL interoperability concerns - but my understanding is you can mix C++, C#, and GodotScript. One of the benefits of using C# is that for the parts that are logic-focused (versus graphics focused) you can use JetBrains Rider as your IDE, which is much more powerful than the Godot editor for code-heavy areas. You could likely use JetBrains' CLion if you went C++, although we had some challenges getting Visual Studio (full, not VSCode) to play nicely with Godot and C# a couple years ago when we tried that. That may have improved since then.

We wrote our own code to read Civ3 legacy file formats and convert the into formats that Godot understands natively. We also didn't take advantage of all the built-in features Godot has, due to not being game programmers and being unaware that it supports things like game loops natively, so we probably did things more the hard way than we needed to. Cross-platform and building has been good, it's possible to build for Mac/Linux/Windows from one PC, and there haven't been too many platform-specific issues to iron out.

I'd encourage those looking at the mini-engine for IV who also own III (or can pick it up cheaply on Steam/GOG) to try out what we've done as an example of it being used in a Civ-like, albeit an isometric one rather than a 3D one. You can have various layers, you can have modals (we did that the hard way initially, due to lack of knowledge), and we don't have regrets about choosing it.
 
lurker's comment:

I see you haven't been hanging out in the Civ3 C&C space a whole lot recently. There has been a slow-and-not-all-that-steady (but moving again) effort in that direction, using Godot, over the past three-plus years. The announcement thread is here and the latest release thread is here.

I was involved with it a good amount for the first year-plus. Although WildWeazel had used Unity a bit, none of us were game programmers, just regular programmers. But Godot was a pleasure to work with, even in its prior iteration (Godot 3). We chose to use C# because it was the common language that those of us who started it shared, and with Civ III as our base goal, there were no DLL interoperability concerns - but my understanding is you can mix C++, C#, and GodotScript. One of the benefits of using C# is that for the parts that are logic-focused (versus graphics focused) you can use JetBrains Rider as your IDE, which is much more powerful than the Godot editor for code-heavy areas. You could likely use JetBrains' CLion if you went C++, although we had some challenges getting Visual Studio (full, not VSCode) to play nicely with Godot and C# a couple years ago when we tried that. That may have improved since then.

We wrote our own code to read Civ3 legacy file formats and convert the into formats that Godot understands natively. We also didn't take advantage of all the built-in features Godot has, due to not being game programmers and being unaware that it supports things like game loops natively, so we probably did things more the hard way than we needed to. Cross-platform and building has been good, it's possible to build for Mac/Linux/Windows from one PC, and there haven't been too many platform-specific issues to iron out.

I'd encourage those looking at the mini-engine for IV who also own III (or can pick it up cheaply on Steam/GOG) to try out what we've done as an example of it being used in a Civ-like, albeit an isometric one rather than a 3D one. You can have various layers, you can have modals (we did that the hard way initially, due to lack of knowledge), and we don't have regrets about choosing it.
Hah that's funny, despite admittedly not hanging out there lately (I miss the Civ3 area but am very focussed atm on finishing my final Civ2 mods, so that I can get to playing more Civ3 mods sooner than later), I actually did think to check if C7 was using Godot these days before making that post and had a quick look but must have ended up in an old thread or missed the text mentioning it lol, doh! I should have messaged you and @WildWeazel about this to double check and then see if you had any advice too. That's great to hear you Civ3 C7 guys have switched over to Godot and have literally gone through the kind of challenges the Civ4 crew are facing, please keep up with the lurking here. ;)

EDIT: I see Suede has done a recent video on your work too!

 
Last edited:
Wow, this is neat. I'm still going through the thread to grok what's happening here, but I echo everything @Dale said above regarding Godot and the use of a game engine. It provides all those things you need at a higher level of abstraction so you can focus on the game logic and assets. As mentioned none of us were even game developers to start with but we've done just fine, even without knowing enough to take full advantage of what Godot offers.
 
As mentioned none of us were even game developers to start with but we've done just fine, even without knowing enough to take full advantage of what Godot offers.
This is the thing. Generally speaking, games don't use everything a game engine offers. Sometimes it's because of developers not being skilled enough and sometimes it's just because the game doesn't actually need that like single player games doesn't really need network support. The question you need to ask is if you can make what you need with the subset of the engine you plan to use rather than how you can use everything in the engine to do something to your game, planned feature or not.

Sure it's plausible to add some new feature to make use of some engine feature, but that's really an extension to an existing game rather than planning it prior to writing the first line of code. I can imagine something like adding the ability to modify 3D models on the fly, like having ships, which change flags according to owner without having to make a model for each CivilizationTypes.

It provides all those things you need at a higher level of abstraction so you can focus on the game logic and assets.
And this is the main thing. We don't need to develop a game. Most of the game logic is in the DLL, so what we do need to do is to make a bridge between the DLL and a game engine. We then come back to converting vanilla assets into Godot compatible files. We don't even need to implement the game loop because that's (mostly) CvGame::update().

I see Suede has done a recent video n your work too!
This video points out the ability to unlock more modding options. We have some of those through the DLL and/or python, but with an open source exe, we can open up for way more options. Orbit/satellite map layer like in Call to power 1, Subterranean map layer. The options are endless if we aren't as restricted on the engine level.
 
This video points out the ability to unlock more modding options. We have some of those through the DLL and/or python, but with an open source exe, we can open up for way more options. Orbit/satellite map layer like in Call to power 1, Subterranean map layer. The options are endless if we aren't as restricted on the engine level.
Yeah indeed multiple links map layers like in Civilization 2 Test of Time (+ Master of Magic, Heroes of Might & Magic, Warlock, Age of Wonders & the other examples you gave) is probably my number 1 wishlist item the Civ3 C7 project and I think I mentioned it earlier in this thread for this Civ4 project too! :)

It suffered greatly in Test of Time due to Civ2's 7 player limit so it would work so much better in Civ3 and Civ4. And as you say the applications and creative ideas that can come from the modding community once available to them are endless!

For example Test of Time came with a Midgard fantasy scenario with 4 linked worlds: underworld caves/dungeons/lava world with a globlin race, under sea with a merfolk race, surface fantasy forest world with humans/elves/barbarians/undead, and lastly a sky world with a bird people.
1739947559848.png

And it came with a scifi scenario with 4 linked worlds: an earth like alien world colonised by human and alien factions who work their way to orbital space flight techs, eventually you reach a space layer map with orbital platforms that unlock techs and equipment that open portals to the rest of the solar system such as a dark dusty glowing mining planet and then sky platforms in the clouds of a gas giant where giant alien machines lurk.
1739947864931.png

Fans of course took this tech and did all sorts of cool stuff eg..

Kestrel did an enhanced version of the famous Mars Now scenario with a underground level where you can use diggers to make passages between caves and build cities (reminding me of the old game Outpost) and then you could eventually fly back and invade a post apocalyptic earth map with it's own underground level. Just imagine @The_J 's Civ4 Mars Now with a underground layer added on.
SMarsNOW.png

Dadais did a Skyrim scenario with a overworld and underworld map with all the caves and dungeons linked to their surface entrances.
SSkyrim01.png SSkyrim02.png

And probably the craziest idea I've seen is Prof Garfield and JPetrowski using the multi world system and some clever LUA coding to make a World War 2 scenario where you build Civ6-7 like external city buildings on the main daytime map but there's a linked second nighttime strategic bombing map that updates based on whatever is built in the daytime map and bombers can destroy buildings at night (where there's no cloud cover) which will then show as rubble on the daytime map.
SOverTheReich1.png SOverTheReich2.png

So yeah if crazy stuff like that can be done in Civ2's ancient engine, I can only imagine what sorts of awesome crazy things could be done in Civ3 and Civ4 if a multi world system was added to the fan rebuilds! :)

.
 
Last edited:
Does it seem possible to train Civ4 AI with machine learning? I know StarCraft2 does it and IIRC FreeCiv too.

Going by https://pythonprogramming.net/starcraft-ii-ai-python-sc2-tutorial/, what you'd do is create an environment in python that makes it easy to write an AI. A learning environment. Python 3 I would hope. It would probably be a headless engine exposing some way to control the human player. You'd then write a rule-based AI. Then, replace the decisions with something like deep reinforcement learning, neural networks, CNNs...

Importantly, if you do Machine Learning, you'll want a FAST engine. But on the plus side, you don't need to care about multithreading as you'd just run N games in parallel.

It may also be possible to use MILPs to solve for truly optimal play of some subset of mechanics. Like, maximise the amount of research you can get from an isolated start (don't need to handle AI). But MILP solvers aren't quite there yet. The HiGHS solver is pretty good, but it's single-threaded at the moment. They should be adding parallel solving soon, and that's when it should get really good. Now, if you were really creative, you might be able to craft a custom branch-and-bound algorithm that also minimises the redundant symmetric states. One would hope that would be even faster, in theory, but it's tough to beat MILP solvers. There's also other tactics like decomposing the problem into N-turn blocks, but I wasn't able to make that fast.

Nif Tools perhaps? I can't speak to anything related to editing them, but from other games running the same engine, this seems to be the standard tools to edit them. But that's just my impression from an evening's googling.
Yes, loading NIFs is a solved problem. Should be easy as pie.

Anyway. An actual progress this time. A milestone. Path verification is working, or at least appears to work. Turn 209 of a 1040x640 map:

Code:
FAStar for Barbarian Worker start=[286, 150] goal=[669, 155] result=1 flags=0 numUnits=1 domain=2 1687ms
Generating path 1486 from { [286, 150] with Barbarian Worker } to { [669, 155] } with maxTurns 2147483647
Vectorised path finder took 808.243ms
Path cost matches.
CmdCiv4 log Info void __cdecl MyCvDLLEngineIFace::AutoSave(bool):
Pathfinder reset.
FAStar for Fluffy Warrior start=[690, 69] goal=[690, 68] result=1 flags=4 numUnits=1 domain=2 4.1201ms
Pathfinder reset.
Performed 6 game updates in 45.8825s.

That is full verification. Checks the path cost of the final path, and checks pathCost+pathValid for each step in A*. That's why the vectorised pathfinder is so slow here.

There's still a chance though that there's some higher level bug somewhere causing different paths to be requested. But the way this works is that all unit pathing requests are passed to an internal FAStar instance if verification is enabled or my pathfinder won't handle it yet. So that's at least minimising the chance of such bugs.

Turning off step verification:

Code:
FAStar for Barbarian Worker start=[285, 149] goal=[669, 155] result=1 flags=0 numUnits=1 domain=2 1587.21ms
Generating path 1485 from { [285, 149] with Barbarian Worker } to { [669, 155] } with maxTurns 2147483647
Vectorised path finder took 5.2291ms
Path cost matches.
Pathfinder reset.
FAStar for Barbarian Worker start=[286, 150] goal=[669, 155] result=1 flags=0 numUnits=1 domain=2 1566.69ms
Generating path 1486 from { [286, 150] with Barbarian Worker } to { [669, 155] } with maxTurns 2147483647
Vectorised path finder took 5.402ms
Path cost matches.
CmdCiv4 log Info void __cdecl MyCvDLLEngineIFace::AutoSave(bool):
Pathfinder reset.
FAStar for Fluffy Warrior start=[690, 69] goal=[690, 68] result=1 flags=4 numUnits=1 domain=2 4.1857ms
Pathfinder reset.
Performed 6 game updates in 37.9029s.

Here's a long path, which goes across the map:

Code:
Pathfinder reset.
FAStar for Barbarian Warrior start=[921, 270] goal=[350, 194] result=1 flags=0 numUnits=1 domain=2 471.223ms
Generating path 1254 from { [921, 270] with Barbarian Warrior } to { [350, 194] city of Carib with Barbarian Warrior } with maxTurns 1
Vectorised path finder took 0.0026ms
FAStar for Barbarian Warrior start=[921, 270] goal=[261, 199] result=1 flags=0 numUnits=1 domain=2 102.758ms
Generating path 1255 from { [921, 270] with Barbarian Warrior } to { [261, 199] city of Tiwanaku with Huayna Capac Archer } with maxTurns 2147483647
Vectorised path finder took 25.4883ms
Path cost matches.

So, 26ms for the longest path. It's okay. Alright. Not as fast as I would like. But I could make it faster if I wanted to by using Block A* without an LDDB, which is actually feasible, but the internal data layout is more complex.

Turn time is ~2.8s with no verification. With 4K barbs running around. Five workers. Some of them are probably building intercontinental barbarian highways.

Current restrictions: Land units only, no unit stacks, AI only.

Landmark locations are computed at startup in ~180ms. This could be done in the background. Landmark distance (cost) fields are computed on-demand, in a background task.

There is a landmark heuristic instance for each "movement type". Combination of: unit MP, movement promotions, team route cost changes.

And a landmark heuristic is optional. It can be computed in parallel, and when it isn't available yet, the pathfinder can just use no heuristic.

The downside of parallel landmark updates is the possibility of non-deterministic pathfinding. This is unlikely as the pathfinder does not use "parent pointers", and only relies on a distance field for path reconstruction, but could still happen if through some fluke of pathing, a plot gets the same path cost with a different number of movement points. This could probably be mitigated by taking the minimum movement points for equal path costs within the pathfinder, or by making landmark updates deterministic.

The heuristic can also be updated by local changes. If a plot changes, you can compute the maximum path cost reduction going through that plot, and subtract it from the heuristic. And I hope that's correct! Then you can have some threshold where you launch another landmark distance field computation task. So in the end, maintaining the landmark distance field has little cost.

Also, to feed the vectorised pathfinder, everything that affects pathCost and pathValid is complied into a per-team bitmap, which is masked by the unit and pathing flags. Thus, calculating pathValid is just a few SIMD instructions, in comparison to the expensive 9x9 loop inside AI_getPlotDanger. So that means there's also a cache for plot danger. It has a lot of dependencies on various pieces of game state, but it's handleable.

Now, I need to ensure that all the caches get all the game state change events they need, and test with verification on a more typically sized map.

*Ah, yes, use AI autoplay! Capital idea.

*Oh. Turn 46 of a more typical map: A* costs inconsistency. Parent gets improved cost, but because of unit movement points, the adjacent plot gets a worse cost. So even the path costs depend on visit order. And that's why, I guess, FAStar will update descendants of a node, and why https://github.com/karadoc/Civ4-K-M...5d86608/CvGameCoreDLL/KmodPathFinder.cpp#L451 exists. This makes costs depend on visit order, and thus the heuristic, and so hurts determinism and path verification. The True solution is 3D path coords, but that would be silly for performance. Or change the step cost function.

*Changed path cost verification to only abort if final unit turn/movement points is different.

*Found value cache verification + path verification for a 64x40 map successful. Turn 11 to turn 458 using AI autoplay, debug build, took half an hour.
The found value cache is the other piece of complex machinery. Very bug-prone. I would like to replace the found value cache with another highly optimised cache-friendly SIMD implementation, but I'm not sure if it would be fast. And there's a couple of divisions that SIMD can't do natively. Would be nice if the whole-map loop in AI_updateFoundValues could be done in 1ms per player assuming all your locally updateable values are up to date.

*1040x640 map from turn 209 to turn 243 verified. Very slow!
 
Last edited:
Hi all, a newbie here. Just wanted to say that I am so excited about this project. My dream is to build a simulation type mod with giant earth map and realistic mechanics, and on-going projects like this makes me picture the future of this (old) game with technical barriers lifted and flexibility improved even more. Really appreciate all the efforts!!
 
Hi all, a newbie here. Just wanted to say that I am so excited about this project. My dream is to build a simulation type mod with giant earth map and realistic mechanics, and on-going projects like this makes me picture the future of this (old) game with technical barriers lifted and flexibility improved even more. Really appreciate all the efforts!!
So like a more detailed version of the Rhye's and Fall mod? Honestly, same.
 
Hi all, a newbie here. Just wanted to say that I am so excited about this project. My dream is to build a simulation type mod with giant earth map and realistic mechanics, and on-going projects like this makes me picture the future of this (old) game with technical barriers lifted and flexibility improved even more. Really appreciate all the efforts!!
You'll definitely be able to go big. And if you're lucky, it'll even be performant. My bitsets have room for 32 players, so... 31 civs sounds good. That should be a good number for a gigantic map.
Although, some of my optimisations in the DLL make assumptions. Two road types, no water cities, which promotions affect movement costs...

I'm actually at the limit in the pathfinder. Using exactly 32 bits for plot info. I could get a bit more room by having a domain-specific plot info array too. If that's not enough, I would need to do an extra SIMD gather in the pathfinder.

Yes, sea-domain units now. They needed an extra three bits and a hack. And I need to support sea units in the SIMD pathfinder because that's what I use in multithreaded unit updates.

The idea I mentioned earlier does work. Here's the steps:
  • Gather up your CvSelectionGroup ids.into an array.
  • Simulate each group update as-if it is the first group to be updated.
    • You basically need a shadow implementation of all the AI you want to do in parallel, and also track the simulated state of the group and its units.
    • Defer "non-trivial" and unimplemented updates to your serial fallback. This includes things like killing units, building improvements, declaring war, capturing cities, split/join groups...
    • Record every dependency (read) and invalidation (write).
    • The A* visit set is a read. The heuristic doesn't change.
    • Build a (hierarchical) bitmap of potential plot writes. Pathing data changes, plot worker mission counts...
  • Build the conflict lookup.
    • For each group, enumerate all reads and writes and add them to a hash table of some form. Hash by plot coord.
    • Use the bitmap to enumerate potential conflicts given an A* visit rect.
  • Perform parallel construction of a Maximal Independent Set:
    • Algorithm on wiki. Blelloch's Algorithm seems to work nicely. It's deterministic too.
    • If every group is a node in a graph, and every edge indicates a conflict, then the MIS is the set of units you can update in parallel.
  • Given the MIS, apply its results to live game data in parallel.
    • Most importantly, you need a parallel implementation of CvUnit::setXY.
    • Python callbacks not supported. Per-unit python updates are not good for performance anyway.
  • Repeat until you only have serial fallback units left.
Hopefully, that's sound in theory, and there's no giant bugs.

For barbarians only, this reduces turn time from ~2.9s to ~2s. which is pretty darn good. All those thousands of barb units giving lots of parallelism. For animals, there's two parallel updates due to their proximity to each other (animals don't stack by themselves). And the other lot is barb galleys.

Here's the flame graph:
Flame graph.png

The left calls to AI_update is for civs (no parallelism). Next to it is "runUpdates" for barbs. Serial updates for the unimplemented AI (barbAttackMove) still takes most time. The parallel updates themselves (findAndUpdateMIS) are in the next smaller block.

And I'm seeing AI_updateFoundValues is still eating CPU... Have to do something about that. Well, it's the first turn of the session. Initial computation.

Enabling parallel updates for all players appears to work, but due to random rolls, turn times take longer. Barbs creating cities, area city target updates. Things that need to be faster. Non-barbs don't have many units anyway, so parallelism will be limited.

Below, the update log. Civ AI buckets groups based on priority. And then there are multiple MISs for each bucket, and then the serial fallback. Civ AI also runs the unit updates twice here (because of the "early-out" I suppose).

Spoiler Parallel update log :
Code:
`anonymous-namespace'::findAndUpdateMIS: Groups = 5, numInSerial = 0, numInMIS = 5, numOutsideMIS = 0
Parallel: 36.9339ms, serial: 0.0001ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 4, numInSerial = 4, numInMIS = 0, numOutsideMIS = 0
Parallel: 0.3145ms, serial: 0.0448ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 275, numInSerial = 3, numInMIS = 262, numOutsideMIS = 10
`anonymous-namespace'::findAndUpdateMIS: Groups = 10, numInSerial = 0, numInMIS = 10, numOutsideMIS = 0
Parallel: 7.4347ms, serial: 0.3535ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 2528, numInSerial = 41, numInMIS = 1906, numOutsideMIS = 581
`anonymous-namespace'::findAndUpdateMIS: Groups = 581, numInSerial = 0, numInMIS = 575, numOutsideMIS = 6
`anonymous-namespace'::findAndUpdateMIS: Groups = 6, numInSerial = 0, numInMIS = 6, numOutsideMIS = 0
PY:Player 18 Civilization Barbarian State Unit Lion was killed by Player 6
Early-out on unit 22 (serial 22)
Parallel: 70.4467ms, serial: 2.6363ms
Player 18 units took 118.465ms to update.
`anonymous-namespace'::findAndUpdateMIS: Groups = 5, numInSerial = 0, numInMIS = 5, numOutsideMIS = 0
Parallel: 0.387ms, serial: 0.0001ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 4, numInSerial = 0, numInMIS = 4, numOutsideMIS = 0
Parallel: 0.3168ms, serial: 0.0001ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 275, numInSerial = 0, numInMIS = 275, numOutsideMIS = 0
Parallel: 0.4282ms, serial: 0.0001ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 2527, numInSerial = 18, numInMIS = 2508, numOutsideMIS = 1
Parallel: 1.4805ms, serial: 2.2518ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 1335, numInSerial = 57, numInMIS = 923, numOutsideMIS = 355
`anonymous-namespace'::findAndUpdateMIS: Groups = 355, numInSerial = 0, numInMIS = 344, numOutsideMIS = 11
`anonymous-namespace'::findAndUpdateMIS: Groups = 11, numInSerial = 0, numInMIS = 11, numOutsideMIS = 0
PY:Player 2 Civilization Incan Empire Unit Quechua was killed by Player 18
Parallel: 36.0653ms, serial: 220.869ms
`anonymous-namespace'::findAndUpdateMIS: Groups = 23, numInSerial = 9, numInMIS = 14, numOutsideMIS = 0
Parallel: 0.6067ms, serial: 0.6277ms
Player 18 units took 263.468ms to update.
MyCvDLLEngineIFace::DoTurn: 210
CmdCiv4 log Info void __cdecl MyCvDLLEngineIFace::AutoSave(bool): 
Updating units for player 0 took 1.2615ms
Updating units for player 0 took 0.0019ms
Performed 6 game updates in 2.03436s.

CPU usage at the moment:
CPU graph.png

Last peak is mostly the barbs. The rest appears to be found values, plot danger, and landmark heuristic computation.
 
You'll definitely be able to go big. And if you're lucky, it'll even be performant. My bitsets have room for 32 players, so... 31 civs sounds good. That should be a good number for a gigantic map.
Although, some of my optimisations in the DLL make assumptions. Two road types, no water cities, which promotions affect movement costs...
Some/few mods already have a limit of 50 civs in a game or even 100. For a real simulationist game 32 is hardly enough. Such mods want to have as many civs as possible even if most them are no challenge. But that's realistic: there are big countries and small countries.
I remember a modder even tried 200 civs limit but that failed. He discovered that the final limit was around 120-130.
 
I'm actually at the limit in the pathfinder. Using exactly 32 bits for plot info.
I added a class called EnumMap to WTP. It takes two template arguments, enum type and stored value. If the type is bool, then the array becomes uint32, each containing 32 bools. If the length is known at compile time, then the array can end up hardcoded as a single or two ints. This means <PlayerTypes, bool> will result in a static array of two ints, through the same code will compile into just a single int (no array) with 32 or less players. If the length is read from xml at runtime or it exceeds 64, then storage will be an uint32 pointer, which is dynamically allocated. The limit I added at 64 bit is configurable so it can be 96 bit static if needed.

In short: I have code, which has no hard upper limit (well no realistic upper limit), but it does lose a bit of performance when exceeding 32 bit. Not much through. As I recall, uint32 block access is based on bitshift of the index and as such no conditional branching.

The downside of my approach is that it requires some explicit information about enum types (like length) at compile time. I have a perl script to generate this at compile time and while it works, it does make the solution less portable for other mods.

I remember a modder even tried 200 civs limit but that failed. He discovered that the final limit was around 120-130.
My understanding is that there is a limit to the amount of leaderheads displayed when starting a new game. Showing the same leaderhead for multiple leaders until the game is fully started can be used as a workaround. I wonder if this is the encountered limit or if it really is the number of players, which is the limit. The DLL contains the array storing the player data and that one will have no problem storing literally thousands of players. If there is a hard limit in the exe, then it's something less obvious, like caching something for each player to see if it's a human player on a network or something similar not directly part of CvPlayer.
 
Some/few mods already have a limit of 50 civs in a game or even 100.
Yes, I'd probably try more than 32 players at some point. It's just that the bitsets are already sized for 32 due to alignment. The next step is 64, then beyond that, you need extra instructions.

I added a class called EnumMap to WTP. It takes two template arguments, enum type and stored value. If the type is bool, then the array becomes uint32, each containing 32 bools. If the length is known at compile time, then the array can end up hardcoded as a single or two ints. This means <PlayerTypes, bool> will result in a static array of two ints, through the same code will compile into just a single int (no array) with 32 or less players. If the length is read from xml at runtime or it exceeds 64, then storage will be an uint32 pointer, which is dynamically allocated. The limit I added at 64 bit is configurable so it can be 96 bit static if needed.
"Small vectors". There's a lot of tiny arrays that they could be useful for.

Can't be used for the pathfinder's plot props array though. It's _mm512_mask_i32gather_epi32, you get 32 bits per element per gather, and that's it. Could do 64 bits, but at half the elements.
 
Back
Top Bottom