Part 2: Go places#

In this section we’ll be writing the go command, and the system to go from one place to another.

To start out we’ll add just two places: "home" and the "town-square". A map of the game world would look something like this:

   +--------+      +--------+    | home   |      | town   |    |        *------* square |    |        |      |        |    +--------+      +--------+

You are welcome to add your own places. However, I strongly recommend that you first get your go command working with these two places.

You also may want to keep a map of the game world in a docstring or another text file. Here’s an example of how you might represent a slightly more populated world.

 1"""
 2Text-based adventure game
 3https://alissa-huskey.github.io/python-class/exercises/adventure.html
 4
 5  =========
 6  World Map
 7  =========
 8
 9            market
10              |
11  home -- town square -- woods -- hill
12              |                    |
13            bakery                cave
14
15"""

Part 2.1: Split reply into command and arguments#

This will be the first command that we’ve written that takes an argument. That is, the player needs to type not just go, but also which direction to go like north.

That means we need to split the string that is returned from input() into a list. That way we if the player types go north we can figure out that go is the command, and north is the direction.

Demo

A. Define do_go()#

  1. [ ] Define a do_go() function that takes one argument: args.

  2. [ ] In do_go() print Trying to go: args

B. In main(), in the while loop#

  1. [ ] Strip the value returned from input() using the .strip() method.

    This means if a player enters " quit" or "quit " the program still knows to call do_quit().

  2. [ ] Call .split() on reply and assign it to the variable args.

    Now the args variable will contain a list where each word is an item in the list.

  3. [ ] Use an if statement to check if args is falsy. If it is, continue.

    This means that if a player doesn’t enter anything, the program will ignore it and start the loop over.

  4. [ ] Remove the first item from args using the .pop() method and assign it to the variable command.

    Now command will contain the first word the player entered, and args will contain a list of the remaining commands. If there were no additional words, then args will be an empty list.

  5. [ ] In each clause of the if statement where we check the value of reply, change it to command.

  6. [ ] Add an elif clause that checks if command is equal to "g" or "go". If it is, call do_go() and pass args.

Code
 1"""
 2Text-based adventure game
 3https://alissa-huskey.github.io/python-class/exercises/adventure.html
 4"""
 5
 6ITEMS = {
 7    "elixir": {
 8        "key": "elixir",
 9        "name": "healing elixir",
10        "description": "a magical elixir that will heal what ails ya",
11        "price": -10,
12    },
13    "dagger": {
14        "key": "dagger",
15        "name": "a dagger",
16        "description": "a 14 inch dagger with a double-edged blade",
17        "price": -25,
18    }
19}
20
21def do_shop():
22    """List the items for sale."""
23    print("\nItems for sale.\n")
24
25    for item in ITEMS.values():
26        print(f'{item["name"]:<13}  {item["description"]}')
27
28    print()
29
30def do_quit():
31    """Exit the game."""
32    print("Ok, goodbye.")
33    quit()
34
35def do_go(args):
36    """Move to a different place"""
37    print(f"Trying to go: {args}")
38
39def main():
40    print("Welcome!")
41    while True:
42        reply = input("> ").strip()
43        args = reply.split()
44
45        if not args:
46            continue
47
48        command = args.pop(0)
49
50        if command in ["q", "quit", "exit"]:
51            do_quit()
52
53        elif command in ["shop"]:
54            do_shop()
55
56        elif command in ["g", "go"]:
57            do_go(args)
58
59        else:
60            print("No such command.")
61            continue
62
63        print()
64
65if __name__ == "__main__":
66    main()
67

Part 2.2: Create PLAYER and PLACES#

Now we’ll make global PLACES dictionary which will store information about the different areas in the game.

Like the ITEMS dictionary, PLACES will be a nested dictionary, where the key is a unique identifier for each place, and the value is a dictionary with detailed information about that place.

The keys of the child dictionary will be:

  • "key" – the same thing as the key

  • "name" – a short description

  • "description" – a longer description

  • "east", "west", "north", "south" – the key to the place in that direction. (More on that next.)

Here is an example:

PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        "description": "A cozy stone cottage with a desk and a neatly made bed.",
    },
}

The "north", "south", "east" and "west" values will be used to tell which way the player can go from a particular place and what is in that direction.

For example, going "east" from "home" is the "town-square".

PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        ...
    },
    "town-square": {
        "key": "town-square",
        "name": "The Town Square",
        "west": "home",
        ...
    },
}
PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        ...
    },
    "town-square": {
        "key": "town-square",
        "name": "The Town Square",
        "west": "home",
        ...
    },
}

Likewise, going "west" from the "town-square" is "home".

PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        ...
    },
    "town-square": {
        "key": "town-square",
        "name": "The Town Square",
        "west": "home",
        ...
    },
}
PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        ...
    },
    "town-square": {
        "key": "town-square",
        "name": "The Town Square",
        "west": "home",
        ...
    },
}

We’ll also make a global PLAYER dictionary that will save information about the current game.

For now it will just have one key, the place, which point to where the player is at. In the do_go() function, we will change that value to move the player from one place to another.

PLAYER = {
    "place": "home",
}
PLACES = {
    "home": {
        "key": "home",
        "name": "Your Cottage",
        "east": "town-square",
        ...
    },
    ...
}

A. At the top of adventure.py#

  1. [ ] Create a PLAYER dictionary with the key "place" and the value "home".

  2. [ ] Create a PLACES dictionary where the key is a unique identifier for each place. The value is a dictionary that with information about each place:

    • "key" – the same thing as the key

    • "name" – a short description

    • "description" – a longer description

    • "east", "west", "north", "south" – the key to the place in that

    Add two places, "home" and "town-square".

Code
 1"""
 2Text-based adventure game
 3https://alissa-huskey.github.io/python-class/exercises/adventure.html
 4"""
 5
 6PLAYER = {
 7    "place": "home",
 8}
 9
10PLACES = {
11    "home": {
12        "key": "home",
13        "name": "Your Cottage",
14        "east": "town-square",
15        "description": "A cozy stone cottage with a desk and a neatly made bed.",
16    },
17    "town-square": {
18        "key": "town-square",
19        "name": "The Town Square",
20        "west": "home",
21        "description": (
22            "A large open space surrounded by buildings with a burbling "
23            "fountain in the center."
24        ),
25    },
26}
27
28ITEMS = {

Part 2.3: Write message functions#

We’re going to take a brief interlude here to write a couple of functions for printing messages to the player.

We’ll add a debug() function which will print messages intended for us (the programmer), but only if a global variable indicates that the program is in debug mode.

We’ll also add a error() function which will print an error message.

Demo

A. At the top of adventure.py#

  1. [ ] Add a global variable DEBUG and set it to True

Code
 1"""
 2Text-based adventure game
 3https://alissa-huskey.github.io/python-class/exercises/adventure.html
 4"""
 5
 6DEBUG = True
 7
 8PLAYER = {
 9    "place": "home",
10}
11

B. Define debug()#

  1. [ ] Write a function named: debug with one parameter: message

  2. [ ] In the function, check if DEBUG is True (or truthy)

    • [ ] If so, then print message

    • [ ] Bonus: Print something before it like "DEBUG: ", or "# ", so you can more easily tell that it is a debug message

Code
1def debug(message):
2    """Print a debug message if in debug mode."""
3    if not DEBUG:
4        return
5    print(f"# {message}")

C. Define error()#

  1. [ ] Write a function named: error with one parameter: message

  2. [ ] Print message with something before it like "Error: ".

Code
1def error(message):
2    """Print an error message."""
3    print(f"! Error: {message}\n")

D. Modify do_go()#

  1. [ ] Call debug() instead of print() for the message Trying to go: args

Code
69def do_go(args):
70    """Move to a different place"""
71    debug(f"Trying to go: {args}")

E. In main(), in the while loop#

  1. [ ] At the beginning of the while loop call debug() with the message You are at: PLACE. Replace PLACE with the value in the PLAYER dictionary associated with the "place" key

    This will print a debug message with your current location every time the loop runs.

  2. [ ] After assigning command, use debug() to print command and args.

  3. [ ] Call error() instead of print() for the message No such command.

Code
 73def main():
 74    print("Welcome!")
 75
 76    while True:
 77        debug(f"You are at: {PLAYER['place']}")
 78
 79        reply = input("> ").strip()
 80        args = reply.split()
 81
 82        if not args:
 83            continue
 84
 85        command = args.pop(0)
 86        debug(f"Command: {command!r}, args: {args!r}")
 87
 88        if command in ["q", "quit", "exit"]:
 89            do_quit()
 90
 91        elif command in ["shop"]:
 92            do_shop()
 93
 94        elif command in ["g", "go"]:
 95            do_go(args)
 96
 97        else:
 98            error("No such command.")
 99            continue
100
101        print()

F. Test debug messages#

  1. [ ] Test with DEBUG set to True as well as with DEBUG set to False

Part 2.4: Fill in go command#

In this section we’ll be filling in the rest of the go command.

We’ll need to make sure that we get a valid direction in args. Then look up where we are now from PLAYER["place"] and PLACES; and look up where we want to go by looking at direction (ie "east") key in the current place dictionary.

Finally, we’ll change the current "place" in the PLAYER dictionary, and print the "name" and "description".

Demo

A. Modify do_go(): ensure that the player typed a valid direction#

In this section we’ll be making sure there is at least one item in the args list and that it is a valid direction.

  1. [ ] Check to see if args is falsy, if so:

    • [ ] Use the error() function to print a message saying: "Which way do you want to go?"

    • [ ] return

  2. [ ] assign the first item of the args list to the variable direction and make it lowercase

  3. [ ] Check if direction is one of "north", "south", "east", "west". If not:

    • [ ] Use the error() function to print a message saying: "Sorry, I don't know how to go: direction.")

    • [ ] return

Code
69def do_go(args):
70    """Move to a different place"""
71    debug(f"Trying to go: {args}")
72
73    # make sure the player included a direction
74    if not args:
75        error("Which way do you want to go?")
76        return
77
78    # get the direction from arguments
79    # and make it lowercase
80    direction = args[0].lower()
81
82    # make sure it's a valid direction
83    if direction not in ('north', 'south', 'east', 'west'):
84        error(f"Sorry, I don't know how to go: {direction}.")
85        return

B. Modify do_go(): look up where the player is at#

In this section we’ll be using the PLAYER["place"] to get the current place from the PLACES dictionary, as shown here.

  1. [ ] get the value from PLAYER associated with the "place" key and assign it to old_name

  2. [ ] get the value from PLACES associated with old_name and assign it to old_place

Code
87    # look up where the user is at now
88    old_name = PLAYER["place"]
89    old_place = PLACES[old_name]

C. Modify do_go(): look up what is in that direction from here#

In this section we’ll use the direction (ie. "east") the player wants to go to look up the name of the next place (if any) in the current place dictionary as seen here.

  1. [ ] use the .get() method on old_place to get the value associated with the direction key and assign it to new_name

  2. [ ] Check if new_name is falsy. If so:

    • [ ] Use the error() function to print a message saying: "Sorry, you can't go direction from here.")

    • [ ] return

Code
91    # look up what is in that direction from here
92    new_name = old_place.get(direction)
93
94    # print an error if there is nothing in that direction
95    if not new_name:
96        error(f"Sorry, you can't go {direction} from here.")
97        return

D. Modify do_go(): figure out where we’re going#

Next we’ll get the info for where the player is going from PLACES, or print an error message if it’s not found.

  1. [ ] use the .get() method on PLACES to get the value associated with the new_name key and assign it to new_place

  2. [ ] Check if new_place is falsy. If so:

    • [ ] Use the error() function to print a message saying: "Woops! The information about new_name seems to be missing."

    This will only happen if you made a mistake somewhere in your code. But just in case we do, we want to have a clear error message so we can tell what went wrong.

    • [ ] return

Code
 99    # look up the place information
100    new_place = PLACES.get(new_name)
101
102    # this should never happen if we write the code correctly
103    # but just in case there is no key in PLACES matching
104    # the new name, print an error
105    if not new_place:
106        error(f"Woops! The information about {new_name} seems to be missing.")
107        return

E. Modify do_go(): update the players place and describe it#

Finally, we can now update the PLAYER dictionary to point to the new place name and print the place information.

  1. [ ] In the PLAYER dictionary change value associated with the "place" key to new_name

  2. [ ] Print the values associated with the "name" and "description" keys of the new_place dictionary

Code
109    # move the player to the new place
110    PLAYER["place"] = new_name
111
112    # print information about the new place
113    print(f"\n{new_place['name']}\n")
114    print(new_place["description"], "\n")