Artificial Intelligence 101

From EDukeWiki
Jump to: navigation, search

I've seen a few threads about writing AI as well as having my own questions, since since I would consider AI to be my strongest point out of all of the programming needs and techniques I have learned over the years, I'd like to go ahead and put some if it down for those new or less experienced to benefit.

What is Artificial Intelligence?

First things first, I'm going to define AI within the scope of this post/tutorial. Artificial Intelligence in EDuke32 is the way in which a computer-controlled actor views the world and decides to make actions based on that view. Looking at the original Duke3D enemies, their decision making process is very simple. Let's look at the Liztroop, one of the more complex enemies.

The Liztroop does a few things: it walks, it runs away, it shoots, it flies, and the Pal 21 ones disappear. For a throwaway enemy that is a lot of things to do! All of these actions are done based on the Liztroop's only goal in view, the player. It decides to shoot at the Player if he is within view, it decides to fly if the Player is above them, it runs away when the Player is too close, and so on.

The basic principal of any AI should be to 1.) decide what it wants, 2.) look for what it wants, and 3.) how to act once it finds it. There can be any number of wants and needs for an AI to make them more complex, but what it comes down to is what the AI is wanting to do at any given point in time.

Setting a Goal

Before you open up any files to start modifying code, the best time saver is to define what you want your AI to do before you even start. Like many things involved in game design, flying by the seat of your pants only serves to burn out your creativity and cause you to waste a lot of time. After more than my fair share of Mod and TC attempts that have both failed and succeeded, time management is key. You also do not want to plan to do something that is outside of your personal skill level. Writing AI is hard but it's not impossible, and having a working knowledge of how the CON language works will go leaps and bounds to keeping you on track.

Once you have decided what you want your AI to do – follow the Player, hunt him down, patrol a given area, look for health or ammo, or any number of such things – the first step is getting the AI to look at the world around him, because you can't make a decision unless you can see!

Viewing the World vs Seeing the World

It goes without saying that AI is only as smart as you make it. Writing AI from scratch is both a great benefit and a ton of work. The benefit is that you do not have to work around any preexisting parameters of the given code, and you can make your AI as smart or as dumb as you would like. The hard work is having to tell the AI about everything in the world, and when it comes to very smart AI, I do mean EVERYTHING.

AI does not know about walls, or water, or jetpacks, or rockets, or pipebombs, or fire, or bottomless cliffs, or anything at all – unless you tell it to know about these things. The most simple AI in the world would be told to face it's target and walk forward. It might hit a wall, it might walk into the path of another enemy, or it might walk off the face of an endless pit. In order for your AI to be smart, it needs to know about the obstacles around it that it may encounter.

Don't want your AI to get stuck on walls? Tell it to perform a check every now and then when it is moving (or should be moving) to see if it is blocked, and either move backwards or jump or duck, or whatever you would prefer.

Don't want your AI to get stuck in the middle of a battle being shot at? Tell it to check for bullets being shot at or near it. This doesn't always work, so you also want to tell your AI how to act when it actually DOES get hit by a bullet.

Don't want your AI to search for something on the other side of the map or behind a wall? Make sure it checks if it can see it's target before trying to get to it. The AI in Duke3D is very omnipotent, it always knows where the player is at at any given point in time. You can easily avoid this with your AI by telling it not to look for it's target if it can't see it, or it hasn't seen it for a small period of time.

AI Concept: Cost

When writing AI, an effective method of decision making it by associating a “cost” with each possible action, and at any given point deciding what action to make based on the “costs” of things we could possibly do. For example, we could make a cost table for a basic AI such as this:

(1) Attack enemy Target (2) Pickup health/ammo Target (3) Find new target (4) Move to new area

This AI would perform a simple “cost search” by looking around and seeing what it could possibly do vs what it wants to do based on the cost. In a room with an Enemy and some Health/Ammo, it would decide to attack the enemy because attacking is “less costly” than picking up Health/Ammo. If there were no enemies around, it would try to pick up Health/Ammo if any existed, and if not it would simply move on.

You might ask why I didn't put the “Find new target” AI routine at the top, and the answer if very simple. If finding a target were the “cheapest” thing it would ever want to do, it would never want to actually DO anything once it has found it. It would move around to find a target such as an Enemy or Health, and once it found it then it would loop again and just try to find it once again.

You can modify this cost on-the-fly to increase the intelligence of your AI. For example of the AI is hurt and has lost health, you could increase the cost of Attacking, or decrease the cost of finding Health so that it's main drive would be to heal itself. Once back to normal you would have to tell it to return to it's “normal mode” and it would start attacking again.

findnearactor: Friend or Foe?

One of the best and worst commands for writing AI is the “findnearactor” command. This command has some great benefits – it can quickly and easily tell us if a given target is within our given distance and we can execute commands right then and there. However this command has some disadvantages, such as a high CPU usage, and the limitation of being able to see through walls. It also can only target a single actor at a time, and can only identify targets by their sprite number.

findnearactor is good for performing simple checks, and is probably best used as a “final check” before making an action. Say our AI is looking for Health/Ammo and it has performed other checks – it has located a Health pickup, can see it, it can actually get to because it is on the same floor and not out of reach. You might then perform a findnearactor to check if it is within distance and of the correct actor type, and then if all of those checks return True you can tell your AI to get it's target.

Personally I think that findnearactor is not needed compared to more thorough checks and can to an extent be rewritten, but I don't expect everyone to share this view. It gets the job done right, but I do advise to double check it's results as it can sometimes give a false positive.

Don't Exceed Your Reach

I said this above and I wanted to restate it – the worst mistake you can do is deciding to write an AI that does everything, only to wind up writing an AI that only does some of them and does it poorly. Given the fluid nature of enemy actors and the Player in the game, writing AI is sometimes difficult since a given scenario may not be able to be reproduced but still gives errors. Do not expect to be able to write a perfect AI the first time, and don't be afraid to go back and enhance existing AI with new or improved code you may have written.