Montana Tech of The University of Montana
Computer Science Department

CSCI 135
Fundamentals of Computer Science I
Fall 2019



ASSIGNMENT 9 - Dungeon

In this assignment, you will be making a game in which you wander around a dungeon.
  • You will use 2D lists.
  • You will develop a class hierarchy using inheritance.
  • You will experience the power of polymorphism.
  • You will use enumerations.

Dungeon game. Our hero is exploring a dungeon collecting gems. The dungeon has a bunch of locked doors that our hero needs to use keys to open. Our hero obviously can't walk through walls, unless of course it is a secret passage! The provided main program DungeonGame.py reads in the dungeon from a file. Here is the d0.txt file and what it looks like:

13 10
3 10
1 1
W W W W W W W W W W W W W
W - W - - - - - - D - - W
W - W - W W W - W W - - W
W - W - S - S - W W - - W
W - - - W W W - W W W O W
W W W - W W W D W W - - W
W W W - W - - - - D - - W
W W W - W - - - - W - - W
W - - - W W W W W W - - W
W W W W W W W W W W W W W

The first line contains the width and height of the dungeon in cells. The second line contains the number of keys followed by the number gems that will be randomly placed in the dungeon. The third line contains the x-index and y-index that the hero starts at. The remaining lines contain a character specifying what is at each location in the dungeon. The string code for cells are as follows:

CodeDescription
WWall, appears as , the hero cannot pass through it.
SSecret passage, appears as , but the hero CAN walk here.
-Passage, appears as , the hero can walk here.
DClosed door, appears as a closed (locked) door , the hero must have a key to open it.
OOpen door, appears as an open (unlocked) door , the hero can walk right through.

