A simple Artificial Intelligence idea

TAD/Hugi

Introduction

A few years ago (around 1998) I came up with this idea to model complex A.I. like behaviour in a very simple way. The technique is so simple that it can be described in one word: "counters". In order to explain the technique there will be some metaphors used in this article which should help make everything crystal clear. They are important because they gave me that initial spark of inspiration which soon turned into a 'flood' (this pun will make sense later on).

The problem

Okay, so you want to model some kinda of intelligent NPC (non player character) which has emotion like quality. The NPC needs to act in a dynamic way, it must change its current action depending on both recent and very distant events which it has experienced.

For example if a NPC has full health, plenty of ammo and a strong wish to mash up other NPCs or players then it should attack. But once the NPC has suffered lots of damage then it should retreat, hide or look for some method of healing itself.

Another example would be, suppose a NPC is trying to attack the player. The NPC has full health and plenty of ammo. It runs directly towards the player but it is quickly getting blapped and loses 70% of it's health in a few seconds. Now this NPC should recognise this as an ambush (or at least a bad place to stop and have a picnic) and run away or look for cover. But the same NPC could suffer 70% damage over a long period of time from various small scale attacks. In this case the target player is very low on ammo and has a few % of health left. In this situation the NPC should continue and finish off the player.

We could write a series of rules for (almost) every possible event, but this would take time, be complex and wouldn't be very flexible. Suppose we want different NPCs to have different characteristics, some very brave and some very nervous.

Time, experience and personality

In order to model a 'personality' we need to try and invoke some basic emotions and a memory in each NPC. We need to keep a dynamic record about each NPC's history and be able to effortlessly update this record over time. The NPC must able to make decisions based on its personality, its current state and its history.

Using a long series of IF... THEN... statements would probably result in a huge mess which would be almost impossible to understand or to debug.

Leaks, Tanks and the Weather?

Right, now lets get to the interesting part, the metaphors.

We have a number of possible actions that a NPC could take.

mood action
flee Run away and hide under the table
restock Look for medical-kit and ammo
hunt Search for a target
attack Shoot, throw grenade etc.
stealth approach a target silently

In order to select from one of the above moods we need to make a decision based on the personality, state and history of a NPC. The history (or 'time') element is the most important. This is because it is the dynamic component, the fluid part which will help us to model the 'moods' of a NPC.

Call a plumber, we have a leak!

I will use the metaphor of a leaking water tank which has no top on it so when it rains the tank will be refilled. If we assign a water tank to each of the above moods then we can use the amount of water in each tank as a weight. This weight will be the factor which will help us choose a particular action from all the others. If one tank has the most water then it has more weight and so it will dominate the NPC's decision.

At this point we have a means of selecting one course of action above all the others (we just have to look at how full each tank is). But how is this going to model the history part of a NPC, that dynamic part of a virtual personality?

Let it rain.

Now during the course of a NPC's day it might encounter the player who fires a rocket and wipes out 90% of its health. When this happens we 'make it rain'. We take the amount of damage and convert it into water. This water is added to one or more of the NPC decision tanks. We could add 60% to the 'flee' tank giving it a sudden influx of weight, 20% could go into the 'restock' tank and 20% into the 'stealth' tank.

As I hope you can see, by converting an input event into water and giving a number of tanks different amounts, we have effectively started multiple decisions. The weight of each water tank allows us later on to choose the best course of action. After a long or heavy attack (such as an ambush) the 'flee' tank will be almost full, making it THE best choice. This would trigger the NPC into a flee action, to run away and hide under a table. ;)

Taking a leak

Remember I said that the tanks were leaking? Well this helps to model time (the history of each emotion/mood). After a long period of time the 'flee' tank will have lost a lot of water, so it's weight will be much lower giving it a lower priority in the decision making process. Of course if the NPC runs into another ambush then the tank will still be mostly full, so it would only take a few hits to fill the flee tank up.

Now suppose the NPC finds a huge cache of health and a heap of weapons. This event would fill up the attack, hunt and possibly the stealth tanks. In affect the NPC's mood has changed into a hunter-like one, it wants to kick some butt.

Call the plumber

The tank metaphor can be extended to model different personalities by adding 'valves' to control the amount of water and how it is added to each of the tanks. A cautious NPC would allow more into the flee tank but a crazy NPC would allow very little into the tank after an ambush.

Implementing the tanks

All we need to model the water tanks, the rain and the leaks are some counters (for the water 'weight' of each tank) and some multipliers which are used to scale an input event into one (or more) tank. You will probably need a multiplier for each tank for each possible event, so a table seems like the best solution.

NPC event flee restock hunt attack stealth
rocket hit 70% 50% 5% 10% 40%
fell off cliff 0% 40% 0% 0% 20%
seen weak target 0% 0% 70% 80% 80%
seen strong target 10% 5% 30% 30% 90%
found medical kit -5% -10% 10% 10% 0%
seen friendly NPC -20% -20% 30% 40% 0%

The above shows an example table with various input events and their 'rain-fall' scalars for each 'mood' tank. As you can see after a rocket-hit the NPC has a strong impulse to run away and restock it's health/ammo. On the other hand if the NPC sees a fellow NPC on his side then he becomes more brave and doesn't feel like running away, he now wants to attack the enemy. The strength of a target enemy also helps to modify the NPC's decision making progress. A weak target looks like an easy kill, so the NPC is more motivated towards attacking it. A very strong target would make the NPC more cautious, it wants to find a good hiding place from where it can launch attacks from.

Final words.

Well I hope the above idea has made some kinda sense. The only problem with all these tables of numbers is finding a good balance so that an NPC appears to shows some intelligence, but also shows signs of making stupid human mistakes.

To mis-quote a classic line from an old musical:

I'm sniping in the rain...

TAD/Hugi