Docstoc

A Spot

Document Sample
A Spot Powered By Docstoc
					Artificial Intelligence

  Exploration in C++




        Jason Bartley
                                                                  Page: 1




What is A.I. anyway?

    I have had many people ask me what A.I. stands for.       I

thought this was a commonly known term, but I have been proven

wrong on many occasions.    So, for all of you that do not know

already, A.I. stands for Artificial Intelligence.    Artificial

Intelligence is what makes a computer seem like it is thinking

like a human being in computer games that you may (or may not)

play.   Artificial Intelligence is intelligence imposed on a

computer by a programmer to allow the computer to act in a human

like way to input from a human being.    If there was no

artificial intelligence in games, the little bots running around

trying to kill you would be frequently running into walls and

blowing themselves up in very stupid ways.    Now that wouldn‟t be

very fun, would it?    To make good intelligence for a computer,

you need to understand and mimic how a human thinks.    For
                                                                 Page: 2

instance, in a shooting game, first you have to locate the

enemy, then you have to move to their location, then you have to

shoot them.    This may not seem too difficult at a first glance,

but if you look deeper into these actions you will see

otherwise.    First, the movement, how should a computer move?

Should it move as fast as it can, or should it move slowly and

stealthily?    There is no clear answer to this question.   It

depends on the situation of the battle around it.    Then you have

to ask yourself what situations would make one type of movement

better than another.   If there are a lot of people around the

path to the target, stealth may be the better choice.    If that

is true, what should be defined as “a lot of people” so that the

computer can decide if the path is highly populated?

    As you can see there are many choices that need to be made

in programming intelligence.    After these choices are made and

written out, they need to be programmed.    All of this is a very

complicated process.    To tackle this job, I addressed various

strategies in AI one at a time.    These included path finding,

fuzzy logic, and finite state machines.
                                                                 Page: 3




Path Finding

    Path finding is the process of moving from one point to

another using a fast and efficient path.    This was one of the

largest parts of my project since there were so many different

ways to find a path.

    The most basic of these was an algorithm that you have

probably seen any time that you drew an arrow on the screen,

such as this one:               .   This algorithm is called the

Bresenham line algorithm.    If you look at the way the line is

formed, you can see that there is a clear pattern to the points.

This pattern can be used in path finding to find a short,

aesthetically pleasing path.    This is

done by determining the total

horizontal and vertical distance that

needs to be traveled by the computer

and then dividing it into an even

number of steps.    The end result comes

up with something that looks like this.

    There is a problem with this algorithm, however.     If any

obstacles are added in the path, the computer won‟t be able to

handle it because of the way the algorithm is designed.    It

makes a straight path and only a straight path.    It does not

take into consideration any other factors, such as obstacles.
                                                               Page: 4

This algorithm is only used to

draw straight lines.     The

first idea that was presented

to me to overcome this problem

was a waypoint system.    The

best way to make and test such

a system would be in a maze

type situation with various

rooms.   The setup I chose looked like the maze to the right. The

letters are each of the waypoints in the maze.

    The code that I developed to traverse this maze first

determined what waypoint the target was closest to by using the

Bresenham algorithm that I previously used.   I checked the path

between each waypoint and the target and if any walls were in

the path, I knew that the waypoint was not the best choice

because if a wall was in the way of a line between the start

point and the waypoint, that particular

waypoint would not be a good choice to

represent the starting point.     Once the

best waypoint was found I used a chart

to determine the progression of

waypoints needed to get to the target

waypoint. Each row represents the
                                                               Page: 5

starting waypoint and each column represents the target

waypoint.   If you start at waypoint A and want to go to waypoint

J, the computer would start in the first row and look at the

eleventh column because the first row is representative of the

starting point A and the eleventh represents the end point J.

As you can see, the letter in

that spot is B.   This says to

the computer that the next

waypoint to get to in order to

get to waypoint J would be

waypoint B.   At this point, the

computer moves to waypoint B and resets the starting waypoint to

B.   The computer then does this process until its current

waypoint matches its target waypoint.   After this condition is

met, it then moves using the Bresenham algorithm to the final

target.   This wasn‟t the best way to move because the computer

had to move to the exact point of the node before it could move

to the actual target.   This sometimes resulted in backtracking

situations, like the screen on the left. This led me to my next

investigation into the world of path finding, the A* algorithm.
                                                                  Page: 6

The A* Algorithm

     This algorithm ended up being the toughest part of my

