-
Notifications
You must be signed in to change notification settings - Fork 20
Exercise1EscapeRoom
Assigned | 1/30/2019 |
Due | 2/4/2019 |
Points | 25 |
From Wikipedia:
An escape room, also known as an "escape game", is a physical adventure game in which players solve a series of puzzles and riddles using clues, hints, and strategy to complete the objectives at hand. Players are given a set time limit to unveil the secret plot which is hidden within the rooms. Escape rooms are inspired by "escape-the-room"–style video games. This is also the likely source of their name.[1] Games are set in a variety of fictional locations, such as prison cells, dungeons, and space stations, and usually the various puzzles and riddles themselves follow the theme of the room.
Your assignment is to create a simple Python text game that simulates an escape room experience. We will work through quite a bit of setup in class.
We will be auto-grading the program, so you must implement the escape room according to our specifications. Here is a high-level description of how the player will escape.
- The player will look in a mirror and notice they have a hairpin.
- The player will take the hairpin. This cannot be done without looking in the mirror.
- The player will unlock a chest with the hairpin.
- The player will open the chest. This cannot be done without unlocking the chest.
- The player will get a hammer from the chest. This cannot be done without opening the chest.
- The player will look at the floor and notice a loose board (floorboard).
- The player will pry open the board with the hammer. This cannot be done without looking at the floor and getting the hammer.
- The player will get some (spy) glasses from the hole in the floor.
- The player will wear the glasses.
- The player will look at the door which has a 4-digit pin. If wearing the glasses, the player notices which digits are smudged.
- The player will try combinations of the smudged numbers until she gets the right one.
Again, this will be auto-graded. Part of what we're testing you on is your ability to follow instructions. Your code MUST follow these details EXACTLY.
You must submit your exercise by uploading your program to GitHub in the following location:
<your_repository_root>/src/exercises/ex1/escape_room.py
The file escape_room.py
must have a class called EscapeRoom
with the following methods:
class EscapeRoom:
def start(self):
'''This method initializes the game'''
def command(self, command_string):
'''This command accepts the user's command within the game'''
def status(self):
'''Reports whether the users is "dead", "locked", or "escaped"'''
The start
method is straight-forward. It initializes your game and gets it ready to play. It has no return value. What you choose to put in here is up to you. It simply gives you the option to not put all initialization values in the constructor.
The command
method takes a command string as input. The command string is something like "look mirror" or "get hammer from chest". It returns the game's response string, such as "You don't know how to do that." or "You open the chest."
Every call to command
should decrement an in-game clock. When the clock reaches zero, the user is killed. When this happens, command
still returns the normal output it would have done for the command_string
but appends the following message on a new line:
Oh no! The clock starts ringing!!! After a few seconds, the room fills with a deadly gas...
This means that, even if a player would have otherwise successfully escaped the room, if the clock reaches zero, they die. By way of example, if the command_string
is wear glasses
the response might be ``You are now wearing the glasses." But if that command then decrements the counter to 0, the response is:
You are now wearing the glasses.
Oh no! The clock starts ringing!!! After a few seconds, the room fills with a deadly gas...
And, as stated, if the player manages to get the door open, even though this would normally end the game successfully, if the counter decrements to 0, they would get this:
You open the door.
Oh no! The clock starts ringing!!! After a few seconds, the room fills with a deadly gas...
After the player either escapes or dies, this command returns either "You Escaped!" or "You Died!" depending on the status.
The status
command indicates whether the player is still locked in the room, has escaped, or is dead. If the door is closed and time is greater than 0, it returns "locked", if the door is open and time is greater than 0, it returns "escaped". And, of course, if time is 0, it returns, "dead".
The auto-tester will run by instantiating an object of your EscapeRoom
class and then:
- calling
start()
once - calling
command()
with a series of commands - exiting when
status()
reports that the player is "dead" or "escaped"
In the subsections below, every command is listed along with legal inputs and expected outputs. You can add additional outputs and inputs if you wish, but the following are required.
The look
command should accept two forms as input:
-
look
with no parameters to look at the room -
look <object>
to look at a specific object -
look in <container>
to look inside the chest or the floor board
Looking at the room should produce the following output:
You are in a locked room. There is only one door
and it has a numeric keypad. Above the door is a clock that reads <time>.
Across from the door is a large mirror. Below the mirror is an old chest.
The room is old and musty and the floor is creaky and warped.
Notice that the description must include the current in-game time. Time must start at 100 and every single time command()
is called, this time must be decremented by 1.
Looking at an object results in the following outputs:
Object | Output |
---|---|
door (without glasses) | "The door is strong and highly secured. The door is locked and requires a 4-digit code to open." |
door (with glasses) | "The door is strong and highly secured. The door is locked and requires a 4-digit code to open. But now you're wearing these glasses you notice something! There are smudges on the digits <comma-separated digits> ." |
mirror (before getting the hairpin) | "You look in the mirror and see yourself... wait, there's a hairpin in your hair. Where did that come from?" |
mirror (after getting the hairpin) | "You look in the mirror and see yourself." |
chest | "An old chest. It looks worn, but it's still sturdy." |
floor | "The floor makes you nervous. It feels like it could fall in. One of the boards is loose." |
board (before looking at floor) | "You don't see that here." |
board (before prying) | "The board is loose, but won't come up when you pull on it. Maybe if you pried it open with something." |
board (after prying) | "The board has been pulled open. You can look inside." |
hairpin (before looking in mirror) | "You don't see that here." |
hairpin (after looking in mirror) | "You see nothing special." |
hammer, glasses (before getting them) | "You don't see that here." |
hammer (after getting it) | "You see nothing special." |
glasses (after getting it) | "These look like spy glasses. Maybe they reveal a clue!" |
clock | "You see nothing special." |
Anything else | "You don't see that here." |
A few things to note. First, the door should have a random 4-digit code every time the game is played. This is easy to do. Using the random
module, code = random.randint(0,9999)
. The list of smudged numbers should be comma separated and in sorted order. So, if the random code is 1962, the output would read, "There are smudges on the digits 1, 2, 6, 9." Here's the code for doing this:
code_string = str(code)
while len(code_string) < 4:
code_string = '0' + code_string # prepend zero's (e.g., 0123)
digits = list(set([digit for digit in code_string])) # set removes duplicates
digits.sort() # even though they're characters, they'll sort in the right order
comma_digit_list = ", ".join(digits)
The second note is that, for simplicity, looking at the clock doesn't tell the time. To check the time, calling look without parameters gives the description of the room and the current clock time as shown above.
Finally, there are two containers to look "in." For simplicity, you will "look in board" even though the more correct description should be "look in hole" or something. But "look in board" allows you to treat the thing you are prying open (the board) as the container.
The following outputs are for looking in these containers:
Container | Output |
---|---|
chest (not open) | "It isn't open!" |
chest (with hammer) | "Inside the chest you see: a hammer." |
chest (after getting the hammer) | "Inside the chest you see: ." |
board (not open) | "Although loose, you can't look in the board until it's pried open." |
board (with glasses) | "Inside the board you see: a glasses." |
board (after getting the glasses) | "Inside the board you see: ." |
anything else | "You can't look in that!" |
Obviously "a glasses" is incorrect English and having a period after an empty list doesn't look good either. But this will make things easier if you write code that automatically puts "a" in front of all objects, etc.
The get
command has two forms. One, to get something that's just "loose" in the room, and one that is for getting something out of a container.
-
get object
to get an object from the room -
get object from container
to get an object out of a container
The following outputs should result from trying to get objects from the room:
Object | Output |
---|---|
hairpin (before it's visible) | "You don't see that." |
hairpin (after it's visible) | "You got it." |
hairpin (after you get it) | "You already have that." |
board (before it's visible) | "You don't see that." |
board (after it's visible) | "You can't get that." |
door, clock, mirror, chest, floor | "You can't get that." |
hammer, glasses (after you get it) | "You already have that." |
anything else | "You don't see that." |
The hairpin is in your hair, of course, but we didn't make hair a container, so get hairpin
(after it's visible) is sufficient. Also, you'll never need to say get hammer
because it's in the container and once you have it, you can't get it again.
Here are the outputs for getting an object from a container
Object | Output |
---|---|
hammer (before chest is open) | "It's not open." |
hammer (after chest is open) | "You got it." |
hammer (after getting it) | "You don't see that." |
glasses (before board is open) | "It's not open." |
glasses (after board is open) | "You got it." |
glasses (after getting it) | "You don't see that." |
any object (from object other than chest or board) | "You can't get something out of that!" |
anything else (from chest or board) | "You don't see that." |
A player can get a list of all the item's they've picked up so far. The inventory
command takes no parameters. The output is always:
You are carrying <comma-separated list>.
Because there are so few items and they have to be picked up in a certain order, the outputs are easy to describe. Each of the following items must, by nature of the escape room, be acquired in order.
Before getting the hairpin:
You are carrying a .
After getting the hairpin but before picking up the hammer:
You are carrying a hairpin.
After picking up the hammer but before getting the glasses:
You are carrying a hairpin, a hammer.
Finally, after getting the glasses:
You are carrying a hairpin, a hammer, a glasses.
Notice, it's easy to join strings together using the join
command. For example, we saw already that you could join numbers together with comma's using join.
", ".join([1,2,3,4]) # outputs "1, 2, 3, 4"
"-".join([1,2,3,4]) # outputs "1-2-3-4"
You can also create a list on the fly using a "list comprehension." Rather than explain it, here are a few examples:
[x * 2 for x in [1,2,3,4]] # creates [2,4,6,8]
[s.lower() for s in ["Hi!", "WHATSUP!"]] # creates ["hi!", "whatsup!"]
So, you can also create a list of concatenated strings. So, if you had an inventory of ["hammer", "glasses"], to create a list of "a hammer", "a glasses":
["a {}".format(object) for object in inventory]
The unlock
command will be used to unlock both the chest and the door. The syntax is always
unlock <object> with <unlocker>
The outputs are as follows:
Object | Output |
---|---|
chest (with hairpin) | "You hear a click! It worked!" |
chest (with anything else) | "You don't have a <unlocker> ." |
chest (after unlocking) | "It's already unlocked." |
door (with correct code) | "You hear a click! It worked!" |
door (with incorrect code) | "That's not the right code!" |
door (with less than 4 digits) | "The code must be 4 digits." |
door (with non-numeric code) | "That's not a valid code." |
door (after unlocking) | "It's already unlocked." |
hairpin, board (not visible) | "You don't see that here." |
hammer, glasses (before getting) | "You don't see that here." |
clock, mirror, hairpin, floor, board, hammer, glasses | "You can't unlock that!" |
anything else | "You don't see that here." |
The open
command is used to both open the chest (after unlocking) and open the door (after unlocking). The syntax is always just
open <object>
The outputs are:
Object | Output |
---|---|
chest, door (before unlocking) | "It's locked." |
chest (after unlocking) | "You open the chest." |
chest (after opening) | "It's already open!" |
door (after unlocking) | "You open the door." |
hairpin, board (not visible) | "You don't see that." |
hammer, glasses (before getting) | "You don't see that." |
clock, mirror, hairpin, floor, board, hammer, glasses | "You can't open that!" |
anything else | "You don't see that." |
Note that, after opening the door, the status should change from "locked" to "escaped" UNLESS THIS COMMAND DECREMENTED THE TIMER TO 0! Assuming that the timer is not zero, however, the game should switch to escaped. Because of this, there is no need to provide an output for trying to open the door if the door is already open.
This specialty instruction is just for prying open the board with the hammer. Thus, the only legal instruction is:
pry board with hammer
Outputs are as follows:
Object | Output |
---|---|
board (not yet visible) | "You don't see that." |
board (with hammer before getting hammer) | "You don't have a hammer." |
board (with hammer) | "You use the hammer to pry open the board. It takes some work, but with some blood and sweat, you manage to get it open." |
board (already open) | "It's already pried open." |
board (with anything else in inventory) | "Don't be stupid! That won't work!" |
board (with anything not in inventory) | "You don't have a <tool> ." |
hairpin (not yet visible) | "You don't see that." |
hammer, glasses (before getting) | "You don't see that." |
door, clock, mirror, hairpin, chest, floor, hammer, glasses | "Don't be stupid! That won't work!" |
anything else | "You don't see that." |
This specialty instruction is for wearing the glasses. Proper use is:
wear glasses
Outputs are:
Object | Output |
---|---|
glasses (before getting glasses) | "You don't have a glasses." |
glasses (after getting them) | "You are now wearing the glasses." |
glasses (after wearing them) | "You're already wearing them!" |
anything else in inventory | "You can't wear that!" |
anything else | "You don't have a <object> ." |
Again, make sure your assignment is in the right place in your Github repo:
<your_repository_root>/src/exercises/ex1/escape_room.py
This assignment is meant to be an enjoyable introduction to Python. Primarily, we want to learn about loops, conditional statements, and storing state. If you can't get the whole assignment done, don't despair. The grading is pretty gentle:
- Correct commands logic: 15
- Incorrect commands logic: 5
- Perfect outputs: 5
The correct commands logic means that, if I put in all the right steps, I escape the room. For example, if your program correctly handles the following command strings:
- look mirror
- get hairpin
- unlock chest with hairpin
- open chest
- get hammer from chest
- look floor
- pry board with hammer
- get glasses from board
- wear glasses
- look door
- unlock door with pin-try-1
- unlock door with pin-try-2
- unlock door with pin-try-n
- open door
Notice, the auto-tester will extract the smudged digits from looking at the door with the glasses and then try combinations until it successfully unlocks.
These 15 out of the 25 points can be programmed with very little difficulty.
The next 5 points will be for ensuring that you can't get the hammer until the chest is open, and so forth. This is NOT going to test incorrect command usage, such as get
without any arguments. Only game logic errors.
Most of the previous two tests don't require perfect output formatting. But if you get all the formatting exactly right, you get the last 5 points of the assignment.
If you really get stuck, submit the program anyway and send us an email asking for a manual (non-automatic) review and grading. We'll give you as many points as we can, and a chance to get some points back over the next few assignments. We'll be using the escape room for multiple exercises so you can add in features late for partial credit.
Bottom Line: Don't let this assignment scare you. It's meant to be fun, and we'll give lots of wiggle room as you're learning your way around