Your dungeon should appear on the screen oriented in the same way as the ASCII text version shown above. We think it is easiest if you setup your 2D array (let's call it map) so map[0][0] is the lower-left cell in the dungeon with the first index being the x-position and the second index being the y-position. So in the above map, the hero is at map[1][1], there is an open door at map[11][5], and so on.

Coordinates and pixels. All cell images in the game are square. The exact image filenames and the size of the square is defined by the Config class. The number of tiles on the screen depends on the width and height read from the first line in the dungeon definition. The provided DungeonGame program sets StdDraw.setCanvasSize() so the entire dungeon can be shown. DungeonGame sets the x-scale and y-scale so the coordinate system matches the pixel size of the canvas. This allows the various objects to draw themselves based on just knowing the tile resolution and their (x, y) indexes in the 2D list.

Gems and keys After your dungeon is created, it gets populated by gems and keys at random cell locations. Only cells that the hero can walk through are allowed to have a gem or key. So passages and secret passages can have a gem or key, but walls cannot. Doors are allowed to have a gem or key, but only if the door is open. A cell can only contain a single gem or key. The cell where the hero starts at should never have a gem or key. When the hero walks into a key, it is added to the hero's key total and the key collection sound effect is played. When the hero walks into a gem, it is added to the hero's gem total and the gem collection sound effect is played. Gems and keys should be drawn on top of the cell background image.

Hero. The hero always starts at the cell specified on the third line of dungeon definition. You can assume this location is a passable cell. The hero defaults to using the image specified by the heroImage method in the Config class, but the Hero API allows the hero to be set to any image via the setImage method. The hero moves based on the following keys:

KeyAction
wHero attempts to walk north.
sHero attempts to walk south.
aHero attempts to walk west.
dHero attempts to walk east.

The hero can only walk in the desired direction if the cell at that location is passable. If the cell is passable, the hero's position is updated and the walking sound effect is played. If the cell is not passable, or if a move would take the hero outside the dungeon bounds, the hero remains in the current position and the hit wall sound effect is played.

Doors. Some doors in the game started closed and locked. If the hero attempts to move into a locked door, and the hero has at least one key, the door opens and the key use sound effect plays. Opening a door costs the hero one key. Once a door is open, it remains open. Note that since the placement of keys is random, it is entirely possible that parts of a dungeon could end up unreachable.

Classes. To get started, download dungeon.zip. The zip file contains images, sounds, dungeon definitions, some completed Python classes, and some classes that you will complete for the assignment. Classes you need to complete have stubbed out methods along with a detailed description of what each method should do. Here is a high-level description of each class:

ClassDescription
Config Like many games, Dungeon involves numerous image and sound assets. Rather than sprinkle the literal names of these assets throughout your code, you should instead use the provided Config.py class. This class provides methods that can be called to obtain image filenames, sound filenames, and the size of cells in pixels. Config also provides a method that maps user input to a game action, and methods that determine what sounds to play when items (i.e. gems or keys) are collected or used. You should not need to modify this class.
Action The Action enumerated type represents the different actions the hero might take in the game. Currently this is just moving to the north, south, east, or west. You should not need to modify this class.
Item The Item type represents things the hero can collect in the game. Currently this is just gems and keys. You should not need to modify this class.
DungeonGame Main program that runs the game. Responsible for reading in the dungeon control file using standard input. Creates the Dungeon and Hero objects. Sets up the drawing canvas and coordinate size. The game continues forever. You should not need to modify this class.
Cell Represent an individual cell location in the dungeon. This is the base class in the inheritance. As such it defines the API that all the other Cell-related data types implement. The Cell type itself is quite basic, it represents an impassable wall with a given background image. You need to complete the missing code in two methods.
CellPassage This class is a child of the Cell class. It is a cell that the hero can walk through. Depending on the image it is created with, it could be a normal passage or a secret passage. It can contain either a gem or a key (but not both). You need to add attribute(s) and implement all methods.
CellDoor This class is a child of the CellPassage class. It is a cell that the hero can sometimes walk through (if the door is already open or if the hero has a key). A CellDoor may be created either open or closed. If the door is open, it can contain either a gem or a key (but not both). You need to add attribute(s) and implement all methods.
Hero Represents the state of the hero in the game. It keeps track of where the hero is, the image used to draw the hero, and how many keys and gems the hero has collected. This class plays sound effects related to collecting or using items. You need to add attribute(s) and implement all methods.
Dungeon Stores the grid of cells containing the dungeon. A dungeon also keeps track of the Hero object. This class plays sound effects related to moving or hitting walls. You need to add attribute(s) and implement all methods.

Test methods. The classes Cell, CellPassage, CellDoor, Dungeon, and Hero have test code provided. This allows you to test most of the methods in each class independent of the other classes. You can modify or add to the test code as you like (we ignore this part during grading). Here are our sample outputs using the provided test code:

ClassGraphical outputText output
Cell
      
% python Cell.py
    
CellPassage
      
% python CellPassage.py
Adding gem to passage,  result =  True
Adding gem to passage,  result = False
Adding key to passage,  result = False
Adding key to passage2, result =  True
Adding key to secret,   result =  True
Adding gem to passage,  result = False
Adding key to passage,  result = False
Adding key to secret2,  result =  True
    
CellDoor
      
% python CellDoor.py
Adding gem to closed door, result = False
Adding key to closed door, result = False
Adding gem to open door,   result =  True
Adding key to open door,   result = False
Adding key to open door2,  result =  True
Adding gem to open door2,  result = False
    
Hero
      
% python Hero.py
hero1, location (0, 0)
hero1, gems = 0, keys = 0
hero1, gems = 1, keys = 0
hero1, gems = 1, keys = 1
hero1, gems = 1, keys = 2
hero1, gems = 2, keys = 2
hero1, using key =  True, gems = 2, keys = 1
hero1, using key =  True, gems = 2, keys = 0
hero1, using key = False, gems = 2, keys = 0
hero1, using gem =  True, gems = 1, keys = 0
hero1, using gem =  True, gems = 0, keys = 0
hero1, using gem = False, gems = 0, keys = 0
hero2, location (1, 0)
    
Dungeon
      
% python Dungeon.py
    
NOTE: Placement of items will vary from the picture.

You might also want to test with the bigger dungeons d1.txt and d2.txt. Here are links to what the dungeons look like in our solution: d1.png, d2.png.

Grade ItemPoints PossiblePoints Earned
Program Compiles and Runs
2
Comments on All Your Classes and Methods
4
Implemented Cell Correctly
5
Implemented CellPassage Correctly
5
Implemented CellDoor Correctly
5
Implemented Hero Correctly
4
Implemented Dungeon Correctly
5
Total
30

Why are my tiles all offset and appearing in the borders of the drawing window? Remember that the StdDraw.picture() method expects the center (x,y) position of the picture, not the lower-left corner. You need to adjust appropriately to get things to line up nicely.

Is it okay to put the literal filenames of images, sounds, and/or the tile size in my classes? No. You should use the methods in the provided Config class. This way if any of these literal values changes, you only need to modify it in one place. It also provides a way (via subclassing Config) to easily change all the images, sound effects, and even the input keys by only modifying one line in your existing code. Cool aye?

I keep getting error messages that say config or some other variable are not defined. What is going on? Be careful of which variable you are using in your code. Attributes that belong to the object in the class should be prefaced with self. Variables that are passed in as parameters can be used as-is.

In Hero.py there are methods that get or set a value of an attribute. Why do I need these? Can't I just change them directly? Well, yes, in Python you can change those attributes directly. In other languages you cannot, for a good (secure programming) reasons. We will talk about this in class, but for now it's a good idea to use "getter" and "setter" methods.

I've forgotten how to use enumerations. Got any hints? You can use them in a manner very similar to data types (classes). You can create a variable of that type in the same way you create an object. To access the name of the enumeration as a string, you would use, for example Item.name. To access the value, you would use, for example, Item.value. In the Item.py file, you will see there is also a method there that counts the number of different values. You would call this method, if needed, by saying Item.count().

How do I set up a 2D list to store all the cells in the dungeon? In Python, it's not necessary to declare and initialize variables, but in this case, it's a good idea to do so. First of all, you need to know the size of the list you need. These values come in as width and height. Height is the number of rows in your list, and width is the number of columns. You have initialized 1D lists with numeric data in them by setting them all to 0. In this case, you know that each entry will contain a Cell, so I would initialize all entries to be a Cell. There is a little bit of weirdness that happens in Python concerning shallow and deep copies, so I will cover this part in class. But for a good reference now, read the page here.

Where did you get the graphics and sounds? We cut out bits from some of the images provided in the set: Danc's Miraculously Flexible Game Prototyping Tiles. The audio effects were obtained from freesound.org.


Extra credit. Make the game better. But please see the note below on how to submit your extra-credit creation. Be sure to submit your regular submission first and ensure it works per the assignment specifications.
Submission. Submit only the classes you implemented: Cell.py, CellPassage.py, CellDoor.py, Hero.py, and Dungeon.py using Moodle. Be sure each submitted source file has the required header with your name and a description of the program. For extra-credit, please submit a single zip file containing all your programs/graphics/sounds/configuration files via the special extra-credit drop box.

Page last updated: November 14, 2019