project.   This was because I had to create every section of code

by myself.   This may not seem too tough, but the concept behind

this algorithm led to MANY problems with debugging.     Before I

ramble on about my code, I suppose I should talk about how this

algorithm actually works.

     The field that I have been using has been divided into a

grid, one spot containing the starting position of the computer

and one spot containing the

destination spot. As you can see

in the diagram to the right, there                OH NO!!

is an obstacle in the way of the

clearest path.   This means that

the Bresenham algorithm cannot be

used.   The waypoint system could

be used again, however it leads to

backtracking and an inefficient

path.   So then, the question is, what should I use?

     The answer, of course, is the famous A* algorithm.     This is

widely known as the best way to determine the quickest path from

one point to another on a grid.     Widely known and famous are

relative, however, because personally I have never heard of this

wonderful algorithm, though I must admit it is quite useful.
                                                                   Page: 7

The object of this algorithm is to find the path with the lowest

overall “score”.   A score is determined for each spot on the

grid using two factors.       The first is the distance from the

starting point along the path and the second is the straight

distance from the current spot to the destination spot. This

second score is known as the heuristic score and will be

referred to as such from here onward.       What happens in the

program iterations is that it chooses a node that has not yet

been “checked”.    It has not been checked if it is on the “open”

list.   In the process of checking, each spot around the spot

being checked is moved into

the open list, given a

score, and is given a

reference to the spot that

it originated from.    The

spot being checked is then

moved into the “closed”

list so that it won‟t

change after that point and

it won‟t be checked.     So

after the first time

through, the grid would
                                                                  Page: 8

look like the diagram on the previous page.     I would say that

this is a good start.      After this first iteration is complete,

the next step is to choose the next spot to check.     This is done

by looking at the spot with the next slowest score.     In this

case, there are 3 spots with the same overall score.     It doesn‟t

really matter which spot it chooses so let‟s choose the spot in

the top right corner.     The program will look around at each

space, ignoring the spaces with obstacles, spaces in the open

list, and spaces in the

closed list.   This will

result in a grid that

looks like this.   Looks

like we are moving in

the right direction eh?

So, let‟s skip ahead a

few steps to save some

time.   The second to

last step will look like

the grid on the next

page.
                                                              Page: 9

Now, if you look at the

open spots, the only

spaces left are those

next to the end spot.

Any one of these spots

could be chosen and any

of them will yield an

end result with the same

length because of the

possibility of diagonal

movement.    When one of

these spots is chosen to

be checked, it will,

again, look at all of

the spots surrounding

it.   When it finds that

one of those spots is

the target spot, it will

end the loop and build

the path.    Starting at

the end spot, which will

be pointing at a spot,

it will trace the path

backwards.    If you follow the arrows the path is clearly traced.
                                                                  Page: 10

The challenge after this process was understood was to translate

it into code.

    Code translation was difficult because of the error

trapping that is necessary.     Every step of this process requires

you to check to make sure that the spot being looked at is a

valid spot.   However, before you embark on this process, some

set-up is needed.   First you need to determine the best way to

store the necessary data.      You will need two arrays, one to

store the open list and one to store the closed list.     Each of

these arrays needs to be the largest possible size.      The size of

the arrays is the product of the length and width of the grid

you are operating on.      Each of these arrays needs to be made up

of a special type of data structure.     This structure needs to

have all the information that might be needed throughout the

program.   This includes the coordinate that it is located in,

the spot that it originated from, the heuristic score, the

distance from the starting spot, and the total score.      Since I

am programming in C++, the structure code would look like this:



    struct spot{

           point position;

           int source;

           int heuristic;

           int distance;
                                                                Page: 11

            int score;

    };



The variable type, point, is another structure that simply has

an x value and a y value stored in it to represent the point on

the grid.

    Now that the data is being stored in arrays with the

correct data structure, the process needs to be programmed.       You

need to begin with a starting and ending point and both of these

need to be sent to the function that is going to be finding the

path.    The starting point then needs to be moved to the closed

list (because it is going to go there in the first step no

matter what).    Now comes the time for the checking process.

Each spot around the starting spot needs to be checked.

    To do this, a couple error trapping issues need to be

considered.    Each of these issues need to be programmed into the

algorithm using if statements.    The first of these is whether or

not the spot being checked is actually in the range of possible

coordinates in the grid.    You simply need to make sure that the

spot that is being checked has an x and y coordinate greater

