Due Friday September 25, 2008 at 11:00am.
(Zork opening screenshot, credit Wikipedia.org)
In this homework you will be creating the core functionality of a simple text-based adventure game. The goals of this assignment are:
Important notes:
Here are the files you will need:
ItemComparator" class)Sections in this document:
The ancestor of all text-based adventure games is Bill Crowther's Adventure, developed originally in Fortran back in 1975. A popular variant was called Zork, and this spawned many sequels (and whose introductory screen can be seen above). If you are interested in experiencing this classic, check out Infocom's official Zork download page (it's free).
Our adventure game is much simpler than these. The player's character moves from room to room exploring a maze, and can pick up and drop items. We will start by building the Rooms, then the Maze, then Items, and finally Characters.
For the complete specification of all classes in this assignment, refer to the Javadocs.
For this assignment, we've created a new (hopefully simpler and more robust) way for you to run the test script and see what grade you've achieved at the moment. The hw2.jar file includes a compiled version of the Tester class that you can run yourself, on whatever machine you are using to develop your solution. Here's what you need to do to run it. (You are strongly encouraged to try this out right now, before beginning on the assignment, and to run the tester to check the parts of your solution as you finish developing them.)
Now there are two methods for running the tests:
java -cp .:hw2.jar Tester
You should see the test script's output in the terminal window. (Of course, it will tell you almost all the tests are failing at the moment!)
Room.java)
To begin, let's set up the basic structure of the world: the Room class.
Take a look at the javadoc documentation for the Room class. Your first job is to implement this class. This should be mostly straightforward...
Here are a few more notes that should help you:
north", "south", "east", and "west". (In the extra credit part of the assignment, if you choose to do it, you'll generalize this to allow arbitrary strings as directions. But, for the non-extra-credit part of the homework, these four are the only ones you have to handle.) This means that, if you like, you can represent the exits with four instance variables of type Room, one for each direction.linkRooms method is worthy of special
mention (see image at right): it creates a bidirectional connection between the rooms. This ensures that if you walk north from
Room 1 into Room 2, and then walk south again, you will end up back in
Room 1 where you started.toString method, which is
used when a Room object is printed to the console, should
return the room's description.lockExit method is not used in question 1 and is not tested by the tester. You'll need to fill in its method header so that the tester code will work with your code, but it doesn't have to do anything yet.> Room r1 = new Room("Ballroom")
> r1
Ballroom
> r1.getExit("north")
null
> Room r2 = new Room("Library")
> r1.linkRooms(r2, "north", "south")
> r1.getExit("north")
Library
Maze.java)Creating large worlds by creating individual rooms and linking them together can be a lot of work. Let's next build a helper class that simplifies the process by taking a two-dimensional array of strings and creating a world from it all at once. For any cell in our grid, if there is a non-null String value in that cell then a room should be created with that string as its description. Exits should be automatically created for any adjacent rooms. For example, if we give it an array that looks like this...
"A"
"B"
"C"
"D"
null
"E"
"F"
"G"
"H"
...it will create a "ring" of rooms whose descriptions are "A", "B", "C", etc. where going "east" from A reaches B, going "west" from B reaches A, going "east" from B reaches C, going "south" from C reaches E, etc.
See the javadocs for full details of what your solution must provide.
> String[][] descriptions = new String[3][3]
> descriptions[1][0] = "A dusty broom closet.";
> descriptions[1][1] = "A large and dimly-lit ballroom.";
> descriptions[0][1] = "A fancy dining room";
> Maze m = new Maze(descriptions, 0, 1) > m.getStartingRoom() A fancy dining room. > m.getStartingRoom().getExit("south") A large and dimly-lit ballroom.
Item.java)Now let's enrich rooms so they can contain things. Much of the work in this part is done for you by the Container class, which is provided for you in hw2.jar. Containers hold objects of the Item class. Items are simply game elements that have a description, and can be stored in containers. For problem 3, it is your job to write the Item class.
Container and Item. Make sure you understand the relationship between them:
Item class so that it follows the specification in the javadocs. Make sure your items interact correctly with Containers - use the sample interactions and tests. '\n'.> Room r = new Room("A large and dimly-lit ballroom.")
> r
A large and dimly-lit ballroom.
> Item i = new Item("Coin", r)
> r
A large and dimly-lit ballroom.
Contents: Coin
Character.java)The last missing component of our adventure game is characters. For this problem you will write the Character class. Because characters are also entities in the game that will be held in Containers (in this case the Room they are in), the Character class will need to have similar functionality to the Item class (in addition to lots more specialized functionality specific to characters, like the ability to move around). In essence, you can think of a Character as a specialized type of Item that can do lots more stuff.
Character class inherit from the Item class. container()" and "moveTo()"
methods - the Character class will inherit them automatically. Character class should have. > Room r = new Room("An ancient temple.")
> Character c = new Character("Indiana Jones", r);
> c
Indiana Jones
> Item i = new Item("Fedora Hat", r)
> c.pickUp("Fedora Hat")
true
> c
Indiana Jones (carrying Fedora Hat)
> Room r2 = new Room("A small, damp stone alcove.")
> r.linkRooms(r2, "north", "south")
> c.move("west")
false
> c.move("north")
true
> c.getLocation()
A small, damp stone alcove.
Contents: Indiana Jones (carrying Fedora Hat)
Room.java)While technically we now have a fully functioning adventure game, our game isn't very interesting at the moment. To spice things up a little bit, let's now go back and add the capability for a door from one room to the next to be locked unless the character attempting to move is carrying the specific key.
> Room r = new Room("A large and dimly-lit ballroom.")
> Room r2 = new Room("A small, dusty broom closet.")
> r.linkRooms(r2, "west", "east");
> r.lockExit("west", "A small silver key");
true
> Character c = new Character("Sherlock Holmes", r);
> c.move("west")
false
> Item i = new Item("A small silver key", r)
> c.pickUp("A small silver key")
true
> c
Sherlock Holmes (carrying A small silver key)
> c.move("west")
true
> c.getLocation()
A small, dusty broom closet.
Contents: Sherlock Holmes (carrying A small silver key)
Finally (if you choose), let's make things a little more interesting by allowing rooms to have exits in "directions" with names other than the compass directions. Observe that we've already left room for this in the design — the methods of Room that deal with directions treat them as strings rather than as members of an enumerated type with just four possibilities. (This is also why the linkRooms method takes a description of the forward and backward directions when creating exits. While it may be well-defined what the opposite of "north" is, its not obvious what the opposite of the direction "a few steps to the left of the dusty old grand piano" is...) Your job is to rewrite your implementations of those methods so that they do the right thing with arbitrary strings.
One way to implement this functionality is to use a HashMap in each Room to represent the association between directions and neighboring rooms. Have a look at the documentation for HashMap (available in many places — it's a standard library class) for a detailed explanation of how it works. If you choose to use it, you'll need to put "import java.util.HashMap;" at the top of your Room.java file OUTSIDE the class definition.
This question doesn't require any new methods anywhere: just change your implementation of the preexisting framework.
> Room r = new Room("A large and dimly-lit ballroom.")
> Room r2 = new Room("A small, dusty broom closet.")
> r.linkRooms(r2, "into closet", "out of closet");
> Character c = new Character("Curious George", r);
> c.move("into closet")
true