├── .gitignore ├── FAQ.md ├── README.md ├── examples ├── guessing_game.py ├── history.txt └── rock_paper_scissors.py ├── object_reference_hints.md └── src ├── adv.py ├── player.py └── room.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.pyc 3 | __pycache__ -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | [Refer to the FAQ in Intro-Python-I](https://github.com/BloomInstituteOfTechnology/Intro-Python-I/blob/master/FAQ.md). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro to Python II 2 | 3 | Up to this point, you've gotten your feet wet by working on a bunch of small Python programs. In this module, we're going to continue to solidify your Python chops by implementing a full-featured project according to a provided specification. 4 | 5 | 6 | ## What We're Building 7 | [What's an Adventure Game? ![vid](https://tk-assets.lambdaschool.com/7928cdb4-b8a3-45a6-b231-5b9d1fc1e002_ScreenShot2019-03-22at5.47.28PM.png)](https://youtu.be/WaZccFqJUT8) 8 | 9 | 10 | ## Goals 11 | 12 | * Put your Python basics into practice by implementing a text adventure game 13 | 14 | * Practice writing code that conforms to a specification 15 | 16 | 17 | ## MVP 18 | 19 | ### Day 1 MVP 20 | 21 | * Create the input command parser in `adv.py` which allows the program to receive player input and commands to move to rooms 22 | in the four cardinal directions. 23 | * Fill out Player and Room classes in `player.py` and `room.py` 24 | 25 | ### Day 2 MVP 26 | 27 | * Make rooms able to hold multiple items 28 | * Make the player able to carry multiple items 29 | * Add items to the game that the user can carry around 30 | * Add `get [ITEM_NAME]` and `drop [ITEM_NAME]` commands to the parser 31 | 32 | ## Specification 33 | 34 | The `/src` directory contains the files `adv.py`, which is where the main logic for the game should live, `room.py`, which will contain the definition of the Room class, and `player.py`, which will contain the definition of the Player class. 35 | 36 | 37 | * Add a REPL parser to `adv.py` that accepts directional commands to move the player 38 | * After each move, the REPL should print the name and description of the player's current room 39 | * Valid commands are `n`, `s`, `e` and `w` which move the player North, South, East or West 40 | * The parser should print an error if the player tries to move where there is no room. 41 | 42 | * Put the Room class in `room.py` based on what you see in `adv.py`. 43 | 44 | * The room should have `name` and `description` attributes. 45 | 46 | * The room should also have `n_to`, `s_to`, `e_to`, and `w_to` attributes 47 | which point to the room in that respective direction. 48 | 49 | * Put the Player class in `player.py`. 50 | * Players should have a `name` and `current_room` attributes 51 | 52 | 53 | * Create a file called `item.py` and add an `Item` class in there. 54 | 55 | * The item should have `name` and `description` attributes. 56 | 57 | * Hint: the name should be one word for ease in parsing later. 58 | 59 | * This will be the _base class_ for specialized item types to be declared 60 | later. 61 | 62 | * Add the ability to add items to rooms. 63 | 64 | * The `Room` class should be extended with a `list` that holds the `Item`s 65 | that are currently in that room. 66 | 67 | * Add functionality to the main loop that prints out all the items that are 68 | visible to the player when they are in that room. 69 | 70 | * Add capability to add `Item`s to the player's inventory. The inventory can 71 | also be a `list` of items "in" the player, similar to how `Item`s can be in a 72 | `Room`. 73 | 74 | * Add a new type of sentence the parser can understand: two words. 75 | 76 | * Until now, the parser could just understand one sentence form: 77 | 78 | `verb` 79 | 80 | such as "n" or "q". 81 | 82 | * But now we want to add the form: 83 | 84 | `verb` `object` 85 | 86 | such as "take coins" or "drop sword". 87 | 88 | * Split the entered command and see if it has 1 or 2 words in it to determine 89 | if it's the first or second form. 90 | 91 | * Implement support for the verb `get` followed by an `Item` name. This will be 92 | used to pick up `Item`s. 93 | 94 | * If the user enters `get` or `take` followed by an `Item` name, look at the 95 | contents of the current `Room` to see if the item is there. 96 | 97 | * If it is there, remove it from the `Room` contents, and add it to the 98 | `Player` contents. 99 | 100 | * If it's not there, print an error message telling the user so. 101 | 102 | * Add an `on_take` method to `Item`. 103 | 104 | * Call this method when the `Item` is picked up by the player. 105 | 106 | * `on_take` should print out "You have picked up [NAME]" when you pick up an item. 107 | 108 | * The `Item` can use this to run additional code when it is picked up. 109 | 110 | * Add an `on_drop` method to `Item`. Implement it similar to `on_take`. 111 | 112 | * Implement support for the verb `drop` followed by an `Item` name. This is the 113 | opposite of `get`/`take`. 114 | 115 | * Add the `i` and `inventory` commands that both show a list of items currently 116 | carried by the player. 117 | 118 | 119 | ## Stretch Goals 120 | 121 | In arbitrary order: 122 | 123 | * Add more rooms 124 | 125 | * Add scoring 126 | 127 | * Subclass items into treasures 128 | 129 | * Add a subclass to `Item` called `LightSource`. 130 | 131 | * During world creation, add a `lamp` `LightSource` to a convenient `Room`. 132 | 133 | * Override `on_drop` in `LightSource` that tells the player "It's not wise to 134 | drop your source of light!" if the player drops it. (But still lets them drop 135 | it.) 136 | 137 | * Add an attribute to `Room` called `is_light` that is `True` if the `Room` is 138 | naturally illuminated, or `False` if a `LightSource` is required to see what 139 | is in the room. 140 | 141 | * Modify the main loop to test if there is light in the `Room` (i.e. if 142 | `is_light` is `True` **or** there is a `LightSource` item in the `Room`'s 143 | contents **or** if there is a `LightSource` item in the `Player`'s contents). 144 | 145 | * If there is light in the room, display name, description, and contents as 146 | normal. 147 | 148 | * If there isn't, print out "It's pitch black!" instead. 149 | 150 | * Hint: `isinstance` might help you figure out if there's a `LightSource` 151 | among all the nearby `Item`s. 152 | 153 | * Modify the `get`/`take` code to print "Good luck finding that in the dark!" if 154 | the user tries to pick up an `Item` in the dark. 155 | 156 | * Add methods to notify items when they are picked up or dropped 157 | 158 | * Add light and darkness to the game 159 | 160 | * Add more items to the game. 161 | 162 | * Add a way to win. 163 | 164 | * Add more to the parser. 165 | 166 | * Remember the last `Item` mentioned and substitute that if the user types 167 | "it" later, e.g. 168 | 169 | ``` 170 | take sword 171 | drop it 172 | ``` 173 | 174 | * Add `Item`s with adjectives, like "rusty sword" and "silver sword". 175 | 176 | * Modify the parser to handle commands like "take rusty sword" as well as 177 | "take sword". 178 | 179 | * If the user is in a room that contains both the rusty sword _and_ silver 180 | sword, and they type "take sword", the parser should say, "I don't know 181 | which you mean: rusty sword or silver sword." 182 | 183 | * Modify the code that calls `on_take` to check the return value. If `on_take` 184 | returns `False`, then don't continue picking up the object. (I.e. prevent the 185 | user from picking it up.) 186 | 187 | * This enables you to add logic to `on_take` to code things like "don't allow 188 | the user to pick up the dirt unless they're holding the shovel. 189 | 190 | * Add monsters. 191 | 192 | * Add the `attack` verb that allows you to specify a monster to attack. 193 | 194 | * Add an `on_attack` method to the monster class. 195 | 196 | * Similar to the `on_take` return value modification, above, have `on_attack` 197 | prevent the attack from succeeding unless the user possesses a `sword` item. 198 | 199 | * Come up with more stretch goals! The sky's the limit! 200 | -------------------------------------------------------------------------------- /examples/guessing_game.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def guessing_game(): 4 | print("Guess the number!") 5 | 6 | secret_number = random.randrange(101) 7 | 8 | while True: 9 | guess = input("Input your guess: ") 10 | 11 | try: 12 | guess = int(guess) 13 | except ValueError: 14 | print("Please enter an integer.") 15 | continue 16 | 17 | print(f"You guessed: {guess}") 18 | 19 | if guess == secret_number: 20 | print("You win!") 21 | break 22 | elif guess < secret_number: 23 | print("Too small!") 24 | else: 25 | print("Too big!") 26 | 27 | if __name__ == '__main__': 28 | guessing_game() -------------------------------------------------------------------------------- /examples/history.txt: -------------------------------------------------------------------------------- 1 | 3,3,0 -------------------------------------------------------------------------------- /examples/rock_paper_scissors.py: -------------------------------------------------------------------------------- 1 | #import module we need 2 | import random 3 | 4 | #file i/o functions for historical results 5 | def load_results(): 6 | text_file = open("history.txt", "r") 7 | history = text_file.read().split(",") 8 | text_file.close() 9 | return history 10 | 11 | def save_results( w, t, l): 12 | text_file = open("history.txt", "w") 13 | text_file.write( str(w) + "," + str(t) + "," + str(l)) 14 | text_file.close() 15 | 16 | #welcome message 17 | results = load_results() 18 | wins = int(results[0]) 19 | ties = int( results[1]) 20 | losses = int(results[2]) 21 | print("Welcome to Rock, Paper, Scissors!") 22 | print("Wins: %s, Ties: %s, Losses: %s" % (wins, ties, losses)) 23 | print("Please choose to continue...") 24 | 25 | 26 | #initialize user, computer choices 27 | computer = random.randint(1,3) 28 | user = int(input("[1] Rock [2] Paper [3] Scissors [9] Quit\n")) 29 | 30 | #gamplay loop 31 | while not user == 9: 32 | #user chooses ROCK 33 | if user == 1: 34 | if computer == 1: 35 | print("Computer chose rock...tie!") 36 | ties += 1 37 | elif computer == 2: 38 | print("Computer chose paper...computer wins :(") 39 | losses += 1 40 | else: 41 | print("Computer chose scissors...you wins :)") 42 | wins += 1 43 | 44 | #user chooses PAPER 45 | elif user == 2: 46 | if computer == 1: 47 | print("Computer chose rock...you win :)") 48 | wins += 1 49 | elif computer == 2: 50 | print("Computer chose paper...tie!") 51 | ties += 1 52 | else: 53 | print("Computer chose scissors...computer wins :(") 54 | losses += 1 55 | 56 | #user chooses SCISSORS 57 | elif user == 3: 58 | if computer == 1: 59 | print("Computer chose rock...computer wins :(") 60 | losses += 1 61 | elif computer == 2: 62 | print("Computer chose paper...you win :)") 63 | wins += 1 64 | else: 65 | print("Computer chose scissors...tie!") 66 | ties += 1 67 | else: 68 | print("Invalid selection. Please try again.") 69 | #print updated stats 70 | print("Wins: %s, Ties: %s, Losses: %s" % (wins, ties, losses)) 71 | 72 | #prompt user to make another selection 73 | print("Please choose to continue...") 74 | #initialize user, computer choices 75 | computer = random.randint(1,3) 76 | user = int(input("[1] Rock [2] Paper [3] Scissors [9] Quit\n")) 77 | 78 | # #game over, save results 79 | save_results(wins, ties, losses) -------------------------------------------------------------------------------- /object_reference_hints.md: -------------------------------------------------------------------------------- 1 | Remember that multiple variables can refer to the same object. 2 | 3 | In the diagram below, there are only 2 `Room` objects, total. (There are more in the game, obviously, but in this diagram, there are 2.) 4 | 5 | There are 5 variables. 3 of them point to the one Room object that is the foyer: 6 | 7 | * `room['foyer']` 8 | * `room['outside'].n_to` 9 | * `player.location` 10 | 11 | The remaining 2 point to the one Room object that is the outside: 12 | 13 | * `room['outside']` 14 | * `room['foyer'].s_to` 15 | 16 | ``` 17 | room['outside'] -> Room("Outside Cave Entrance") 18 | ^ 19 | | 20 | room['foyer'].s_to 21 | 22 | 23 | room['foyer'] -> Room("Foyer") <- player.location 24 | ^ 25 | | 26 | room['outside'].n_to 27 | ``` 28 | 29 | If you want to move the player (who is in the foyer in the diagram) to another room, you just need to reassign that to any variable that points to that other room. 30 | 31 | So if the player is in the foyer and types `s` to go south, we could set: 32 | 33 | ``` 34 | player.location = room['foyer'].s_to # we were in the foyer, then went south 35 | ``` 36 | 37 | and after that, the variable references would look like this, with player location pointing to the outside object: 38 | 39 | ``` 40 | player.location 41 | | 42 | v 43 | room['outside'] -> Room("Outside Cave Entrance") 44 | ^ 45 | | 46 | room['foyer'].s_to 47 | 48 | 49 | room['foyer'] -> Room("Foyer") 50 | ^ 51 | | 52 | room['outside'].n_to 53 | ``` 54 | 55 | _Assigning doesn't copy the object. It just makes another reference to the same object._ 56 | -------------------------------------------------------------------------------- /src/adv.py: -------------------------------------------------------------------------------- 1 | from room import Room 2 | 3 | # Declare all the rooms 4 | 5 | room = { 6 | 'outside': Room("Outside Cave Entrance", 7 | "North of you, the cave mount beckons"), 8 | 9 | 'foyer': Room("Foyer", """Dim light filters in from the south. Dusty 10 | passages run north and east."""), 11 | 12 | 'overlook': Room("Grand Overlook", """A steep cliff appears before you, falling 13 | into the darkness. Ahead to the north, a light flickers in 14 | the distance, but there is no way across the chasm."""), 15 | 16 | 'narrow': Room("Narrow Passage", """The narrow passage bends here from west 17 | to north. The smell of gold permeates the air."""), 18 | 19 | 'treasure': Room("Treasure Chamber", """You've found the long-lost treasure 20 | chamber! Sadly, it has already been completely emptied by 21 | earlier adventurers. The only exit is to the south."""), 22 | } 23 | 24 | 25 | # Link rooms together 26 | 27 | room['outside'].n_to = room['foyer'] 28 | room['foyer'].s_to = room['outside'] 29 | room['foyer'].n_to = room['overlook'] 30 | room['foyer'].e_to = room['narrow'] 31 | room['overlook'].s_to = room['foyer'] 32 | room['narrow'].w_to = room['foyer'] 33 | room['narrow'].n_to = room['treasure'] 34 | room['treasure'].s_to = room['narrow'] 35 | 36 | # 37 | # Main 38 | # 39 | 40 | # Make a new player object that is currently in the 'outside' room. 41 | 42 | # Write a loop that: 43 | # 44 | # * Prints the current room name 45 | # * Prints the current description (the textwrap module might be useful here). 46 | # * Waits for user input and decides what to do. 47 | # 48 | # If the user enters a cardinal direction, attempt to move to the room there. 49 | # Print an error message if the movement isn't allowed. 50 | # 51 | # If the user enters "q", quit the game. 52 | -------------------------------------------------------------------------------- /src/player.py: -------------------------------------------------------------------------------- 1 | # Write a class to hold player information, e.g. what room they are in 2 | # currently. 3 | -------------------------------------------------------------------------------- /src/room.py: -------------------------------------------------------------------------------- 1 | # Implement a class to hold room information. This should have name and 2 | # description attributes. --------------------------------------------------------------------------------