than zero and a coordinate that is less than the highest

possible x or y coordinate.    Next you need to check that the

spot is not occupied by an obstacle.   This is also very simple

since you should have a pre-defined character that will serve as
                                                               Page: 12

an obstacle.   Next is to check whether the spots being checked

are in the open or closed list.    This is simply done by checking

the point part of the structure against all of the filled

elements of each of the arrays.    Of course if any of these

checks turn out to be true, then the program has to skip this

spot and go onto the next one it has to check.   However, if they

don‟t turn out to be true, then the initialization of the new

spot needs to be done.

    This process is very quick and simple.    The only thing that

needs to be done is to figure out all of the values that need to

be placed into the structure of the array.    The first thing that

is needed is the position, but this is already known because how

could you ever check the spot for obstacles and compare the

coordinates with the coordinates already in the open and closed

list if you haven‟t already determined the position.    The next

variable to take care of is the source variable.    The source is

the location number of the item in the closed list that you are

using as the spot to check during the current loop.    The next

variable is the heuristic score.   This is calculated by finding

the straight line distance from the current spot to the target

spot.   This can be done using a separate function that will

count the number of loops that it will take to get to the target

by using a simple algorithm.   This function should look

something like this:
                                                             Page: 13




    int findscore(point target, point start)

    {

         int distance=0;

         while(target.x!=start.x||target.y!=start.y)

         {

              distance++;

              if(target.x<start.x)

                    start.x--;

              else if(target.x>start.x)

                    start.x++;

              if(target.y<start.y)

                    start.y--;

              else if(target.y>start.y)

                    start.y++;

         }

         return distance;

    }



This procedure just takes an imaginary object and moves it

towards the target spot and adds one to the distance variable

during each of the iterations of the loop.

    The next variable to take care of is the distance variable.

This number is simply one more than the distance variable from
                                                             Page: 14

the source spot.    Lastly, the total score needs to be computed

by adding together the heuristic and the distance variables in

the spot structure that you have just been initializing.    Now

that this has been done, you now have your first spot structure

in the open list.    This process then needs to be repeated for

the other seven spots around the spot that you are checking.

    When this is done, if one of the spot that has been checked

is not the target spot, then you will need to choose the next

spot to check and move it to the closed list.    This is done by

checking all of the spots in the open list and choosing the spot

that has the lowest overall score.    It does not matter if there

are multiple spots with the lowest score because they will all

be checked at some point or another so you can just choose the

spot that you first come to with the lowest score.    Then comes

the hard part in this particular part of the process (at least

it was hard for me; I have always had difficulty with this).

You need to move the spot from the open list to the closed list.

To move it to the closed list is pretty simple: just copy the

information from the spot in the open list to the end of the

next available spot in the closed list.   The next, more

complicated, part that needs to be done is the process of moving

the remaining spots in the open list down to fill in the now

open spot.   This can be done with the code below.
                                                               Page: 15

    for (i=lowest+1; i<openl; i++)

    {

           open[i-1].pos.x=open[i].pos.x;

           open[i-1].pos.y=open[i].pos.y;

           open[i-1].heur=open[i].heur;

           open[i-1].score=open[i].score;

           open[i-1].source=open[i].source;

           open[i-1].dist=open[i].dist;

    }

    openl--;



    In this code, openl is the length of the open list and

lowest is the array index in the open list of the lowest score

spot.   The openl variable then needs to be decreased by one

after the end of this code because you have successfully removed

one of the spot structures from the array.

    The previously explained procedures need to be repeated

until one of two things happen.   The first is the one thing that

you do not want to happen: the open list length becomes zero.

This means that the entire space that the starting spot was

enclosed in has been checked and the target spot was not

reached.   This means that there is no path to the target, a

normally undesirable situation.   In the event of this problem,

you need to stop the loop so that an infinite loop is not
                                                               Page: 16

created and there should probably be some sort of output to tell

you that this situation has happened.

      The other possibility, of course, is that the target spot

was found during one of the checking loops.    If the target spot

is found, no more checking needs to be done and the loop needs

to be immediately ended.    The final step that needs to be done

to complete the path is to trace the path backwards.    The target

spot should have been given a source so the logical thing to do

is to start

there.   A new

array of points

needs to be made

to represent the

path that needs

to be taken.

Starting at the

end point, you

transfer the

point variable from the closed list into the new path array.

Then you use the source variable to trace backwards along the

path.    If this is done correctly, the path will end up looking

