CSCI 151 Project 2: Adventure!


You are in MC Reynolds 315.  Banks of desks stretch southwards
and upwards into the gloom.  The blackboard broods, dark and empty,
like a giant watchful eye.  The door is to the west.

There is an eraser here.

? take eraser
Taken.

? inventory
You are carrying:
  an eraser

? go west

You are standing at the east end of the hallway on the third
floor of MC Reynolds. 315 is to the east, and Dr. Goadrich's
office is to the south. The hallway continues to the north and
west.

? go south
You start to go that way but then change your mind.

? go north

You are standing in a hallway on the third floor of MC Reynolds,
which extends to the south.  To the east is 317; to the north,
a doorway; to the west, a bathroom.

? go east

You are in MC Reynolds 317.  It is bright and sunny.
The chalkboard is covered with pictures and writing.

There is a stick of chalk here.

? drop eraser
Dropped.

Overview

In this project, you will implement an adventure game engine which can read in a description of a world and allow the user to explore it, as shown in the example above.

The goals of this project are to:

Timeline

Don’t Panic

This project might at first glance appear huge, and there is certainly a lot to read and understand before you can get started. But (a) it is not quite as bad as it looks (trust me!) and (b) it is easy to develop incrementally, so you can get something very simple working first and then keep adding features as you go. Also, (c) you have plenty of time—get started early, ask for help when you are stuck, and keep plugging away at it.

As a point of reference, my solution (which implements all the required extensions, though not an additional extension) is about 500 lines of code, not counting the starter code. But keep in mind that a sizable fraction of that (perhaps half?) consists of blank lines, comments, and boring getter/setter methods.

A Note on Dictionaries and Sets

In this project you will almost certainly use a lot of dictionaries. For a fast Java dictionary implemention, you should use the HashMap class. We don’t yet know how it is implemented (we will learn about it later in the semester) but you already know how to use it.

In addition, you can use HashSet if you want to keep a collection of things in no particular order, and be able to easily add and remove items. For example, this sort of structure is ideal for keeping track of the player’s inventory.

A Note on Reading

Read this project description carefully! There are a lot of moving parts to this project and a lot of things that have to be explained. You will certainly not be able to remember everything and will need to refer back to this document frequently. I strongly suggest printing this project description on paper so you can more easily refer to it, write notes, and so on.

Requirements

Here are the basic requirements for the project, which must be completed individually.

Other basic requirements (the version you turn in on April 12 does not need to have these implemented yet, though I strongly suggest you try to get them implemented by then):

A project which meets all the above basic requirements and uses good code style and documentation will receive a B.

Note that it is not a requirement to make your output look exactly like mine in the example above! Use your creativity and judgment. If you have an idea of something you would like to do differently and are unsure whether it would meet the requirements, feel free to ask.

To receive an A, you should do the above in addition to extending your engine with additional features. See the section on Extensions below for details.

Materials

I have provided some starter code you can use.

Below you will find more information about the contents of each provided file.

Direction.java and Verb.java

Direction.java represents a direction in which the player can move, such as “south”, “northwest”, or “up”. Verb.java represents something the player can do, such as “go”, “take”, or “drop”.

Both are implemented as enumerations, declared using the enum keyword, which is like a special class that provides a list of possible values. You can refer to these special values by prefixing the name of the enumeration: for example, Direction.SOUTHEAST or Verb.GO. Adding a new direction or a new kind of action (if you decide to do so while making your own extensions to the project) is as simple as adding a new name to the appropriate list.

Both Direction and Verb also provide a parse method, which can be used to convert from a String. For example, Direction.parse("up") returns Direction.UP.

Command.java

Command.java provides the Command class, which is responsible for taking a String representing something the player types, and breaking it down into pieces such as a Verb and/or a Direction. In particular, a Command may contain three pieces of information extracted from the player’s String:

For example, Command c = new Command("look north") creates a Command object which breaks down the string look north into pieces. Calling c.getVerb() will return Verb.LOOK, and c.getDirection() will return Direction.NORTH.

As another example, Command c = new Command("look at eraser") will create a Command c such that c.getDirection() is Direction.UNKNOWN, but c.getNoun() is "eraser". Note that small words like "at" are simply ignored.

