Artificial Unintelligence

To answer ThorHammerz question, ChooseFromTopChoices is a method of CvWeightedVector which must be defined in the engine and not the DLL.
Thanks! I was starting to think my application was malfunctioning/buggy :goodjob:

False, CvWeightedVector is defined in a header that is included in the DLL, I think it's CvWeightedVector.h. It's part of the "External Dependancies", not the main solution, so maybe that's why you couldn't find it. I know for a fact that you can change it, since one of the edits that is included in v9 is a fix for precisely the ChooseFromTopChoices function: I made it so that ties are included, eg. if there are 3 choices tied for last place, the function will process all three of them instead of just the first one (similar things happen for 3 or more tied for second-to-last, 4 or more tied for third-to-last, etc.).
 
False, CvWeightedVector is defined in a header that is included in the DLL, I think it's CvWeightedVector.h. It's part of the "External Dependancies", not the main solution, so maybe that's why you couldn't find it. I k.

Another question: if I wanted to make changes to these, would I need to "add" them to the main solution? Or would editing them (and then compiling the main solution... since I understand they are just dependencies) as-they-are suffice...?
 
Yeesh, swing and a miss, strike 3. Batting .000 lately.

If it makes you feel any better, it took me 6 months to realize that my SQL changes loaded from XML weren't actually being applied because I did not set OnModActivated events... the BE version of the mod is still non-functional as a result of this blunder.

Another question: if I wanted to make changes to these, would I need to "add" them to the main solution? Or would editing them (and then compiling the main solution... since I understand they are just dependencies) as-they-are suffice...?

You can just edit the CvWeightedVector.h file, it behaves just like any other file in the solution. Since the entire CvWeightedVector template is defined in CvWeightedVector.h, I don't see how you'd be able to change the class from other files without first modifying the class in the original file.
 
You can just edit the CvWeightedVector.h file, it behaves just like any other file in the solution. Since the entire CvWeightedVector template is defined in CvWeightedVector.h, I don't see how you'd be able to change the class from other files without first modifying the class in the original file.

Ah, I see. Thanks for the explanation :goodjob:
 
If it makes you feel any better, it took me 6 months to realize that my SQL changes loaded from XML weren't actually being applied because I did not set OnModActivated events... the BE version of the mod is still non-functional as a result of this blunder.

Ha, well, I guess it's not Schadenfreude if we're commiserating with each other.
 
Ha, well, I guess it's not Schadenfreude if we're commiserating with each other.

To keep with the theme, I finally realized why my Lua events weren't triggering after spending about a week troubleshooting the issue: I mistakenly used != instead of ~= in one line of code. Silly Lua with its silly symbols (I know ~ means "not", but still). :crazyeye:

The script is loading fine, though it does slow down the game quite a bit; I'm considering having it be toggleable by XML. Once I get the crashes sorted out (the latest one was caused by Statue of Liberty working off of specialist count, but specialist counts are not initiated the first time city flavors are updated), I'll commit everything to GitHub.
 
To keep with the theme, I finally realized why my Lua events weren't triggering after spending about a week troubleshooting the issue: I mistakenly used != instead of ~= in one line of code. Silly Lua with its silly symbols (I know ~ means "not", but still). :crazyeye:

Yeah, switching between languages can get annoying sometimes. I'm working in Lua, C++, and Python all at the same time right now, and I'm constantly having to catch myself when I put a : or a then after an if in C++ or start throwing curly brackets around in Python and Lua.

The script is loading fine, though it does slow down the game quite a bit; I'm considering having it be toggleable by XML. Once I get the crashes sorted out (the latest one was caused by Statue of Liberty working off of specialist count, but specialist counts are not initiated the first time city flavors are updated), I'll commit everything to GitHub.

Nice, I'm looking forward to v10.
 
How have you handled how the tactical AI values units? In particular, what have you done relating to the XML entry PERCENT_DANGER_FOR_FORWARD_MUSTER (iirc), and wherever that value is used in the rest of the code.

I've found in my own tinkering that once you crank that high enough, the "AI shuffle" seems to be completely eliminated.
 
