CPSC 240 — OOA&D — Spring 2024

"Zork III"

Due: Monday, March 25, midnight Thursday, March 28, midnight

Zork III: Items & inventory

Your mission in Zork III is twofold:

  1. Create support for basic items. Basic items don't actually do anything, but they can at least generate colorful messages, and you can pick them up and drop them.
  2. Refactor your rudimentary support for commands into a Command inheritance hierarchy that will be more adequate for representing the wider variety of commands you'll implement in Zork++.

Setting up your team development environment

In order to work effectively together, you'll set up a private github repo that allows you to share code. Here's how.

  1. Each team member should create an account on github if you don't have one already. The three of you should share these with each other, and then one of you should send me one email with subject line "CPSC 240 Team N github usernames" (with the "N" replaced with your team number). This email should Cc: the other two members of your team. In the body of the email, you should include the three github usernames for your team, along with which human name goes with which github username.
  2. Choose a starting point. This step is an important one which should not be taken lightly. Your team must put your heads and your hearts together, and decide which team member's Zork II project will be the launching point for your Zork III project.

    If all has gone well to this point of the semester, your three Zork II's should be eminently compatible from a design standpoint. Therefore, if team member A's Zork II is chosen, it shouldn't require a great deal of mental adjustment on the part of team members B and C to understand it. You all have Rooms, CommandFactories, etc., that all work essentially the same way.

    How then should you choose? Basically, you should choose a Zork II program that works well and is written cleanly. The weirder and more convoluted it is, the flimsier the foundation it will provide on which to build. The buggier it is, the buggier your Zork III (and beyond) is likely to be. All else being equal, "vanilla" and "straightforward" are better than "innovative" and "complex." All three of you should look over all three of the Zork II programs, and make an intelligent decision as to which one to go forward with. This will require compromise and sacrifice, humility and a team spirit.

    Theoretically — but very theoretically — you could merge your three programs so that your Zork III starting point contains some classes from each. Theoretically. I would not advise this, however, unless you really do scrutinize all three programs in detail and convince yourselves that they really are plug-and-play compatible. In my experience, this is rarely the case, even for programs that were architected and envisioned identically. There's just too many little fiddly assumptions that every piece of code makes that are likely to produce bugs when hodgepodged together.

    (You may, if you wish, begin with my own Zork II program, which is in Canvas. To use Stephen as a starting point, one team member should download zorkII_stephen.git from Canvas, upload it to his/her cpsc.umw.edu account, and execute these commands:
    $ git clone zorkII_stephen.git zorkProject
    $ cd zorkProject
    $ git remote rm origin
    
    and then proceed with step 3.)
  3. The team member whose Zork project was chosen as the team's starting point (or the one who copied Stephen's in step 2) should carefully add his/her project to y'all's private shared github repo so that all three of you can henceforth collaborate on it. Replacing "N" with your actual team number, this student should execute the following commands from within his/her project directory:
    $ git remote add origin git@github.com:UMWComputerScience/240-spr2024-teamN.git
    $ git checkout -b main
    $ git push --set-upstream origin main
    
    He/she will be prompted for a github userid and password. You will know this step worked if you can open your browser, surf to https://github.com/UMWComputerScience/240-spr2024-teamN, and see all your code.
  4. Each of the other two team members will, in their own cpsc.umw.edu directories, either rename (via the "mv" command) or nuke (via the "rm -rf" command) their zorkProject directory. Then, replacing "N" with your actual team number, each of those two team members should execute the following command:
    $ git clone git@github.com:UMWComputerScience/240-spr2024-teamN.git zorkProject
    
    You will know this worked if you two can each "cd zorkProject" and browse around and see all of your first team member's code.

The requirements

Commands

Your Zork III game engine will support the following four additional commands: "look", "take", "drop", and "i" (for "inventory"). In addition, the dungeon file may specify item-specific commands for certain items; for instance, "wear necklace" or "ignite lightsaber". For Zork, item-specific commands won't do anything except output a custom message to the player; in Zork++, however, they will trigger events that will have some effect on the game.

