Many battles fought again by tongue

Strange day today. Not just because it would have been Mike’s birthday, but because for some reason I found myself fixing a bug in Doomdark’s Revenge. Back in January I had a bug reported to me by Simon Foston, I managed to get some save games from him and just needed to find some time to look at it. Now, it’s taken a little while for me to find that time, but for some reason I looked at it today. It wasn’t a conscious decision, I was just looking through some emails that needed dealing with and noticed Simon’s bug report.
A quick look through the code and with a tip off from Simon’s report, it became apparent that the ghosts of dead lords were continuing to take part in battles. I checked the original code and it looked as if the bug was there too, however, I then found that the isDead check was happening later in the process. Strangely what it means is that battles at a dead lords location are processed as part of a dead characters turn, and not as part of other characters in the location. In my case, I’d missed the later isDead check and therefore the dead lord actually took part in the battle.

 

So, it seemed strange to be working on a bug in Doomdark’s Revenge that included the dearly departed, today of all days. I’m sure Mike had a wry smile..

We must find friends as well as enemies…

A new bug came to light in Doomdark’s Revenge. I had a report of a crash bug that occurs after 138 days, that’s one hundred, one score, and eighteen days since the Moonprince rode forth into the Icemark.

The problem appeared to be that an AI character’s liege was getting set to himself. This causes a problem in the AI logic for a character choosing to follow their liege. The AI goes something like this…

I want to follow my liege, but my liege is dead, so I need to follow my liege’s liege and this character will become my new liege. The code ripples up the liege tree until it finds someone to follow, or bails and decides to hunt down Luxor instead. When the bug occurs it follows the tree and finds a liege who is dead but they are also their own liege, and thus we get stuck in an infinite loop.

Once I found this as the source of the crash, I needed to work out WHY it occurs.

There are only two places where the liege can change, the aforementioned follow liege routine, and being recruited.

I stuck some debug info on both cases and set the game to run on automatic to see if the issue triggered, and it did.

Here is the scenario…

Anvarorn starts with Fangrorn being his liege. Fangrorn’s liege is Shareth. Fangrorn gets recruited by Anvortheon the Barbarian, and thus his loyalty changes to the barbarians, and his new liege becomes Anvortheon. Anvarorn decides to follow his Liege, who is still Fangrorn. When he gets to the same location as him he notices that they are not the same loyalty and thus tries to recruit him. He succeeds. Thus Fangrorn’s liege becomes Anvarorn. So we now have a circular liege issue. This becomes a problem if Fangrorn dies, because in this instance Anvarorn decides to follow his liege, finds that his liege is dead so takes his liege’s liege as his new liege and therefore becomes his own liege!

I went back and checked the original code, and this issue can happen. The only place you would notice it would be on one of the description screens where it would say, Anvarorn’s liege is Anvarorn – or words to that affect. At worst the character would end up following themselves and end up not moving. This is something that has been mentioned as possibly happening in the current version.

When I implemented the liege tree walk, I did just that, I implemented it as a walk up the tree, and because of the circular issue, a dead lord who is their own liege will create a circular loop if they are someone else’s liege. The original code doesn’t do that, it only takes the next liege up the stack and therefore slowly makes its way up the liege tree over a number of nights. Thus no infinite loop.

This would possibly occur with characters following their foe. If their foe is dead it walks the liege tree of the foe to find the next foe.

Fix to follow soon…

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.