How have you handled how the tactical AI values units? In particular, what have you done relating to the XML entry PERCENT_DANGER_FOR_FORWARD_MUSTER (iirc), and wherever that value is used in the rest of the code.

I've found in my own tinkering that once you crank that high enough, the "AI shuffle" seems to be completely eliminated.

I increased percent danger for forward muster to 40 (from 25); IIRC, the value is used to determine whether an operation's muster point can be shifted forward (muster points are where an operation's rally point is, operations are how the AI handles groups of units gathered around a common objective like taking a city), but that bit's in the operations control of TacticalAI, which is already a pain to work with and full of holes.

What do you mean by how the Tactical AI values units? If you mean whether I have changed how it evaluates which targets it should attack and in what order, then no, I have not changed it (yet). If you mean how the AI values its own units, I have changed it a bit for v9, but not nearly as much as I want to, and v10 should hopefully bring things closer to where I want them in terms of the AI preserving its units while also trying to dish out as much damage as possible.
 
What do you mean by how the Tactical AI values units?
I had interpreted danger for forward muster to reference the danger threshold beyond which the AI will refuse to engage in an action, at least in so far as the units assigned to an operation. It seemed a reasonable interpretation, without the context of the code behind the tactical AI.

For my own personal AI XML-only mod, I've cranked it up to 80, and increased the values for entries like attrition. The goal is an AI willing to casually sacrifice units, rather than attempting to retreat damaged units. The result has been an AI capable of at least leveraging its primary advantage, namely the production and maintenance bonuses it receives.

My suspicion had been that much of the "AI shuffle" was due to an AI which was unwilling to accept high losses, achieved by setting low thresholds for when the AI would begin avoiding combat.

For example, the AI would disembark the unit. On the next turn, it would calculate the danger of attacking as being too great, and instead re-embark the unit. Having read through this thread, I understand that re-embarking may have been a bug or oversight, but all that suggests to me is that adjusting that specific behavior would result in the AI instead retreating the unit over land.

I take it you're trying to produce an AI that values units as the player does, including retreating injured units when possible. I'm sure that comes at a high cost of processing time, which I've seen you repeatedly identify as the main pitfall of your mod.
 
I always felt that the combination of 1 UPT and the promotion rules meant that giving the AI tons of extra units was a bad way to increase difficulty since it just leads to traffic jams and farming the AI for XP.

A more elegant solution would be to give the AI units free promotions at higher difficulties - range 3 double-shooting archers should not be a human-player-only phenomenon.
 
hi guys,

great to see this project is still moving ... i just wanted to let you know that a while back i've merged most of AUI v9 into the Community Patch. are you aware of this?

there is some danger of duplicate effort here and of course merging is always a pain - have you considered using the CP dll as a basis for your work?

https://github.com/LoneGazebo/Community-Patch-DLL
 
What modifications need to be made?
I honestly don't know, but I've read comments on the mod's workshop page about people playing the mod in multiplayer, so no modifications need to be made as far as I'm aware. Feel free to enlighten me if I'm wrong

hi guys,

great to see this project is still moving ... i just wanted to let you know that a while back i've merged most of AUI v9 into the Community Patch. are you aware of this?

there is some danger of duplicate effort here and of course merging is always a pain - have you considered using the CP dll as a basis for your work?

https://github.com/LoneGazebo/Community-Patch-DLL