look

Typing "look" is the way a player can see again the full description of a room they have previously entered.

Basement hallway You can go u to Rotunda. You can go s to Lab. You can go n to Back hallway. > u Rotunda You can go u to Rotunda balcony. You can go d to Basement hallway. > look Rotunda You are in a beautiful round entry chamber, with tall white pillars that seemingly reach to the skies. There is an elevator here. You can go u to Rotunda balcony. You can go d to Basement hallway. >

take

If the player types "take itemName", and an item with that name is in the current room, it will be added to their inventory (presently held items) and an appropriate message will be printed:

> take cupcake Cupcake taken. >

Other obvious cases should be appropriately handled:

> take ukulele There's no ukulele here. > take cupcake You already have the cupcake. > take Take what? >

Interestingly, a player can refer to most items by more than one name. The name the game prints is called its primary name, and the other names players can use to refer to it are called its aliases.

> take dessert You already have the cupcake. > take sword Broadsword taken. >

For brevity, the player can put the special word "all" after a take command, in which case all items in the room will be taken.

> take all Football taken. HockeyPuck taken. MandarinOrangeSalad taken. >

Finally, an adventurer cannot carry more than 40 total units of items. If he/she tries to pick up an item that would take them over this limit, print a denial message:

> take notebook Notebook taken. > take pencil Your load is too heavy. > i You are carrying: A figurine A laborSavingDevice A platypus A notebook > drop notebook Notebook dropped. > take pencil Pencil taken. > i You are carrying: A figurine A laborSavingDevice A platypus A pencil >

drop

If the player types "drop itemName", and an item with that name is in their inventory, it will be dropped into the current room:

Big Room You are in a very large room. > drop cupcake Cupcake dropped. > look Big Room You are in a very large room. There is a cupcake here. > drop sword Broadsword dropped. > look Big Room You are in a very large room. There is a cupcake here. There is a broadsword here. > drop ukulele You don't have a ukulele. > drop Drop what? >

The command "drop all" is also an option, of course:

> drop all PistonAssembly dropped. Football dropped. HockeyPuck dropped. MandarinOrangeSalad dropped. >

inventory

If the player types "i" or "inventory", their current list of items will be displayed (as primary names):

> i You are carrying: A cupcake A banjo A trampoline >

Print a reasonable message for empty inventories:

> i You are empty-handed. >

item-specific commands

Players can type arbitrary verb/noun combinations. If the noun matches the name of an item that is either in their inventory or in the current room, and if the verb corresponds to an item-specific verb for that noun, the corresponding message (specified in the dungeon file) will be output. Otherwise, they will receive an appropriate response:

> kick DrPepper The can skitters down the hallway. > kick soda The can skitters down the hallway. > break StarWarsToy Whoops! > break DrPepper You can't break the DrPepper. > teleport StarWarsToy You can't teleport the StarWarsToy. > eat donut There's no donut here. > assimilate Assimilate what? >

File format: dungeon file (.zork)

The Zork III dungeon file format is as follows (changes in red):

James Farmer Hall Zork III ===
Items: magicWand 5 --- WawaTravelMug,mug 10 --- DrPepper,can,soda 10 kick:The can skitters down the hallway. shake:A liquid fizzes menacingly inside the can. drink:Gulp, gulp -- that was GOOD! *belch* --- StarWarsToy,Yoda 5 touch:Yoda says, "Do, or do not! There is no try." break:Whoops! --- chainsaw 35 --- donut 7 eat:You feel mildly guilt-ridden. --- ===
Rooms: Rotunda You are in a beautiful round entry chamber, with tall white pillars that seemingly reach to the skies. There is an elevator here. --- Basement hallway Contents: DrPepper A long, white hallway stretches to the east and west. It is cold here, and you can detect the faint smell of body odor. A vending machine hums softly in the corner. --- Back hallway Comfortably cool, this hallway features stylish lines and cutting-edge decor. --- Stephen's office Contents: StarWarsToy,WawaTravelMug,donut This is a cluttered office, with many geeky toys sprawling on a desk. There are posters of Dune, Star Wars, and UMW Women's Basketball on the walls. --- Room 054 Sunlight streams through tall windows and illuminates a brilliant classroom. --- Lab The scent of unwashed bodies is stronger in this place. Rows of state-of-the-art computers line the room, with happy young people typing merrily away at several of them. They look up and wave hello to you. The door locks behind you. --- Rotunda balcony Contents: chainsaw,magicWand You stand on a circular white balcony overlooking an entry hall. Columnar bannisters in ancient Grecian style stand between you and the precipice. --- === Exits: Rotunda u Rotunda balcony --- Rotunda balcony d Rotunda --- Rotunda d Basement hallway --- Basement hallway u Rotunda --- Basement hallway s Lab --- Room 054 w Back hallway --- Back hallway e Room 054 --- Basement hallway n Back hallway --- Back hallway s Basement hallway --- Back hallway n Stephen's office --- Stephen's office s Back hallway --- ===

