One interesting comment that I've been seeing a lot lately around various message boards and such where people talk about AI War is the comment that the game is a labor a love, and that I'm clearly not looking to get rich off of it. It's actually refreshing to see that sort of opinion, since I was all but accused of being pretty mercenary (and in one case "one step above a viagra ad") in the early days of trying to promote the game (a super, super, hard thing for a new indie developer with no track record and no connections).
Which opinion is right? Am I a merciless scammer, or am I a idealistic philanthropist that is making games for pocket change and the good of mankind? While I am in some senses flattered by the latter view, I think I clearly fall somewhere in the middle, like pretty much anyone who starts a small business.
Why Start Coding A Game?
Yes, originally I did start coding this game because it "scratched an itch" that I personally had (for a large, complicated co-op RTS game with great AI), but I had already been coding games for a long time before that. I always saw it as a hobby until I started working on the Alden Ridge game, which I originally saw as a promotional item that I would give away for free to generate publicity for the novel by the same name that I was writing at the same time.
Alden Ridge (the game) eventually took on a life of its own, and truthfully I had started coding that to scratch a different itch, anyway: I love the game Lode Runner: The Legend Returns, but none of its sequels, and I wanted something that in some senses was similar, but really different. So a cooperative top-down game with zombies chasing you was born. As Alden Ridge took on more and more of a life of its own, I realized how many hundreds of hours I was sinking into it, and started to think it would be nice if I could actually get some money for that.
Transcending Hobbyism
I mean, I love making games and in some senses view it almost as a hobby, but then again they take a lot of time to code and I don't want to spend all my off-hours time doing it. I want my "hobby" to be my job (doesn't everyone?), and so to do that I need to charge some money and actually have people find and play my games. There's a difference between charging a fair price and gouging people, though, and I try to stay well on the side of the former, of course. I also don't go for the glitzy features that I think will grab people superficially, instead focusing on fun factor and the sorts of features I'm interested in. I try to make the best sort of product I can, for my main audience (me), and then I try to make sure that it turns out to be as accessible as possible for other people once that original vision is somewhat codified. I mostly only start thinking about other players fairly late in the process, because that's the point where the core game has become something interesting and fun in its own right (and hopefully somewhat original, since I wasn't trying to follow trends or mimic other games too heavily).
I guess that is one thing that makes me not a hobbyist, though -- I do think about what other people want, intensely, and I spent months refining the product to be as easy to pick up as possible. I just wait until really late in the process before I start doing that, otherwise all the inital designs start trending toward unoriginal directions.
Anyway, so I primarily made AI War for my own RTS group when I realized that there were no other products on the market that did specifically what we wanted, and that there didn't seem to be any on the horizon (we'd been playing on RTS after another for 10 years by the point I started coding AI War, and none of them had been exactly the right match for more than a year at a time, so I guess that goes without saying). Given that through Alden Ridge I had already come to a realization that it would make sense to do game development as a fulltime job instead of working a separate business software job and then coming home and doing game development, I knew from the start that I was planning on selling AI War. I still went about designing it in a really personal fashion, though, and I think a lot of what is original about the game came from that sort of design attitude.
What Are The Real Motivations?
So why am I doing this? For love or for money? Because clearly I love doing it and for a long time I did this sort of thing for free, but now I'm charging money and trying to make a functioning business out of this, with staff and everything. I guess the answer is both. Am I trying to get wildly rich off of this? Well, not in the sense of someone who thinks they are cashing in or following a get-rich-quick scheme. Game design is hard, it takes a ton of work, and then once the game is "done" it is still not done. I do a lot of work for free even after the product is "done," when most developers big and small pretty much walk away or issue a few minor patches. There are definitely some notable exceptions, such as the Evochron Legends developer, who so far as I can tell has a pretty similar outlook to mine.
Free Content
If I was a big publisher I'd probably be charging for my weekly free DLC releases, but instead I give them away for free. Why exactly am I creating those releases, anyway? Well, they make the game better, in some cases a lot better, but I was already getting really positive reviews before most of the free DLC that has already come out, so I probably could have gotten away with just a couple of bugfix patches. Doing the free DLC certainly generates a lot of goodwill, and a certain amount of word of mouth, but I'm not certain that it's entirely enough to really warrant the amount of time that goes into that free DLC if I was just looking at dollars and cents.
Oh, so that's why people think I'm doing it for love instead of money. Well, I guess they're right. I improve the game because I want to, because coding the game is almost like a game to me in some senses, and because I play the game myself and want it to get ever bigger and better. And because I'm just a polisher and a completionist by nature, I suppose.
Generating My Own Funding
But I'm also in it for the money. I want AI War to sell well enough that I can afford to hire a few people (composer, artist, another game designer or two, and maybe one other programmer in addition to myself), and so that we can be financially secure for a few years as we work on other games. The more security we have, the more likely we are to not try to trend-follow or rush products to market so that we can pay our mortgages, etc. So from that sense, I'm very serious about the money and do as much promotion as I can. Because I love it, and I want to be able to do this as my fulltime job, forever. I don't want to have to consult a publisher about every little decision, and I don't want to go into debt to finance projects, so that means that my earlier projects like AI War need to sell well enough to finance the later ones.
Really, I think that's pretty typical for your average startup company. They want to make enough money to keep doing what they love to do for as long as they can. I guess some startups form with an eye towards burning through venture capital and then selling the company for a profit, but personally I don't ever plan on selling Arcen Games unless something really drastic happens. It's not part of the current plan, anyway -- that's completely counter to my goal of being able to do what I love for as long as I can. Suppose I did sell the company, what would that get me? If it gave me a bunch of money, I'd just use that to start another game development company, so what would the point be? Vacations are nice, but I'd get bored as hell sitting on the beach drinking fruity beverages.
If AI War continues on its present track, I actually stand to make a good deal of money -- potentially millions, if it goes really well. Am I counting on that? No. If it happens, am I going to run out and buy a porshe? Also no. I'm going to invest that money as conservatively as I can, and then hire myself and a small team, and we're going to get cracking on lots more games. We'll then make the best damn 2D games we can make, for as long as we have money to make them. At best, I see myself living an upper-middle-class lifestyle, with a reasonable but not extravagant house and the financial independence to make the sort of games that I want, while still having time to spend with my family.
Don't Piss It All Away!
When I look at other indie developers that made it big, and then spent the money on huge crazy salaries and bonuses, expensive office space, and other serious nice-to-have window dressings, it makes me really sad. That's why people start assuming they were all about the money, I think. If they really have a passion for making games, they'll do everything in their power to make sure they can keep doing just that. Of course, I have the advantage of having ridden the small business roller coaster in the past, and I've seen from front row seats what extravagant, wasteful spending can do to a company.
That's the key thing that I think a lot of people misunderstand about money. They assume that it's a constant inflow, that if they hit it big with their first game that all their later games will be similarly well recieved. I have no such illusions. I very much hope that all my games will be well recieved, but I want to be able to create ones that are more niche than commercial in nature (whatever that means -- by many measures, AI War is pretty niche, but at the same time there still seems to be quite a commercial market for it). I want AI War to make a lot of money so that the second and third games can flop and it won't kill the company. Not that I want those games to flop, but I want to have the buffer there. I also want to have the buffer of time, so that if I need an extra 6 months of development, that's no problem instead of something that the company has to go into debt for.
Financial Independence = Creative Freedom?
Part of why the biggest game development companies (Blizzard and Valve and Nintendo, I'm mostly thinking of here) are so successful is that they have financial independence. Valve could afford to go six years between products, and then Half Life 2 was amazing. Nintendo can afford to put a lot of time and money into some really tangential things (the Wii in general, Wii Fit, etc), and then is able to polish them to the point that they turn out to be really good and thus also successful. Same with Blizzard and their products.
I'm not Blizzard, Valve, or Nintendo. But financial independence should be the goal of any game developer, or indeed any software developer in general. That's the only cure I know of for rushed deadlines, buggy releases, or too-conservative designs that wind up being very derivative.
I make games because I love to. Period. But I also need money so that I can hire a staff to help with things like music and art, and so that I can actually do this as my day job myself. So, like anyone else who is serious about game development, I'm doing it for love and money. Only hobbyists do it purely for love, and I don't think anyone can make real games if they are doing it purely for money. That's what those movie tie-in games are often about, and many of them hardly deserve the moniker "game." So while I'm flattered that people see how much I love what I do, and assume such altruistic motives, the reality is a little more gray than either black or white. Isn't it always?
Tuesday, July 28, 2009
Wednesday, July 22, 2009
Thoughts on Piracy and DRM
First, DRM Stinks
I try to make it known that AI War doesn't contain any DRM -- there's a license key to transform the demo version into the full version, and that's it. No phoning home, no usage tracking, no limited numbers of installs, no crazy drivers that take over your system. DRM sucks, in so many ways, and I've always hated it. When I buy music, I use Amazon MP3 because it's DRM-free. When I have music, I want to be able to use it on any device I have, or any computer -- I use a laptop as well as a desktop, and switch back and forth between both.
With games, it's much the same thing. I want to own the game, whether I got it through an online distribution service or from a brick and mortar store. I want to be able to play that game ten years from now. I still pull out my original cartridge of Mario 2 every few years, or Quake II, or Half-Life, or Silent Hill, or The Ocarina of Time, or Emperor: Rise of the Middle Kingdom, or a dozen others. With some sort of crazy DRM with those titles, I'd probably have to resort to piracy just to play them at all at this late stage. Thankfully, none of those titles have more than a CD check and license key (and obviously the console titles don't have even that).
For all of the above reasons, plus many others -- such as the fact that most DRM gets immediately cracked anyway -- I don't support DRM.
But, Piracy Also Stinks
Today Google Alerts notified me of a page on NowTorrents, which informed me that over 40,000 people have been tracked downloading illegal copies of AI War. Is that number accurate? Who the heck knows, but it sure outpaces the number of sales we've had so far. I've read all sorts of articles by other developers who fall on various sides of this debate, and from what I can tell a piracy rate of 90% or more is very common. Depending on how many sites people are pirating AI War through, and how accurate tracker numbers are, we're probably at that number or even well above it. It's hard to say, because there's no one central reliable source that tallies pirate downloads.
So, I should spring right into action, right? Start adding DRM to my games, or... or... or something, right? Despite the fact that this will alienate my existing and potential good customers, if I can convert even a few of the pirates to paying customers, that might pay off pretty well, right? I seem to recall some other articles talking about the conversion rate of pirates to paying customers being something like 1 in 1,000 pirates can be converted. That's just 40 sales from all those NowTorrents pirates. That's pretty sobering, isn't it?
Even if it was 400 sales, I don't think the negative consequences from adding DRM would be worth it. Goodwill is worth more. So, for all intents and purposes, this is a non-event for me. People are pirating my game, like I knew they would if it ever became popular. I didn't try very hard to stop them, because I knew I couldn't. No one ever has, and I'm against DRM anyway. But it's still kind of a kick to the nuts to see this sort of thing.
So, How To Stop Piracy?
I have never seen a stronger argument for console development than this sort of thing. Pirating of console games is very hard because of all the specialized hardware -- sure you get a lot of knock-off discs in China and so forth, but not so much over here. I have nothing against consoles, but I'm frankly a PC developer at heart. I love the platform, and I don't really want to switch. I imagine I'll port some titles to consoles in the future at some point, but I can't imagine a time when they are my primary focus.
Well, okay, so what can we do to shore up the PC? The reason that piracy is harder on consoles is because of all the specialized, non-cross-compatible, vendor-locked, or otherwise proprietary hardware. So that's what we need with the PC, right? Some sort of crazy anti-piracy stuff built right into our hardware, or maybe we should have specialized hardware dongles like high-end software packages sometimes do. Wait a minute, though -- isn't the awesome thing about PCs how configurable and open they are? I really don't want to change that -- my biggest gripe with Apple is how closed all their hardware is, after all. I'd probably use OSX if it could run on my PC hardware, but I refuse to buy proprietary hardware from a single vendor for a computer. Any sort of changes in that direction are not a positive step forward for the PC, and I think that myself and most other PC enthusiasts do not want to live in a future where that is the norm.
Okay, so that leaves some sort of central server authentication -- well, but wait, that can be hacked out, it's basically just DRM. Well, if people have to be logged in at all times, like with an MMO, then that's pretty hard to hack. Actually, that seems like a pretty darn good way to stop piracy to me, but usually that is paired with a subscription fee, which seems unfair to customers except in the MMO context (and I don't even play those, because I don't want to be saddled with a subscription where I feel like I have to play a game a certain amount each month to feel like I'm retaining the value of my money). And anyway, there are a lot of people who have a lot of issues with the MMO model, and I'd be alienating all of them if I switched to any sort of online-only model. Look at all of the (largely well deserved) flak that Starcraft II is getting for the removal of LAN play.
Uh... okay, so what does this leave? Basically I think that rules out everything. I can't stop piracy without doing something quasi-nefarious myself, and that wouldn't really even fully stop the piracy, anyway. So I guess that puts us in sociological territory, where I need to make people not want to pirate the game. If price is the barrier, then maybe setting a lower price would convert some people -- but wait, AI War is already 1/3 the price of its competitors. Even less expensive games, like World of Goo, have been pirated more than mine. So price doesn't seem to affect this.
What else makes people want to pirate? The inclusion of nasty DRM is an often-cited example -- but I already avoided that. Once again, I'm out of ideas.
Sticking it to the Man
Price, DRM... those are the two biggest-cited reasons for excusing piracy. Stick it to the man, and all that (but if a tiny indie developer has become "the man" then I don't know what to say to that). In the end, people pirate because they want to. They either don't have any money, or don't have enough to spend on all the entertainment they want. So they buy some entertainment, and then steal whatever overflow they can't afford. Or maybe it's just that they are in the habit of piracy, and hardly even think about it because it is, essentially, a victimless crime against rich fat cats and celebrities who don't really need the money, anyway. Or maybe it's just because piracy is so easy.
Who the heck knows -- I certainly don't. I don't think there's any one reason. People have been violating copyright and pirating and plagiarizing since long before the modern era. There's just something, deep down, that makes people believe that these sorts of ethereal products, or knowledge, can't or shouldn't be protected. In a lot of senses, when it comes to things like patents, I agree that the current laws are ridiculous. We definitely give too much protection to patent holders (or grant too many frivolous patents, one way or the other). But when it comes to products, items that most people would never dream of stealing off the shelves of a retail store, I take issue with the attitude that it's okay to steal it if it's digital.
The Only Solution Is Not Really A Solution At All
The value of a product, be it a CD, a book, a movie, or a game, is not in the plastic and cardboard of the item sitting on store shelves. It's in the product itself. As we move ever more towards a digital-only or digital-primarily age of distribution for these sorts of products, that's something that people need to remember. That's the only solution to piracy that I can see. In real life, we depend on people doing the right thing even when there are no authority figures or policemen present. Our society would collapse if people committed crimes every time big brother wasn't watching. Without being too melodramatic, I think it's reasonable to say that there are similar ethical issues at stake with online/digital societies. If we can't trust each other, at least to some degree, then there isn't much of a society there.
I'm not big brother, I'm not "the man," I'm not a rich fat cat that won't notice a few missed sales. Any indie developer, and frankly most developers period except maybe those at the very top of the sales charts, need all the sales they can get. I'm not an authority figure, I'm not watching you, and I'm not pointing a bunch of nasty DRM weapons at you or your computer. Please don't steal from me if you like the products I create. And if you don't like the products I create, why are you downloading it from a torrent? Either way, pirates or no, life goes on. There's nothing content providers can really do about it except appeal to people's better nature, but so far that hasn't really seemed to make much of a difference. That's rather sad.
If you took the time to read an article on this subject, chances are that you're not part of the problem, anyway.
I try to make it known that AI War doesn't contain any DRM -- there's a license key to transform the demo version into the full version, and that's it. No phoning home, no usage tracking, no limited numbers of installs, no crazy drivers that take over your system. DRM sucks, in so many ways, and I've always hated it. When I buy music, I use Amazon MP3 because it's DRM-free. When I have music, I want to be able to use it on any device I have, or any computer -- I use a laptop as well as a desktop, and switch back and forth between both.
With games, it's much the same thing. I want to own the game, whether I got it through an online distribution service or from a brick and mortar store. I want to be able to play that game ten years from now. I still pull out my original cartridge of Mario 2 every few years, or Quake II, or Half-Life, or Silent Hill, or The Ocarina of Time, or Emperor: Rise of the Middle Kingdom, or a dozen others. With some sort of crazy DRM with those titles, I'd probably have to resort to piracy just to play them at all at this late stage. Thankfully, none of those titles have more than a CD check and license key (and obviously the console titles don't have even that).
For all of the above reasons, plus many others -- such as the fact that most DRM gets immediately cracked anyway -- I don't support DRM.
But, Piracy Also Stinks
Today Google Alerts notified me of a page on NowTorrents, which informed me that over 40,000 people have been tracked downloading illegal copies of AI War. Is that number accurate? Who the heck knows, but it sure outpaces the number of sales we've had so far. I've read all sorts of articles by other developers who fall on various sides of this debate, and from what I can tell a piracy rate of 90% or more is very common. Depending on how many sites people are pirating AI War through, and how accurate tracker numbers are, we're probably at that number or even well above it. It's hard to say, because there's no one central reliable source that tallies pirate downloads.
So, I should spring right into action, right? Start adding DRM to my games, or... or... or something, right? Despite the fact that this will alienate my existing and potential good customers, if I can convert even a few of the pirates to paying customers, that might pay off pretty well, right? I seem to recall some other articles talking about the conversion rate of pirates to paying customers being something like 1 in 1,000 pirates can be converted. That's just 40 sales from all those NowTorrents pirates. That's pretty sobering, isn't it?
Even if it was 400 sales, I don't think the negative consequences from adding DRM would be worth it. Goodwill is worth more. So, for all intents and purposes, this is a non-event for me. People are pirating my game, like I knew they would if it ever became popular. I didn't try very hard to stop them, because I knew I couldn't. No one ever has, and I'm against DRM anyway. But it's still kind of a kick to the nuts to see this sort of thing.
So, How To Stop Piracy?
I have never seen a stronger argument for console development than this sort of thing. Pirating of console games is very hard because of all the specialized hardware -- sure you get a lot of knock-off discs in China and so forth, but not so much over here. I have nothing against consoles, but I'm frankly a PC developer at heart. I love the platform, and I don't really want to switch. I imagine I'll port some titles to consoles in the future at some point, but I can't imagine a time when they are my primary focus.
Well, okay, so what can we do to shore up the PC? The reason that piracy is harder on consoles is because of all the specialized, non-cross-compatible, vendor-locked, or otherwise proprietary hardware. So that's what we need with the PC, right? Some sort of crazy anti-piracy stuff built right into our hardware, or maybe we should have specialized hardware dongles like high-end software packages sometimes do. Wait a minute, though -- isn't the awesome thing about PCs how configurable and open they are? I really don't want to change that -- my biggest gripe with Apple is how closed all their hardware is, after all. I'd probably use OSX if it could run on my PC hardware, but I refuse to buy proprietary hardware from a single vendor for a computer. Any sort of changes in that direction are not a positive step forward for the PC, and I think that myself and most other PC enthusiasts do not want to live in a future where that is the norm.
Okay, so that leaves some sort of central server authentication -- well, but wait, that can be hacked out, it's basically just DRM. Well, if people have to be logged in at all times, like with an MMO, then that's pretty hard to hack. Actually, that seems like a pretty darn good way to stop piracy to me, but usually that is paired with a subscription fee, which seems unfair to customers except in the MMO context (and I don't even play those, because I don't want to be saddled with a subscription where I feel like I have to play a game a certain amount each month to feel like I'm retaining the value of my money). And anyway, there are a lot of people who have a lot of issues with the MMO model, and I'd be alienating all of them if I switched to any sort of online-only model. Look at all of the (largely well deserved) flak that Starcraft II is getting for the removal of LAN play.
Uh... okay, so what does this leave? Basically I think that rules out everything. I can't stop piracy without doing something quasi-nefarious myself, and that wouldn't really even fully stop the piracy, anyway. So I guess that puts us in sociological territory, where I need to make people not want to pirate the game. If price is the barrier, then maybe setting a lower price would convert some people -- but wait, AI War is already 1/3 the price of its competitors. Even less expensive games, like World of Goo, have been pirated more than mine. So price doesn't seem to affect this.
What else makes people want to pirate? The inclusion of nasty DRM is an often-cited example -- but I already avoided that. Once again, I'm out of ideas.
Sticking it to the Man
Price, DRM... those are the two biggest-cited reasons for excusing piracy. Stick it to the man, and all that (but if a tiny indie developer has become "the man" then I don't know what to say to that). In the end, people pirate because they want to. They either don't have any money, or don't have enough to spend on all the entertainment they want. So they buy some entertainment, and then steal whatever overflow they can't afford. Or maybe it's just that they are in the habit of piracy, and hardly even think about it because it is, essentially, a victimless crime against rich fat cats and celebrities who don't really need the money, anyway. Or maybe it's just because piracy is so easy.
Who the heck knows -- I certainly don't. I don't think there's any one reason. People have been violating copyright and pirating and plagiarizing since long before the modern era. There's just something, deep down, that makes people believe that these sorts of ethereal products, or knowledge, can't or shouldn't be protected. In a lot of senses, when it comes to things like patents, I agree that the current laws are ridiculous. We definitely give too much protection to patent holders (or grant too many frivolous patents, one way or the other). But when it comes to products, items that most people would never dream of stealing off the shelves of a retail store, I take issue with the attitude that it's okay to steal it if it's digital.
The Only Solution Is Not Really A Solution At All
The value of a product, be it a CD, a book, a movie, or a game, is not in the plastic and cardboard of the item sitting on store shelves. It's in the product itself. As we move ever more towards a digital-only or digital-primarily age of distribution for these sorts of products, that's something that people need to remember. That's the only solution to piracy that I can see. In real life, we depend on people doing the right thing even when there are no authority figures or policemen present. Our society would collapse if people committed crimes every time big brother wasn't watching. Without being too melodramatic, I think it's reasonable to say that there are similar ethical issues at stake with online/digital societies. If we can't trust each other, at least to some degree, then there isn't much of a society there.
I'm not big brother, I'm not "the man," I'm not a rich fat cat that won't notice a few missed sales. Any indie developer, and frankly most developers period except maybe those at the very top of the sales charts, need all the sales they can get. I'm not an authority figure, I'm not watching you, and I'm not pointing a bunch of nasty DRM weapons at you or your computer. Please don't steal from me if you like the products I create. And if you don't like the products I create, why are you downloading it from a torrent? Either way, pirates or no, life goes on. There's nothing content providers can really do about it except appeal to people's better nature, but so far that hasn't really seemed to make much of a difference. That's rather sad.
If you took the time to read an article on this subject, chances are that you're not part of the problem, anyway.
UK Gamer Interview with Chris Park about AI War
UK Gamer interviews Chris Park, the developer of AI War, about design decisions made in the game, promotion methods for indie developers, and the future of AI War and Arcen Games.
Read The Interview
Read The Interview
Sunday, July 19, 2009
Designing Emergent AI, Part 4: Asymetrical Goals
The first part of this article series was basically an introduction to our AI design, and the second part of this article series took a look at some of the LINQ code used in the game, as well as discussing danger levels and clarifying a few points from the first article. The third part of this series covered the limitations of this approach. The topic, this time, is the asymmetrical system used for things like resource management in AI War.
This topic is a very challenging one for me to approach, because there are such heated feelings on both sides of it amongst game designers. Just recently I remembered that I had actually already written an article on this topic, targeted at my alpha testers in order to explain to them the conceptual shift that I was planning at that time. They were skeptical of the idea at the time (as I would have been if someone else had suggested it to me), and this article went a long way toward convincing them that the concept might have some merit -- the most convincing thing of all, of course, was when they could actually see how well the AI worked in practice.
The game had been in development for less than three months at the time I originally wrote this, but amazingly almost all of it still holds true now, 2 months after the game's public release (the things that no longer hold true are how some of the AI types played out in the end, and the division of AI ships into offensive/defensive groups -- these are minor points that existing AI War players will notice the discrepancy in, but otherwise this makes no difference to the overall ideas being presented here).
ABOUT THE ARTIFICIAL INTELLIGENCE IN AI WAR
Originally Written January, 2009
About the AI in most RTS Games
Most Real-Time Strategy (RTS) games use a style of AI that tries to mimic what a human player might do. The AI in these games has all the same responsibilities and duties as the human players, and the success of the AI is predicated on it properly simulating what a human might do.
These sorts of AI rely heavily on exceedingly complex Finite State Machines (FSMs) -- in other words, "if the situation is X, then do Y, then Z." This sort of deterministic algorithm takes a long time to design and program, and is overall pretty predictable. Worst of all, these algorithms tend not to have very interesting results -- invariably, facing this sort of AI is sort of like playing against another human, only stupider-yet-faster. Clever players are able to trick the AI by finding patterns and weaknesses in the algorithms, and the AI tends to be slow to respond to what the players are doing -- if it responds at all. This is after months of work on the part of some poor AI programmer(s).
Nondeterministic AI in other Games
In most modern AI design, nondeterminism is an important goal -- given the same inputs, the AI shouldn't always give EXACTLY the same output. Otherwise the AI is inhumanly predictable. The chief ways of combating this predictability are fuzzy logic (where inputs are "fuzzified," so that the outputs are also thus less precise) or some variation of a learning AI, which grows and changes over time (its historical knowledge makes it act differently over the course of its existence).
The problem with a standard learning AI is that it can easily learn the wrong lessons and start doing something strange. Debugging is very difficult, because it's hard to know what the AI thinks it is doing and why. Also, until it has some base amount of historical data, it seems that it will either be a) wildly unpredictable and unproductive, or b) using deterministic methods that make it predictable. It can be like teaching an amoeba to tap-dance -- but instead it starts setting things on fire, and you wonder what made it think that was part of what it should do.
Therefore, even with a learning AI, you're likely to have a pretty predictable early game. Plus, if the game supports saving, the entire historical knowledge of the AI would have to be saved if the AI is to keep track of what it was doing (and keep its learned intelligence). This can make save files absolutely huge, among other various disadvantages.
Semi-Stateless AI in AI War
For AI War, I wanted a nondeterministic AI that was not dependent on having any historical knowledge. Of course, this means a pretty fair amount of fuzzy logic by definition -- that is indeed in place -- but I also wanted some of the characteristics of a learning AI. Essentially, I wanted to model data mining practices in modern business applications (something I'm imminently familiar with from my day job). My rule of thumb was this: At any given point in time, the AI should be able to look at a set of meaningful variables, apply some rules and formulas, and come to the best possible conclusion (fuzzified, of course).
A human example: At chess tournaments, often the grandmasters will play against the normal-human players 40 or so at a time (purely for fun/publicity). The 40 lower-ranked players sit at tables in a ring around the room, each with their own chess game against the grandmaster. The grandmaster walks slowly around the room, making a move at each game in sequence. There is no way that the grandmaster is remembering the state of all 40 games; rather, he analyzes each game as he comes to it, and makes the best possible move at the time. He has to think harder at games where there is a particularly clever player, but by and large the grandmaster will win every game out of the 40 because of the skill gap. The general outcome is that the grandmaster picks the cleverest of the other players, and lets them win on purpose (if there is a prize -- if the grandmaster is showing off, they'll just beat everyone).
The AI in AI War is a lot like that grandmaster -- it is capable of coming to the game "blind" about once per second, and making the best possible choices at that time. Minor bits of data are accumulated over time and factored in (as the grandmaster might remember details about the cleverer opponents facing him), but overall this is not necessary. The AI also remembers some data about its past actions to a) help it follow through on past decisions unless there is a compelling reason not to, and b) to help prevent it from falling into patterns that the opponents can take advantage of.
Decentralization of Command In Other RTS Games
One important factor in creating an interesting AI opponent is that it must be able to do multiple things at once. In all too many games, the AI will just have one military arm moving around the board at a time, which is not what a human player would typically do. Where are the diversions, the multi-front attacks, the flanking?
In AI War, it was important to me that the tactical and strategic commanders be able to perform as many different activities as makes sense given the units at hand. Typically this would be accomplished by creating multiple independent "agents" per AI player, and assigning control each unit to a particular agent. You then run into issues of the agents having to negotiate and coordinate among themselves, but in AI War's semi-stateless environment it is even worse -- how do you intelligently divide up the units among these arbitrary agents if you have to keep reassigning them? And how to know when to spawn more agents, versus consolidate useless agents? These problems can all be solved, but they are not something to be attempted lightly, and nor will they be kind to the CPU. The central problem is that of resource management. Not only the delegation of control of existing units, but trying to balance tactical/strategic elements with the generation of resources and the production of new units.
Which brings me to my next point...
Resource Management In AI War
I struggled with the command decentralization issue for several days, trying to come up with a solution that would meet all of my many criteria, and ultimately came to a realization: what matters is not what the AI is actually doing, but what the visible effect is to the human players. If the AI struggles with all these things invisibly, burning up programmer hours and CPU cycles, and then comes to result A, wouldn't it be better to just shortcut some of that and have it arrive at result A? Specifically, I realized that the economic simulations had a very low payoff as far as the players were concerned. If I took out the human-style economic model for the AI -- no resources, no techs, just a generalized, linearized ship-creation algorithm -- what would the impact be?
First of all, this change makes it so that the AI players do not use builders, factories, reactors, or any of the other economy-related ships. This changes the gameplay to a mildly significant degree, because the human players cannot use the strategy of targeting economic buildings to weaken the AI. This is definitely a drawback, but in most RTS games the AI tends to have so many resources that this is generally not a viable strategy, anyway.
Having decided that this gameplay shift was acceptable, I set about designing a ship-creation algorithm. This is harder than it sounds, as each AI has to know a) what ships it is allowed to build at any given point in the game, b) how much material it is allow to spend per "resource event," and other factors that keep things fair. Note that this is NOT your typical "cheating AI" that can just do whatever it wants -- the AI plays by different rules here, but they are strict rules that simulate essentially what the AI would otherwise be doing, anyway.
Decentralization of Command In AI War
Now that the economy is handled, we are back to the issue of decentralization. Each AI is now allowed to build a certain number of ships of specific types during each resource event, but how to intelligently choose which to build? Also, how to decide what to do with the ships that already exist?
First off, the AI needs to divide its units into two categories -- offensive and defensive. Most games don't do this, but in AI War this works very effectively. Each AI decides that it wants to have a certain number of ships of certain types defending each planet or capital ship that it controls. It's first priority is production to meet those defensive goals.
Any units that aren't needed for defense are considered offensive units. These get handed over to the strategic-routing algorithm, which is described next. Each unit-producing ship controlled by the AI player will build offensive units based on a complex algorthm of fuzzy-logic and weighting -- the result is a fairly mixed army that trends towards the strongest units the AI can produce (and the favored units of a given AI type), but which never fully stops building the weaker units (which are always still useful to some degree in AI War).
Strategic Routing in AI War
The strategy planning in AI War consists of deciding what units to send to which planets. Defensive units tend not to leave their planet unless the thing they are protecting also leaves, so this pretty much just means routing around the offensive units.
This takes the form of two general activities: 1) attacking -- sending ships to the planet at which they should be able to do the most amount of damage; and 2) retreating from overwhelming defeats, when possible (no other RTS game AI that I have encountered has ever bothered with this).
The AI does not use cheap factors such as player score, who the host is, or any other non-gameplay variables in these sorts of decisions. All its decisions are based on what units are where, and what it currently calculates the outcome of a conflict is most likely to be.
Tactical Decisions in AI War
When it comes to tactical decision-making, this is actually one of the conceptually simpler parts of the AI. Each unit tries to get to its optimal targeting range, and targets the ships it is best able to hurt. It will stay and fight until such time as it dies, all its enemies are dead, or the Strategic Routing algorithm tells it to run away.
AI Types in AI War
When I think about the human-emulating AI found in most RTS games, it strikes me that this is extremely limiting. In most genres of game, you face off against a variety of different enemies that have powers that differ from those of the human players. Some opponents are vastly stronger, others are weaker, and all are distinctive and interesting in their own way. In RTS games, everyone is pretty much the same -- or at least things are balanced to the point of approximate fairness -- and I think this harms the longevity of these games.
What seems more interesting to me, and what I've decided to do in AI War, is to provide a wide variety of types of AI, each with their own strengths, weaknesses, and genuinely unique behaviors. Some have ships that the player can never get, others start with many planets instead of one, some never capture planets but instead roam around the neutral planets as lawless raiders. The possibilities are endless, especially when playing against multiple AI types in a single game.
The result is situations that are often intentionally unfair -- as they would be in real war. You can simulate being the invading force into a galaxy controlled by evil aliens, or simulate the opposite -- they're invading you. I think of this as being rather like the situation in Ender's Game. You can have AIs that are timid and hide, or others that come after you with unbridled aggression -- Terminators, or Borg, or whatever you want to call them. Some will use strange, alien combat tactics to throw you off guard, while others will routinely use confusing diversions to mask what their true objective is. Others will outclass you in terms of technology from the very start, others will have vastly more manpower but inferior technology -- the list goes on and on.
The whole goal of a game like this is to provide an interesting, varied play experience for the players. If all the AIs are essentially the same except for their general demeanor (as in most other games), that doesn't provide a lot of options. AI War's style of varied AIs is not "AI cheating" in the traditional sense -- each type of AI has specific rules that it must follow, which are simply different than the rules the human players are held to.
Think of this as the offense and defense in a football game: each team has wildly different goals -- one to score, one to not let the other team score -- but the overall success of one team versus another is determined based on how well they do in their respective goals. In football, the teams also routinely switch off who is on offense and who is on defense, and that's where the analogy to AI War starts to break down a bit. In AI War, it would be more like if one football team always played offense, but the defenders were allowed to have an extra three guys on the field. Maybe the defenders could only score by forcing a turnover and running the ball all the way back, but that would be significantly easier due to the extra players.
The AI Types in AI War are like that -- they unbalance the rules on purpose, not to cheat, but to provide something genuinely new and varied.
The Future of AI in AI War
At present, the AI does not yet make very interesting tactical decisions -- flanking, firepower concentration on key targets, etc. These and other "behaviorlets" will be added to future iterations of the AI. The AI will evaluate the behaviorlet conditions during tactical encounters, and employ them when it deems it makes sense to do so.
In fact, this concept of "behaviorlets" is the main thing that is left to do with the AI all across the board. Right now the AI is very by-the-numbers, which makes it predictable except where the fuzzy logic makes it somewhat randomized. This is comparable to the end state of many other RTS game AIs (yet it took me days instead of months or years), but with the architecture of AI War, I can continue to add in new "behaviorlets" in all aspects of the AI, to make it progressively more formidable, varied, and human-seeming. Example behaviorlets include scouting, sniping, mine laying and avoidance, using staging areas for attacks, economy targeting, use of transports, planet denial, and more. All of these things can be programmed into the existing architecture with relative ease; the complexity is bounded to the behaviorlet itself, rather than having to worry about how the behaviorlet will interact with larger elements of (for example) your typical AI finite state machine. This makes for a very object-oriented approach to the AI, which fits my thinking style.
Computer Requirements
This is a very modular AI system that can be extended over time, and yet it is also extremely efficient -- it is already able to control tens of thousands of ships without significant slowdown on a dual-core computer. However, it is being designed with dual-core computers in mind, and it is highly unlikely to run well at all on a single-processor computer.
On the other hand, the AI logic is only run on the game host (also unusual for an RTS game -- possibly another first), which means that single-threaded computers can join someone else's multiplayer game just fine. Given that this is the largest-scale RTS game in existence at the moment in terms of active units allowed during gameplay (by at least a factor of two), this is also pretty notable.
In Conclusion (We Return To The Present)
As noted at the start of this article, the main body of this article was written when the game was still in alpha stages, with the prior few months having been spent testing out the mechanics, the networking, the graphics pipeline, etc, in pure player-vs-player (pvp) modes. I had spent a week or so with the AI in a more traditional model, and felt like that was not working the way I wanted on any level, and so I decided to go back to the underlying principles of what I was trying to accomplish to see if there was a simpler approach.
After a few more days of thought, and then maybe three days of coding, I had a basic prototype of the AI working. I wrote this article then to let my alpha testers know what I was planning, and then spent the next three months refining, expanding, and completing the AI (and the rest of the game) before a beta in April. The game spent a month in beta, ironing out bugs and balance issues, and then went on sale on the Arcen site and then Stardock's Impulse in May.
This asymmetrical AI is the aspect of the game design that is most commonly criticized by other programmers or game designers who have not actually played the game. From my growing list of players, and the few reviews that the game has received so far (our exposure is still low, but growing due to the game's huge popularity on Impulse and elsewhere), this hasn't seemed to be an issue for anyone who actually plays the game. That comes back to my core realization with the AI: it doesn't matter what the AI is really doing, it matters what the AI seems to be doing, and what it makes the player do.
This style of AI creates a lot of unique gameplay situations, provides a good, fair-seeming challenge, and in general provides a degree of variety you don't encounter with other AIs. This isn't going to power sentient robots anytime soon, but in terms of creating an interesting game it seems to have paid off. That's really the goal, isn't it? To create a game that people find fun, challenging, and interesting? To get too hung up on the semantics of one approach versus another, and which is more "authentic" is counterproductive in my book. We really ought to have more games experimenting with various approaches to AI design, because I think it could make for some really fun games in all sorts of genres.
Next Time?
I have a number of other articles planned about the game, most notably a series on game design ideas that flopped for one reason or another and thus didn't make it into the final game -- an exploration of the failures-along-the-way is always fun and illuminating. But as far as the topic of AI goes, I've covered all of the points I set out to cover. Unless readers bring up more topics that they'd like me to address, this will probably be the final article in my AI series.
Part 5 of this series is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
This topic is a very challenging one for me to approach, because there are such heated feelings on both sides of it amongst game designers. Just recently I remembered that I had actually already written an article on this topic, targeted at my alpha testers in order to explain to them the conceptual shift that I was planning at that time. They were skeptical of the idea at the time (as I would have been if someone else had suggested it to me), and this article went a long way toward convincing them that the concept might have some merit -- the most convincing thing of all, of course, was when they could actually see how well the AI worked in practice.
The game had been in development for less than three months at the time I originally wrote this, but amazingly almost all of it still holds true now, 2 months after the game's public release (the things that no longer hold true are how some of the AI types played out in the end, and the division of AI ships into offensive/defensive groups -- these are minor points that existing AI War players will notice the discrepancy in, but otherwise this makes no difference to the overall ideas being presented here).
ABOUT THE ARTIFICIAL INTELLIGENCE IN AI WAR
Originally Written January, 2009
About the AI in most RTS Games
Most Real-Time Strategy (RTS) games use a style of AI that tries to mimic what a human player might do. The AI in these games has all the same responsibilities and duties as the human players, and the success of the AI is predicated on it properly simulating what a human might do.
These sorts of AI rely heavily on exceedingly complex Finite State Machines (FSMs) -- in other words, "if the situation is X, then do Y, then Z." This sort of deterministic algorithm takes a long time to design and program, and is overall pretty predictable. Worst of all, these algorithms tend not to have very interesting results -- invariably, facing this sort of AI is sort of like playing against another human, only stupider-yet-faster. Clever players are able to trick the AI by finding patterns and weaknesses in the algorithms, and the AI tends to be slow to respond to what the players are doing -- if it responds at all. This is after months of work on the part of some poor AI programmer(s).
Nondeterministic AI in other Games
In most modern AI design, nondeterminism is an important goal -- given the same inputs, the AI shouldn't always give EXACTLY the same output. Otherwise the AI is inhumanly predictable. The chief ways of combating this predictability are fuzzy logic (where inputs are "fuzzified," so that the outputs are also thus less precise) or some variation of a learning AI, which grows and changes over time (its historical knowledge makes it act differently over the course of its existence).
The problem with a standard learning AI is that it can easily learn the wrong lessons and start doing something strange. Debugging is very difficult, because it's hard to know what the AI thinks it is doing and why. Also, until it has some base amount of historical data, it seems that it will either be a) wildly unpredictable and unproductive, or b) using deterministic methods that make it predictable. It can be like teaching an amoeba to tap-dance -- but instead it starts setting things on fire, and you wonder what made it think that was part of what it should do.
Therefore, even with a learning AI, you're likely to have a pretty predictable early game. Plus, if the game supports saving, the entire historical knowledge of the AI would have to be saved if the AI is to keep track of what it was doing (and keep its learned intelligence). This can make save files absolutely huge, among other various disadvantages.
Semi-Stateless AI in AI War
For AI War, I wanted a nondeterministic AI that was not dependent on having any historical knowledge. Of course, this means a pretty fair amount of fuzzy logic by definition -- that is indeed in place -- but I also wanted some of the characteristics of a learning AI. Essentially, I wanted to model data mining practices in modern business applications (something I'm imminently familiar with from my day job). My rule of thumb was this: At any given point in time, the AI should be able to look at a set of meaningful variables, apply some rules and formulas, and come to the best possible conclusion (fuzzified, of course).
A human example: At chess tournaments, often the grandmasters will play against the normal-human players 40 or so at a time (purely for fun/publicity). The 40 lower-ranked players sit at tables in a ring around the room, each with their own chess game against the grandmaster. The grandmaster walks slowly around the room, making a move at each game in sequence. There is no way that the grandmaster is remembering the state of all 40 games; rather, he analyzes each game as he comes to it, and makes the best possible move at the time. He has to think harder at games where there is a particularly clever player, but by and large the grandmaster will win every game out of the 40 because of the skill gap. The general outcome is that the grandmaster picks the cleverest of the other players, and lets them win on purpose (if there is a prize -- if the grandmaster is showing off, they'll just beat everyone).
The AI in AI War is a lot like that grandmaster -- it is capable of coming to the game "blind" about once per second, and making the best possible choices at that time. Minor bits of data are accumulated over time and factored in (as the grandmaster might remember details about the cleverer opponents facing him), but overall this is not necessary. The AI also remembers some data about its past actions to a) help it follow through on past decisions unless there is a compelling reason not to, and b) to help prevent it from falling into patterns that the opponents can take advantage of.
Decentralization of Command In Other RTS Games
One important factor in creating an interesting AI opponent is that it must be able to do multiple things at once. In all too many games, the AI will just have one military arm moving around the board at a time, which is not what a human player would typically do. Where are the diversions, the multi-front attacks, the flanking?
In AI War, it was important to me that the tactical and strategic commanders be able to perform as many different activities as makes sense given the units at hand. Typically this would be accomplished by creating multiple independent "agents" per AI player, and assigning control each unit to a particular agent. You then run into issues of the agents having to negotiate and coordinate among themselves, but in AI War's semi-stateless environment it is even worse -- how do you intelligently divide up the units among these arbitrary agents if you have to keep reassigning them? And how to know when to spawn more agents, versus consolidate useless agents? These problems can all be solved, but they are not something to be attempted lightly, and nor will they be kind to the CPU. The central problem is that of resource management. Not only the delegation of control of existing units, but trying to balance tactical/strategic elements with the generation of resources and the production of new units.
Which brings me to my next point...
Resource Management In AI War
I struggled with the command decentralization issue for several days, trying to come up with a solution that would meet all of my many criteria, and ultimately came to a realization: what matters is not what the AI is actually doing, but what the visible effect is to the human players. If the AI struggles with all these things invisibly, burning up programmer hours and CPU cycles, and then comes to result A, wouldn't it be better to just shortcut some of that and have it arrive at result A? Specifically, I realized that the economic simulations had a very low payoff as far as the players were concerned. If I took out the human-style economic model for the AI -- no resources, no techs, just a generalized, linearized ship-creation algorithm -- what would the impact be?
First of all, this change makes it so that the AI players do not use builders, factories, reactors, or any of the other economy-related ships. This changes the gameplay to a mildly significant degree, because the human players cannot use the strategy of targeting economic buildings to weaken the AI. This is definitely a drawback, but in most RTS games the AI tends to have so many resources that this is generally not a viable strategy, anyway.
Having decided that this gameplay shift was acceptable, I set about designing a ship-creation algorithm. This is harder than it sounds, as each AI has to know a) what ships it is allowed to build at any given point in the game, b) how much material it is allow to spend per "resource event," and other factors that keep things fair. Note that this is NOT your typical "cheating AI" that can just do whatever it wants -- the AI plays by different rules here, but they are strict rules that simulate essentially what the AI would otherwise be doing, anyway.
Decentralization of Command In AI War
Now that the economy is handled, we are back to the issue of decentralization. Each AI is now allowed to build a certain number of ships of specific types during each resource event, but how to intelligently choose which to build? Also, how to decide what to do with the ships that already exist?
First off, the AI needs to divide its units into two categories -- offensive and defensive. Most games don't do this, but in AI War this works very effectively. Each AI decides that it wants to have a certain number of ships of certain types defending each planet or capital ship that it controls. It's first priority is production to meet those defensive goals.
Any units that aren't needed for defense are considered offensive units. These get handed over to the strategic-routing algorithm, which is described next. Each unit-producing ship controlled by the AI player will build offensive units based on a complex algorthm of fuzzy-logic and weighting -- the result is a fairly mixed army that trends towards the strongest units the AI can produce (and the favored units of a given AI type), but which never fully stops building the weaker units (which are always still useful to some degree in AI War).
Strategic Routing in AI War
The strategy planning in AI War consists of deciding what units to send to which planets. Defensive units tend not to leave their planet unless the thing they are protecting also leaves, so this pretty much just means routing around the offensive units.
This takes the form of two general activities: 1) attacking -- sending ships to the planet at which they should be able to do the most amount of damage; and 2) retreating from overwhelming defeats, when possible (no other RTS game AI that I have encountered has ever bothered with this).
The AI does not use cheap factors such as player score, who the host is, or any other non-gameplay variables in these sorts of decisions. All its decisions are based on what units are where, and what it currently calculates the outcome of a conflict is most likely to be.
Tactical Decisions in AI War
When it comes to tactical decision-making, this is actually one of the conceptually simpler parts of the AI. Each unit tries to get to its optimal targeting range, and targets the ships it is best able to hurt. It will stay and fight until such time as it dies, all its enemies are dead, or the Strategic Routing algorithm tells it to run away.
AI Types in AI War
When I think about the human-emulating AI found in most RTS games, it strikes me that this is extremely limiting. In most genres of game, you face off against a variety of different enemies that have powers that differ from those of the human players. Some opponents are vastly stronger, others are weaker, and all are distinctive and interesting in their own way. In RTS games, everyone is pretty much the same -- or at least things are balanced to the point of approximate fairness -- and I think this harms the longevity of these games.
What seems more interesting to me, and what I've decided to do in AI War, is to provide a wide variety of types of AI, each with their own strengths, weaknesses, and genuinely unique behaviors. Some have ships that the player can never get, others start with many planets instead of one, some never capture planets but instead roam around the neutral planets as lawless raiders. The possibilities are endless, especially when playing against multiple AI types in a single game.
The result is situations that are often intentionally unfair -- as they would be in real war. You can simulate being the invading force into a galaxy controlled by evil aliens, or simulate the opposite -- they're invading you. I think of this as being rather like the situation in Ender's Game. You can have AIs that are timid and hide, or others that come after you with unbridled aggression -- Terminators, or Borg, or whatever you want to call them. Some will use strange, alien combat tactics to throw you off guard, while others will routinely use confusing diversions to mask what their true objective is. Others will outclass you in terms of technology from the very start, others will have vastly more manpower but inferior technology -- the list goes on and on.
The whole goal of a game like this is to provide an interesting, varied play experience for the players. If all the AIs are essentially the same except for their general demeanor (as in most other games), that doesn't provide a lot of options. AI War's style of varied AIs is not "AI cheating" in the traditional sense -- each type of AI has specific rules that it must follow, which are simply different than the rules the human players are held to.
Think of this as the offense and defense in a football game: each team has wildly different goals -- one to score, one to not let the other team score -- but the overall success of one team versus another is determined based on how well they do in their respective goals. In football, the teams also routinely switch off who is on offense and who is on defense, and that's where the analogy to AI War starts to break down a bit. In AI War, it would be more like if one football team always played offense, but the defenders were allowed to have an extra three guys on the field. Maybe the defenders could only score by forcing a turnover and running the ball all the way back, but that would be significantly easier due to the extra players.
The AI Types in AI War are like that -- they unbalance the rules on purpose, not to cheat, but to provide something genuinely new and varied.
The Future of AI in AI War
At present, the AI does not yet make very interesting tactical decisions -- flanking, firepower concentration on key targets, etc. These and other "behaviorlets" will be added to future iterations of the AI. The AI will evaluate the behaviorlet conditions during tactical encounters, and employ them when it deems it makes sense to do so.
In fact, this concept of "behaviorlets" is the main thing that is left to do with the AI all across the board. Right now the AI is very by-the-numbers, which makes it predictable except where the fuzzy logic makes it somewhat randomized. This is comparable to the end state of many other RTS game AIs (yet it took me days instead of months or years), but with the architecture of AI War, I can continue to add in new "behaviorlets" in all aspects of the AI, to make it progressively more formidable, varied, and human-seeming. Example behaviorlets include scouting, sniping, mine laying and avoidance, using staging areas for attacks, economy targeting, use of transports, planet denial, and more. All of these things can be programmed into the existing architecture with relative ease; the complexity is bounded to the behaviorlet itself, rather than having to worry about how the behaviorlet will interact with larger elements of (for example) your typical AI finite state machine. This makes for a very object-oriented approach to the AI, which fits my thinking style.
Computer Requirements
This is a very modular AI system that can be extended over time, and yet it is also extremely efficient -- it is already able to control tens of thousands of ships without significant slowdown on a dual-core computer. However, it is being designed with dual-core computers in mind, and it is highly unlikely to run well at all on a single-processor computer.
On the other hand, the AI logic is only run on the game host (also unusual for an RTS game -- possibly another first), which means that single-threaded computers can join someone else's multiplayer game just fine. Given that this is the largest-scale RTS game in existence at the moment in terms of active units allowed during gameplay (by at least a factor of two), this is also pretty notable.
In Conclusion (We Return To The Present)
As noted at the start of this article, the main body of this article was written when the game was still in alpha stages, with the prior few months having been spent testing out the mechanics, the networking, the graphics pipeline, etc, in pure player-vs-player (pvp) modes. I had spent a week or so with the AI in a more traditional model, and felt like that was not working the way I wanted on any level, and so I decided to go back to the underlying principles of what I was trying to accomplish to see if there was a simpler approach.
After a few more days of thought, and then maybe three days of coding, I had a basic prototype of the AI working. I wrote this article then to let my alpha testers know what I was planning, and then spent the next three months refining, expanding, and completing the AI (and the rest of the game) before a beta in April. The game spent a month in beta, ironing out bugs and balance issues, and then went on sale on the Arcen site and then Stardock's Impulse in May.
This asymmetrical AI is the aspect of the game design that is most commonly criticized by other programmers or game designers who have not actually played the game. From my growing list of players, and the few reviews that the game has received so far (our exposure is still low, but growing due to the game's huge popularity on Impulse and elsewhere), this hasn't seemed to be an issue for anyone who actually plays the game. That comes back to my core realization with the AI: it doesn't matter what the AI is really doing, it matters what the AI seems to be doing, and what it makes the player do.
This style of AI creates a lot of unique gameplay situations, provides a good, fair-seeming challenge, and in general provides a degree of variety you don't encounter with other AIs. This isn't going to power sentient robots anytime soon, but in terms of creating an interesting game it seems to have paid off. That's really the goal, isn't it? To create a game that people find fun, challenging, and interesting? To get too hung up on the semantics of one approach versus another, and which is more "authentic" is counterproductive in my book. We really ought to have more games experimenting with various approaches to AI design, because I think it could make for some really fun games in all sorts of genres.
Next Time?
I have a number of other articles planned about the game, most notably a series on game design ideas that flopped for one reason or another and thus didn't make it into the final game -- an exploration of the failures-along-the-way is always fun and illuminating. But as far as the topic of AI goes, I've covered all of the points I set out to cover. Unless readers bring up more topics that they'd like me to address, this will probably be the final article in my AI series.
Part 5 of this series is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
Range Checks - Approximation vs Accurate
Range checking is a hugely important topic for AI War, since it is relevant for targeting, movement, and calculation of other values such as danger levels, etc. So some of the key optimizations that I talked about in the past had to do with using distance approximation methods instead of more highly accurate distance calculations.
Here's the accurate distance calculations:
public static int DistanceBetweenPoints( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
FInt preSqF = (FInt)( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) );
int val = Sqrt( Abs( preSqF ) ).IntValue;
if ( val < 0 )
return -val;
return val;
}
public static FInt DistanceBetweenPointsF( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
FInt preSqF = (FInt)( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) );
return Mat.Abs( Sqrt( Abs( preSqF ) ) );
}
public static double DistanceBetweenPointsD( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
return Math.Abs( Math.Sqrt( Math.Abs(
( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) ) ) ) );
}
Three different variations there show you how to get an integer, double, or fixed-integer format result. This uses my FInt struct, which you can get the full source code for over at StackOverflow.
Those are the less-efficient methods of calculating a range check, and I've posted them mostly for illustrative purposes. If you need a highly accurate range check, or if you need to just run a few range checks, by all means use those. However, they run vastly more slowly than a distance approximation method primarily because of the use of the Sqrt function (either the FInt version below, or the built-in .NET one for doubles). Sqrt is a surprisingly expensive call in terms of CPU, because it is an iterative function. See below:
public static FInt Sqrt( FInt f, int NumberOfIterations )
{
if ( f.RawValue < 0 ) //NaN in Math.Sqrt
throw new ArithmeticException( "Input Error" );
if ( f.RawValue == 0 )
return (FInt)0;
FInt k = f + FInt.OneF >> 1;
for ( int i = 0; i < NumberOfIterations; i++ )
k = ( k + ( f / k ) ) >> 1;
if ( k.RawValue < 0 )
throw new ArithmeticException( "Overflow" );
else
return k;
}
public static FInt Sqrt( FInt f )
{
byte numberOfIterations = 8;
if ( f.RawValue > 0x64000 )
numberOfIterations = 12;
if ( f.RawValue > 0x3e8000 )
numberOfIterations = 16;
return Sqrt( f, numberOfIterations );
}
All of this goes to show the need for a distance approximation method when you don't need quite as high a degree of accuracy, or when you only need to tell relatively how far apart two points are compared to one another. In AI War's case, this is good enough for 99% of all distance checks. The only times I need to use the more CPU-intensive, precise methods above is as a final check when ships are moving the last little bit of space in towards a destination, and other similar situations. Everywhere else, the following distance approximation method is instead used:
EDIT: Thanks to a reader comment, I finally have the original source for this again: Rafael Baptista The approximate distance function is all his, and his article does a great job of explaining the way the accuracy works. Please his article for the approximation method used.
Here's the accurate distance calculations:
public static int DistanceBetweenPoints( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
FInt preSqF = (FInt)( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) );
int val = Sqrt( Abs( preSqF ) ).IntValue;
if ( val < 0 )
return -val;
return val;
}
public static FInt DistanceBetweenPointsF( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
FInt preSqF = (FInt)( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) );
return Mat.Abs( Sqrt( Abs( preSqF ) ) );
}
public static double DistanceBetweenPointsD( Point P1, Point P2 )
{
int xd = P2.X - P1.X;
int yd = P2.Y - P1.Y;
return Math.Abs( Math.Sqrt( Math.Abs(
( ( (long)xd * (long)xd ) + ( (long)yd * (long)yd ) ) ) ) );
}
Three different variations there show you how to get an integer, double, or fixed-integer format result. This uses my FInt struct, which you can get the full source code for over at StackOverflow.
Those are the less-efficient methods of calculating a range check, and I've posted them mostly for illustrative purposes. If you need a highly accurate range check, or if you need to just run a few range checks, by all means use those. However, they run vastly more slowly than a distance approximation method primarily because of the use of the Sqrt function (either the FInt version below, or the built-in .NET one for doubles). Sqrt is a surprisingly expensive call in terms of CPU, because it is an iterative function. See below:
public static FInt Sqrt( FInt f, int NumberOfIterations )
{
if ( f.RawValue < 0 ) //NaN in Math.Sqrt
throw new ArithmeticException( "Input Error" );
if ( f.RawValue == 0 )
return (FInt)0;
FInt k = f + FInt.OneF >> 1;
for ( int i = 0; i < NumberOfIterations; i++ )
k = ( k + ( f / k ) ) >> 1;
if ( k.RawValue < 0 )
throw new ArithmeticException( "Overflow" );
else
return k;
}
public static FInt Sqrt( FInt f )
{
byte numberOfIterations = 8;
if ( f.RawValue > 0x64000 )
numberOfIterations = 12;
if ( f.RawValue > 0x3e8000 )
numberOfIterations = 16;
return Sqrt( f, numberOfIterations );
}
All of this goes to show the need for a distance approximation method when you don't need quite as high a degree of accuracy, or when you only need to tell relatively how far apart two points are compared to one another. In AI War's case, this is good enough for 99% of all distance checks. The only times I need to use the more CPU-intensive, precise methods above is as a final check when ships are moving the last little bit of space in towards a destination, and other similar situations. Everywhere else, the following distance approximation method is instead used:
EDIT: Thanks to a reader comment, I finally have the original source for this again: Rafael Baptista The approximate distance function is all his, and his article does a great job of explaining the way the accuracy works. Please his article for the approximation method used.
Saturday, July 18, 2009
techZing 10 - AI War
"Justin and Jason talk with Christopher Park, founder of Arcen Games and developer of a space-based RTS game called AI War: Fleet Command, about how he was able to release a sophisticated computer game in under seven months and create a formidable artificial intelligence in only a matter of weeks. The discussion also covers the marketing of an independent game, cooperative game play, multi-threading, optimizing .NET, LINQ, GPUs, SlimDX and the future of artificial intelligence."
Listen to this podcast on techZing!
Listen to this podcast on techZing!
Wednesday, June 17, 2009
How to stream an ogg file using DirectSound in SlimDX.
With a nudge in the right direction from the SlimDX devs, I've finally got a working streaming solution for DirectSound in SlimDX. This will be a part of the 1.007 release of AI War, improving performance in the game, but I also wanted to share the code for this since it isn't out there anywhere else.
First of all, let me note that this entire solution is really just an adaptation of an MDX Directsound streaming solution by Dr Gary. Secondly, it uses the wonderful Ogg Vorbis Decoder (a C# wrapper for the standard ogg dlls) by Atachiants Roman. And, lastly, of course this uses the SlimDX library.
Here's the code, including a compiled test executable. Do with it what you will. If you want a deep explanation of what is going on there, you want to read Dr Gary's article above. This is basically his work, after all, and he explains it very nicely.
Here are the major things I added/changed:
1. SlimDX is used instead of MDX, which obviously necessitated a lot of changes.
2. Rather than use Dr Gary's wav stream class, I'm using the ogg stream class. This required even more changes, especially since the ogg calls are apparently not threadsafe (Dr. Gary was using his wav stream object from multiple threads, which the ogg library did not like one bit).
3. The way I am pulling data off of the ogg stream is a bit different from how the original code was pulling data off of the wav stream. In the original, the stream was seeking every time, to prevent data skips and loss. In my version, I'm using a loop to make sure that this isn't neccessary, and thus there's a minor performance gain.
4. I took out support for looping. This code was originally being adapted for AI War, which doesn't need looping in the same sense. If you want to loop the file, just do like I'm doing in the sample and start playing it again after it completes. My goal was to keep this simple.
5. There are a variety of other minor changes, just switching it to be more in line with the AI War codebase. This was for my own purposes, but since I wanted to make the code public these changes are also seen here.
That's it! Happy coding.
First of all, let me note that this entire solution is really just an adaptation of an MDX Directsound streaming solution by Dr Gary. Secondly, it uses the wonderful Ogg Vorbis Decoder (a C# wrapper for the standard ogg dlls) by Atachiants Roman. And, lastly, of course this uses the SlimDX library.
Here's the code, including a compiled test executable. Do with it what you will. If you want a deep explanation of what is going on there, you want to read Dr Gary's article above. This is basically his work, after all, and he explains it very nicely.
Here are the major things I added/changed:
1. SlimDX is used instead of MDX, which obviously necessitated a lot of changes.
2. Rather than use Dr Gary's wav stream class, I'm using the ogg stream class. This required even more changes, especially since the ogg calls are apparently not threadsafe (Dr. Gary was using his wav stream object from multiple threads, which the ogg library did not like one bit).
3. The way I am pulling data off of the ogg stream is a bit different from how the original code was pulling data off of the wav stream. In the original, the stream was seeking every time, to prevent data skips and loss. In my version, I'm using a loop to make sure that this isn't neccessary, and thus there's a minor performance gain.
4. I took out support for looping. This code was originally being adapted for AI War, which doesn't need looping in the same sense. If you want to loop the file, just do like I'm doing in the sample and start playing it again after it completes. My goal was to keep this simple.
5. There are a variety of other minor changes, just switching it to be more in line with the AI War codebase. This was for my own purposes, but since I wanted to make the code public these changes are also seen here.
That's it! Happy coding.
Monday, June 15, 2009
Designing Emergent AI, Part 3: Limitations
The first part of this article series was basically an introduction to our AI design, and the second part of this article series took a look at some of the LINQ code used in the game, as well as discussing danger levels and clarifying a few points from the first article. The second article was basically just for programmers and AI enthusiasts, as is this third one. The topic, this time, is the manner in which this approach is limited.
This Approach Is Limited?
Well, of course. I don't think that there is any one AI approach that can be used to the exclusion of all others. And our AI designs are already heavily hybridized (as the first article in the series mentions), so it's not like I'm using any single approach with AI War, anyway. I used the techniques and approaches that were relevant for the game I was making, and in some respects (such as pathfinding), I blended in highly traditional approaches.
The purpose of this article is to discuss the limits of the new approaches I've proposed (the emergent aspects, and the LINQ aspects), and thereby expose the ways in which these techniques can be blended with other techniques in games beyond just AI War. Let's get started:
Where Emergence Quickly Breaks Down
To get emergent behavior, you need to have a sufficient number of agents. And to keep them from doing truly crazy stuff, you need to have a relatively simple set of bounds for their emergence (in the case of AI War, those bounds are choosing what to attack or where to run away to. Basically: tactics). Here are some examples of places that I think it would be massively ineffective to try for emergent behavior:
1. A competitive puzzle game like tetris. There's only one agent (the cursor).
2. Economic simulations in RTS games. That's all too interrelated, since any one decision has potentially massive effects on the rest of the economy (i.e., build one building and the opportunity cost is pretty high, so some sort of ongoing coordination would be very much needed).
3. Any sort of game that requires historical knowledge in order to predict player actions. For example, card games that involve bluffing would not fare well without a lot of historical knowledge being analyzed.
4. Any game that is turn-based, and which has a limited number of moves per turn, would probably not do well with this because of the huge opportunity cost. Civilization IV could probably use emergent techniques despite the fact that it is turn-based, but Chess or Go are right out.
Why Emergence Works For AI War
1. There are a lot of units, so that gives opportunities for compound thinking based on intelligence in each unit.
2. Though the decision space is very bounded as noted above (what to attack, or where to retreat to), that same decision space is also very complex. Just look at all of the decision points in the LINQ logic in the prior article. There are often fifty ways any given attack could succeed to some degree, and there are a thousand ways they could fail. This sort of subtlety lets the AI fuzzify its choices between the nearly-best choices, and the result is effective unpredictableness to the tactics that feels relatively human.
3. There is a very low opportunity cost per decision made. The AI can make a hundred decisions in a second for a group of ships, and then as soon as those orders are carried out it can make a whole new set of decisions if the situation has changed unfavorably.
4. Unpredictableness is valuable above most all else in the tactics, which works well with emergence. As long as the AI doesn't do something really stupid, just having it choose effectively randomly between the available pretty-good choices results in surprise tactics and apparent strategies that are really just happenstance. If there were fewer paths to success (as in a racing game, for example), the boundedness of decisions to make would be too tight to make any real opportunities for desirable emergent behavior.
Learning Over Time
One intentional design choice that I made with AI War was to keep it focused on the present. I remember seeing Grandmaster Chess players at Chess tournaments, where they would play against 50 regular ranked players at once, walking around the room and analyzing each position in turn. On most boards they could glance at the setup and make their move almost instantly. On a few they would pause a bit more. They would still typically win every game, just choosing whoever put up the best fight to cede a victory to (that person typically got a prize).
I figured it would be nice to have an AI with basically those capabilities. It would look at the board at present, disregarding anything it learned in the past (in fact, it would be just a subject matter expert, not a learning AI in any capacity), and then it would make one of the better choices based on what it saw. This is particularly interesting when players do something unexpected, because then the AI does something equally unexpected in response (responding instantly to the new, unusual "board state").
I feel like this works quite well for AI War, but it's not something that will evolve over time without the players learning and changing (thus forcing the AI to react to the different situations), or without my making extensions and tweaks to the AI. This was the sort of system I had consciously designed, but in a game with fully symmetrical rules for the AI and the humans, this approach would probably be somewhat limited.
The better solution, in those cases, would be to combine emergent decision making with data that is collected and aggregated/evaluated over time. That collected data becomes just more decision points in the central LINQ queries for each agent. Of course, this requires a lot more storage space, and more processing power, but the benefits would probably be immense if someone is able to pull it off with properly bounded evaluations (so that the AI does not "learn" something really stupid and counterproductive).
Isn't That LINQ Query Just A Decision Tree?
One complaint that a few AI programmers have had is that the LINQ queries that I've shared in the second article aren't really that different from a traditional decision tree. And to that, I reply: you're absolutely right, that aspect is not really too different. The main advantage is an increase in readability (assuming you know LINQ, complex statements are much more efficiently expressed).
The other advantage, however, is that soft "preferences" can easily be expressed. Rather than having a huge and branching set of IF/ELSE statements, you have the ORDER BY clause in LINQ which makes the tree evaluation a lot more flexible. In other words, if you have just this:
So, while the idea of the LINQ query is basically the same as the decision tree in concept, in practice it is not only more readable, but it can be vastly more effective depending on how complex your tree would otherwise be. You don't even have to use LINQ -- any sorting algorithm of sufficient complexity could do the same sort of thing. The novelty of the approach is not LINQ itself, but rather using a tiered sorting algorithm in place of a decision tree. You could also express the above LINQ code in C# as:
Combining Approaches
In AI War, I use basically the following approaches:
1. Traditional pathfinding
2. Simple decision-engine-style logic for central strategic decisions.
3. Per-unit decision-engine-style logic for tactics.
4. Fuzzy logic (in the form of minor randomization for decisions) throughout to reduce predictability and encourage emergence given #3.
5. Soft-preferences-style code (in the form of LINQ and sorts) instead of hard-rules-style IF/ELSE decision tree logic.
If I were to write an AI for a traditional symmetrical RTS AI, I'd also want to combine in the following (which are not needed, and thus not present, in AI War):
6. A decision tree for economic management (using the sorting/LINQ approach).
7. A learning-over-time component to support AI at a level for really great human players in a symmetrical system.
If I were to write an AI for a FPS or racing game, I'd probably take an approach very similar to what those genres already do. I can't think of a better way to do AI in those genres than they already are in the better games in each genre. You could potentially combine in emergence with larger groups of enemies in war-style games, and that could have some very interesting results, but for an individual AI player I can't think of much different to do.
For a puzzle game, or a turn-based affair like Chess, there again I would use basically the current approaches from other games, which are deep analysis of future moves and their consequences and results, and thus selection of the best one (with some heuristics thrown in to speed processing, perhaps.).
Performers or adventure games could also use mild bits of swarm behavior to interesting effect, but I think a lot of players (myself included) really do like the convention of heavily rules-based enemies in those genres. These games don't really tend to feature AI that is meant to mimic humans, rather AI that is meant to just follow simple rules that the human players can learn and remember.
The sort-style decision tree might be easier to code and might bring results slightly more efficiently in all of the above cases, but the end result for the player wouldn't be much different. Still, making AI code easier to program and read is a good thing for everyone (lower costs/time to develop, fewer bugs based on poor readability, etc).
Next Time
Part 4 of this series talks about the asymmetry in this AI design.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
This Approach Is Limited?
Well, of course. I don't think that there is any one AI approach that can be used to the exclusion of all others. And our AI designs are already heavily hybridized (as the first article in the series mentions), so it's not like I'm using any single approach with AI War, anyway. I used the techniques and approaches that were relevant for the game I was making, and in some respects (such as pathfinding), I blended in highly traditional approaches.
The purpose of this article is to discuss the limits of the new approaches I've proposed (the emergent aspects, and the LINQ aspects), and thereby expose the ways in which these techniques can be blended with other techniques in games beyond just AI War. Let's get started:
Where Emergence Quickly Breaks Down
To get emergent behavior, you need to have a sufficient number of agents. And to keep them from doing truly crazy stuff, you need to have a relatively simple set of bounds for their emergence (in the case of AI War, those bounds are choosing what to attack or where to run away to. Basically: tactics). Here are some examples of places that I think it would be massively ineffective to try for emergent behavior:
1. A competitive puzzle game like tetris. There's only one agent (the cursor).
2. Economic simulations in RTS games. That's all too interrelated, since any one decision has potentially massive effects on the rest of the economy (i.e., build one building and the opportunity cost is pretty high, so some sort of ongoing coordination would be very much needed).
3. Any sort of game that requires historical knowledge in order to predict player actions. For example, card games that involve bluffing would not fare well without a lot of historical knowledge being analyzed.
4. Any game that is turn-based, and which has a limited number of moves per turn, would probably not do well with this because of the huge opportunity cost. Civilization IV could probably use emergent techniques despite the fact that it is turn-based, but Chess or Go are right out.
Why Emergence Works For AI War
1. There are a lot of units, so that gives opportunities for compound thinking based on intelligence in each unit.
2. Though the decision space is very bounded as noted above (what to attack, or where to retreat to), that same decision space is also very complex. Just look at all of the decision points in the LINQ logic in the prior article. There are often fifty ways any given attack could succeed to some degree, and there are a thousand ways they could fail. This sort of subtlety lets the AI fuzzify its choices between the nearly-best choices, and the result is effective unpredictableness to the tactics that feels relatively human.
3. There is a very low opportunity cost per decision made. The AI can make a hundred decisions in a second for a group of ships, and then as soon as those orders are carried out it can make a whole new set of decisions if the situation has changed unfavorably.
4. Unpredictableness is valuable above most all else in the tactics, which works well with emergence. As long as the AI doesn't do something really stupid, just having it choose effectively randomly between the available pretty-good choices results in surprise tactics and apparent strategies that are really just happenstance. If there were fewer paths to success (as in a racing game, for example), the boundedness of decisions to make would be too tight to make any real opportunities for desirable emergent behavior.
Learning Over Time
One intentional design choice that I made with AI War was to keep it focused on the present. I remember seeing Grandmaster Chess players at Chess tournaments, where they would play against 50 regular ranked players at once, walking around the room and analyzing each position in turn. On most boards they could glance at the setup and make their move almost instantly. On a few they would pause a bit more. They would still typically win every game, just choosing whoever put up the best fight to cede a victory to (that person typically got a prize).
I figured it would be nice to have an AI with basically those capabilities. It would look at the board at present, disregarding anything it learned in the past (in fact, it would be just a subject matter expert, not a learning AI in any capacity), and then it would make one of the better choices based on what it saw. This is particularly interesting when players do something unexpected, because then the AI does something equally unexpected in response (responding instantly to the new, unusual "board state").
I feel like this works quite well for AI War, but it's not something that will evolve over time without the players learning and changing (thus forcing the AI to react to the different situations), or without my making extensions and tweaks to the AI. This was the sort of system I had consciously designed, but in a game with fully symmetrical rules for the AI and the humans, this approach would probably be somewhat limited.
The better solution, in those cases, would be to combine emergent decision making with data that is collected and aggregated/evaluated over time. That collected data becomes just more decision points in the central LINQ queries for each agent. Of course, this requires a lot more storage space, and more processing power, but the benefits would probably be immense if someone is able to pull it off with properly bounded evaluations (so that the AI does not "learn" something really stupid and counterproductive).
Isn't That LINQ Query Just A Decision Tree?
One complaint that a few AI programmers have had is that the LINQ queries that I've shared in the second article aren't really that different from a traditional decision tree. And to that, I reply: you're absolutely right, that aspect is not really too different. The main advantage is an increase in readability (assuming you know LINQ, complex statements are much more efficiently expressed).
The other advantage, however, is that soft "preferences" can easily be expressed. Rather than having a huge and branching set of IF/ELSE statements, you have the ORDER BY clause in LINQ which makes the tree evaluation a lot more flexible. In other words, if you have just this:
ORDER BY comparison 1,You would be able to have a case where you have 1 as true, 2 as false, and 3 as true just as easily as all three being true, or having false, true, false, or any other combination. I can't think of a way to get that sort of flexibility in traditional code without a lot of duplication (the same checks in multiple branches of the tree), or the use of the dreaded and hard-to-read gotos.
comparison 2,
comparison 3
So, while the idea of the LINQ query is basically the same as the decision tree in concept, in practice it is not only more readable, but it can be vastly more effective depending on how complex your tree would otherwise be. You don't even have to use LINQ -- any sorting algorithm of sufficient complexity could do the same sort of thing. The novelty of the approach is not LINQ itself, but rather using a tiered sorting algorithm in place of a decision tree. You could also express the above LINQ code in C# as:
someCollection.Sort( delegate( SomeType o1, SomeType o2 )In fact, throughout the code of AI War, statements of that very sort are quite common. These are more efficient to execute than the equivalent LINQ statement, so on the main thread where performance is key, this is the sort of approach I use. In alpha versions I had it directly in LINQ, which was excellent for prototyping the approaches, but then I converted everything except the AI thread into these sorts instead of LINQ, purely for performance reasons. If you're working in another language or don't know SQL-style code, you could easily start and end with this kind of sorting.
{
int val = o1.property1.CompareTo( o2.property1 );
if ( val == 0 )
val = o1.property2.CompareTo( o2.property2 );
if ( val == 0 )
val = o1.property3.CompareTo( o2.property3 );
return val;
} );
Combining Approaches
In AI War, I use basically the following approaches:
1. Traditional pathfinding
2. Simple decision-engine-style logic for central strategic decisions.
3. Per-unit decision-engine-style logic for tactics.
4. Fuzzy logic (in the form of minor randomization for decisions) throughout to reduce predictability and encourage emergence given #3.
5. Soft-preferences-style code (in the form of LINQ and sorts) instead of hard-rules-style IF/ELSE decision tree logic.
If I were to write an AI for a traditional symmetrical RTS AI, I'd also want to combine in the following (which are not needed, and thus not present, in AI War):
6. A decision tree for economic management (using the sorting/LINQ approach).
7. A learning-over-time component to support AI at a level for really great human players in a symmetrical system.
If I were to write an AI for a FPS or racing game, I'd probably take an approach very similar to what those genres already do. I can't think of a better way to do AI in those genres than they already are in the better games in each genre. You could potentially combine in emergence with larger groups of enemies in war-style games, and that could have some very interesting results, but for an individual AI player I can't think of much different to do.
For a puzzle game, or a turn-based affair like Chess, there again I would use basically the current approaches from other games, which are deep analysis of future moves and their consequences and results, and thus selection of the best one (with some heuristics thrown in to speed processing, perhaps.).
Performers or adventure games could also use mild bits of swarm behavior to interesting effect, but I think a lot of players (myself included) really do like the convention of heavily rules-based enemies in those genres. These games don't really tend to feature AI that is meant to mimic humans, rather AI that is meant to just follow simple rules that the human players can learn and remember.
The sort-style decision tree might be easier to code and might bring results slightly more efficiently in all of the above cases, but the end result for the player wouldn't be much different. Still, making AI code easier to program and read is a good thing for everyone (lower costs/time to develop, fewer bugs based on poor readability, etc).
Next Time
Part 4 of this series talks about the asymmetry in this AI design.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
Choosing a DirectX Platform In C#
Apparently my game AI War: Fleet Command is the first selling game to use the SlimDX development framework. Does it reflect poorly on me that I didn't know that until today, a month after my game was actually released? Well, that knowledge probably wouldn't have scared me off from using their library, anyway -- the proof is in the pudding, and I liked their pudding the best out of all the ones I tried.
At the time I remember seeing a "projects that use SlimDX" page, and I even looked a few of those, but I had no idea that they were all freeware, hobbyist, or yet to be released. Color me surprised. In this article, I'm going to talk a bit about why I wound up choosing SlimDX, my experiences with it, and what other frameworks I used before it.
Early Alpha: Managed DirectX (MDX)
When I first started prototyping AI War, I did so in MDX simply because that's what my other games in the past have been coded in. However, as is widely known, MDX simply isn't supported anymore. There are some bugs and glitches in there, a few things that don't perform as well as they should, and a lot of the latest stuff (and anything beyond DX9) is not well supported to my knowledge. Overall I was pretty happy with MDX, though, and for my purposes it worked well enough to use it for the first four or so months of work on AI War.
After a certain point, however, I was having performance issues that I suspected were due to MDX. Also, it was coming up to where I needed to start adding music to the game, and I didn't want to just use DirectShow, which I had used in the past and found very limiting. Since I knew MDX was out of date, I figured it would be a good time to start looking for a more robust, modern platform.
Evaluating the XNA Framework
The first place that I turned was to the XNA framework, since it was Microsoft's spiritual successor to MDX. I actually converted my game Alden Ridge to be XNA-based for a while (never did try with AI War), and in general the conversion was pretty painless and quick. Some things are done differently, but overall the transition was smooth and the XNA way of naming things is a little more sensible sometimes. I skipped a lot of their higher-level Game classes and such, instead just embedding the XNA content in a panel in my window, same as I had been doing with MDX previously.
Very quickly I found out that having Shader Model 2.0 as a minimum hardware requirement for Alden Ridge was not going to be a good thing. It meant that my and my wife's laptops, both IBM Thinkpads (R42 and T40, respectively), could no longer run the game even though they had been able to run it fine with MDX.
That was a dealbreaker for me with XNA, but the other big issue for me was how resources, particularly audio, were handled in the 2.0 version of XNA (they may have changed this since, I am not certain). Basically I would have to run all of my audio through a special conversion tool that they provided, and then it would sit on the disk in a not-compressed format taking way more room than nice little ogg or MP3 files do. This would, in effect, make Alden Ridge an 800MB download for users instead of a 150MB download -- this would have been a dealbreaker on its own, too.
In the future I have some interest in porting some of my games to the XBox, and XNA is the obvious way to go with that. Conversion from MDX to XNA was easy given that I had kept most of the library-touching code pretty isolated, so that is something that I can approach when the time comes without having to build in XNA support all along.
Other Libraries
I looked at a number of other libraries as well, such as the Tao Framework and others based on Mono and OpenGL. Those were interesting, and in the future I might want to work with them to get some better cross-platform support, but converting all my code to Mono and OpenGL instead of some form of DirectX is going to be a lot more time consuming. I also had worries about what performance might be like on two totally new platforms, so I didn't want to sink days into this and then still find out it was unworkable for some reason or another.
Switching To SlimDX
I found SlimDX basically through google searches, and once I had exhausted all of the more official paths to modern DirectX in C# I decided to give them a try. To be clear, I wasn't wary because of their status as "hobbyists" or because I was worried the project would die, I was wary simply because it was small, new, and had relatively few tutorials. Basically all the things they say on their home page. All of those points are simply The Way Things Are(tm), and are not at all a reason not to choose SlimDX -- it's quite a good product (as I'll explain below).
Conversion to their library from MDX was crazy easy, even moreso than when I converted to XNA, and the first thing I noticed was how much smaller their installer and runtime dlls were than the MDX equivalents. And how many fewer of them there were (everything is in one convenient SlimDX.dll, rather than being spread out through nearly a dozen MDX dlls).
When I got everything working in SlimDX, there was a noticeable performance jump from MDX. Often on the order of 10% or so, and occasionally even more than that (30% during some key spikes). This was on the August 2008 version of SlimDX, which apparently had some performance issues that are not present in the newer March 2009 version. Those issues were primarily with matrix math, which I use a fair bit of for all the transforms in AI War, but I saw little difference between the two versions of SlimDX. March 2009 was perhaps a bit faster, but it was not nearly so notable as the original jump from MDX had been.
The conversion to SlimDX took part of one afternoon, and then suddenly things were running faster, so I decided to stay with the library given that I knew it was also under active maintenance (unlike MDX). And it works just fine on older graphics cards, same as MDX does.
The only problems that I really encountered with SlimDX were:
1) At first, they did not support Force Feedback, which is an issue for Alden Ridge. They have added support for that in the latest June 2009 release, but I've been so tied up with post-release work for AI War that I have not had a chance to try it yet.
2) The performance of the Line class in SlimDX was terrible, but it was still at least on par with MDX so I suspect this was an issue with the underlying DirectX code and not SlimDX itself. A simple solution was to just have a 1px square sprite that I transform and scale into drawing very nice lines (angled or straight). That's a fair bit of matrix transformation when there are a lot of lines on the screen, and SlimDX handled those with no issue whatsoever.
3) When I was originally trying to set up music in the game, I ran into a lot of trouble trying to choose between XAudio2 and DirectSound for playback. I had already settled on using an excellent C#-based ogg vorbis decoder by Atachiants Roman (which I also highly recommend as the best solution for decoding ogg files in C#), but I was having some issues with both tools. With some advice from the SlimDX team, I managed to cobble together a home-grown streaming approach using DirectSound, but I know it is not the ideal way to handle music streaming. I will probably update that in a future release of AI War, but for now it is reliable and works well enough, the main issue is that it wastes around 70MB of RAM that could be put to some other use. I'm told that the SlimDX team is planning to later add a sample that shows how to properly handle streaming in DirectSound in SlimDX, and that will be the trigger for me to update AI War in that capacity.
In general, I suppose the low volume of tutorials was my only complaint with SlimDX, and that's something that I can't really fault them for given their newness and small size. There were enough tutorials in their SDK that I was able to get everything going except for the optimal DirectSound streaming, and that's pretty darn good as far as I'm concerned.
I should also point out that my needs as far as graphics go are pretty limited, I was mostly using their matrixes, DirectX9 sprites and text classes, and things like that. I can't really comment on the 3D aspects, or the DX10 or DX11 support, though I've seen the classes there and they are reputed to be very good. My experience with their DirectInput, DirectSound, XAudio2, and Direct3D9 components have left me feeling like this is a very solid library throughout, and the clear choice for C# developers who want to do DirectX development but don't need XBox 360 support.
Update: I now have a streaming solution for SlimDX.
At the time I remember seeing a "projects that use SlimDX" page, and I even looked a few of those, but I had no idea that they were all freeware, hobbyist, or yet to be released. Color me surprised. In this article, I'm going to talk a bit about why I wound up choosing SlimDX, my experiences with it, and what other frameworks I used before it.
Early Alpha: Managed DirectX (MDX)
When I first started prototyping AI War, I did so in MDX simply because that's what my other games in the past have been coded in. However, as is widely known, MDX simply isn't supported anymore. There are some bugs and glitches in there, a few things that don't perform as well as they should, and a lot of the latest stuff (and anything beyond DX9) is not well supported to my knowledge. Overall I was pretty happy with MDX, though, and for my purposes it worked well enough to use it for the first four or so months of work on AI War.
After a certain point, however, I was having performance issues that I suspected were due to MDX. Also, it was coming up to where I needed to start adding music to the game, and I didn't want to just use DirectShow, which I had used in the past and found very limiting. Since I knew MDX was out of date, I figured it would be a good time to start looking for a more robust, modern platform.
Evaluating the XNA Framework
The first place that I turned was to the XNA framework, since it was Microsoft's spiritual successor to MDX. I actually converted my game Alden Ridge to be XNA-based for a while (never did try with AI War), and in general the conversion was pretty painless and quick. Some things are done differently, but overall the transition was smooth and the XNA way of naming things is a little more sensible sometimes. I skipped a lot of their higher-level Game classes and such, instead just embedding the XNA content in a panel in my window, same as I had been doing with MDX previously.
Very quickly I found out that having Shader Model 2.0 as a minimum hardware requirement for Alden Ridge was not going to be a good thing. It meant that my and my wife's laptops, both IBM Thinkpads (R42 and T40, respectively), could no longer run the game even though they had been able to run it fine with MDX.
That was a dealbreaker for me with XNA, but the other big issue for me was how resources, particularly audio, were handled in the 2.0 version of XNA (they may have changed this since, I am not certain). Basically I would have to run all of my audio through a special conversion tool that they provided, and then it would sit on the disk in a not-compressed format taking way more room than nice little ogg or MP3 files do. This would, in effect, make Alden Ridge an 800MB download for users instead of a 150MB download -- this would have been a dealbreaker on its own, too.
In the future I have some interest in porting some of my games to the XBox, and XNA is the obvious way to go with that. Conversion from MDX to XNA was easy given that I had kept most of the library-touching code pretty isolated, so that is something that I can approach when the time comes without having to build in XNA support all along.
Other Libraries
I looked at a number of other libraries as well, such as the Tao Framework and others based on Mono and OpenGL. Those were interesting, and in the future I might want to work with them to get some better cross-platform support, but converting all my code to Mono and OpenGL instead of some form of DirectX is going to be a lot more time consuming. I also had worries about what performance might be like on two totally new platforms, so I didn't want to sink days into this and then still find out it was unworkable for some reason or another.
Switching To SlimDX
I found SlimDX basically through google searches, and once I had exhausted all of the more official paths to modern DirectX in C# I decided to give them a try. To be clear, I wasn't wary because of their status as "hobbyists" or because I was worried the project would die, I was wary simply because it was small, new, and had relatively few tutorials. Basically all the things they say on their home page. All of those points are simply The Way Things Are(tm), and are not at all a reason not to choose SlimDX -- it's quite a good product (as I'll explain below).
Conversion to their library from MDX was crazy easy, even moreso than when I converted to XNA, and the first thing I noticed was how much smaller their installer and runtime dlls were than the MDX equivalents. And how many fewer of them there were (everything is in one convenient SlimDX.dll, rather than being spread out through nearly a dozen MDX dlls).
When I got everything working in SlimDX, there was a noticeable performance jump from MDX. Often on the order of 10% or so, and occasionally even more than that (30% during some key spikes). This was on the August 2008 version of SlimDX, which apparently had some performance issues that are not present in the newer March 2009 version. Those issues were primarily with matrix math, which I use a fair bit of for all the transforms in AI War, but I saw little difference between the two versions of SlimDX. March 2009 was perhaps a bit faster, but it was not nearly so notable as the original jump from MDX had been.
The conversion to SlimDX took part of one afternoon, and then suddenly things were running faster, so I decided to stay with the library given that I knew it was also under active maintenance (unlike MDX). And it works just fine on older graphics cards, same as MDX does.
The only problems that I really encountered with SlimDX were:
1) At first, they did not support Force Feedback, which is an issue for Alden Ridge. They have added support for that in the latest June 2009 release, but I've been so tied up with post-release work for AI War that I have not had a chance to try it yet.
2) The performance of the Line class in SlimDX was terrible, but it was still at least on par with MDX so I suspect this was an issue with the underlying DirectX code and not SlimDX itself. A simple solution was to just have a 1px square sprite that I transform and scale into drawing very nice lines (angled or straight). That's a fair bit of matrix transformation when there are a lot of lines on the screen, and SlimDX handled those with no issue whatsoever.
3) When I was originally trying to set up music in the game, I ran into a lot of trouble trying to choose between XAudio2 and DirectSound for playback. I had already settled on using an excellent C#-based ogg vorbis decoder by Atachiants Roman (which I also highly recommend as the best solution for decoding ogg files in C#), but I was having some issues with both tools. With some advice from the SlimDX team, I managed to cobble together a home-grown streaming approach using DirectSound, but I know it is not the ideal way to handle music streaming. I will probably update that in a future release of AI War, but for now it is reliable and works well enough, the main issue is that it wastes around 70MB of RAM that could be put to some other use. I'm told that the SlimDX team is planning to later add a sample that shows how to properly handle streaming in DirectSound in SlimDX, and that will be the trigger for me to update AI War in that capacity.
In general, I suppose the low volume of tutorials was my only complaint with SlimDX, and that's something that I can't really fault them for given their newness and small size. There were enough tutorials in their SDK that I was able to get everything going except for the optimal DirectSound streaming, and that's pretty darn good as far as I'm concerned.
I should also point out that my needs as far as graphics go are pretty limited, I was mostly using their matrixes, DirectX9 sprites and text classes, and things like that. I can't really comment on the 3D aspects, or the DX10 or DX11 support, though I've seen the classes there and they are reputed to be very good. My experience with their DirectInput, DirectSound, XAudio2, and Direct3D9 components have left me feeling like this is a very solid library throughout, and the clear choice for C# developers who want to do DirectX development but don't need XBox 360 support.
Update: I now have a streaming solution for SlimDX.
Wednesday, June 3, 2009
Designing Emergent AI, Part 2: Queries and Code
The first part of this article series has been a hit with a lot of people, yet criticized by others for being too introductory/broad. Fair enough, starting with this article I'm going to get a lot lower-level. If you're not a programmer or an AI enthusiast, you probably won't find much interest beyond this point.
What Do You Mean, It Works Like A Database?
First question that most people are asking is in what way my AI code can possibly be like a database. In a past article (Optimizing 30,000+ Ships In Realtime In C#) I already talked about how I am using frequent rollups for performance reasons. That also helps with the performance of the AI, but that's really not the meat of what I'm talking about.
I'm using LINQ for things like target selection and other goal selection with the AI in the game, and that really cuts down on the amount of code to make the first level of a decision (it also cuts down on the amount of code needed in general, I'd estimate that the entirety of the decision-making AI code for the game is less than 20,000 lines of code). Here's one of the LINQ queries for determining target selection:
Blogger eats a lot of the formatting there, but hopefully you can see what is going on based on the comments in green. In some ways you could call this a decision-tree (it does have multiple tiers of sorting), but the overall code is a lot more brief and (when properly formatted with tabs, etc) easier to read. And the best thing is that, since these are implemented as a sort, rather than distinct if/else or where clause statements, what you arrive at is a preference for the AI to do one thing versus another thing.
There are a lot of things that it takes into consideration up there, and there are a few different modes in which it can run, and that provides a lot of intelligence on its own. But that's not enough. The loop that actually evaluates the above logic also adds some more intelligence of its own:
Nothing real surprising in there, but basically it has a few more decision points (most of these being hard rules, rather than preferences). Elsewhere, in the pursuit logic once targets are selected, ships have a preference for not all targeting exactly the same thing -- this aspect of them watching what each other are doing is all that is really needed, at least in the game design I am using, to make them do things like branching and splitting and hitting more targets, as well as targeting effectively.
Rather than analyzing the above code point by point, I'll mostly just let it speak for itself. The comments are pretty self-explanatory overall, but if anyone does have questions about a specific part, let me know.
Danger Levels
One important piece of logic from the above code that I will touch on is that of danger levels, or basically the lines of code above where it is evaluating whether or not to prefer a target based on how well it is defended by nearby ships. All ships have a 30% chance to disregard the danger level and just go for their best targets, and some ship types (like Bombers) do that pretty much all the time, and this makes the AI harder to predict.
The main benefit of an approach like that is that it causes the AI to most of the time try to pick off targets that are lightly defended (such as scrubbing out all the outlying harvesters or poorly defended constructors in a system), and yet there's still a risk that the ships, or part of the ships will make a run at a really-valuable-but-defended target like your command station or an Advanced Factory. This sort of uncertainty generally comes across as very human-like, and even if the AI makes the occasional suicide run with a batch of ships, quite often those suicide runs can be effective if you are not careful.
Changing The AI Based On Player Feedback
Another criticism that some readers had about the first AI article was to do with my note that I would fix any exploits that people find and tell me about. Fair enough, I didn't really explain myself there and so I can understand that criticism. However, as I noted, we haven't had any exploits against the AI since our alpha versions, so I'm not overly concerned that this will be a common issue.
But, more to the point, what I was actually trying to convey is that with a system of AI like what you see above, putting in extra tweaks and logic is actually fairly straightforward. In our alpha versions, whenever someone found a way to trick the AI I could often come up with a solution within a few days, sometimes a few hours. The reason is simple: the LINQ code is short and expressive. All I have to do is decide what sort of rule or preference to make the AI start considering, what relative priority that preference should have if it is indeed a preference, and then add in a few lines of code. That's it.
With a decision tree approach, I don't think I'd be able t to do that -- the code gets huge and spread out through many classes (I have 10 classes for the AI, including ones that are basically just data holders -- AIUnit, etc -- and others that are rollups -- AIUnitRollup, which is used per player per planet). My argument for using this sort of code approach is not only that it works well and can result in some nice emergent behavior, but also that it is comparably easy to maintain and extend. That's something to consider -- this style of AI is pretty quick to try out, if you want to experiment with it in your next project.
Next Time
Part 3 of this series talks the limitations of this approach.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
What Do You Mean, It Works Like A Database?
First question that most people are asking is in what way my AI code can possibly be like a database. In a past article (Optimizing 30,000+ Ships In Realtime In C#) I already talked about how I am using frequent rollups for performance reasons. That also helps with the performance of the AI, but that's really not the meat of what I'm talking about.
I'm using LINQ for things like target selection and other goal selection with the AI in the game, and that really cuts down on the amount of code to make the first level of a decision (it also cuts down on the amount of code needed in general, I'd estimate that the entirety of the decision-making AI code for the game is less than 20,000 lines of code). Here's one of the LINQ queries for determining target selection:
var targets =
//30% chance to ignore damage enemy can do to them, and just go for highest-value targets
( unit.UnitType.AIAlwaysStrikeStrongestAgainst ||
AILoop.Instance.AIRandom.Next( 0, 100 ) < 30 ?
from obj in rollup.EnemyUnits
where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post
Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter,
obj.LocationCenter ) < Configuration.GUARD_RADIUS )
orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority
obj.GetHasAttackPenaltyAgainstThis( unit ) ascending, //ships that we have penalties against are the last to be hit
(double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy out of its total health
obj.IsProtectedByForceField ascending, //avoid ships that are under force fields
obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies
Mat.ApproxDistanceBetweenPoints( obj.LocationCenter, unit.LocationCenter ) ascending, //how close am I to the enemy
obj.UnitType.ShieldRating ascending, //how high are their shields
unit.UnitType.AttackPower ascending, //go for the lowest-attack target (economic, probably)
obj.Health ascending //how much health the enemy has left
select obj
:
from obj in rollup.EnemyUnits
where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post
Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter,
obj.LocationCenter ) < Configuration.GUARD_RADIUS )
orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority
( chooseWeaklyDefendedTarget ?
obj.UnitType.TripleBasicFirePower >= obj.NearbyMilitaryUnitPower :
( chooseStronglyDefendedTarget ?
obj.UnitType.TripleBasicFirePower < obj.NearbyMilitaryUnitPower : true ) ) descending, //lightly defended area
(double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy out of its total health
obj.IsProtectedByForceField ascending, //avoid ships that are under force fields
obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies
obj.GetHitPercent( unit ) descending, //how likely I am to hit the enemy
unit.GetAttackPowerAgainstThis( obj, false ) descending, //how much damage the enemy can do to me
obj.Health ascending //how much health the enemy has left
select obj
);
Blogger eats a lot of the formatting there, but hopefully you can see what is going on based on the comments in green. In some ways you could call this a decision-tree (it does have multiple tiers of sorting), but the overall code is a lot more brief and (when properly formatted with tabs, etc) easier to read. And the best thing is that, since these are implemented as a sort, rather than distinct if/else or where clause statements, what you arrive at is a preference for the AI to do one thing versus another thing.
There are a lot of things that it takes into consideration up there, and there are a few different modes in which it can run, and that provides a lot of intelligence on its own. But that's not enough. The loop that actually evaluates the above logic also adds some more intelligence of its own:
bool foundTarget = false;
foreach ( AIUnit enemyUnit in targets )
{
if ( enemyUnit.Health <= 0 || enemyUnit.CloakingLevel == CloakingLevel.Full )
continue; //skip targets that are already dead, or are cloaked
if ( unit.CloakingLevel == CloakingLevel.Full &&
enemyUnit.UnitType.ShipType == ShipType.Scout )
continue; //don't give away the position of cloaked ships to scouts
if ( unit.CloakingLevel != CloakingLevel.None &&
enemyUnit.UnitType.TachyonBeamRange > 0 )
continue; //cloaked ships will not attack tachyon beam sources
if ( enemyUnit.UnitType.VeryLowPriorityTarget )
continue; //if it is a very low priority target, just skip it
if ( enemyUnit.IsProtectedByCounterMissiles && unit.UnitType.ShotIsMissile )
continue; //if enemy is protected by counter-missiles and we fire missiles, skip it
if ( enemyUnit.IsProtectedByCounterSnipers && unit.UnitType.ShotIsSniper )
continue; //if enemy is protected by counter-sniper flares and we fire sniper shots, skip it
if ( enemyUnit.GetAttackPowerAgainstThis( unit, false ) == 0 )
continue; //if we are unable to hurt the enemy unit, skip attacking it
if ( unit.EffectiveMoveSpeed == 0 && !unit.UnitType.UsesTeleportation &&
enemyUnit.GetHitPercent( unit ) <>
continue; //stop ourselves from targeting fixed ships onto long-range targets
gc = GameCommand.Create( GameCommandType.SetAttackTarget, true );
gc.FGObjectNumber1 = unit.FGObjectNumber;
gc.FGObjectNumber2 = enemyUnit.FGObjectNumber;
gc.Integer1 = 0; //Not Attack-Moving
unit.LastAICommand = gc.AICommand;
AILoop.Instance.RequestCommand( gc );
foundTarget = true;
break;
}
//if no target in range, and guarding, go back to base if out of range
if ( !foundTarget && unit.GuardingObjectNumber > 0 )
{
Point guardCenter = unit.GuardingObject.LocationCenter;
if ( Mat.ApproxDistanceBetweenPoints( guardCenter, unit.LocationCenter ) >
Configuration.GUARD_RADIUS )
Move( unit, guardCenter );
}
Nothing real surprising in there, but basically it has a few more decision points (most of these being hard rules, rather than preferences). Elsewhere, in the pursuit logic once targets are selected, ships have a preference for not all targeting exactly the same thing -- this aspect of them watching what each other are doing is all that is really needed, at least in the game design I am using, to make them do things like branching and splitting and hitting more targets, as well as targeting effectively.
Rather than analyzing the above code point by point, I'll mostly just let it speak for itself. The comments are pretty self-explanatory overall, but if anyone does have questions about a specific part, let me know.
Danger Levels
One important piece of logic from the above code that I will touch on is that of danger levels, or basically the lines of code above where it is evaluating whether or not to prefer a target based on how well it is defended by nearby ships. All ships have a 30% chance to disregard the danger level and just go for their best targets, and some ship types (like Bombers) do that pretty much all the time, and this makes the AI harder to predict.
The main benefit of an approach like that is that it causes the AI to most of the time try to pick off targets that are lightly defended (such as scrubbing out all the outlying harvesters or poorly defended constructors in a system), and yet there's still a risk that the ships, or part of the ships will make a run at a really-valuable-but-defended target like your command station or an Advanced Factory. This sort of uncertainty generally comes across as very human-like, and even if the AI makes the occasional suicide run with a batch of ships, quite often those suicide runs can be effective if you are not careful.
Changing The AI Based On Player Feedback
Another criticism that some readers had about the first AI article was to do with my note that I would fix any exploits that people find and tell me about. Fair enough, I didn't really explain myself there and so I can understand that criticism. However, as I noted, we haven't had any exploits against the AI since our alpha versions, so I'm not overly concerned that this will be a common issue.
But, more to the point, what I was actually trying to convey is that with a system of AI like what you see above, putting in extra tweaks and logic is actually fairly straightforward. In our alpha versions, whenever someone found a way to trick the AI I could often come up with a solution within a few days, sometimes a few hours. The reason is simple: the LINQ code is short and expressive. All I have to do is decide what sort of rule or preference to make the AI start considering, what relative priority that preference should have if it is indeed a preference, and then add in a few lines of code. That's it.
With a decision tree approach, I don't think I'd be able t to do that -- the code gets huge and spread out through many classes (I have 10 classes for the AI, including ones that are basically just data holders -- AIUnit, etc -- and others that are rollups -- AIUnitRollup, which is used per player per planet). My argument for using this sort of code approach is not only that it works well and can result in some nice emergent behavior, but also that it is comparably easy to maintain and extend. That's something to consider -- this style of AI is pretty quick to try out, if you want to experiment with it in your next project.
Next Time
Part 3 of this series talks the limitations of this approach.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
Tuesday, June 2, 2009
Designing Emergent AI, Part 1: An Introduction
A lot of people have been curious about how the AI in AI War: Fleet Command works, since we have been able to achieve so much more realistic strategic/tactical results compared to the AI in most RTS games. Part 1 of this series will give an overview of the design philosophy we used, and later parts will delve more deeply into specific sub-topics.
Decision Trees: AI In Most RTS Games
First, the way that AI systems in most games work is via giant decision trees (IF A, then C, IF B, then D, etc, etc). This can make for human-like behavior up to a point, but it requires a lot of development and ultimately winds up with exploitable flaws. My favorite example from pretty much every RTS game since 1998 is how they pathfind around walls; if you leave a small gap in your wall, the AI will almost always try to go through that hole, which lets human players mass their units at these choke points since they are "tricking" the AI into using a hole in the wall that is actually a trap. The AI thus sends wave after wave through the hole, dying every time.
Not only does that rules-based decision tree approach take forever to program, but it's also so exploitable in many ways beyond just the above. Yet, to emulate how a human player might play, that sort of approach is generally needed. I started out using a decision tree, but pretty soon realized that this was kind of boring even at the basic conceptual level -- if I wanted to play against humans, I could just play against another human. I wanted an AI that acted in a new way, different from what another human could do, like playing against Skynet or the Buggers from Ender's Game, or something like that. An AI that felt fresh and intelligent, but that played with scary differences from how a human ever could, since our brains have different strengths and weaknesses compared to a CPU. There are countless examples of this in fiction and film, but not so many in games.
Decentralized Intelligence
The approach that I settled on, and which gave surprisingly quick results early in the development of the game, was simulating intelligence in each of the individual units, rather than simulating a single overall controlling intelligence. If you have ever ready Prey, by Michael Crichton, it works vaguely like the swarms of nanobots in that book. The primary difference is that my individual units are a lot more intelligent than each of his nanobots, and thus an average swarm in my game might be 30 to 2,000 ships, rather than millions or billions of nanobots. But this also means that my units are at zero risk of ever reaching true sentience -- people from the future won't be coming back to kill me to prevent the coming AI apocalypse. The primary benefit is that I can get much more intelligent results with much less code and fewer agents.
Strategic Tiers
There are really three levels of thinking to the AI in AI War: strategic, sub-commander, and individual-unit. So this isn't even a true swarm intelligence, because it combines swarm intelligence (at the individual-unit level) with more global rules and behaviors. How the AI decides which planets to reinforce, or which planets to send waves against, is all based on the strategic level of logic -- the global commander, if you will. The method by which an AI determines how to use its ships in attacking or defending at an individual planet is based on a combination of the sub-commander and individual-ship logic.
Sub-Commanders
Here's the cool thing: the sub-commander logic is completely emergent. Based on how the individual-unit logic is coded, the units do what is best for themselves, but also take into account what the rest of the group is doing. It's kind of the idea of flocking behavior, but applied to tactics and target selection instead of movement. So when you see the AI send its ships into your planet, break them into two or three groups, and hit a variety of targets on your planet all at once, that's actually emergent sub-commander behavior that was never explicitly programmed. There's nothing remotely like that in the game code, but the AI is always doing stuff like that. The AI does some surprisingly intelligent things that way, things I never thought of, and it never does the really moronic stuff that rules-based AIs occasionally do.
And the best part is that it is fairly un-trickable. Not to say that the system is perfect, but if a player finds a way to trick the AI, all they have to do is tell me and I can usually put a counter into the code pretty quickly. There haven't been any ways to trick the AI since the alpha releases that I'm aware of, though. The AI runs on a separate thread on the host computer only, so that lets it do some really heavy data crunching (using LINQ, actually -- my background is in database programming and ERP / financial tracking / revenue forecasting applications in TSQL, a lot of which came across to the AI here). Taking lots of variables into effect means that it can make highly intelligent decisions without causing any lag whatsoever on your average dual-core host.
Fuzzy Logic
Fuzzy logic / randomization is also another key component to our AI. A big part of making an unpredictable AI system is making it so that it always make a good choice, but not necessarily the 100% best one (since, with repetition, the "best" choice becomes increasingly non-ideal through its predictability). If an AI player only ever made perfect decisions, to counter them you only need to figure out yourself what the best decision is (or create a false weakness in your forces, such as with the hole in the wall example), and then you can predict what the AI will do with a high degree of accuracy -- approaching 100% in certain cases in a lot of other RTS games. With fuzzy logic in place, I'd say that you have no better than a 50% chance of ever predicting what the AI in AI War is going to do... and usually it's way less predictable than even that.
Intelligent Mistakes
Bear in mind that the lower difficulty levels make some intentionally-stupid decisions that a novice human might make (such as going for the best target despite whatever is guarding it). That makes the lower-level AIs still feel like a real opponent, but a much less fearsome one. Figuring out ways in which to tone down the AI for the lower difficulties was one of the big challenges for me, actually. Partly it boiled down to just withholding the best tactics from the lower-level AIs, but also there were some intentionally-less-than-ideal assumptions that I also had to seed into its decision making at those lower levels.
Skipping The Economic Simulation
Lastly, the AI in AI War follows wholly different economic rules than the human players (but all of the tactical and most strategic rules are the same). For instance, the AI starts with 20,000+ ships in most games, whereas you start with 4 ships per player. If it just overwhelmed you with everything, it would crush you immediately. Same as if all the bad guys in every level of a Mario Bros game attacked you at once, you'd die immediately (there would be nowhere to jump to). Or if all the enemies in any given level of an FPS game just ran directly at you and shot with perfect accuracy, you'd have no hope.
Think about your average FPS that simulates your involvement in military operations -- all of the enemies are not always aware of what you and your allies are doing, so even if the enemies have overwhelming odds against you, you can still win by doing limited engagements and striking key targets, etc. I think the same is true in real wars in many cases, but that's not something that you see in the skirmish modes of other RTS games.
This is a big topic that I'll touch on more deeply in a future article in this series, as it's likely to be the most controversial design decision I've made with the game. A few people will likely view this as a form of cheating AI, but I have good reasons for having done it this way (primarily that it allows for so many varied simulations, versus one symmetrical simulation). The AI ships never get bonuses above the players, the AI does not have undue information about player activities, and the AI does not get bonuses or penalties based on player actions beyond the visible AI Progress indicator (more on that below). The strategic and tactical code for the AI in the game uses the exact same rules as constrain the human players, and that's where the intelligence of our system really shines.
Asymetrical AI
In AI War, to offer procedural campaigns that give a certain David vs Goliath feel (where the human players are always David to some degree), I made a separate rules system for parts of the AI versus what the humans do. The AI's economy works based on internal reinforcement points, wave countdowns, and an overall AI Progress number that gets increased or decreased based on player actions. This lets the players somewhat set the pace of game advancement, which adds another layer of strategy that you would normally only encounter in turn-based games. It's a very asymmetrical sort of system that you totally couldn't have in a pvp-style of skirmish game with AI acting as human standins, but it works beautifully in a co-op-style game where the AI is always the enemy.
Next Time
This provides a pretty good overview of the decisions we made and how it all came together. In the next article, which is now available, I delve into some actual code. If there is anything that readers particularly want me to address in a future article, don't hesitate to ask! I'm not shy about talking about the inner workings of the AI system here, since this is something I'd really like to see other developers do in their games. I play lots of games other than my own, just like anyone else, and I'd like to see stronger AI across the board.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
Decision Trees: AI In Most RTS Games
First, the way that AI systems in most games work is via giant decision trees (IF A, then C, IF B, then D, etc, etc). This can make for human-like behavior up to a point, but it requires a lot of development and ultimately winds up with exploitable flaws. My favorite example from pretty much every RTS game since 1998 is how they pathfind around walls; if you leave a small gap in your wall, the AI will almost always try to go through that hole, which lets human players mass their units at these choke points since they are "tricking" the AI into using a hole in the wall that is actually a trap. The AI thus sends wave after wave through the hole, dying every time.
Not only does that rules-based decision tree approach take forever to program, but it's also so exploitable in many ways beyond just the above. Yet, to emulate how a human player might play, that sort of approach is generally needed. I started out using a decision tree, but pretty soon realized that this was kind of boring even at the basic conceptual level -- if I wanted to play against humans, I could just play against another human. I wanted an AI that acted in a new way, different from what another human could do, like playing against Skynet or the Buggers from Ender's Game, or something like that. An AI that felt fresh and intelligent, but that played with scary differences from how a human ever could, since our brains have different strengths and weaknesses compared to a CPU. There are countless examples of this in fiction and film, but not so many in games.
Decentralized Intelligence
The approach that I settled on, and which gave surprisingly quick results early in the development of the game, was simulating intelligence in each of the individual units, rather than simulating a single overall controlling intelligence. If you have ever ready Prey, by Michael Crichton, it works vaguely like the swarms of nanobots in that book. The primary difference is that my individual units are a lot more intelligent than each of his nanobots, and thus an average swarm in my game might be 30 to 2,000 ships, rather than millions or billions of nanobots. But this also means that my units are at zero risk of ever reaching true sentience -- people from the future won't be coming back to kill me to prevent the coming AI apocalypse. The primary benefit is that I can get much more intelligent results with much less code and fewer agents.
Strategic Tiers
There are really three levels of thinking to the AI in AI War: strategic, sub-commander, and individual-unit. So this isn't even a true swarm intelligence, because it combines swarm intelligence (at the individual-unit level) with more global rules and behaviors. How the AI decides which planets to reinforce, or which planets to send waves against, is all based on the strategic level of logic -- the global commander, if you will. The method by which an AI determines how to use its ships in attacking or defending at an individual planet is based on a combination of the sub-commander and individual-ship logic.
Sub-Commanders
Here's the cool thing: the sub-commander logic is completely emergent. Based on how the individual-unit logic is coded, the units do what is best for themselves, but also take into account what the rest of the group is doing. It's kind of the idea of flocking behavior, but applied to tactics and target selection instead of movement. So when you see the AI send its ships into your planet, break them into two or three groups, and hit a variety of targets on your planet all at once, that's actually emergent sub-commander behavior that was never explicitly programmed. There's nothing remotely like that in the game code, but the AI is always doing stuff like that. The AI does some surprisingly intelligent things that way, things I never thought of, and it never does the really moronic stuff that rules-based AIs occasionally do.
And the best part is that it is fairly un-trickable. Not to say that the system is perfect, but if a player finds a way to trick the AI, all they have to do is tell me and I can usually put a counter into the code pretty quickly. There haven't been any ways to trick the AI since the alpha releases that I'm aware of, though. The AI runs on a separate thread on the host computer only, so that lets it do some really heavy data crunching (using LINQ, actually -- my background is in database programming and ERP / financial tracking / revenue forecasting applications in TSQL, a lot of which came across to the AI here). Taking lots of variables into effect means that it can make highly intelligent decisions without causing any lag whatsoever on your average dual-core host.
Fuzzy Logic
Fuzzy logic / randomization is also another key component to our AI. A big part of making an unpredictable AI system is making it so that it always make a good choice, but not necessarily the 100% best one (since, with repetition, the "best" choice becomes increasingly non-ideal through its predictability). If an AI player only ever made perfect decisions, to counter them you only need to figure out yourself what the best decision is (or create a false weakness in your forces, such as with the hole in the wall example), and then you can predict what the AI will do with a high degree of accuracy -- approaching 100% in certain cases in a lot of other RTS games. With fuzzy logic in place, I'd say that you have no better than a 50% chance of ever predicting what the AI in AI War is going to do... and usually it's way less predictable than even that.
Intelligent Mistakes
Bear in mind that the lower difficulty levels make some intentionally-stupid decisions that a novice human might make (such as going for the best target despite whatever is guarding it). That makes the lower-level AIs still feel like a real opponent, but a much less fearsome one. Figuring out ways in which to tone down the AI for the lower difficulties was one of the big challenges for me, actually. Partly it boiled down to just withholding the best tactics from the lower-level AIs, but also there were some intentionally-less-than-ideal assumptions that I also had to seed into its decision making at those lower levels.
Skipping The Economic Simulation
Lastly, the AI in AI War follows wholly different economic rules than the human players (but all of the tactical and most strategic rules are the same). For instance, the AI starts with 20,000+ ships in most games, whereas you start with 4 ships per player. If it just overwhelmed you with everything, it would crush you immediately. Same as if all the bad guys in every level of a Mario Bros game attacked you at once, you'd die immediately (there would be nowhere to jump to). Or if all the enemies in any given level of an FPS game just ran directly at you and shot with perfect accuracy, you'd have no hope.
Think about your average FPS that simulates your involvement in military operations -- all of the enemies are not always aware of what you and your allies are doing, so even if the enemies have overwhelming odds against you, you can still win by doing limited engagements and striking key targets, etc. I think the same is true in real wars in many cases, but that's not something that you see in the skirmish modes of other RTS games.
This is a big topic that I'll touch on more deeply in a future article in this series, as it's likely to be the most controversial design decision I've made with the game. A few people will likely view this as a form of cheating AI, but I have good reasons for having done it this way (primarily that it allows for so many varied simulations, versus one symmetrical simulation). The AI ships never get bonuses above the players, the AI does not have undue information about player activities, and the AI does not get bonuses or penalties based on player actions beyond the visible AI Progress indicator (more on that below). The strategic and tactical code for the AI in the game uses the exact same rules as constrain the human players, and that's where the intelligence of our system really shines.
Asymetrical AI
In AI War, to offer procedural campaigns that give a certain David vs Goliath feel (where the human players are always David to some degree), I made a separate rules system for parts of the AI versus what the humans do. The AI's economy works based on internal reinforcement points, wave countdowns, and an overall AI Progress number that gets increased or decreased based on player actions. This lets the players somewhat set the pace of game advancement, which adds another layer of strategy that you would normally only encounter in turn-based games. It's a very asymmetrical sort of system that you totally couldn't have in a pvp-style of skirmish game with AI acting as human standins, but it works beautifully in a co-op-style game where the AI is always the enemy.
Next Time
This provides a pretty good overview of the decisions we made and how it all came together. In the next article, which is now available, I delve into some actual code. If there is anything that readers particularly want me to address in a future article, don't hesitate to ask! I'm not shy about talking about the inner workings of the AI system here, since this is something I'd really like to see other developers do in their games. I play lots of games other than my own, just like anyone else, and I'd like to see stronger AI across the board.
AI Article Index
Part 1 gives an overview of the AI approach being used, and its benefits.
Part 2 shows some LINQ code and discusses things such as danger levels.
Part 3 talks about the limitations of this approach.
Part 4 talks about the asymmetry of this AI design.
Part 5 is a transcript of a discussion about the desirability of slightly-nonideal emergent decisions versus too-predictable "perfect" decisions.
Part 6 talks about player agency versus AI agency, and why the gameplay of AI War is based around keeping the AI deviously reactive rather than ever fully giving it the "tempo."
Subscribe to:
Posts (Atom)