like the picture above.     You may notice that somewhere in the

middle of the path that it goes downward when it doesn‟t need

to.   This may not seem like the best path, but it still is.
                                                               Page: 17

Because of the ability to travel diagonally, this path is just

as short as if a perfectly straight path.    The reason this

“sagging” path was chosen in the end was because it is closer to

the target in that area so those spots had a lower overall score

than the other spots.

      The

amazing thing

about the A*

algorithm is

its

versatility.

It can be

easily changed

for different

terrain.    For

instance, if certain spots on the board cost more “movement

points” such as a mountain or some rocks, it will avoid it if

necessary.    If you look at this new grid above, I added

different spots that have various values.    The „^‟ are worth two

movement points and the „&‟ are worth three.    To adapt the

previously made program to take these special spots into

consideration, all you need to do is change the way the distance

variable is calculated.    Where previously all you did was add

one to the source spot‟s distance, you now would add the value
                                                              Page: 18

of the space that

you are moving

into.   For

instance, if the

spot is filled

with a &, then

you add three to

the previous

distance instead

of a one which

would be added if the spot is a „ „.     If you watch the path now

(pictured above), you can see the path is clearly affected by

the additions.    This modification to the algorithm can be used

for many applications.     An example would be if you are making a

game that involves tanks.    If a tank is pointing in a direction,

then you obviously do NOT want to be in front of it.     That would

hurt.   So what you can do to counteract this clear problem is to

set up a cone.    Anything within this defined cone will have a

larger cost to move within, so therefore you will avoid the area

in front of the tank.

    I liked this algorithm so much that I decided to make a

game out of it.    This was easily done because of a single

function called kbhit().    This function returns a true or false

statement depending on whether or not the keyboard was hit.     You
                                                              Page: 19

can put an if() statement in the main loop with the kbhit()

function in it and whenever you hit the keyboard, then it will

execute the code within and you can input the key that was

pressed normally with cin>>.   This is tremendously useful in a

game that requires chasing like the one I made.   I started

myself in the upper left corner of the maze and put the AI in a

random spot on the board.   Whenever I hit w, a, s, or d then I

moved my player up, left, down, or right respectively.   Whenever

the keyboard was not hit or after I hit the keyboard, the

computer moved.   Each time that I moved, the path was

recalculated.   This was a good way for me to put off moving onto

another subject that could potentially be much less interesting.

However, it had to be done eventually.
                                                               Page: 20

Finite State Machines

    So therefore,

after much

procrastination and

deliberation as to

what the next subject

of study would be, I

chose finite state

machines.    You may be

wondering, “what the

heck is a finite state


machine?????”.    The

best example of a

finite state machine

has to do with our

favorite little yellow

circle, Pacman.    Each

of the ghosts that are

out to get our circle

buddy has two states.

One is chase and one

is flee.    In the chase

state, the ghosts, obviously, chase Pacman.   The ghosts are
                                                                Page: 21

almost always in this state.    They only change when one of the

“Power Dots” are consumed.   They then switch states to the flee

state.   In this state they flee from our hero.    These ghosts are

called state-based machines because of the way they behave.

Pretty simple concept isn‟t it?

    To illustrate this, I made a game of ants.     The screen on

the previous page shows two screens.     The first screen

eventually becomes something like the second screen after a

certain amount of time.   You may be wondering how this happens.

    Well, you can see that there is a capital R and B near the

clumps of red and black ants.    Each of these is a base for the

respective colony of ants.     At the beginning of the game there

is two of each color ant next to their base.    They move around

randomly until they find food.    When they find food, they return

to their base and another ant spawns.    The ant that brought the

food back then gets thirsty (of course!) and needs to find

water.   After water is found, it looks for food again.     All of

this is done through a state-based machine.    Each ant is an

individual state based machine.     Each has four possible states:

forage, go home, thirsty and dead.     “Dead” happens when an ant

lands on the poison scattered around the board or when it moves

a certain number of spaces without finding food or water.      Since

each ant is its own machine, they all need to be a structure

like above.   The good thing about structures is that they can
                                                                Page: 22

have functions in them as well.    So an ant class could look

something like this.



    struct ai_Entity

    {

         int type;

         int state;

         int row;

         int col;

         int life;

         void newant(int mapmap[maxRows][maxCols],int ntype,

int nstate, int nrow, int ncol, int nlife);

         void forage(int map[maxRows][maxCols]);

         void goHome(int map[maxRows][maxCols], ai_Entity

list[maxEntities]);

         void thirsty(int map[maxRows][maxCols]);

         void dead(int map[maxRows][maxCols]);

    };



