Homework 1: Grid Path Network

[Updated: 01.08.14 - added grading criteria]

The purpose of this exercise is to acquaint you with the python game engine we will be using in the class.

One of the main uses of artificial intelligence in games is to perform path planning, the search for a sequence of movements through the virtual environment that gets an agent from one location to another without running into any obstacles. For now we will assume static obstacles. In order for an agent to engage in path planning, there must be a topography for the agent to traverse that is represented in a form that can be efficiently reasoned about. The simplest topography is a grid. Think of an imaginary latice of cells superimposed over an environment such that an agent can be in one cell at a time. Moving in a grid is relatively straightforward: from any cell, an agent can traverse to any of its four (or eight) neighboring cells.

The agent can traverse from the center of any cell to the center of any adjacent cell, which can be visualized as a network of paths, shown in blue below.

In this assignment, you will write the code to generate a path network based on a grid. The path network should work on any given terrain of obstacles and allow the agent to traverse between any two points (as long as there exists a path wide enough for the agent to fit) without colliding with an obstacle.

But first, you need to become familiar with the python game engine in which you will be working.


What you need to know

The game engine is object-oriented. The primary object is the GameWorld, which is a container for all other obstacles. Most importantly, the GameWorld object contains the terrain of the virtual environment. The terrain is represented as a list of Obstacle objects, which themselves are polygons---lists of points and lines. The GameWorld also manages the agents, bullets, resources (things that agents can gather) and computes collisions between all objects and obstacles. The GameWorld also does important stuff like run the real-time game loop and maintain the rendering windows, but you shouldn't need to worry about that. What you do need to know is that every iteration of the game loop, called a tick, the update method is called on all dynamic objects.

Below are the important bits of information about objects that you will be working with or need to know about for this assignment.

GameWorld

GameWorld is defined in core.py

Member functions:

Obstacle

Obstacle is defined in core.py. An Obstacle is a polygon through which Agents cannot move.

Member functions:

Agent

Agent is defined in core.py. Agent is the class type of the player avatar or non-player characters. Aside from drawing itself, an Agent knows how to move (which it inherits from its super-class Mover) and shoot. If it is moving to a particular destination, it updates its location every tick. Agents maintain a timer to control how often it can shoot.

The Agent class itself does not know how to move around an environment without coliding with obstacles. When instructed to move, it will move in a straight line from its current position to a target position. The intelligence in how to avoid obstacles is contained in a sub-component of the Agent, called the Navigator.

Member variables:

Member functions:

Navigator

Navigator is defined in core.py. A Navigator contains the smarts for how to get around in the game world. Its primary function is to compute a path between two points that steers the Agent clear of any obstacles. This can be done in many different ways. Different AI techniques will sub-class from Navigator.

Member variables:

Member functions:

RandomGridNavigator

RandomGridNavigator is defined in randomgridnavigator.py. The RandomGridNavigator does two things. First, it creates a gird-based path network, as described above. Second, it causes the Agent to perform a random walk of the path network. The random path terminates after 100 path nodes and the Agent moves directly to its destination from the last random point reached. Thus, the Agent can possibly collide with obstacles if random path does not reach the destination before the threshold is reached.

The size of the cells are set to be twice the radius of Agent.

Member variables:

Member functions:

Miscellaneous utility functions

Miscellaneous utility functions are found in utils.py.

Making new maps

It is good to test your techniques on new maps. If you want to make new maps, make a copy of runbasic.py and edit the call to world.initializeTerrain().

initializeTerrain(polygons, color, linewidth, sprite) instantiates the physical Obstacle objects (specifically ManualObstacle objects) in a game. A list of polygons is given, such that there is one polygon for each obstacle. A polygon is a list of points where a point is a tuple of the form (x, y). The color of the line is given, in the form (red, green, blue) where each values is between 0 and 255. The default color is black (0, 0, 0). Linewidth indicates how thick to make the line of the polygon. Obstacles will be filled with repeating sprites if the optional sprite parameter is given as a string pathname to an image file.

To make maps of different sizes, GameWorld can be initialized with three parameters. The first is a seed to help reproduce random values. If seed is None, the current system time is used as the seed. The second is the dimensions of the world in the form (x, y). The third is the dimensions of the screen in the form (x, y). The dimensions of the screen should be equal to or smaller than the dimensions of the world.


Instructions

You must superimpose a grid-based path network over an arbitrary game world terrain consisting of obstacles such that an Agent can traverse the environment. Because we haven't gotten to the part where the Agent can be controlled intelligently, we will use a the RandomGridNavigator, which walks randomly around for a while before proceeding directly to its destination.

Step 1: Download and install the game engine. This must be done in two parts. First, you must install PyGame, the underlying rendering engine. Follow the instructions for Windows, Mac, and Linux found in the zip file. See the installation instructions.

Step 2: Verify the installation is successful by running the game engine:

You should see a top-down view of the game world, consisting of some obstacles (black polygons), some resources (crystal sprites), and an agent. This agent is the player avatar, meaning it is controlled by the player. When you click on a point on the screen, the agent will proceed directly there. runbasic.py uses the base Navigator class.

Step 3: Run the grid navigator code:

You will see the same thing as before with runbasic.py. However, when you click on the screen, the agent will not move. This agent uses RandomGridNavigator, which must first be completed by you.

Step 4: Modify randomgridnavigator.py and complete the myCreatePathNetwork() function. myCreatePathNetwork() must do two things.

  1. It must compute a list of points, such that each point (x, y) is the center of a grid cell. Cells must be of the size contained within the self.cellSize member variable. Cells must not intersect with any of the obstacles or the edges of the world.
  2. It must compute the path network as a list of lines where a line is of the form ((x1, y1), (x2, y2)). Each line connects the centers of adjacent cells. We will be using 8-adjacency, meaning a cell is adjacent to the ones the the north, south, east, west, northeast, northwest, southeast, and southwest.

Step 5: Test your implementation:


Grading

This homework assignments is worth 10 points. Your solution will be graded by autograder. The autograder will look for grid cells that intersect with obstacles or the boundaries of the map. For every map that your solution is tested against, 2 points will be deducted if at least one cell intersects with an obstacle or the boundaries of the map. The autograder will test your solution on 5 maps, two of which are provided as test maps.


Hints

Debugging within the game engine can be hard. Print statements will be one possible way of figuring out what is going on.

It can also be helpful to draw debugging information, such as lines and points, to the screen.

To draw a cross on the screen use the following:

In general, you should be able to get all the data you need from the GameWorld, Obstacles objects, and the Agent through getter functions. If you find yourself directly accessing member variables of other objects, you may want to rethink your approach.


Observations

For grid-based navigation, it is not necessary to store the path nodes and path network as is done in this assignment. The (x, y) position for any cell can be computed quickly as the offset from the origin point. Storing path nodes and the path network is unnecessary storage. Cells in un-navigable spaces can be marked with a bitmask or other technique. However, in this assignment we ask you to convert a grid into a path node network to make use of the existing game engine infrastructor for handling path networks and to allow one to become familiar with the version of the game engine that will be used in subsequent assignments.


Submission

To submit your solution, upload your modified randomgridnavigator.py. All work should be done within this file.

You should not modify any other files in the game engine.

DO NOT upload the entire game engine.