Hey ilteroi, some of your work on AStar made it into earlier versions of AuI, and your merger work is much appreciated. Progress is slow but steady: if you're following the experimental branch, I'm currently in the process of cleaning up the 767 different warnings that get generated at level 4 (I've got it down to around 200). Since a lot of those warnings are generated due to Firaxis' RNG system mixing int types with unsigned shorts and unsigned longs, and because I feel their RNG system does not run fast enough to justify its low quality, I'm also considering swapping out Firaxis' LC generator for an SFMT one. C++ std's slower Mersenne Twister implementation is 2x slower than Firaxis' RNG on paper, but in practice, the difference is 0.005 ms per number generated, which is really small, and since the difference will be even smaller with SFMT, I don't think Firaxis' LC generator's minor performance improvement is worth its comparatively terrible RNG quality.

I was originally planning on merging CPP's DLL with AuI's after v10 came out, both because v10 will feature a lot of *big* systems overhauls (eg. DangerPlots is being rebuilt from scratch, yield/modifier valuing system unified, "ghostfinder" system for judging how easy it would be to reinforce a city, TacticalAI attack target prioritization will be rebuilt from scratch) and because a majority of those overhauls completely scrap the AI work I've done up to v9 (eg. the yield/modifier unification scraps most of my new trade route prioritization code and most of my belief scoring code).
And yes, I'm sorry to say that this means quite a lot of your work towards merging AuI v9 and CPP will be for naught when v10 is finally ready. I don't advise you start merging the experimental branch, either: the code is still unstable, and even if my WIP DangerPlots system already looks very promising, it is definitely not ready, mainly because I haven't modified TacticalAI and HomelandAI code enough to account for the new DangerPlots, resulting in the AI being way too conservative with its units.
 
ok - sounds good, looking forward to try v10 then.

have you taken a look with a profiler though? most of the time is actually spent in the pathfinder - if you could find a way to speed that up, that would be a real breakthrough. although speed is quite good in CP right now, but with a faster pathfinder the AI could use more complex algorithms to generate its moves ...

come to think of it, the other big problem is that multiplayer mode doesn't work (at least in CP, but it wasn't much better in vanilla i've heard, so i assume AUI has the same issue). there are desyncs all the time. those really have gazebo and me stumped, so if you want to take a look into it you're more than welcome ;)
 
have you taken a look with a profiler though? most of the time is actually spent in the pathfinder - if you could find a way to speed that up, that would be a real breakthrough. although speed is quite good in CP right now, but with a faster pathfinder the AI could use more complex algorithms to generate its moves ...
Last time I looked at the mod with a profiler, I remember seeing that the majority of A* calls were about whether a unit could enter a tile (CvUnit::CanMoveOrAttackInto() and its cousins). Besides your neighbors caching thing and a myriad of minor optimizations I made via reordering certain calls, the three biggest A* performance optimizations I implemented were enabling proper caching, eliminating redundant calls for the CvUnit::CanMoveOrAttackInto() function family, and implementing an A* turn limiter. If they aren't in v9, you can find all three in the experimental branch, and I'm fairly certain those changes are stable enough for porting.

An odd thing I noticed about how the pathfinder uses instances AStarNode is that, well... caching only exists on paper. AStarNode and its siblings have a boatload of members that are made to cache more expensive calls for each node that are independent of path, so that these calls are only made the first time the pathfinder hits a node, not every time it runs through that node. The pathfinder functions cache away these variables alright, and they fetch values from the cache wherever they're needed... but every time the pathfinder enters the plot validation function, it will recalculate the cached values, even if those values were already calculated the last time the A* node was checked for validation.
My solution: I added a new flag into the AStarNode classes that gets set when values are cached for the node, and this flag gets unset when the object is reset (ie. when a new path is calculated with a different unit, from a different starting plot, or with reuse disabled). Then, the pathfinder checks whether this flag is set whenever it would want to (re)calculate the cached values, and simply skips these expensive function calls if the flag is set. I also had to make some other modifications to the pathfinder functions to cover my bases, eg. the path's starting plot needs to be cached before the function returns true.

The CvUnit::CanMoveOrAttackInto() function and its siblings have a LOT of redundant calls, ie. they check for the same values two or three times over the course of the function's execution. Worse still, the pathfinder functions that call these functions can call them multiple times: IIRC, there are cases where a pathfinder will end up calling CvUnit::canEnterTerrain() six times for a single plot. Since these functions are fairly expensive, cutting down on pointless calls speeds things up considerably.

The A* turn limiter is my dirty way of making sure units who are 20 turns away from a target don't waste time by checking whether they can reach a target within a turn; it's optional without my A* road fix, since you can easily rely on a heuristic, but since my A* road fix can potentially increase the heuristic's range by 3x, the A* turn limiter is almost mandatory to cut down on processing time. It works in a fairly simple fashion: as the pathfinder constructs possible paths to the target, it will not accept tiles whose turn count exceeds whatever value the turn limiter is set to. If a unit is just outside of the turn limit, the pathfinder does run slower, since instead of completing the path, exiting the pathfinder, and returning false, it will instead branch out with all its other paths until it no longer has any valid tiles before returning false. In all other cases though, the fact that a unit 3 turns away does not need to go through constructing a 3-turn long path when it wants to check whether the target is 1 turn away does speed things up.

Word of warning about readability: I did reorganize the AStar class so that it uses FastDelegates instead of function pointers, because that way I can reference members of the particular A* object without having to constantly pass an AStar pointer through arguments; if you're keeping AStar's function pointer setup make sure you add a pFinder-> in front of any calls to internal AStar functions or members.

I also messed about with trying to get OpenMP to work with the pathfinder, mainly by seeing if I could somehow parallelize the child validator, but I was not successful: even when the game did not freeze up on me, it refused to validate any tiles outside of ones lying straight NE of the starting plot. It might be worth a look though.

come to think of it, the other big problem is that multiplayer mode doesn't work (at least in CP, but it wasn't much better in vanilla i've heard, so i assume AUI has the same issue). there are desyncs all the time. those really have gazebo and me stumped, so if you want to take a look into it you're more than welcome ;)

I haven't really been getting reports about desyncs, but I haven't heard much about how well the mod works in multiplayer as well: I'll get a comment or two on the workshop page every month that alludes to the person either using the mod in multiplayer or planning on using it, then never hear from them again. From what I can tell, a lot of the time those re/desyncs are caused by Lua interactions, though I can't really explain why; v9 doesn't use any new Lua code, which might explain why the mod doesn't cause desyncs (if it works in multiplayer), and I'm hoping the 4 new event systems I added in v10 don't cause sync issues, either (they're for dynamically changing the flavor of a building, unit, project, or process via Lua code, eg. Petra getting Growth and Production flavor for every desert tile within city radius of the city in question).
If the desync is caused by the RNG though, you could fiddle with CvDllGameContext::RandomNumberGeneratorSyncCheck() (if you haven't already tried) to have it pause the DLL before the resync happens so that you can have a good look at the game state (or, if you want to analyze other people's resyncs, have the DLL create a minidump right before the resync).
 
i've already ported the turn limiter, but the rest of the pathfinder optimizations seem new to me. i'll take a look and see if i can merge them.

the desyncs are hard because we have only part of the source code ... RNG doesn't seem to be the issue ... for a time we suspected the FAutoArchives, because it is not entirely clear which variables need to be included there, but no progress so far ... finally we have the glorious calculateSyncChecksum() function which is apparently never called. quite a mess.

btw, just checked your most excellent change log and there are definitely some small changes i'll cherrypick right now, e.g. observer fixes, unit power calculation. so thanks for that!
 
the desyncs are hard because we have only part of the source code ... RNG doesn't seem to be the issue ... for a time we suspected the FAutoArchives, because it is not entirely clear which variables need to be included there, but no progress so far ... finally we have the glorious calculateSyncChecksum() function which is apparently never called. quite a mess.
Insert a breakpoint at the start of FSerialization::SyncPlayer() maybe? It's at the top of CvPlayer.cpp, and I'm guessing it's called whenever player data needs to be synced, so having a breakpoint before the data is synced would let you browse the player object's stats to see where the desync is. You could also do this with FSerialization::SyncCities(), FSerialization::SyncPlots(), and FSerialization::SyncUnits(), in case the desync was not caused by player data.

btw, just checked your most excellent change log and there are definitely some small changes i'll cherrypick right now, e.g. observer fixes, unit power calculation. so thanks for that!

Careful with the unit power calculation changes, I honestly have not tested them yet for balance (since CS bullying depends on unit power).
Also, setting observers to have met all players does not let them see demographics: the demographics UI code is set to only reveal a player when they have met you, not when you have met them (and if you let players meet observers, it'll make the AI do weird things).
 
Hey Delnar, I just wanted to check for sure, are you okay with people cherrypicking some of your changes? I'm working on integrating my own Hierarchical Task Network/A* planner implementation into the Civ 5 DLL and I'm trying to reuse code wherever I can, but I'd obviously like to reuse the better version of that code. It's likely this thing will never see the light of day as it's a huge undertaking and I'm largely doing it as a proof of concept, but you never know.
 
Back
Top Bottom