11 Artificial Intelligence (AI)¶
In this chapter we are going to add some basic decision making into the mob created in chapter 09 MOB to act as a simple Artificial Intelligence (AI) in the game. In this guide the words Artificial Intelligence is a extradition. AI is an interesting area of research for the Air Force Research Lab. AI enables smart phones, autonomous vehicles, internet search, and other computer applications. AI is used in video games to control MOBs. Recently AI has been used to beat human experts at chess and the ancient game of go. This guide is going to provide you a very simple foundation on which to learn about AI. To get started we are going to implement a new class to hold logic for the mob in a separate file and then use the logic written there to move the mob.
New Class File¶
First, make a new file called AI_class_TVR.py (with your initials replacing TVR). If you look at the first few lines of any of the previous tutorials, take note of the import statements that tell the file to pull in information from previously defined files and libraries. In the new AI_class_TVR.py file, we only need one import command. At the top of the new file, type the following:
from __future__ import division
This helps the code be compatible with different versions of python which deal with certain inputs and operations slightly differently.
Now we need to declare the class and write a function to initialize the object. Type the following below the previous line:
class AI:
def __init__(self):
# mode: follow, run, or none
self.mode = "none"
# status: active or not
self.status = False
# keep track of last movement
self.count = 15
This creates a new class called AI
and defines the __init__
function.
In __init__
, notice the member variables declared. mode
will store what
type of action the AI will take. status
determines whether or not the AI is
active and making decisions. Finally count
will track how long it has been
since the AI has taken action as to prevent it from acting too often and too
quickly. Recall that this function is executed whenever the frame is refreshed
in the display window, which occurs 60-120 times a second depending on the
frame rate. If the mob were to move at every frame update it would be unneeded
computation.
Our initial goal is to make the AI follow or run away from Dr. Steve. Add two
functions below __init__
on line 18 as follows:
def follow(self, xm, zm, x, z):
'''
guide the AI away from given coordinates
xm and ym are current position, x and z are player position
'''
move = [False, False, False, False]
dist = [(xm - x), (zm - z)]
if dist[0] > 1.25:
move[2] = True
elif dist[0] < -1.25:
move[3] = True
if dist[1] > 1.25:
move[1] = True
elif dist[1] < -1.25:
move[0] = True
return move
def run_away(self, xm, zm, x, z):
'''
make AI run from these x z coordinates
'''
move = [False, False, False, False]
dist = [(xm - x), (zm - z)]
if dist[0] < 0 and dist[0] > -3:
move[2] = True
elif dist[0] > 0 and dist[0] < 3:
move[3] = True
if dist[1] > -3 and dist[1] < 0:
move[1] = True
elif dist[1] < 3 and dist[1] > 0:
move[0] = True
return move
These two functions are very similar in terms of their logic. The first
function follow
takes xm
and zm
, the x and z coordinates of the
object controlled by the AI, and x
and z
, the location of what the AI
is trying to follow or run away from. In both functions there is an object
called move
. This is a list of booleans (an object that can take on the
value of either True
or False
) that will store what direction(s)
the object should be moved to accomplish the specified task. The following
if-else statements then determine which items in move
should be true and
returns this list of actions.
Using the New AI Class - Part 1¶
Now that we have defined a new class in a new file we need some way of using this file in the code we’ve already written. Copy the 10_health_part_4.py code from the previous example (replacing TVR with your initials) as follows:
cp 10_health_part_4.py 11_AI_part_1_TVR.py
We first need to let this file access and use the class from AI_class_TVR.py.
To do this, go to the top of 11_AI_part_1_TVR.py and add the line
import AI_class_TVR
so the first few lines of the file read as follows:
from __future__ import division
import sys, os
import math
import random
import time
import AI_class
from collections import deque
from pyglet import image
from pyglet.gl import *
from pyglet.graphics import TextureGroup
from pyglet.window import key, mouse
Your code should slightly differ here in that the line that reads
import AI_class
above should read AI_class_TVR
in your code where TVR
is replaced by your initials. The basic rule when importing from another file
is that if you have a file called “name_here.py”, any files that want to use
code written in “name_here.py” should have a statement similar to
import name_here
.
Next go to the end of the __init__
function in the Model
class around
line 170 and add an instance of this AI object so that the end of the
__init__
function reads as follows:
This creates an instance of the AI class in the model object that we can use.
Next after the end of the check_mob_dist
function add the following:
This function executes the moves specified by the code in the AI class. The
modes must be set correctly. Go to the on_key_press
method and add the
following to the end:
Notice by adding self.model.mob_loaded = True
for when ‘F’ is pressed, the
mob will inflict damage on Dr. Steve when it is following him. It will not
cause damage when the mob is running away.
Find the update
function on line 675 in the window class and add a call to
move_mob
at the end while commenting out the call to process_mob
. The
function should then read as follows:
Notice by commenting out the call to process_mob
we are ensuring the program
uses the logic from the new AI_class instead of what was written in
chapter 09 MOB.
Execute the code, you should be able to press ‘L’ to load the mob and then either ‘F’ or ‘R’ to make the mob follow Dr. Steve or run away from him. Don’t forget that when the mob is following Dr. Steve, it can reduce his health if it gets too close. Be careful! You don’t want to get game over.
It is worth discussing that even though we are describing this behavior as AI, it is simply the application of an algorithm on some input that results in a direction that points toward Dr. Steve. We can see the mob as following Dr. Steve, but it is really just following a set mathematical pattern that we implemented. Thus this behavior can be perceived as a Artificial Intelligence that follows Dr. Steve by someone who doesn’t know how the code works. Most Artificial Intelligence software implemented today follow simple mathematical patterns.
No More Walking Through Walls - Part 2¶
Let’s perform a scientific experiment.
Run the program from the previous section above and press “L” to launch the mob. Now place some blocks between yourself and the mob. Press “F” so the mob starts to follow you. Wait, that’s not right. The mob walked straight through the blocks and made them disappear! Let’s fix this bug and prevent the mob from walking through walls.
As is the guide custom copy the 11_AI_part_1.py code from the previous example (replacing TVR with your initials) like below:
cp 11_AI_part_1.py 11_AI_part_2_TVR.py
First, we need to add a variable to keep track of the mob’s height. In the Model
class’ __init__
function, recall we already have two variables
self.mob_x_position
and self.mob_z_position
to track the mob at ground
level. Add another variable just after these two so it reads as follows:
Now jump down to the check_mob_dist
function. Let’s change it to account
for differences in height by changing the function definition to read as below:
The main difference is that now when we calculate the distance between the mob and Dr. Steve, we also consider any difference in height. If we didn’t do this, the game would consider two objects at the same x and z coordinates as being at the same position, regardless of what their y coordinates were. This would be equivalent to saying two people on different floors of a building were standing next to each other.
Now find where we defined the functions mob_move_*
where *
is a
direction. Previously we ignored the height at which the mob was located so we
just had it at a constant y position of -1 which is ground level. Now that we
are tracking the height of the mob, we need to change this. Throughout these
functions, you will notice calls to self.add_block
and
self.remove_block
. In each call there is a -1. Replace each -1 with
self.mob_y_position
. For example, mob_move_right
should now read as
follows:
Next we need to create methods similar to those we just edited that move the
mob vertically. Just after mob_move_backward
add the following two
functions:
These functions are almost identical to those defined previously except that now we can move the mob in any direction.
Now go to where the different textures were defined (where we defined
TEXTURE_PATH
) and add the following line:
ALL_TEXTURE = [GRASS, SAND, BRICK, STONE, MOB_STATE1, MOB_STATE2]
Go to the move_mob
function (just after check_mob_dist
). We need to
make many changes here, so just replace move_mob
with the following two
functions:
The first function checks to see whether there is already an object at the
given position. If there is nothing there, it returns True
. If something is
already there it returns False
. Note that when we say a function “returns”
something, this means that when we use the function, not only can we change
values passed to the function, but we get some extra value(s) back.
Let’s walk through the changes to move_mob
. First, we declare a new
variable moved_up
. This will help us track whether the mob just moved
vertically and help us determine if the mob can move down. The next part of the
code is identical to what we had previously: check what mode the mob is in and
determine what direction it should move. Next we have the statements that
actually move the mob in the specified direction(s). Each direction has almost identical logic so let’s only examine one of them closely (the section under the
if statements reading if out[0]
).
First we get the position, called pos
, of the space in front of the mob. We
check if it is available with a call to check_avail
. If there is already
something there, we want to try and go over it. So we redefine pos
to be the
place one in front and one up from the mob’s current location and make another
call to check_avail
. If the space directly in front of the mob is available,
move there. If it isn’t and the space above is available, the mob moves there.
Otherwise, the mob shouldn’t move up or forward at this time.
We use this same logic for each direction, checking one square in the specified direction and one above and then moving to the available spot. At the end of the function, notice the below few lines:
self.model.mob_move_down()
pos = (self.model.mob_x_position,
self.model.mob_y_position-1,
self.model.mob_z_position)
This says that while the space just below the mob is available, the mob is above ground level, and the mob did not just move up, the mob should move down a space. This prevents the mob from floating around while it’s chasing Dr. Steve or running away from him. That would just be spooky!
Try running the code again. Now if you build a barrier one block high, the mob can get over it. Notice though that the mob now follows the same rules you do, it can’t get over more than one block so if you build a wall two or more blocks high it gets stuck.
If you go back to the section containing the build hills code (in the model’s
_initialize
function), you can uncomment this section and watch the mob run
up and down the hills. If it is easier you can copy/paste the code from the
file called 11_AI_part_2.py
around line 198 - 217.
Limitations¶
This is a very basic computer controlled character. While it will try and follow Dr. Steve wherever he goes, the mob cannot determine if there is a possible path to him. For example, if you build some steps and go up them, the mob may not follow Dr. Steve up the steps unless it was running toward the steps from the correct direction. It may get stuck on the sides. To fix this, the game would require a much more complicated algorithm or AI that can look at potential moves it could find a route to get to Dr. Steve.