Let’s talk Irene – Developing a good enough TBS AI


Disclaimer: I haven’t dabbled much in Artificial Intelligence before beginning this project. This article is aimed at developers starting out to develop a TBS game with no prior experience, looking for a place to start their journey. I’m not claiming this is the right way to handle AI and I won’t vouch for the sanity of the code (or anyone reading it).

Welcome to part II of my ongoing attempt to document my progress in designing a “Good enough” turn based strategy AI for my upcoming game Mutant Gangland. In this article I will focus on documenting the improvements I added to Harrold (the 8th AI iteration). This new version will be dubbed Irene and she will, hopefully, play a even better game then her older brother did. My focus in this iteration is on smart unit creation. Till now I only worried about getting the AI to move the units across the map and accomplish each units goal. One thing I notice from play tests, and from other people’s feedback, is that it’s easy to kinda rush the AI with a bunch of units early on and catch them of guard. Previously Harrold created his units pseudo-random, based on a few tables (or arrays) for predetermined situations. Here’s an example:

self._dispositionList[1] = { SCOUT, SCOUT, RIFLE }
self._dispositionList[2] = { SCOUT, RIFLE }
self._dispositionList[3] = { CHAINSAW, RIFLE }
self._dispositionList[4] = { SCOUT, ROKKIT}
self._dispositionList[5] = { CHAINSAW, ROKKIT }
self._dispositionList[6] = { SCOUT, RIFLE, SCOUT }
self._dispositionList[7] = {RIFLE, RIFLE, RIFLE}

if bool == true then — so, defensive mode
if threat == 1 then
building:createUnits(unit:_returnTurn(), self._dispositionList[7])
building:createUnits(unit:_returnTurn(), self._dispositionList[6])
local rnd = math.random(2, 5)
building:createUnits(unit:_returnTurn(), self._dispositionList[rnd])

Apparently, I cannot ident code in this theme. Hopefully there’s a plugin available for this.

In the early turns (first 3) he would create units based on the first few tables, first turn focusing on getting some scouts out and a Rifle unit. The second turn another scout and a Rifle, and so on. Mid game he would just create units at random from tables 2 to 5 and, if under threat, he would go all out and spawn units from table 6 and 7. In order to detect what situation he was in, Harrold would perform a check at the start of his turn to see if a enemy unit is in range of assaulting one of his buildings. That check would return true and a threat value. If a Battle Unit was nearby one of his houses or temples, the threat value would be 0 (since Battle Units cannot conquer) but if a Assault Unit would wander in range of his temples Harrold would spawn Rifles or Rokkits at all of his temples and send them off to engage it. With Irene I hope on fixing two big problems that severely hindered Harrold:

  1. Players can game him to spawn rifle units again and again till he would run out of money. Since the AI would send his Battle Units against assaults, the player could just keep 1 scout near a temple and move him around to get the AI to chase him, while getting a few other units to occupy the now undefended temples.
  2. Harrold treated all temples as a group and would create units based on a threat to one temple.

In order to solve this issue I decided to add another component to the AI (besides the Tactical One) and it works a little bit like this:

  1. At the start of the turn, make a list of all owned temples.
  2. Loop through each building in the list and check to see if any enemy units are in range.
  3. If there’s a battle unit in range, leave it to the Tactical Component to decide what to do with it.
  4. If an assault unit (that can conquer the building) is within range, score that unit.
  5. Spawn a unit with a bigger score than the enemy unit. Our unit must have the lowest monetary value (we don’t want the AI to spend all it’s resources on creating Rokkits in order to defeat a enemy with 4 HP).
  6. If there are no threats in range, then check the ratio of assault and battle units. (example: If no assault units are on the field, or are less then the minimum desired amount, then create an assault unit and hand him over to the tactical component. The same can be said about Battle Units.
  7. Move to the next temple on the list.

With this new behavior in place I decided to pit Irene (PINK) against Harrold (BLUE) on a map I previously designed and, to my surprise, Irene did not only beat Harrold, but she did it in 2 minutes and had 60% of the map covered in units. Here’s a video I have recorded of Irene dominating the battlefield:

Truth be told I was expecting Irene to win the battle but not by so much. Having a better control over unit creation indeed balanced the game towards her side. The rule by witch she spawns unit takes in factors such as:

  • earlyFactor: How many turns have passed. Spawning of assault units is severely influenced by this, as we want to capture as many buildings as possible in the early game
  • unitFactor: The ratio between how many units of one class we want / how many units of said class we have
  • unitScore: The score of the unit we want to create (based on it’s HP, Mobility and Damage)
  • the cost of the unit: How much money does that unit cost.

In order to create the units, I loop through a table containing all the units that are available for that team and score them based on the factors above. The rule I currently use (and made for Irene) is:

equationToDecide = earlyFactor + unitScore * unitFactor * (self._player[team].coins - unitCost)

One thing that’s a tad messed up in Irene at this point is the tendency to not create Rokkiter’s. Currently, the price for a Rifle unit is 40 coins, while a Rokkit costs 120. Scoring the unit would return a score of 60 points for a Rifle and 140 for a Rokkit. From her point of view, it’s better to create 3 Rifle units rather then one Rokkiter. Hopefully by changing a few weights (pointsPerHP, pointsPerMobility, pointsPerDamage) I can fine tune her behavior a bit and get her to make even better decisions.

That’s it for this article. Next thing I want to handle on the AI side is to get Irene to use Rokkit’s properly and in a strategic fashion (since it’s a range/artillery unit). Thanks for reading and if you have any questions or suggestions, be sure to post them.


2 thoughts on “Let’s talk Irene – Developing a good enough TBS AI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s