As you can see there is a variable for the ant type, a variable

for the state, the row, the column, how much life it has (the

amount of time before it dies if it doesn‟t find food or water)

and a bunch of functions.   Each of these functions are called

depending on the state variable.    If the state variable
                                                                Page: 23

indicates that the ant is in the thirsty state, it, of course,

calls the thirsty function.    The same is true for the rest of

the functions except the “new ant” function.

    The “new ant” function is called when the ant is first

spawned.   The ants need to be stored in an array so when a new

ant is created, the new ant function is called to initialize all

the variables in the structure.   After the machine is organized,

all that is left is writing the code.

    The main things to keep in mind while writing the code for

this particular example is that when the ant is in each of the

individual states, only certain objects can be run over.      For

instance, when the ant is in the forage state, only food and

poison can be run over.   The other thing that gave me a headache

was the go home function.   Everything needs to be avoided on the

way back to the base so some type of algorithm needs to be used

to be able to accomplish that.    Of course, having written the

algorithms previously, it was fairly simple to integrate it into

the new code.   Just a few variable names needed to be changed

and a few error trappings needed to be added.

    State based machines are used everywhere in AI.     In my

opinion they can be applied to almost all gaming situations.

They can even be used in complicated games like Halo, which is a

famous first person shooter.    All of the enemies that you

encounter can be broken down into a couple of states.    Some
                                                             Page: 24

could be patrol, chase, attack, and flee.   All of them would be

triggered by some sort of event just like in the ant game.    The

trick is knowing what the event should be to trigger each state.

Once that is done, all that is left is to execute the code that

you designate for the specified state.
                                                                    Page: 25




Fuzzy Logic

      The next area of study has the best name of the areas that

I looked at, fuzzy logic.     Anything fuzzy must be nice and fun

and cuddly right?    Sadly, this is an incorrect assumption.

Fuzzy logic refers a decision making process.        It allows the

computer to think more like a human.

      For example if you come across a basketball player, what do

you think?     “This person is 6 feet 11.5 inches tall”?    No of

course not!     You think something like, “This person is very

tall”.   The computer doesn‟t know what “very” means though.

Coincidentally, it also doesn‟t know what “somewhat” and “not

very” means.     You need to tell it what each means.     How is that

possible?     Decimals, that‟s how.    Think of this: There are three

possibilities for height: short, average and tall.        A male of

about 5 foot 8 inches is a little below average so how can you

tell that to the computer?     This person belongs to both the

short and average membership groups, but less so to the short

group because the average male height is 5 feet 10 inches.           A 5

foot 8 inch male is below average, but not by much.        So

determine that this person is 25% short and 75% average.        This

can be called somewhat short.     Now the question is how to code

it.   Using graphs, that‟s how!       There are a few fundamental

graphs that can be used with fuzzy logic.        Their various shapes
                                                               Page: 26

are pictured to

the right.    Each

of them has a name

that I am going to

use from this

point forward.

These graphs have

a maximum value of

one and a minimum

value of 0.    They

represent the

percentage of membership in each possible option.     So for the

height example again you could use a fuzzy grade to represent

the short option, a fuzzy triangle to represent the average

option and a reverse grade to represent

the tall option.      The end result would look something like the

graph to the right.

The vertex of the triangle would be around 5 feet 10 inches

since that is the average

height for males.

    I found that the best

way to create these graphs

to be used in a program is
                                                                Page: 27

with a set of fairly simple functions.   I will use the fuzzy

trapezoid as an example.    The function would be as follows:



    double FuzzyTrapezoid(double value, double x0, double x1,

double x2, double x3)

    {

         double result=0;

         double x=value;

         if(x<=x0 || x>=x3)

                result=0;

         else if (x>=x1 && x<=x2)

                result=1;

         else if (x>x0 && x<x1)

                result= (x/(x1-x0))-(x0/(x1-x0));

         else

                result= (-x/(x3-x2))+(x3/(x3-x2));

         return result;

    }

The variables sent to the function are the value that is to be

analyzed (the height of the person in this case), the value that

is to serve as the bottom left corner of the trapezoid, the

value for the top left, the value for the top right and the

value for the bottom right.   The rest of the function is just a
                                                               Page: 28

