A document from MCS 260 Fall 2021, instructor David Dumas. You can also get the notebook file.

MCS 260 Fall 2021 Project 2

  • Course instructor: David Dumas

Instructions

Deadline is 6pm CDT on Friday October 8, 2021

Collaboration policy and academic honesty

This project must be completed individually. Seeking or giving aid on this assignment is prohibited; doing so constitutes academic misconduct which can have serious consequences. The only resources you are allowed to consult are the ones listed below. If you are unsure about whether something is allowed, ask. The course syllabus contains more information about the course and university policies regarding academic honesty.

Resources you are allowed to consult

  • Documents and videos posted to the course web page
  • Any of the optional textbooks listed on the course web page

Ask if you are unsure whether a resource falls under one of these categories.

What to submit

The rest of this document describes a program adventure.py you need to write and submit to Gradescope. That is the only file you need to submit for this project.

When to submit

The autograder will open on or before October 4, 2021. An announcement will be made on the course web page when it is available. At that time it will be possible to submit your project. It's a good idea to submit a program that does something (i.e. is valid Python) as early as possible so you can see what the autograder report looks like.

The last version you submit will determine your score. Remember that a submission after the deadline will not receive any credit unless you ask for and receive an extension.

Get the starter pack now

This project involves a program that reads data from a file. The details are explained below, but you'll want to have some sample data to use while developing it. This is provided in the form of a starter pack. I suggest you download it right away:

A later section of this document explains exactly what's in the starter pack, but I suggest you download it even before you've read the entire project description.

Quick summary of the task: Text adventure

In this project you will write a text adventure game that follows certain specifications. This type of game, which is also known as interactive fiction, is based on printing a description of a situation and waiting for the user to enter a command that specifies their next action. The situation changes based on the user's action and the process begins again (unless the user action triggers a win or loss). In outline form, the operation of a simple game of this type looks like this:

  1. Initialize variables to store the world map and current state of the game
  2. Display information about the player's location
  3. Determine whether the game is supposed to end right now (e.g. win or loss); if so, print a message and exit
  4. Wait for the user to enter a command (e.g. "north")
  5. Modify the game state to reflect the action indicated by the user command (e.g. change location). If the game rules prohibit the user from performing the requested action, print a message to that effect.
  6. Return to step 2

While most text adventure games have a concept of objects that can be picked up and used for some purposes in the game, the simple game you'll write for this project lacks these features. It will only support actions that move the user from one place to another.

Before diving into the details, here is a sample session of gameplay to give you a sense of the game format:

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> west
You can't go that way.

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> east
This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.
> north
This is the escalator area.  To the south is the main hallway.  Up the escalator is the second floor.
> up
You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.
> west
You aren't ready to go there yet.

You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.
>

Notice in this transcript the user is not able to enter a room. That's because that room is the final goal (entering wins the game), but some other rooms in the game world are marked as "required to visit" before it is possible to win. Because the player hasn't visited those rooms in this transcript, they cannot yet win.

In the story of this game, we can see that the player is meant to find their laptop and charger before going to the study session. The sample map (map1) contains two rooms that, when visited, explain that the user has located one of these objects.

World map and room characteristics

The game takes place in a world that consists of rooms. Each room has a short name that is a string (e.g. "bookstore", "hallway") as well as the following characteristics:

  • description: a string describing this room and its exits
  • exits: the directions in which the player can move out of this room, and where they lead
  • roomtype: One of the strings "normal", "win", "lose", indicating whether this is a normal room, a location that triggers winning the game (if they have visited all required rooms), or a location that triggers losing the game
  • mustvisit: a boolean indicating whether or not it is required that the player visit this room before they can win the game

While your program is running, the entire world map will be stored in a dictionary which has the short names of rooms as its keys. The value associated with one of these keys is another dictionary that contains the data about that room. The keys of each of these inner dictionaries are "description", "exits", "roomtype", and "mustvisit", with values specifying the data listed above. Here is an example showing what such a world map looks like as a Python dictionary literal:

In [43]:
# The code below is in the starter pack as map1.py
worldmap = {
    "start": {
        "description": "Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.",
        "exits": {"north": "panda", "south": "cardoffice", "east": "hallway"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "panda": {
        "description": "You are in Panda Express, surrounded by students eating lo mein.  The only exit is to the south.",
        "exits": {"south": "start"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "cardoffice": {
        "description": "You are standing outside the I-card office.  There are at least fifty students standing here, waiting for the office to open so they can get their IDs renewed.  The office is closed and locked.  To the north is the entrance lobby.",
        "exits": {"north": "start"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "hallway": {
        "description": "This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.",
        "exits": {"north": "escalators", "south": "bookstore", "east": "halsted", "west": "start"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "halsted": {
        "description": "You are in the entrance lobby of SCE near Halsted street.  To the east, where you would expect the outer doors to be, there is the event horizon of a gravitational singularity (a black hole).  To the west is the hallway.",
        "exits": {"east": "blackhole", "west": "hallway"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "bookstore": {
        "description": "You are in the UIC bookstore.  Against the back wall (to the south) is an area where computers are sold and repaired.  To the north is the bookstore exit.",
        "exits": {"north": "hallway", "south": "computershop"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "computershop": {
        "description": "As you enter the computer shop, a helpful clerk recognizes you and hands you the laptop they recently repaired for you.  You accept it gratefully.  The rest of the bookstore is to the north.",
        "exits": {"north": "bookstore"},
        "mustvisit": True,
        "roomtype": "normal"
    },
    "blackhole": {
        "description": "You fall into the gravitational singularity.  In time, your study partners wonder what happened to you.",
        "exits": {},
        "mustvisit": False,
        "endgame": True,
        "roomtype": "lose"
    },
    "escalators": {
        "description": "This is the escalator area.  To the south is the main hallway.  Up the escalator is the second floor.",
        "exits": {"south": "hallway", "up": "floor2main"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "floor2main": {
        "description": "You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.",
        "exits": {"west": "seating", "east": "amazon", "down": "escalators"},
        "mustvisit": False,
        "roomtype": "normal"
    },
    "amazon": {
        "description": "You are in the Amazon package pickup room.  You use the code from your email to open a locker and retrieve your charger.  The exit is to the west.",
        "exits": {"west": "floor2main"},
        "mustvisit": True,
        "roomtype": "normal"
    },
    "seating": {
        "description": "You enter the seating area and sit down with your friends from MCS 260.  You open your laptop and plug in your charger.  Together, you and your friends complete the remaining problems from Worksheet 29 in no time.  SUCCESS!",
        "exits": {},
        "mustvisit": False,
        "roomtype": "win"
    }
}

Notice the way the exits from a room are specified: the value associated with "exits" is a dictionary whose keys are directions and values give the new location if the user moves in that direction.

In your final program, you are expected to load the world map from a file and store it in a dictionary. The section Loading the world map below describes how to do this. But as you are first developing your game, you may want to be able to test it before you write the part that will load the world map from a file. One way to do that is to insert a sample map directly into your code. To make this easier, the starter pack contains files with both dictionary literals for pasting into your code (good for use early on), and JSON files that you can use to test loading the world map from a file (as is required in the final version).

To clarify how to work with the world map, here are some code examples (assuming the map is in a variable named worldmap, as above).

In [44]:
# Get the description of the room called "hallway"
worldmap["hallway"]["description"]
Out[44]:
'This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.'
In [45]:
# A list of all the directions the user can go if they are in the room called "escalators" 
worldmap["hallway"]["exits"].keys()
Out[45]:
dict_keys(['north', 'south', 'east', 'west'])
In [46]:
# If the user is in "hallway" and they move "north", what room will they move to?
worldmap["hallway"]["exits"]["north"]
Out[46]:
'escalators'
In [47]:
# Make a list of all the rooms the user is required to enter before they can win the game
[ roomname for roomname in worldmap if worldmap[roomname]["mustvisit"] ]
Out[47]:
['computershop', 'amazon']
In [48]:
# A boolean indicating whether or not the room whose name is stored in `x`
# is one that triggers winning the game.  Here we test it on the room
# called "seating".
x = "seating"
worldmap[x]["roomtype"] == "win"
Out[48]:
True
In [49]:
# Testing the same boolean expression on the room called "hallway"
x = "hallway"
worldmap[x]["roomtype"] == "win"
Out[49]:
False

Precise specification of game operations

The game must load the world map as described in the next section.

Then, it must make a list of all the rooms that the user is required to visit.

A variable should be created to track the user's location, which is initialized to the value "start". (Every world map is required to have a room with this name.)

The game must then enter a loop as follows:

  • Print the description of the current location.
  • If the game has been won or lost by entering this room, exit. (Don't print a special message; a description of the reason for winning or losing is expected to be contained in the room description.)
  • If this is one of the rooms the user needs to enter in order to win, record that this one has been visited.
  • Print the prompt "> " (greater than followed by a single space) and wait for a line of input, which we'll call the command.
  • If the command is not one of the exits of the current room, print You can't go that way. and start the loop again.
  • If the command is the name of an exit of the current room, and if entering the room in that direction would trigger winning the game, check to see whether the user has visited all the required rooms. If not, print You aren't ready to go there yet. and start the loop again.
  • Otherwise, the command is the name of an exit that the user is currently allowed to take. Determine where that exit leads (using the "exits" dictionary of the current room) and make that the new location. Start the loop again.

Loading the world map

When your program is run, it should expect to be given a filename as the first command line argument. This will be the name of a JSON file which your program must read using Python's json module. This file should be read when the program starts, and then closed. Nothing should be written to this file or any other.

As of the date of release of this project description, we haven't discussed loading JSON files yet. That's coming soon. For now, you can just embed the map data directly in adventure.py, e.g. like this:

In [50]:
# pre-map code

worldmap = {
    "start": {
        "description": "Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.",
        "exits": { "north": "panda",
                   "south": "cardoffice",
                   "east": "hallway" },
        "mustvisit": False,
        "roomtype": "normal"
    },
    # OTHER ROOMS GO HERE, OMITTED FOR BREVITY
}

# some more program code

Later, you can write a function load_world_map(fn) that reads the map from filename fn and returns a dictionary, and then replace the structure above with:

In [ ]:
# pre-map code (including definition of load_world_map function)

worldmap = load_world_map(sys.argv[1])

# some more program code

Full transcript of sample game 1a: loss

Here is a transcript of what it should look like when your game is run with sample map 1, and with a certain sequence of movement commands that result in a loss. The command to run this game might be

python adventure.py map1.json

assuming map1.json from the starter pack has been saved in the same directory as adventure.py.

Transcript:

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> west
You can't go that way.

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> down
You can't go that way.

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> east
This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.
> south
You are in the UIC bookstore.  Against the back wall (to the south) is an area where computers are sold and repaired.  To the north is the bookstore exit.
> north
This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.
> east
You are in the entrance lobby of SCE near Halsted street.  To the east, where you would expect the outer doors to be, there is the event horizon of a gravitational singularity (a black hole).  To the west is the hallway.
> north
You can't go that way.

You are in the entrance lobby of SCE near Halsted street.  To the east, where you would expect the outer doors to be, there is the event horizon of a gravitational singularity (a black hole).  To the west is the hallway.
> east
You fall into the gravitational singularity.  In time, your study partners wonder what happened to you.

Full transcript of sample game 1b: win

Here is a transcript of what it should look like when your game is run with sample map 1, and with a certain sequence of movement commands that result in a win. The command to run this game might be

python adventure.py map1.json

assuming map1.json from the starter pack has been saved in the same directory as adventure.py.

Transcript:

Your goal is to get your laptop and charger and meet your friends to work on MCS 260.  You are standing in UIC's Student Center East, near the revolving doors leading to the main quad.  To the east is a hallway leading to the bookstore and the escalators.  To the south is the I-card office.  To the north is Panda Express.  The revolving doors to the west don't seem to be working right now.
> east
This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.
> south
You are in the UIC bookstore.  Against the back wall (to the south) is an area where computers are sold and repaired.  To the north is the bookstore exit.
> south
As you enter the computer shop, a helpful clerk recognizes you and hands you the laptop they recently repaired for you.  You accept it gratefully.  The rest of the bookstore is to the north.
> noth
You can't go that way.

As you enter the computer shop, a helpful clerk recognizes you and hands you the laptop they recently repaired for you.  You accept it gratefully.  The rest of the bookstore is to the north.
> north
You are in the UIC bookstore.  Against the back wall (to the south) is an area where computers are sold and repaired.  To the north is the bookstore exit.
> north
This is the main hallway of SCE, with the escalator to the north, the bookstore to the south, the Halsted lobby to the east, and the quad lobby to the west.
> north
This is the escalator area.  To the south is the main hallway.  Up the escalator is the second floor.
> up
You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.
> west
You aren't ready to go there yet.

You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.
> east
You are in the Amazon package pickup room.  You use the code from your email to open a locker and retrieve your charger.  The exit is to the west.
> west
You are standing at the top of the escalators, on the second floor of SCE.  Dunkin Donuts is here, but it is closed.  To the west is a large seating area.  You can see your friends from MCS 260 waving to you from a table there.  To the east is an Amazon package pickup area.  Down the escalator is the first floor.
> west
You enter the seating area and sit down with your friends from MCS 260.  You open your laptop and plug in your charger.  Together, you and your friends complete the remaining problems from Worksheet 29 in no time.  SUCCESS!

Full transcript of sample game 2a: win

Here is a transcript of what it should look like when your game is run with sample map 2. Sample map 2 is a much simpler map; it has three rooms arranged in an east-west hallway. The user starts in the middle and needs to visit the east room before they can win. To win, they simply enter the west-most room in the hallway.

The command to run this game might be

python adventure.py map2.json

assuming map2.json from the starter pack has been saved in the same directory as adventure.py.

Here is the transcript:

You are in a hallway that runs east-west.  To the west, at the end of the hallway you see a treasure chest held closed by a comically large padlock.  To the east, you can see a sign at the end of the hallway that reads "KEY HERE".
> west
You aren't ready to go there yet.

You are in a hallway that runs east-west.  To the west, at the end of the hallway you see a treasure chest held closed by a comically large padlock.  To the east, you can see a sign at the end of the hallway that reads "KEY HERE".
> east
You reach the east end of the hallway and find a table with a key sitting on top.  You take the key.  The only way to go is west, back to the place where you started.
> west
You are in a hallway that runs east-west.  To the west, at the end of the hallway you see a treasure chest held closed by a comically large padlock.  To the east, you can see a sign at the end of the hallway that reads "KEY HERE".
> west
You go to the treasure chest, use the key to open its padlock, and lift the lid.  Inside is a thick leather-bound book titled "A pictorial tour of the Unicode standard". It has been your dream to own this book ever since lecture 7 of MCS 260.  You take the book and smile with satisfaction.

Starter pack contents

The starter pack contains two sample world maps, each of them in two formats:

  • map1.json - JSON file of sample map 1 (adventure in SCE), which your final program should be able to read
  • map1.py - A dictionary literal containing the same data as map1.json that you can paste into your program during the early stages of development, before you have the JSON loader working.
  • map2.json - JSON file of sample map 2 (boring hallway mission), which your final program should be able to read
  • map2.py - A dictionary literal containing the same data as map2.json that you can paste into your program during the early stages of development, before you have the JSON loader working.

The start pack is a zip file, meaning you download a single file and then extract multiple files from it. Windows, Mac, and Linux all support zip files. If you download the starter pack and cannot figure out how to extract its contents, ask the instructor or TA.

IMPORTANT: Code structure and style requirements

These are NOT the same as project 1, so read carefully.

Required header

The first three lines of your Python program must be comments in the following format:

# MCS 260 Fall 2021 Project 2
# Full Name
# Individual work declaration

In the second line, replace Full Name with your full name.

In the third line, replace Individual work declaration with a single full sentence, written in your own words, explaining that you completed the project individually and followed the rules in the syllabus and project description.

These comments should be immediately followed by a file-level docstring (as explained below).

Use of docstrings

The file adventure.py must have a docstring at the file level (i.e. first statement must be a string literal describing what it does), and each function in this program must have a docstring.

Use of functions

Your program must a function to load the world map from the JSON file. This function should take a filename as its only argument, and return a dictionary.

You may have other functions in your program, but this is not required.

Every function that appears in your code must be called at some point. That is, having unused functions is not permitted.

Use good variable names

You are expected to choose variable names that communicate a variable's purpose in a concise way, but without being too terse. Uninformative low-effort names like intvar or mystring are not acceptable. Single-letter variable names can be used, but sparingly, and should usually be avoided for lists or other complex data structures. Most of the time, a good variable name will be a single word or compound word, like location or nextroom or worldmap, but descriptive multiword names like required_rooms_not_visited are also acceptable.

Use comments to explain, not to disable code

You are encouraged, but not required, to include comments that contain explanatory text.

Do not use comments to disable code that you don't want to run. Instead, remove such code before submitting.

How your project will be graded

Autograder: 55 points

The autograder tests your program and grades it based on its behavior. The following tests will be run:

  1. Was a file called adventure.py submitted? (5 points)
  2. Does the Python interpreter accept the contents of adventure.py as valid Python code? (5 points)
  3. Does adventure.py have a docstring for the file, and a docstring in every function? (5 points)
  4. Operational tests (several tests, 40 points total): The autograder will run your program with several different test maps (which will follow the specifications above, but which are not available to you in advance). For each one, it will test various actions and check whether your program matches the expected behavior. When the autograder is released, you can see the full list of tests by submitting code to it.

Manual review: 5 points

I will review your code and look for adherence to the style guidelines given above, and examine your method of solving the problem. If I see that your program does not do what was requested in this document, but the error was not detected in the automated testing, a deduction may be given at this point. The scores assigned by the autograder will not be changed during manual review unless I discover some kind of intentional wrongdoing (such as an attempt to circumvent or reverse-engineer the autograder's operation).

Hints

Don't open files for writing

Your program only needs to read the world map, so you'll want to open it with mode "r". If you open it for writing, the contents will be erased and you'll need to download or extract the starter pack again.

Don't submit any world maps on Gradescope

Your program needs to load whatever map is specified on the command line. The autograder will test it on several maps, and you won't have access to these in advance. While you definitely want to use sample maps like map1.json and map2.json from the starter pack while you're testing your project, you shouldn't submit these.

Printing long strings is OK

The description of a room in the world map is often a long string. Printing it will typically result in wrapping around to multiple lines in the terminal, with some words split between the end of one line and the start of the next. You don't need to perform any word wrapping or do anything else to make this look nicer.

Always test locally

As you write your project, test it locally on your own computer or the lab computer you use for your work. That is, run it in powershell or Terminal and make sure it works. It is much harder to debug a broken program based solely on reports you get from the autograder compared to working with your local Python interpreter.

There might not be any "mustvisit" rooms

It is valid to have a world map with none of the rooms marked "mustvisit". A map like this will behave more like a maze: To win, the user only needs to find their way to a room that triggers a win.

A reminder

Here is a quote from the syllabus:

"If you are ever tempted to cheat, please do not take the risk! Instead, contact the course staff and discuss what you are struggling with. Extension requests are always given serious consideration while instances of cheating are never tolerated."

We know about all the common ways that students might try to cheat on CS projects.

Making and sharing your own world maps

If you feel inspired, you are welcome to make your own world maps compatible with adventure.py and share them on the course Discord server. (For example, you might make a map with 1000 hexagonal rooms connected in a random way and call it "Nightmare in BSB".)

Revision history

  • 2021-09-22 Initial publication