The changes are as follows:

  1. A new "Items:" section follows the standard header. Zero or more items will follow, terminated as usual by a "---" delimiter. Each item consists of (1) a primary name plus zero or more aliases (comma-separated), (2) a weight (in unspecified units), and (3) zero or more item-specific commands, each of which has a verb and a message separated by a colon.
  2. An item's initial location is specified in the "Contents:" line of the relevant "Rooms:" section. It is comma-separated, with no spaces. This line should be absent if a room is initially empty. If an item does not appear in the Contents section of any room, then it is initially not in the dungeon at all. (Perhaps in Zork++ it can be conjured with a spell, created by transforming a different item or by assembling components, etc.)

File format: save file (.sav)

The new save file format is as follows:

Zork III save data Dungeon file: /home/stephen/teaching/240/zork/files/farmerv3.zork Room states: Stephen's office: beenHere=true --- Room 054: beenHere=true Contents: DrPepper,burrito --- Basement hallway: beenHere=true --- Back hallway: beenHere=false Contents: chainsaw,magicWand --- Rotunda: beenHere=true Contents: StarWarsToy --- ===
Adventurer: Current room: Stephen's office Inventory: WawaTravelMug,donut

Changes include:

  1. Any item that was in a room at save-time will appear in the "Contents:" line at the end of that room entry. (The primary name of the item, not its alias(es).)
  2. There is now an "Adventurer:" delimiter line, stylistic.
  3. If the user possessed any item(s) at save-time, their primary names will be listed in the "Inventory:" line, comma-separated, with no spaces. This line should be absent if the user had an empty inventory at save-time.

The design

And now, here's how you're going to accomplish all this.

I present two UML class diagrams for Zork III. The first shows the new Item class and the classes it's related to. Note that this diagram only highlights the Zork-III-related changes; other variables/methods in previously-existing classes still exist as before, as do other classes.

Notes:

  1. An Item has a name, weight, and a Hashtable of verb/message pairs (called "messages"). It also has a constructor for hydration.
  2. .goesBy() simply returns true if the Item is known by a certain name. For instance, if the i variable is pointing to a "DrPepper" item, then i.goesBy("DrPepper") and i.goesBy("can") should return true, while i.goesBy("HanSoloActionFigure") should return false.
  3. The other Item methods should be self-explanatory.
  4. A Dungeon maintains a Hashtable of all Items anywhere in the dungeon so they can be easily looked up by (primary) name. You'll find this useful for the hydration sequence.
  5. GameState maintains collections of Items to represent each room's up-to-date contents, and also the adventurer's inventory.
  6. Room's additional methods are probably pretty easy to figure out. The last two on GameState, though, are a bit trickier. These getter methods each return the Item that matches a particular name (if any) but the .getItemFromInventoryNamed() one fetches an Item only from the player's inventory, whereas .getItemInVicinityNamed() looks also in the current room for the item. The latter method is useful because Zork will allow you to kick the DrPepper can even if you're not holding it, as long as it's there in the room you're in. Each of these methods should throw a NoItemException if the name they're passed doesn't correspond to any item in their inventory (or inventory + current room).