There is a bit more you can do with Command; take a look at the comments in Command.java for more info.

Hendrix.yaml

The provided Hendrix.yaml file describes a simple world centered around the third floor of MC Reynolds. Note that you do not have to write code to read Hendrix.yaml (or any other .yaml files)! Instead, you should use the provided SnakeYAML library to read them for you. See the section below on snakeyaml and AdventureDemo.java for an explanation of how to do this.

Hendrix.yaml uses the standard YAML format, which is intended to be a simple, human-readable format for describing structured data.

Entries in the file are separated by three hyphens ---. Let’s look at a single entry from Hendrix.yaml as an example.

type: location
id: MC317
name: MC Reynolds 317
desc: You are in MC Reynolds 317.
longdesc: |
  You are in MC Reynolds 317.  It is bright and sunny.
  The chalkboard is covered with pictures and writing.
exits:
  west: hallwayEB
  out: hallwayEB
items:
  - name: stick of chalk
    aliases: [chalk]
    desc: |
      It is a fat, high-quality piece of Japanese chalk,
      with the word "Hagoromo" written on the side in
      black letters.  You seem to recall that Dr. Yorgey
      always uses this kind of chalk.  Perhaps he
      accidentally left it here after teaching Algorithms.
    goal: yorgey
    goalmessage: |
      You return the chalk to Dr. Yorgey, who seems very grateful.
    score: 5
  - name: blackboard
    aliases: [board, chalkboard, pictures, picture, writing]
    portable: false
    desc: |
      The blackboard has several pictures of graphs traced
      over in yellow, green, blue, and orange chalk, along
      with lots of writing.  You can make out a few words
      like "lemma" and "theorem", but the rest seems to be
      about things like "flows" and "cuts" and "values".
      You realize it is probably left over from Algorithms.
  - name: sun
    portable: false
    desc: You could hurt your eyes that way!

As you can see, an entry consists of a top-level dictionary with keys like type, id, name, and so on. Let’s go through each key one by one:

A few miscellaneous notes:

snakeyaml-1.18.jar and AdventureDemo.java

You do not have to write code to read in YAML files! Instead, you should use the provided SnakeYAML library to read them for you. This will return some structured objects from which you can extract the information you want. Doing things this way has several advantages:

AdventureDemo.java provides an example of how to use SnakeYAML to parse a .yaml file, and how to extract information from its output. It also demonstrates how to read user input and parse it using the provided Command class. Feel free to use AdventureDemo.java as the basis for your own implementation.

Design document

By April 8, you should turn in a design document planning out the overall structure of your implementation. Your design document should contain a list of classes you plan to develop. Under each class, list:

Think about the different entities you will need, and what information they will keep track of. As one example, you will most likely want a Player class, which contains:

You may think of other things that should go in the Player class as well. Of course, you should also describe the methods that Player will have.

A good implementation will probably have something like 5-10 classes (including the provided starter code), although yours may have more if you add extra features.

Basic working version

By April 12, you should turn in a basic working version which:

Extensions

In order to get an A on the project, you should implement the required extensions in addition to at least one additional significant feature. See the Additional extensions section below for some ideas.

Required extensions

Additional extensions

Most other extensions will require you to extend the .yaml file format with additional keys or entry types. Fortunately, that is as easy as simply adding them, and then looking for the new key or entry type in your engine. For example, suppose you wanted to add a weight key to items. You would simply add something like weight: 10 as a key-value pair in the description of an item; then, in your engine, you could do something like

Integer w = itemMap.get("weight");
int weight = (w == null) ? 5 : w;

In this example, I chose to use a default weight of 5 if the value w is null (meaning the key weight was not found). This means my engine will still be compatible with .yaml files that do not specify a weight key for items; it will simply use a default weight.

Here are some ideas for other ways you could extend your engine and the types of worlds it can support. Of course, you should not feel limited to these. Use your imagination! Please come by and discuss your ideas with me; I am happy to help you brainstorm and plan how to implement them.

What to Hand In

For your final submission, you should turn in a .zip file containing: