The Icemark was indeed a changed place

An interesting thing came from fixing the battle code in Doomdark’s Revenge, it highlighted a bug that is in the original code, that I can’t tell if ever caused an outward problem.

It is all to do with loyalty.

Loyalty in DDR is about race. Which race are you loyal to: Giants, Dwarves, Hearstealer, Moonprince… etc. When a lord recruits another lord, they become their liege and the new vassal becomes loyal to the same loyalty race.

When a AI lord moves in to a location, they check the loyalty of any lords present and if there is a difference in loyalty, they may attempt to recruit.

The problem is – if a lord who has vassals is recruited the loyalty of the vassals does not change, but the loyalty of the lord does. The liege of the vassals does not change either. So these lords have a liege of a lord who is not loyal to the same race as them. And they will potentially attempt to follow them or follow the same instructions. Even though they are technically the enemy.

Should the vassals ever find themselves in the same location as their liege, they are no longer the same loyalty so they will attempt to recruit them. In this instance the, the vassals becomes the liege of their liege, but their own liege does not change. Confused yet?

What we have here, is circular lieges.

Example:

Glormeon the Giant’s liege is Imagrorn the Giant, and Imagrorn’s liege is Varagrim. But let’s say Imagrorn is recruited by Carormand the Barbarian, then Imagorn’s liege is now Carormand, and his loyalty is to the Barbarians. But Glormeon’s liege is still Imagrorn, however his loyalty is still to the Giants.

The first problem here is that these two lords now see themselves as enemies or potential recruits. So they could fight or recruit. But also Glormeon will follow orders of Imagrorn even though they are enemies. Which may be tracking him down or following his enemies, or liege… and should Glormeon enter a location with Imagrorn he may attempt to recruit them. If this is successful then Glormeon’s liege would be Imagrorn and Imagrorn’s liege would be Glormeon.

In the original this situation would occur. Which I think really just messes with the AI. But in the remake, I traverse the liege tree to work out who is the overall lord in charge, and if we have a circular liege then the game crashes!

So the question is – how to fix.

So I remove the liege traversal and allow the original messy AI or do I break the liege link as soon as recruitment happens… ie: When Imagrorn is recruited and his liege changes to Carormand, then Glormeon’s new liege becomes Varagrim. Or should Glormeon remain loyal to Imagrorn and change his race loyalty to the Barbarians?

As a bug fix the first seems the quickest and keeps the gameplay the same. The other two options the potentially becomes gameplay rules.

This also doesn’t take in to account that the human player, ultimately The Moonprince, may be the loyalty race of some of these lords.

I am tracking the bug here on the GitHub repos for anyone interested.

Should we prepare to do battle against the wind and snow?

I was looking at a battle issue reported recently that battle reports were not always being displayed. I’ve had reports before that the AI Lords don’t appear to engage in battle the same as the original, but I have tested this many times and not been able to find an issue other than the AI being unpredictable and a little shaky.

I had recently done some testing while adding the new game rules in place and had my lords attacked, and indeed failed an approach of lord and been attacked, and attacked a lord and battle had commenced, so on the whole it seemed to be working.

I poured over the code again to check the AI conditions of battle and noticed something interesting. The rule is that if the lord can’t walk forward and it is dawn and they are not blocked, then there is a 50% chance of them staying in the current location and ending their turn.

Analysing that a little; blocked means exactly that, they cannot move in the direction they want to under any circumstances. This is usually because of Icy Wastes or there are too many people in the location. Can’t walk forward is usually a transitory check, something in the location they are in or entering could potentially stop them entering, but nothing that would break the game. The Dawn check is just helping to differentiate moving into a location during the turn from moving out of a location at the start of the turn.

The code that makes those decisions is separate from the code that is handling the AI Turns logic. So I went to look at that to check under what condition the lord could not walk forward, and the thing that caught my eyes was – if there is an enemy in the current location. Combine that with the checks in the AI Turns logic and what you have is, there is a 50% chance that a lord will leave a battle at the start of his turn, but if they enter a location with an enemy they will always stop to fight.

The problem was that this flag was being reset by another piece of logic which was actually more to do with the player and not the AI, and also some shared LoM logic. The net effect was, if you attack an enemy lord, or have a failed approach, then the enemy will pretty much always move on and not engage. Now, this seems counter to what my testing has shown, so I can only posit that there was something else happening in these situations that was keeping the enemy at the same location.

Once I realised this it was a quick fix to isolate the piece of code that was overwriting the can’t walk forward flag. However, this then highlighted another bigger issue, and that was that the lord was not considering the enemy as their enemy, and thus the trigger was not occuring.