The other explanatory diagram I'll provide is the one that depicts the new Command type hierarchy:

Do not be afraid of the large number of classes. For one thing, they are almost all quite small. For another, note that the universe itself is composed of lots and lots of little things all working together, not 2 or 3 humongous monolithic things. Get used to the idea of creating a little class that represents one particular thing and does its little job well.

In the diagram you see:

Command
This class is gonna get gutted. The functionality that's in there now will be moved to MovementCommand, SaveCommand, and maybe a few other places. It will become a shadow of its former self: an abstract superclass that simply defines one abstract method (.execute()) that its subclasses will be required to implement.
Reminder: italics on a UML class diagram signifies an abstract class/method.
CommandFactory
Instead of instantiating a Command object directly (which will be impossible, since Command is now abstract), this class will instantiate the appropriate subclass of Command. Hint: to parse its input, the CommandFactory should split the String into words. (The .split() method of String is useful here.)
subclasses
Each subclass of Command will have its own constructor and .execute() method to do its job. It should be pretty easy to figure out what each one of these does; let me know if you have problems. Remember: .execute()'s job is to update the state of the game (if applicable), and return an appropriate String to get printed to the player in response to that command.

Sample dungeon

Also for this assignment each team member must create his/her own .zork file in the Zork III format that is smallish but at least mildly creative. Here are the details:

  1. You should create your .zork file in a new subdirectory called "files" at the top level of your project directory (i.e., files should be a child directory of zorkProject, and a sibling of src).
  2. Name the file something short. "spaceStation.zork" and "pyramids.zork" are examples of a good length filename.
  3. Check your .zork file into git, using the same procedures you do for .java source files.
  4. This sample dungeon must include at least ten rooms, at least eight items, and at least twenty different item-specific commands for your various items. (I don't mean 20 verbs and messages for each item; I mean at least 20 total verbs/message among all your items.)

Recommended sequence

Here's my suggestion for where to begin:

  1. Do surgery on your Command class and create the beginnings of the inheritance hierarchy. For now, just do the MovementCommand, SaveCommand, and UnknownCommand subclasses. Make your CommandFactory able to instantiate these two types. Get it all working so your Zork program behaves just as it did in Zork II.
  2. Write the Item class in its entirety. The hardest thing here is the constructor; write a main() to test that it can hydrate from a file that contains only an item entry. Make sure it works with this file:
    fidgetSpinner 2 spin:Wheeeee! ---
    and this one:
    HeavyButBoringItem 1000000 ---
    and this one:
    football 8 throw:The football soars through the air. kick:Through the uprights! deflate:*Psssssssst.* Tom Brady would be proud. ---
  3. Make all the changes to the Room class, including hydrating items.

    Unfortunately, you're not going to be able to fully test this step until you finish step 4, below.

    Note that when the adventurer enters a room, there should be a line printed for each item the room contains:
    There is a StarWarsToy here. There is a WiiProController here.
  4. Make all the changes to the Dungeon class, including hydrating items. Make sure you can hydrate farmerv3.zork.
  5. Make all the changes to the GameState class, including persisting/hydrating items. Test that you can save and load a .sav file in the proper format.
  6. Write LookCommand, TakeCommand, DropCommand, and InventoryCommand, and get them all working. Make sure you can look around, pick up items, take them elsewhere, drop them, look at your inventory, save your progress, and pick up where you left off.
  7. Write ItemSpecificCommand and get it working with some funny messages.
  8. Finally, each team member write your sample 10-room dungeon (see above) and play each others' dungeons.

Turning it in

To turn in this assignment, one team member send an email to cpsc240submissions@gmail.com with subject line "CPSC 240 Zork III turn-in", Cc:'ing the other team members. No need to include any attachments: Kenzie and I will simply clone your team's github repo and test that. (Make sure all of your latest code is committed and pushed, and also double-check that each team member's .zork file is in the files directory.)

We need help!

Send email with subject line "CPSC 240 Zork HELP!!!" Use a number of exclamation points reflective of the urgency of the request.