Hello, and welcome to the first of a number of blog entries from Grenade Tree Games that will chronicle our adventures through the wild world of independent game development!
Today’s post will cover our programming team’s learning experiences as we tackled one of the more daunting aspects of game programming, artificial intelligence, for Outland 17. Being a turn-based game where tactics are one of the core gameplay elements, the standard that Outland 17’s AI absolutely needs to meet is already well above what many games could get away with.
For one thing, turn-based combat where all the enemies in play are generally always observable by the player means the AI is always in the spotlight, and there’s no room for smoke and mirrors. Not only that, but the player is presented with a wide variety of options for squad compositions and builds and thus the AI needs to be able to react believably to a wide variety of tactical situations they could be presented with.
But enough about how you feel sorry for us because of how hard we had to work, let’s get into how we did it.
How Does the AI In Outland 17 “Think?”
The first thing to understand about game AI is that computers aren’t really advanced enough to understand the concept of ideas. Now, that probably sounds obvious in light of the lack of a machine uprising happening (so far), but let’s look at what that means in a little more detail.
Game AI can’t just determine something like “Currently, shooting Steve is better than advancing my position,” because a computer can’t just know what either of those things are. What computers can understand however, is that 10 is more than 8, and thus the fundamental challenge in producing Outland 17’s AI is how to dynamically convert “shooting Steve” into a 10 and “advancing” into an 8. Fortunately for us, our game deals heavily in stats, so most things are already a number or have a number associated with them, even factors like where someone is standing.
The problem, however, is how to convert the subjective worth of all these numbers into objective numbers the game AI can use. For instance, health would generally be considered more valuable than chance to dodge, but how much more valuable? How do you quantify the highly situational value of mobility?
The simple answer is creating a lot of simple mathematical formulas that eventually get combined into one final number for every possible action an AI can take, then testing and refining each formula until each stat’s formula produces a range of numbers that we feel is appropriate for that stat’s importance.
This gets more complicated when some stats are used in multiple formulas (including hidden variables we give ourselves to customize different NPCs to give them “personality” in combat), and many formulas use 2-3 different stats, if not more, but that’s one of many reasons games need to be tested so extensively.
Initially our game AI simply used these formulas to look at their present situation and the “quality” of each possible action they could presently take, but it was apparent that for them to really challenge the player on a consistent basis in terms of raw tactics, they would also need to think about the future beyond the present turn. Otherwise they might have the opportunity to position themselves to win the battle in just a couple of turns, but they wouldn’t see it because they can only look at the potential for immediate results.
Learning To Think Ahead
Our first attempt at changing this involved a system that would essentially run an invisible copy of the game in the background and run for several turns looking at all the possible actions everyone could take, then looking strictly at which first action led to the best potential outcome. This would be game AI that thinks like chess players making their moves by always anticipating many moves in advance.
The problem with this approach is that while Outland 17 may not have strategies as complex as those of high level chess play, we have a lot more going on each turn in terms of variables to consider, such as variable chance for attacks to hit their target, and an Action Point system instead of simply one move per turn. What this means is that, even though the background copy of the game cuts out all the unnecessary delays like animations, audio, etc., simulating more than about a half a dozen actions into the future starts to lead to too long of a delay while the AI decides what to do, simply due to CPU limitations.
The reason it gets so bad is that each new turn has its actions branch out from the last turn, leading to exponential growth for every new turn simulated, and measures to restrict which actions are simulated can only go so far without limiting the usefulness of the simulation. Furthermore, because each individual unit gets their own turn, that means a full turn rotation for a 4 vs. 4 fight would actually be 8 turns, with most of those turns having the potential for multiple actions before ending the turn.
So, since having the game AI simulate less than at least one full round of turns would mean they would completely ignore anyone whose turn is too far removed from their own, and because some players MIGHT not like a game where the AI takes 15+ minutes to decide what to do, we decided we needed to try yet another new approach.
Speed Up the Game AI!
Luckily for us, the third approach we found wasn’t actually very “new.” Our first system of rating each action based on the action itself and the present situation had been working decently before we abandoned it, so the solution was to revert back to that, and to simply insert other systems into it.
We took all the code we created for our future turn simulating system so that it would only simulate turns for the AI whose turn we’re thinking about. That way the game AI could understand simple concepts like “if I keep moving towards something for a few turns, I’ll reach it,” without being bogged down by all the factors other characters could change on the way there. After rating the outcomes like this, we simply plug that number into the long list of numbers that already get thrown together to rate an action.
Additionally, we also added a smaller, third system that records what each character has been doing in past turns so they can get higher priority if they’ve been doing something the AI is would particularly like to stop (which again involves translating all this to numbers and adding it to the existing sequence of formulas).
By using a hybrid system like this, that looks at the past, present, and future, rather than relying entirely on one of those, we are on track to create AI that will challenge players to plan their actions and make tactically sound decisions. All that’s left is yet more testing and refining to get all those pesky numbers right.
We hope this has been helpful getting you aspiring programmers started! We’ll be sharing more development insight in the coming weeks, make sure to sign up for our Tree-Mail newsletter to receive the latest info!