Again, I checked out the area of the code that handles this, and clearly it was based on LoM, in that Luxor is the friend, and Doomdark/Shareth is the foe. But this does not take in to account the multi-race nature of DDR. So what in effect was happening is that the AI was not really considering the players lords as enemy, or indeed not always correctly considering the enemy AI lords as enemy, which means they would often over look them. Obviously there were fighting, but again I think other issues were causing them to stay in the location of another lord. And as long as multiple lords are in the same location, the battle algorithm would get the friend/enemy check correct and they would fight.

Now, this looks like it has always been wrong, ie: nearly 10 years since I released the games. But I suspect the problem is actually due to the merging of the code bases and likely crept in in the later stages of v1 and then rolled over in to v2.

To Luxor, everything now grew clear

Clumping together...I’ve spent some more time looking at the AI for Doomdark’s Revenge, trying to work out why it doesn’t quite appear to be playing like the original. One thing I noticed is that I have completely misunderstood the recruiting logic when it comes to Loyalty and Treachery. I made changes in the last version, but I am going to need to revert them.

The approach algorithm

  • compare the the attributes of the lords and looking for matches gain +1 for each match.
  • If the character being approached is not loyal then +1
  • If the character being approached is treacherous then * 2
  • if the recruiting character is the liege of the character being approached then +3
  • If the recruiting character carries a crown of persuasion then +2
  • If the score is greater than 3 then the approach will succeed.

The basic concept that I have misunderstood is: Loyal characters are less likely to be recruited away form their current liege and un-loyal characters are more likely, therefore the algorithm gets a +1 for none loyal characters. And that treacherous characters are more likely to leave and thus the *2

The next thing I have missed is the lords following the objectives of their lieges.

It works like this.

If the lord has a liege and that liege is following their liege or their foe, then we must follow our liege. Otherwise pick a new objective.

There is a 32% chance that we will pick a new objective. Although that should be 25% because we could pick the objective we already have. That leaves a 68%/75% chance that we continue doing what they were already doing.

The problem for me is the first check. If we use Shareth as an example. She has a 12.5% chance that she will choose to follow either her foe or leader. As she has no leader she reverts to Luxor, which is her foe. So she has a 12.5% chance that she will follow Luxor. All the lords that follow her now have a 100% chance of following Shareth, and this ripples all the way down the stack of lords. Which at the start of the game means that 47 Icelords will disregard what they are doing and follow her.

The mistake I had made is that I had made the following lords take the objectives of their liege when their liege was following their liege or foe. What this means is that when the liege is following their foe the lord follows their own foe. So using Shareth again as the example, when she is following her liege ( Luxor by default ) then all her minions will head to her location, but when she is following her foe ( Luxor ), then all her minions are heading to their foe and not to her location. So as an example, Imgaril the Icelord would be heading to Imgorthand the Fey, who, is likely the the other direction of Shareth.

Hopefully this fix should make the game more like the original, but it bothers me that it is a flawed AI. I ran the game for the first ten days, up until the first battle took place, I ran it on the emulator too to compare notes. Here is what Shareth did over those days.

  1. Head Home
  2. Head Home
  3. Head Home
  4. Follow Luxor
  5. Head Home
  6. Follow Luxor
  7. Follow Luxor
  8. Follow Luxor
  9. Search for object
  10. Head home

Now Talormane does this

  1. Head Home
  2. Head Home
  3. Search for object
  4. Search for object
  5. Follow Lorelorn
  6. Follow Lorelorn
  7. Follow Lorelorn
  8. Follow Lorelorn
  9. Follow Lorelorn
  10. Follow Asorthane

The reason for the delayed follow on day 4 is because Talormane is following Lorelorn who is following Shareth, but Lorelorn is lower in the processing order than Talormane, and thus Talormane doesn’t know that Lorelorn is going to follow Shareth in that turn.

The final thing that I changed was that there is a 6.25% chance that the change of objective will be DO NOTHING. This is especially important for being in a battle with someone who is not the lords foe, because it means that without this the lord will always leave the battlefield. The mistake I had made was that I persisted the do nothing as an objective, i.e.. The lords objective becomes do nothing. But it shouldn’t, it should stay the same as the previous objective, and this turn that objective is ignored.

Going back to Shareth. If she chose to DO NOTHING then her objective would no longer be follow liege or foe, which means that her followers would be able to perform whichever objective they chose. However, if her previous objective had have been follow liege or foe, then her followers should still be heading towards her when she chooses to do nothing. This would have the affect of allowing them to catch up on her.