series of if-else statements that will determine the percentage

of membership to the selected option.

       The first two if statements are for the zero and 100%

membership.   For the zero percent, it checks to see if the value

sent was greater than x3 or if it is less than the x0 variable.

For the 100 percent, it checks to see if it is in between the x1

and x2 variables.

       So what is that jumble of division and addition in those

other if-else statements?    That is a calculation of a line going

through the two points necessary to create a trapezoid.    These

find the y value of the line and therefore give the percentage

value.    Now, you might think that this seems pretty much useless

so far right?   I just haven‟t shown you a good enough example

yet.

       For an excellent example, take into consideration a war-

like situation.   A good way to classify the amount of danger you

are in is by the distance of a set of enemy troops and by the

number of troops in that set.   This is perfect for fuzzy logic

because the danger could be small, medium or large and any

degree of those options.    The question a general watching the

war scene should be asking is how many troops he should deploy

in order to be safe.    Now that the concept is (or should be)

understood, lets show off some code.
                                                                 Page: 29

    The first thing that needs to be done, of course, is to

input the data necessary for success.    This is pretty simple

because all you need is the distance and the number of troops.

The hard part is the fuzzification and defuzzification of the

data.   Pretty nice names huh?   I thought so.   There are two

steps to fuzzification of data, the obtaining of the percentage

of membership to each option and the compilation of those

percentages into something that can be used later in the

defuzzification process.   The process of the compilation and the

defuzzification will be explained later.    So now for the first

snippet of code:



    void fuzzification(int num, int dis, double threat[3])

    {

          double numthreat[3];

          double disthreat[3];

          numthreat[0]=FuzzyReverseGrade(num, 5, 9);

          numthreat[1]=FuzzyTrapezoid(num, 5, 9, 15, 19);

          numthreat[2]=FuzzyGrade(num, 15, 19);

          disthreat[0]=FuzzyReverseGrade(dis, 7, 17);

          disthreat[1]=FuzzyTrapezoid(dis, 7, 17, 32, 42);

          disthreat[2]=FuzzyGrade(dis, 32, 42);
                                                               Page: 30

This is the start of the fuzzification function.    As you can

see, the first thing to do is to make two arrays of three cells

each that will take in the percentages that will be needed for

the calculations.   One of these arrays is for the threat created

by the number of units and one is for the threat generated by

the distance away from your base.    As you can see, I made

function calls to each of the types of graphs.     The num variable

is the number of troops, the dis variable is the distance those

troops are away from the base, and the threat variable is an

array that is sent to the function so that it can be used later

in another function.

    You may be wondering right about now what actually happens

to the dis and num variables and what happens to the two arrays

declared in the function.    Well let‟s set up an example.    Let‟s

say that the number in the num variable is 8.    Now step through

the function.   It comes to the FuzzyReverseGrade function call

first.   If you refer back to the last page, and think about the

two numbers being sent to the Fuzzy function, you can tell that

the number that is going to be stored in numthreat[0] is going

to be approximately 0.25.

    The next function is the FuzzyTrapezoid.     Using the same

reasoning, you can see that the number is going to be

approximately 0.75.    The third function you come across is the

FuzzyGrade function.    This will return a value of 0 because the
                                                              Page: 31

number 8 is too small.     The numbers that were just stored can be

referred to as the low threat, medium threat, and high threat

options respectively.

    Next, let‟s say that the dis variable has the number 13

stored in it.   Now you go through the same process and see that

the numbers that will be crunched out will be 0.6, 0.4 and 0.

So what‟s next?     Well, now you have to compile these two

unrelated threats into three overall threat numbers.     To do that

you need use three new types of fuzzy functions which are called

the FuzzyAND, the FuzzyOR, and the fuzzyNOT.



double fuzzyAND(double a, double b)

{

    double min;

    if (a<b)

           min=a;

    else

           min=b;

    return min;

}



double fuzzyOR(double a, double b)

{

    double max;
                                                              Page: 32

    if (a>b)

           max=a;

    else

           max=b;

    return max;

}



double fuzzyNOT(double a)

{

    return 1.0-a;

}


These functions work fairly simply.   Since we are trying to make

the computer think more like a human, you can think of these

functions as ways to put the numbers in a type of sentence.

This leads us to the final area in the fuzzification function

which makes use of these three functions.



threat[0]=fuzzyOR(fuzzyAND(numthreat[0], disthreat[1]),

    fuzzyOR(fuzzyAND(numthreat[0], disthreat[2]),

    fuzzyAND(numthreat[1], disthreat[2])));

threat[1]=fuzzyOR(fuzzyOR(fuzzyAND(numthreat[0], disthreat[0]),

    fuzzyAND(numthreat[1], disthreat[1])),

    fuzzyAND(numthreat[2], disthreat[2]));
                                                               Page: 33

threat[2]=fuzzyOR(fuzzyAND(numthreat[2], disthreat[0]),

    fuzzyOR(fuzzyAND(numthreat[2], disthreat[1]),

    fuzzyAND(numthreat[1], disthreat[0])));

}



    This may seem like a very complicated piece of code, but it

