"AI AI D'oh!" by Troy H. Cheek on Aug 03, 2009
The other day, I was injured at work. I'd tell you the details, but they're highly classified.
Boss: "You can't tell anyone the details of this incident. They're Highly Classified."
Troy: "The details are highly classified. Got it."
Boss: "No, they're Highly Classified."
Troy: "I said highly classified."
Boss: "No, Highly Classified."
Troy: "Um, highLY classiFIED?"
Boss: "I'm beginning to think you aren't taking this seriously."
Damn me and my speech impediment. I still can't seem to speak in capital letters. Everyone else seems to find it so easy.
Anyway, like every other workplace injury, I spent more time waiting for clearance to return to work than I actually missed out from work because of the injury. I mused that over during my next few days off. Not that I got off from work because of the injury, mind you. I was cleared to return to work immediately. It was just that somehow any time I'm injured, it's on my last working day before having a few off. This happens even when I had just checked the schedule hours before and thought that I had seen that I was scheduled to work the next three days. Apparently, pain makes me remember the schedule incorrectly.
During my unexpected time off, I started a project I'd been thinking about for some time. As I've mentioned before, I enjoy an Open Source Real Time Strategy game known as XTA which runs on the Spring Engine. Most people enjoy RTS games because they get to play against other people online. I, on the other hand, prefer to play against the computer. I have a philosophical problem with using a computer possessing a million times the processing power of the ones used to land men on the moon to play the 21st century equivalent of chess by mail. Besides, computer opponents are available whenever I am, don't complain about playing the same map 20 times in a row, don't call me names, etc.
The greatest thing about a computer opponent is that, in theory at least, I could create my own. Having a little spare time, I decided to put theory into practice.
The Spring Engine ships with three (3) computer-controlled opponents, called Skirmish AIs. These are AAI, KAI, and RAI. Technically, these aren't true Artificial Intelligence. Only AAI even pretends to learn, and it keeps throwing away its files and starting from scratch with every game. Still, I'll always have a soft spot for AAI because it was the first Skirmish AI I played against. It also beat me quite handily for some time.
AAI, as near as I can tell, uses a base mentality. It defines a little section of its map as belonging to it, claims a few resources, sets up a few defenses, builds up an attack force, and sends it out. KAI is more aggressive, claiming the map's whole land area as its own. RAI is not so aggressive, but expands into water as well as on land, and probably understands defenses and weapons of mass destruction better than anything else.
There are a few other Skirmish AIs, but they're either old and not maintained or new and still under development. I'm not going to install old, obsolete versions of Spring or new, experimental versions of Spring just to play against a particular AI. I did that once when all the AIs stopped working, but that was a special case.
In starting to program my own skirmish opponent, I first had to decide what programming language to use. The Spring Engine has several interfaces for AI, but I decided to go with Lua. For those who don't know, Lua is a scripting language which is included in several games as well as standalone development packages. Lua allows end users to write special helper utilities which run inside the game like they're actually part of it. An example of a Lua script in Spring is the Metal Maker AI which automatically turns off the metal making machines when you're running low on energy. I was going to create something a bit more complicated than that, though it occurred to me that I'd have to include that functionality as well.
An advantage of Lua scripts is that Lua widgets in Spring can do pretty much anything a human player could do manually and not a whole lot that they can't. A big part of my game was already automated by Lua. A script kept my metal makers working. Another ordered damaged fighting units to retreat. Another micro-managed units to keep them at maximum effective weapons range from their opponent, cutting down on damage from enemy fire. I'd just recently written one which was intended to automatically send small scouting parties out to harass the enemy. I'd made some mistake on that one, as it kept sending out full-blown attacks. I realized that with all these Lua scripts going at once, all I had to do was create an economic and manufacturing base, select which fighting units I wanted to build, and let my scripts fight the battle for me.
Once I started automating the economy, I realized I was ready to at least attempt an actual skirmish AI. In fact, I just realized that I started hacking the Economy AI to become a Skirmish AI before I actually had it working to create the economy. I'll have to go back and fix that some day.
My unnamed Skrimish AI, hereafter referred to as "the AI," starts by building a few metal mines, solar energy collectors, and metal makers. It controls the metal makers, turning them off when energy is low (and also remembering to collect more energy in the future). Once the AI has a little bit of an economic base, it creates its first factory. A factory can build other units, in this case all our fighting units.
In a real game, me against an opponent, selecting the proper fighting units is one of the things I always kept for myself. With my AI, I realized that I really didn't have a set strategy that I could program into it. Instead, I simply told it to build whatever unit it had the least of. That would give a good mix. No matter what the opponent threw at the AI, it would have some unit to counter it. Of course, it would have a lot of junk units that couldn't do much of anything against most opponents, but that was better than being loaded for bear when it comes to land units only to be raped and pillaged by a bunch of air units.
I let the Harass AI handle sending out fighting units. It's actually pretty simple. When an enemy unit enters the AI's line of sight, it dispatches the nearest available unit to intercept. Every two seconds or so after that, if the enemy unit is still in sight, it sends the next nearest available unit. This way, it sort of automatically scales itself to match the threat. If it's just an enemy scouting party, only a few units will be dispatched before the scout is destroyed or is scared off. If it's a major incursion, pretty soon every available unit will be on the scene. I'll eventually have to incorporate this into the main Skirmish AI. I also need to make it a bit smarter. Right now, it tries to send land units to targets they can't reach, like battleships just at the edge of radar range but in water way too deep for tanks to reach.
The Retreat AI tells units to return to base when they're badly damaged. While the original would send the units back on patrol when they'd recovered, I modified it to just leave them in the middle of the base. Sure, it takes them longer to get back to the front lines, but if an enemy unit gets into the base itself, the Harass AI has units handy to take care of the problem. I also need to incorporate this into the main Skirmish AI.
Teching up is a problem. In XTA, it's best to start with a low technology factory producing low technology units using low levels of resources. Later in the game, you want to switch to higher technology factories producing higher technology units using higher levels of resources. The problem is that if the AI switches to the higher technology too early, it lacks the resources to create new units and production bogs down. On the other hand, if the AI waits too long to tech up, then its low technology units are overpowered by the enemy's fancier ones. I haven't come up with a good solution to this yet. I have trouble with this one in my own games.
Another problem I haven't really solved for the AI yet is defense. Set defenses, as opposed to mobile units, are useful for defending the base because they're always there when you need them. Some of them also have enough range that they can be used offensively. One strategy I would love to replicate is artillery creep. For this, you build an artillery piece and let it plaster the enemy until he withdraws. Then you move into the area he just vacated and build another artillery piece. Lather, rinse, repeat. Before long, you're blasting his base.
I had thought that writing the AI would be hard, and believe you me it has not been easy, but I'm surprised at how well it's coming together. My AI can already beat AAI and RAI regularly. It has more trouble with KAI, which always seems to press an attack just as my AI is teching up. Once it can take them 1on1, it's time for 2on1 and 3on1. My goal is for my AI to eventually beat a team of AAI, KAI, and RAI working together. This combination always gives me the most trouble. Once it can do that, I just might release it for others to try.
One more technical hurdle to overcome is that I'm writing this all as a Lua widget. What that means is that the AI is pretty much pretending to be me, entering the same kinds of commands that I would enter if I was actually playing the game myself. To be actually useful as a Skirmish AI, said AI has to be a Lua gadget running as part of XTA itself. I don't know how to do that.