[This is the next post in a series on building an AI to play Exospecies. previous post]
The abstract HTN algorithm can be implemented in a bunch of different ways. We need a way to:
None of these seems like much of a challenge. They could be easily implemented in any language I can think of. So, why do academic researchers mostly end up implementing them in LISP or Prolog and taking a “logic programming” approach to their implementation? Nothing I could find in the literature or online described why this was the case. Some theories:
That final reason led me down the rabbit hole. Logic programming was always something I had meant to investigate and maybe this was a perfect use for it. I decided to make my prototype implementation using this approach and see if there was any benefit.
I’m going to jump way ahead here and outline the finished product and then we’ll come back and go through each piece in turn to understand them and see how they are built.
The AI engine has a bunch of general purpose code that I will be open sourcing eventually to save others the trouble of having to figure it out. It has nothing to do with Exospecies, and for that matter nothing to do with Game AI, it can be used for lots of different applications. It is worth noting that it is all written in C++ so it can be more easily ported to different platforms. While it might sound like a lot of complicated stuff, it turns out the entire engine is not really that much code (1500 lines of C++ for the parser/compiler and 4800 for everything else, excluding blank lines and comments). A huge goal was to keep it simple, debuggable, understandable.
The HTN Planner engine does the work that was described in my previous blog entry about Heirarchical Task Networks. It creates a plan given a set of Methods, Operators and Facts. It also allows for “Axioms”, which are simply helper rules. This engine was modelled very closely on the work done by the [SHOP planner team] (http://www.cs.umd.edu/projects/shop/description.html), and is basically a SHOP planner.
The Prolog Resolver is a simple implementation of the Prolog language that is used for Method resolution, storing facts and for creating Axioms. There are open source Prolog engines out there that I could have used, but they are large code bases and implement all kinds of details required by the full Prolog language. I wanted something that was small, easy to understand, debug and customize. It doesn’t implement all of Prolog (a notable exclusion is functionality for lists), but it has everything I’ve needed for the Exospecies AI and is easy to extend. I found The Art of Prolog to be invaluable when implementing this. This writeup on Unification from the University of Iowa was also key. There is tons of information on the Internet about Prolog, the language, etc. including online Prolog compilers like SWISH that were a huge help.
The Prolog Rule Store is a pretty simple name/value pair store that stores Prolog rules and allows for making changes to them while tracking the differences to keep memory usage down. Because the HTN algorithm (and Prolog) backtracks when it fails, there can be a ton of copies of the Rule Store around with small changes.
The HTN Domain Store really is just a very simple name/value pair store, and it is just there for completeness. It is a tiny amount of code.
Finally, while it is possible to build Prolog and HTN objects like Rules, Methods, etc using code, it is much easier to write them in text, so I needed a parser/compiler that converts text into objects in the stores. I have a simple, template-based parsing engine that I’ve used for this (and lots of other Exospecies parsing tasks)
To actually use the engine for any purpose, you’ve got to do a few things:
The next many blog entries will walk through how these different pieces work and interrelate and how they are implemented. I’ll be sharing the code for the engine when I get a chance to clean it up.
I got all wrapped up in trying to write the blog post for my survey of AI algorithms, how I classified them, etc. and even I got bored. So, I’m switching to the topic I actually wanted to write about: Hierarchical Task Networks (HTNs, sometimes called HTN Planners).
I used them in the first Exospecies AI prototype. I started there for a few reasons: It is a proven algorithm that had already been used in published games, it builds an AI that behaves like a human, and it appeared to be one of the fastest algorithms due to its hierarchical, depth-first, human-scripted approach.
Turns out there’s lots of information out there on HTNs but I couldn’t find enough to understand them to the level where I could build one from scratch. I also didn’t find any good examples of using one for strategy game AI. The next few posts will try to correct that. I may come back to writing up the survey once I get some distance from it.
What I did find was two buckets of stuff on the Internet:
The confusing part was that, if you digest the Bucket 2 stuff, HTNs seem like a relatively simple tool (along the lines of state machines) for helping you to organize a giant script that just tells the system what to do. I guess I was expecting something more magical here. So, I figured something must have gotten lost in translation from the really theoretical proofs, logic and scientific sounding stuff in the “literature”. I dug deep into that and I think I understand it now. Let me take you on the journey…
In broad strokes, an HTN solves a problem using a very human planning approach that centers on abstraction and alternatives.
Alternatives: Let’s use a travel example from the famous SHOP HTN Planner. Imagine you asked me how to get from downtown to the park. I might say, “Well, it is 2Km away but, if it is nice out, I would just walk it. However, if it is raining and you’ve got taxi fare, I’d take the taxi. Otherwise, if you have bus fare, just take a bus.” A abstract thing you accomplish like “travelling from one place to another” is known in HTNs as a “Task”. The 3 alternatives I gave for accomplishing it are known as “Methods” in HTNs. Note that each Method had a “Condition” that went with it: walk if it is nice out, take a taxi if you have the fare, etc.
Abstraction: Note that these 3 Methods are at a pretty high level of abstraction. You might further ask me how to accomplish the “TakeABus” Method. I’d say something like, “Well, wait for the #1 bus at the ‘Downtown’ bus stop, pay the driver $1, and ride it to the ‘Park’ stop.”. These three tasks are known in HTNs as “Subtasks” of the “TakeABus” Method. In HTNs, Methods do what they do by decomposing into Subtasks. Now, this is a pretty complete answer for a human but maybe not if we were talking to a robot. In that case we might need to further decompose the task of “PayTheDriver$1” into something like “Reach into your pocket, grab a $1 bill, hand to driver”. Each of those could further be decomposed until we finally have something a robot could do. The final step of an HTN, the part that actually gets done, is called an “Operator”. How detailed you need to get before you are done (and have an Operator) depends on the kind of system you are building. A big part of designing an HTN model is getting your head around what the right abstractions are.
So, HTNs have these parts:
The algorithm itself is pretty simple. You start with a Task or Goal list like
Let’s see how the above travel example would work more formally. I’ve made up a little pseudo-computer-language just to help with the example. It is described as it is used:
Note a few things about this HTN model:
Now let’s do the algorithm:
We found two
So, we found two solutions. Depending on what you are doing, you could stop with the first solution and just use it, or generate all of them and somehow evaluate which is the best:
You can see how the HTN algorithm starts with a high-level goal and breaks it down into more detailed plans until you have a set of Operators that your system can “just do”, ordered in the proper order. This all works because a human has carefully crafted the Methods and Operators to make sense for your problem. In practice, you don’t end up “thinking about the algorithm” so much. Really, you just end up thinking about the base operations your system can do and about how you’d teach someone to do the things you need done.
HTNs are a natural way to describe how to solve a planning problem where you start at a high level and refine your way down to a solution. They have some great features:
And they have some downsides:
In the future posts I’ll talk about how I actually implemented an HTN and different approaches you could take, as well as how I actually implemented the Exospecies AI as an HTN.
Like any design problem, the “best” approach for an AI algorithm depends on what you’re trying to optimize for and the constraints of the problem. So, let’s get some guardrails up by defining the game more formally and listing goals and natural computer advantages. We are going to need all the advantages we can get.
This is meant to be a boring distillation of the game mechanics, not a “pitch” for the game! Implementation of algorithms will be very dependent on the rules of the game, so this is here as a reference you can come back to.
The goal of Exospecies is to find and destroy all of your opponent’s supply units. How is described below.
Exospecies turns are complicated because each represents a period of time that actions occur in. Lots can change in this period of time: units can move into or out of range, go underground, explode and splatter poison, etc. Thinking about how things may play out over the time represented by a turn is key to winning.
Here’s how it works: Once the players have chosen their actions, the Exospecies engine calculates what happened. It does this by breaking down the turn into twenty points in time (“timeslices”) called “ticks”. It then plays out actions over this time period: units move, run into each other, attack, etc. What team gets to go first in any given timeslice alternates for fairness. This matters, for example, when two units are trying to move to the same square in one timeslice.
Some types of games have been extensively studied and formalized in the field of Game Theory. Classifying Exospecies using formal terms, when I could find one, will make it easier to evaluate algorithm applicability.
It is tempting to think of Exospecies as just a variant of chess when looking at AI algorithms since chess is a well understood domain for AI with a huge amount of research and solutions available. Unfortunately, there are some big differences:
In chess, the results of a move are deterministic: if you move a piece, it moves. There are several places in Exospecies where the result of an action is not:
In chess, each player can see all the state of the game. In Exospecies, there are several places where a player won’t know the entire state of the game:
If you make a tree with the starting state of a game at the root and each branch representing a possible move, once you’ve finished with all possibilities you’ll have a tree. The bigger the tree, the harder it is to analyze. Chess has a notoriously large game tree. But, in chess, a game piece has only one thing it can do: move. In Exospecies, “move” is one of 4-5 actions any piece can perform. In addition, these actions can be quite involved: modifying the game board, causing explosions, combining with other actions from other units, etc. This makes the tree of potential moves much larger than chess since:
The number of actions available in a turn is limited by energy, so that prunes off large chunks of the tree. However, considering that you can still do about 4 different actions, selected from a number of alternatives, to chess’ 1 piece/1 action means the potential tree is still substantially larger.
In chess, a single turn consists of one move and results in a new position for that piece and potentially removing an opponent’s piece. In Exospecies, the turn can involve many units moving for a period of time, interacting during that time, tiles on the map changing, units getting poisoned, etc. The cost of calculating the next state of the game is expensive and often noticeable by humans in the game (you’ve seen the roiling fireball when you end a turn, right?).
In chess, a unit move is only constrained by whether tiles are open. In Exospecies, this is also true, but it is also constrained by amount of global energy, which is constrained by what supply units you have and what other units are doing. Actions besides Move can have other dependency chains as well. This makes reasoning about the game more difficult, sometimes a player has to follow dependencies backward to set themselves up to do something in a few turns.
In chess, players take turns moving their pieces. In Exospecies, both players take a turn and the result of the turn is then calculated.
There are a few characteristics shared between chess and Exospecies:
Not surprisingly, many of the properties of Exospecies make it very hard for building an AI that plays well. I designed the game to be a rich and deep strategy game for humans, which unfortunately means it will be hard for computers to reason about.
At the outset, not knowing a lot about the solutions that are out there, I wanted to make sure I was clear about the characteristics I wanted in the AI. It’s unclear how many I’ll get, but if you don’t have goals…
The last point needs some explanation: many (most?) strategy games end up cheating in their AI algorithm because building a winning AI is so hard. It could be anything from knowing things that are supposed to be secret (e.g. the opponent’s base) or getting an advantage in odds (e.g. random damage is always high). Players naturally don’t like AIs that cheat because it feels like, well, cheating.
Knowing where the computer has natural advantages will help me build an AI that enhances them when possible. Here’s what I’ve got so far:
OK, I keep talking about cheating, but I really do want to avoid it. However, given how often it is employed by necessity, I feel compelled to think a bit about where the computer could cheat if I absolutely had to?
OK, now that I’ve got the problem framed up a bit, my next post will be starting a shallow survey of different approaches. I’ll map them to the points above and filter out a couple of experiments to try.
This is a post in a series on building a computer player for Exospecies. Next Post.
Exospecies was always meant to have a computer player (aspirationally called A.I.s in the game business)…eventually. I figured I’d launch with only multiplayer (i.e. human vs. human play) since writing a good computer player for a strategy game is notoriously hard. Really, beyond notoriously hard (witness Google’s Go AI). I don’t have PhD’s on my team and I don’t have one. I was going to need time to write a decent A.I. Plus: people are more interesting to play against, right? So my plan was, launch multiplayer, then build an A.I., then launch that. It gets the game in the market quicker, I get feedback from real humans playing (which will surely be more challenging than people playing computers), and then do the A.I. player when it is clear what strategies are being developed by humans. This plan also gave me the runway I’d need.
What I discovered (and was told by basically everyone in the industry, like “Wow, multiplayer only, huh? Gutsy.”) is multiplayer-only games are hard to succeed with. Some reasons:
Having a computer player gets rid of a bunch of roadblocks and fills lots of gaps.
What finally pushed me over the edge to slip Exospecies until it had an A.I. was my beta feedback coupled with early reviews. The feedback was essentially: “I want to play the computer to learn, and I want to play the computer when nobody else is around, and you really need a computer player. Really.” It was loud and clear, and it only took me running up to the final launch week and getting some blistering reviews to get it through my skull (more on that later in a postmortem).
While there is an enormous amount of information on the Internet and in books about A.I. techniques across the spectrum from pure research to practical use in games, there were plenty of places where I’ve been going down dark holes and having a real challenge figuring out either how algorithms work, how to implement them practically, or even how to understand the academic literature. I thought that writing up a blog might save someone else the anguish later and help me collect my thoughts. I thought it might also be entertaining to see how a naive developer gets slapped around by a really hard problem and then rises above it all in glory! (that’s how I’m hoping this ends).
Follow along as I learn and attempt to explain the amazing world of A.I. as applied to game development!