really isn‟t that bad.   The first variable that needs to be

stored is the threat[0] variable which will act as the low

threat option.   That line of code says, “the threat is low if

there is a low number of units AND they are a medium distance

away, OR it there is a low number of units AND they are a far

distance away, OR if there is a medium number of units AND they

are a far distance away”.   Seems pretty simple now doesn‟t it?

    So then, to figure out the exact number that is crunched

out you just need to refer back to the numbers that are in each

of the variables and use them in the AND and OR functions.      The

end result will give you 0.25.   You then do the same thing for

the medium and high threat functions which come out to be 0.4

and 0.6.   As you can see, these numbers do not add up to 1.0.

This is not a problem.   It is all resolved in the

defuzzification function.

    I know you may feel some attachment to your fuzzified

numbers and the new, almost human-like thinking of the computer,

but it still can‟t do anything with those numbers that were
                                                              Page: 34

found by fuzzifying.    Something else needs to be done.   This is

where defuzzification comes into play.    It takes this new data

and converts it into something raw that the computer can use

correctly.    The hard part about this is finding the best way to

convert it.   I used the function:



void defuzzification(int num, double threat[3])

{

    int

          deploy=num+(threat[0]*2+threat[1]*3+threat[2]*6)/(thre

          at[0]+threat[1]+threat[2]);

    cout<<"You must deploy "<<deploy<<" units."<<endl;

}



In this function, I declare an integer variable (since you can

only deploy whole numbers of units, how on earth would .5 of a

soldier be able to fight???) named deploy and set it equal to

the calculation on the right of it.     There is no set formula for

defuzzification, unlike fuzzification.    You just need to play

around using some basic knowledge until you get consistent

results that work.     The first thing that I thought of when I

thought of deploying troops was that I need to deploy at least

the number of troops that they have so I made sure to add in the

num variable.
                                                              Page: 35

    Next, I knew that the threat[2] variable obviously needed

to have higher weight than the other two parts of that array so

I put in the addition statements multiplying each by a different

number.   Lastly, the division was used because it gave much more

controlled numbers than without it.   Using this code, to stop an

enemy invasion of 8 units, a distance of 13 (insert unit of

measure here) away, you must deploy 13 units.   Seems like a good

number right?   It makes sense due to the proximity of the troops

but the limited number of them.
                                                                Page: 36

The End

    I must say, it has been quite a ride dealing with AI this

semester.    Pathfinding, state-based machines, and fuzzy logic

all provided their own headaches and triumphant victories.

Pathfinding algorithms provided a challenge, particularly those

in the area of error trapping and more particularly, the A*

algorithm.   However, finally seeing my little character moving

from the starting spot to the target spot was gratifying.

    The difficulty I encountered in programming state-based

machine was getting each behavior to work correctly and

programming the switches from one behavior to another.

Nevertheless, it was very satisfying seeing my hordes of ants

crawling around the screen looking for food and water.    Actually

being able to understand what was going on was very difficult

when using fuzzy logic, but finally being able to apply it to

another program was the reward I received after I understood the

basic principles of fuzzification.

    In my exploration time I learned some of the most useful

concepts in the field of artificial intelligence.    Each of my

three areas of study has great usefulness in game programming

and it is likely that I will use them all in the future in some

form or another.    I think that the pathfinding AI that I learned

is going to prove to be the most useful concept in the future

due to its necessity in each and every game.    Fuzzy logic‟s
                                                            Page: 37

decision making abilities, state based machine‟s behavior

organization, and path finding‟s movement can be used either

singly or together, and they will maximize the potential of any

AI.   There are other algorithms and data organization methods

that I would like to learn about in college and I plan on

looking at them to later continue my study of AI.

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:5
posted:8/6/2011
language:English
pages:38