Part 15: Eat & Drink
Contents
Part 15: Eat & Drink#
In this section we will add the eat and drink commands, which may effect the player’s health.
Part 15.1: Add command#
In this section we’ll add the eat and drink commands.
Demo
A. In test_game.py
define test_do_consume_no_args()
#
We’re going to use the do_consume()
function for both the eat and drink
commands. This means that later when we define the do_consume()
function, it
will take two arguments: the action
(either "eat"
, or "drink"
), and
args
(the usual list of words typed by the user).
First we’ll write a parametrized test which we expect to fail. It will have one
parameter action
with the two cases "eat"
and "drink"
.
It should test that when you call do_consume(action, [])
a debug and error
message is printed.
Need help?
[ ]
Import thedo_consume
function.[ ]
Addtest_do_consume()
function with two parameters:capsys
andaction
[ ]
Immediately above thedef
line call@pytest.mark.parametrize()
with the following arguments:A list containing the string
"action"
A list containing the strings:
"eat"
and"drink"
[ ]
Calldo_consume()
with two arguments:action
and an empty list.[ ]
Assign the results ofcapsys.readouterr().out
to the variableoutput
[ ]
Write an assert statement that checks that the debug message"Trying to ACTION: []"
is inoutput
[ ]
Write an assert statement that checks that an error message like"What do you want to ACTION?"
is inoutput
[ ]
Run your tests. They should fail.
Code
6from adventure import (
7 MAX_HEALTH,
8 debug,
9 do_consume,
10 do_drop,
11 do_pet,
12 do_read,
13 error,
14 header,
15 health_change,
16 inventory_change,
17 is_for_sale,
18 place_add,
19 place_has,
20 player_has,
21 wrap,
22 write,
23)
649@pytest.mark.parametrize("action", ["eat", "drink"])
650def test_do_consume_no_args(capsys, action):
651 # WHEN: The player types drink without an item
652 do_consume(action, [])
653
654 output = capsys.readouterr().out
655
656 # THEN: The debug message should be in output
657 assert f"Trying to {action}: []" in output, \
658 "Debug message should be in output"
659
660 # AND: An error message should be printed
661 assert f"What do you want to {action}?" in output, \
662 "User error should be in output"
B. In adventure.py
define do_consume()
#
Now we’ll add the do_consume()
function to our game. Unlike our other do_
functions it will take two arguments: the action
(either "eat"
, or
"drink"
), and args
(the usual list of words typed by the user).
It should print a debug message, then it should check if args
is empty and if
so print an error message then return.
Need help?
[ ]
Add ado_consume()
function with two parameters:action
andargs
.[ ]
In it, use thedebug()
function to print something like"Trying to ACTION: args."
.[ ]
Check ifargs
is falsy. If so, print an error message and return.[ ]
Run your tests again. They should now pass.
do_consume()
739def do_consume(action, args):
740 """Eat or drink something"""
741 debug(f"Trying to {action}: {args}")
742
743 if not args:
744 error(f"What do you want to {action}?")
745 return
C. In adventure.py
modify main()
: add eat and drink#
Finally, add the code in main()
so that when the player types "eat"
or
"drink"
, the do_consume()
function is called.
Need help?
[ ]
Add an elif that checks if command is"eat"
or"drink"
.[ ]
if so, calldo_consume()
and pass thecommand
andargs
.
main()
831 if command in ("q", "quit", "exit"):
832 do_quit()
833
834 elif command in ("shop"):
835 do_shop()
836
837 elif command in ("g", "go"):
838 do_go(args)
839
840 elif command in ("x", "exam", "examine"):
841 do_examine(args)
842
843 elif command in ("l", "look"):
844 do_look()
845
846 elif command in ("t", "take", "grab"):
847 do_take(args)
848
849 elif command in ("i", "inventory"):
850 do_inventory()
851
852 elif command in ("r", "read"):
853 do_read(args)
854
855 elif command == "drop":
856 do_drop(args)
857
858 elif command == "buy":
859 do_buy(args)
860
861 elif command == "pet":
862 do_pet(args)
863
864 elif command in ("eat", "drink"):
865 do_consume(command, args)
866
867 else:
868 error("No such command.")
869 continue
Part 15.2: Is the item in inventory?#
In this section we’ll check to make sure the item is in the player’s inventory.
Demo
A. In test_game.py
define test_do_consume_not_in_inventory()
#
In this section we’ll write a parametrized test which we expect to fail. It will have one
parameter action
with the two cases "eat"
and "drink"
.
The function should test that if the player tries to eat or drink something that is not in inventory an error message will be printed.
Need help?
1. Define a parametrized test_do_consume_not_in_inventory()
function
…
[ ]
Addtest_do_consume_not_in_inventory()
function with two parameters:capsys
andaction
.[ ]
Immediately above thedef
line call@pytest.mark.parametrize()
with the following arguments:The string
"action"
A list that contains the strings
"eat"
and"drink"
2. GIVEN: The player does not have an item in inventory
…
[ ]
Setadventure.PLAYER["inventory"]
to an empty dictionary
3. WHEN: You call do_consume() with that item
…
[ ]
Calldo_consume()
two arguments:action
, and a list containing a string like"something tasty"
[ ]
Assign the results ofcapsys.readouterr().out
to the variableoutput
4. THEN: An error message should be printed
…
[ ]
assert that an error message like"Sorry, you don't have any 'something tasty' to ACTION."
is inoutput
4. Run your tests. They should fail.
test_do_consume_not_in_inventory()
665@pytest.mark.parametrize("action", ["eat", "drink"])
666def test_do_consume_not_in_inventory(capsys, action):
667 # GIVEN: The player does not have an item in inventory
668 adventure.PLAYER["inventory"] = {}
669
670 # WHEN: You call do_consume() with that item
671 do_consume(action, ["something tasty"])
672
673 output = capsys.readouterr().out
674
675 # THEN: An error message should be printed
676 assert f"Sorry, you don't have any 'something tasty' to {action}." in output, \
677 "User error should be in output"
B. In adventure.py
modify do_consume()
: check inventory#
Now we’ll modify do_consume()
function to check that the player has the
current item in inventory. If not an error message will be printed and the
function will return.
Need help?
[ ]
Join all items inargs
into a single string seperated by spaces, and make it lowercase. Assign it to the variablename
.[ ]
Write an if statement that checks if the player has the item. If not:[ ]
Use theerror()
function to print a message like:
"Sorry, you don't have any name to action."
[ ]
return
do_consume()
739def do_consume(action, args):
740 """Eat or drink something"""
741 debug(f"Trying to {action}: {args}")
742
743 if not args:
744 error(f"What do you want to {action}?")
745 return
746
747 # get the item entered by the user and make it lowercase
748 name = " ".join(args).lower()
749
750 # make sure the item is in this place or in the players inventory
751 if not player_has(name):
752 error(f"Sorry, you don't have any {name!r} to {action}.")
753 return
Part 15.3: Is the item consumable?#
In this section we’ll check to make sure the item can be eaten or drunk, based
on if it has a "eat-message"
or "drink-message"
key.
Demo
A. In test_game.py
define test_do_consume_cant_consume()
#
In this section we’ll write a parametrized test which we expect to fail. It
will have one parameter action
with the two cases "eat"
and "drink"
.
The function should test that if the player tries to eat or drink an item that
does not have a "action-message"
key an error message will be printed.
Need help?
1. Define a parametrized test_do_consume_cant_consume()
function
…
[ ]
Addtest_do_consume_cant_consume()
function with two parameters:capsys
andaction
.[ ]
Immediately above thedef
line call@pytest.mark.parametrize()
with the following arguments:The string
"action"
A list that contains the strings
"eat"
and"drink"
2. GIVEN: An item exists with no eat-message or drink-message key
…
[ ]
Create a fake item inadventure.ITEMS
with a key like"something tasty"
. The value should be a dictionary containing:[ ]
the key"name"
and a value like"something tasty"
3. AND: That item is in the player’s inventory
…
[ ]
Add the item to the player’s inventory
4. WHEN: You call do_consume() with that item
…
[ ]
Calldo_consume()
with two arguments:action
, and a list containing the key to your fake item, ie"something tasty"
[ ]
Assign the results ofcapsys.readouterr().out
to the variableoutput
4. THEN: An error message should be printed
…
[ ]
assert that an error message like"Sorry, you can't action 'something tasty'."
is inoutput
4. Run your tests. They should fail.
test_do_consume_cant_consume()
680@pytest.mark.parametrize("action", ["eat", "drink"])
681def test_do_consume_cant_consume(capsys, action):
682 # GIVEN: An item exists with no eat-message or drink-message key
683 adventure.ITEMS["something tasty"] = {
684 "name": "something tasty"
685 }
686
687 # AND: That item is in the player's inventory
688 inventory_change("something tasty")
689
690 # WHEN: You call do_consume() with that item
691 do_consume(action, ["something tasty"])
692
693 output = capsys.readouterr().out
694
695 # THEN: An error message should be printed
696 assert f"Sorry, you can't {action} 'something tasty'." in output, \
697 "User error should be in output"
B. In adventure.py
modify do_consume()
: check item#
Now we’ll modify do_consume()
function to check to make sure the item can be
eaten or drunk, based on if it has a "eat-message"
or "drink-message"
key.
If not an error message will be printed and the function will return.
Need help?
[ ]
Get the item using by callingget_item()
with the argumentname
and assign the result to the variableitem
.[ ]
Write an if statement that checks if the key"action-message"
is in theitem
dictionary. If not:[ ]
Use theerror()
function to print a message like:
"Sorry, you can't action 'name'.'"
[ ]
return
[ ]
Run your tests. They should pass.
do_consume()
752def do_consume(action, args):
753 """Eat or drink something"""
754 debug(f"Trying to {action}: {args}")
755
756 if not args:
757 error(f"What do you want to {action}?")
758 return
759
760 # get the item entered by the user and make it lowercase
761 name = " ".join(args).lower()
762
763 # make sure the item is in this place or in the players inventory
764 if not player_has(name):
765 error(f"Sorry, you don't have any {name!r} to {action}.")
766 return
767
768 # get the item dictionary
769 item = get_item(name)
770
771 # make sure it is an item you can consume
772 if f"{action}-message" not in item:
773 error(f"Sorry, you can't {action} {name!r}.")
774 return
Part 15.4: Modify health_change()
#
In this section we’re going to modify the health_change()
function so that it
returns the amount that health was actually changed, which may be different
than the amount
argument.
This is so that if, for example, the player’s health is at 90
and they
drink a potion that is +20
health, we can tell the player that they
gained 10
health points.
A. In test_game.py
modify test_health_change()
#
In this section, we’ll add a parameter diff
to the parametrization of the
test_health_change()
function. Then we’ll need to save the result of calling
health_change()
and assert that it is equal to diff
.
(You may also want to rename result
to health
, so we can use the variable
name result
for the returned value.)
Need help?
[ ]
Change the nameresult
tohealth
in:
The first argument to
@pytest.mark.parametrize
The parameters to the
test_health_change()
functionThe assert statement
[ ]
Add a parameterdiff
to:
The first argument to
@pytest.mark.parametrize
The parameters to the
test_health_change()
function
[ ]
Add a number fordiff
to each of the parametrization tuples, which should be the difference between their start health and end health.[ ]
Assign the value returned from ofhealth_change()
to the variableresult
[ ]
In theTHEN
section, add: AND: The value returned should be the difference in points \
assert that
result
is equal todiff
Run your tests. They should fail.
test_health_change()
57@pytest.mark.parametrize(
58 "start, amount, health, diff, message", [
59 (50, 10, 60, 10, "a positive number should be added"),
60 (50, -10, 40, -10, "a negative number should be subtracted"),
61 (20, -30, 0, -20, "the min health should be 0"),
62 (90, 20, MAX_HEALTH, 10, f"the max health should be {MAX_HEALTH}"),
63 ]
64)
65def test_health_change(start, amount, health, diff, message):
66 # GIVEN: The player has some health
67 adventure.PLAYER["health"] = start
68
69 # WHEN: You call health_change()
70 result = health_change(amount)
71
72 # THEN: The player health should be adjusted
73 assert adventure.PLAYER["health"] == health, message
74
75 # AND: The value returned should be the difference in points
76 assert result == diff, \
77 "The value returned should be the difference in health"
B. In adventure.py
modify health_change()
: return difference#
Now we’ll modify the health_change()
function to return the difference
between the player’s health before and after it is changed.
Need help?
[ ]
At the beginning of the function, save the value ofPLAYER["health"]
to the variablebefore
.[ ]
At the end of the function return the result of subtractingbefore
fromPLAYER["health"]
health_change()
348def health_change(amount: int) -> int:
349 """Add the (positive or negative) amount to health, but limit to 0-100.
350 Return the actual amount added to player health."""
351
352 # save the current player health
353 before = PLAYER["health"]
354
355 # change the player health
356 PLAYER["health"] += amount
357
358 # don't let health go below zero
359 if PLAYER["health"] < 0:
360 PLAYER["health"] = 0
361
362 # cap health
363 if PLAYER["health"] > MAX_HEALTH:
364 PLAYER["health"] = MAX_HEALTH
365
366 # return the difference
367 return PLAYER["health"] - before
Part 15.5: Print message with delay#
In this section we’re going to print the eat or drink message.
Demo
A. In test_game.py
define test_do_consume()
#
In this section we’ll write a test_do_consume()
function.
The function should test that when a player tries to eat or drink a consumable
item, the "eat-message"
or "drink-message"
is printed.
We’ll parameratize the test with the parameters action
("eat"
” or
"drink
), and item
(a dictionary to add to adventure.ITEMS
).
Tip
You can set adventure.WIDTH
to extra wide to avoid wrapping. That way when
you assert "some string" in ouput
you can be sure that all words are on the
same line.
Need help?
1. Define a parametrized test_do_consume()
function.
…
[ ]
Addtest_do_consume()
function with four parameters:capsys
,action
, anditem
.[ ]
Immediately above thedef
line call@pytest.mark.parametrize()
with the following arguments:The string
"action, item"
A list of tuples with each tuple on its own line, with values for:
action
:"eat"
or"drink"
item
: a dictionary for the fake item to add toadventure.ITEMS
. it should include keys for:"name"
,"health"
, and"eat-message"
or"drink-message"
[ ]
Make two tuples, one for"eat"
and one for"drink"
.
2. GIVEN: An item exists
…
[ ]
Assign the variablename
to the valueitem["name"]
[ ]
Create a fake item inadventure.ITEMS
with the keyname
. The value should be theitem
dictionary (from params).
3. AND: The player has the item in their inventory
…
[ ]
Callinventory_change()
with the argumentname
4. AND: The width is set wide to avoid wrapping
…
[ ]
setadventure.WIDTH
to a large number like200
5. WHEN: You call do_consume() with that item
…
[ ]
calldo_consume
with the argumentsaction
, and a list containing the itemname
.[ ]
assign the varibleoutput
to the results of callingcapsys.readouterr().out
6. THEN: The message contents should be in output
…
[ ]
assign the varibleline
to the first item in theitem[f"{action}-message"] tuple
[ ]
assert thatline
is in output
7. Run your tests. They should fail.
test_do_consume()
704@pytest.mark.parametrize(
705 "action, item",
706 [
707 (
708 "drink",
709 {
710 "name": "atmosphere",
711 "drink-message": (
712 "You drink in the warm and cheerful atmosphere.",
713 ),
714 "health": 5,
715 },
716 ),
717 (
718 "eat",
719 {
720 "name": "your feelings",
721 "eat-message": (
722 "It's the wee hours of the morning,",
723 "and you're eating your feelings.",
724 ),
725 "health": -5,
726 },
727 ),
728 ]
729)
730def test_do_consume(capsys, action, item):
731 # GIVEN: An item exists
732 name = item["name"]
733 adventure.ITEMS[name] = item
734
735 # AND: The player has the item in their inventory
736 inventory_change(name)
737
738 # AND: The width is set wide to avoid wrapping
739 adventure.WIDTH = 200
740
741 # WHEN: You call do_consume() with that item
742 do_consume(action, [name])
743
744 output = capsys.readouterr().out
745
746 # THEN: The message contents should be in output
747 line = item[f"{action}-message"][0]
748 assert f" {line}" in output, \
749 f"The {action}-message should be printed."
B. In adventure.py
modify do_consume()
, at the end#
In this section we’ll modify the do_consume()
function to wrap and print each
item in the "eat-message"
or "drink-message"
and then sleep.
Need help?
[ ]
print a blank line[ ]
Iterate overitem[f"{action}-message"]
with the variablesentence
. In the for loop:[ ]
printsentence
by calling thewrap()
function[ ]
print a blank line[ ]
sleep forDELAY
seconds
[ ]
Run your tests. They should pass.
do_consume()
784def do_consume(action, args):
785 """Eat or drink something"""
786 debug(f"Trying to {action}: {args}")
787
788 if not args:
789 error(f"What do you want to {action}?")
790 return
791
792 # get the item entered by the user and make it lowercase
793 name = " ".join(args).lower()
794
795 # make sure the item is in this place or in the players inventory
796 if not player_has(name):
797 error(f"Sorry, you don't have any {name!r} to {action}.")
798 return
799
800 # get the item dictionary
801 item = get_item(name)
802
803 # make sure it is an item you can consume
804 if f"{action}-message" not in item:
805 error(f"Sorry, you can't {action} {name!r}.")
806 return
807
808 print()
809 for sentence in item[f"{action}-message"]:
810 wrap(sentence)
811 print()
812 sleep(DELAY)
C. In adventure.py
modify ITEMS
and PLACES
#
In this section we’ll add or modify items in ITEMS
to include an
"eat-message"
or "drink-message"
key. Then if needed we’ll add those items
to PLACES
.
ITEMS
163ITEMS = {
164 "elixir": {
165 "key": "elixir",
166 "name": "healing elixir",
167 "description": "a magical elixir that will heal what ails ya",
168 "price": -10,
169 "drink-message": (
170 "You uncork the bottle.",
171 "The swirling green liquid starts to bubble.",
172 "You hesitatingly bring the bottle to your lips...",
173 "then quickly down the whole thing!",
174 "Surprisingly, it tastes like blueberries.",
175 "You feel an odd tingling sensation starting at the top of your head... ",
176 "...moving down your body...",
177 "...down to the tips of your toes.",
178 ),
179 },
180 "dagger": {
181 "key": "dagger",
182 "name": "a dagger",
183 "description": "a 14 inch dagger with a double-edged blade",
184 "price": -25,
185 },
186 "water": {
187 "key": "water",
188 "name": "bottle of water",
189 "can_take": True,
190 "description": (
191 "A bottle of water."
192 ),
193 "drink-message": (
194 "You pull the cork from the waxed leather bottle.",
195 "You take a deep drink of the cool liquid.",
196 "You feel refreshed.",
197 ),
198 },
199 "mushroom": {
200 "key": "mushroom",
201 "name": "a mushroom",
202 "can_take": True,
203 "description": (
204 "A mushroom."
205 ),
206 "eat-message": (
207 "You shove the whole mushroom in your mouth...",
208 "Things start to look swirllllly...",
209 "Your tummy doesn't feel so good.",
210 ),
211 },
212 "desk": {
213 "key": "desk",
214 "name": "a desk",
215 "description": (
216 "A wooden desk with a large leather-bound book open on "
217 "its surface."
218 ),
219 },
220 "book": {
221 "key": "book",
222 "can_take": True,
223 "name": "a book",
224 "description": (
225 "A hefty leather-bound tome open to an interesting passage."
226 ),
227 "title": (
228 "The book is open to a page that reads:"
229 ),
230 "message": (
231 "At the edge of the woods is a cave that is home to a three "
232 "headed dragon, each with a different temperament. ",
233
234 "Legend says that if you happen upon the dragon sleeping, the "
235 "brave may pet one of its three heads. ",
236
237 "Choose the right head and you will be rewarded with great "
238 "fortunes. ",
239
240 "But beware, choose poorly and it will surely mean your doom!",
241 ),
242 },
243 "gems": {
244 "key": "gems",
245 "name": "gems",
246 "description": (
247 "A pile of sparkling gems."
248 ),
249 },
250 "dragon": {
251 "key": "dragon",
252 "name": "a three-headed dragon",
253 "description": (
254 f"A colossal dragon with heads of {', '.join(COLORS[0:-1])}, "
255 f"and {COLORS[2]}."
256 ),
257 },
258}
259
PLACES
85PLACES = {
86 "home": {
87 "key": "home",
88 "name": "Your Cottage",
89 "east": "town-square",
90 "description": "A cozy stone cottage with a desk and a neatly made bed.",
91 "items": ["desk", "book", "water"],
92 },
93 "town-square": {
94 "key": "town-square",
95 "name": "The Town Square",
96 "west": "home",
97 "east": "woods",
98 "north": "market",
99 "description": (
100 "A large open space surrounded by buildings with a burbling "
101 "fountain in the center."
102 ),
103 },
104 "market": {
105 "key": "market",
106 "name": "The Market",
107 "south": "town-square",
108 "items": ["elixir", "dagger"],
109 "can": ["shop", "buy"],
110 "description": (
111 "A tidy store with shelves full of goods to buy. A wooden hand "
112 "painted menu hangs on the wall."
113 ),
114 },
115 "woods": {
116 "key": "woods",
117 "name": "The Woods",
118 "east": "hill",
119 "west": "town-square",
120 "description": (
121 "A dirt road meanders under a canopy of autumn leaves in brilliant "
122 "hues of gold and crimson.",
123
124 "You hear a stream burbling somewhere out of sight. Leaves crunch "
125 "under your feet on the sun dappled forest floor.",
126
127 "You see an ancient moss-covered hollow tree, its gnarled and twisted "
128 "branches looming over you. On the opposite side, a fallen log juts "
129 "partway into the road.",
130 ),
131 "items": ["mushroom"],
132 },
133 "hill": {
134 "key": "hill",
135 "name": "A grassy hill",
136 "west": "woods",
137 "south": "cave",
138 "description": (
139 "A winding path leads up the slope of a grassy hill. The air is "
140 "warm here.",
141 "At the top of the hill, you see that the path continues to the "
142 "down to the south. In that direction you can make out a cave by "
143 "the shore of a lake."
144 ),
145 "items": ["dragon"],
146 },
147 "cave": {
148 "key": "cave",
149 "name": "A cave",
150 "north": "hill",
151 "description": (
152 "Your footsteps echo as you step into the vast cavern.",
153 "Shafts of sunlight slice through the gloom, playing against the "
154 "landscape of glittering treasure.",
155 "Resting atop a mound of gold, a colossal dragon rests curled up snugly. "
156 "Its three enormous heads snore softly, each in turn.",
157 ),
158 "items": ["dragon"],
159 "can": ["pet"],
160 },
161}
162
Part 15.6: Consume#
In this section we’ll make sure that when you eat or drink something it is removed from inventory and your health is changed accordingly.
Demo
A. In test_game.py
modify test_do_consume()
#
In this section we’ll modify the test_do_consume()
function to test that the
player’s health and inventory has changed and that a message is printed to tell
them so.
In order to do so we’ll add two parameters to the test: health
(for the
player’s health after consuming) and message
for the text that will be
printed about the change.
Need help?
1. Modify test_do_consume()
to add parameters for health
and message
.
…
[ ]
In the first argument of@pytest.mark.parametrize()
add the parameters"health, message"
to the end of the string.[ ]
Add values for each tuple:A number like
55
, for the amount of health the player will have after consuming the itemA string like
"lost 5"
for part of the text that the player will see
[ ]
In the definition oftest_do_consume()
add the parametershealth
andmessage
.
2. [ ]
Under GIVEN add : AND: The player has a certain amount of health
…
[ ]
Set adventure.PLAYER["health"]
to a specific number like 50
3. [ ]
Under THEN add : The player’s health should be changed
…
[ ]
Assert that adventure.PLAYER["health"]
equals health
4. [ ]
Under THEN add : The item should be removed from inventory
…
[ ]
Assert that the player does not have name
5. [ ]
Under THEN add : A message about the action take should be printed
…
[ ]
Assert that f"You {message} point"
is not in output
6. [ ]
Run your tests. They should fail.
test_do_consume()
704@pytest.mark.parametrize(
705 "action, item, health, message",
706 [
707 (
708 "drink",
709 {
710 "name": "atmosphere",
711 "drink-message": (
712 "You drink in the warm and cheerful atmosphere.",
713 ),
714 "health": 5,
715 },
716 55,
717 "gained 5",
718 ),
719 (
720 "eat",
721 {
722 "name": "your feelings",
723 "eat-message": (
724 "It's the wee hours of the morning,",
725 "and you're eating your feelings.",
726 ),
727 "health": -5,
728 },
729 45,
730 "lost 5",
731 ),
732 ]
733)
734def test_do_consume(capsys, action, item, health, message):
735 # GIVEN: An item exists
736 name = item["name"]
737 adventure.ITEMS[name] = item
738
739 # AND: The player has the item in their inventory
740 inventory_change(name)
741
742 # AND: The player has a certain amount of health
743 adventure.PLAYER["health"] = 50
744
745 # AND: The width is set wide to avoid wrapping
746 adventure.WIDTH = 200
747
748 # WHEN: You call do_consume() with that item
749 do_consume(action, [name])
750
751 output = capsys.readouterr().out
752
753 # THEN: The message contents should be in output
754 line = item[f"{action}-message"][0]
755 assert f" {line}" in output, \
756 f"The {action}-message should be printed."
757
758 # AND: The player's health should be changed
759 assert adventure.PLAYER["health"] == health, \
760 "The player's health should be changed."
761
762 # AND: The item should be removed from inventory
763 assert not player_has(name), \
764 "The item should be removed from inventory."
765
766 # AND: A message about the action take should be printed
767 assert f"You {message} point" in output, \
768 "A message about the action take should be printed"
B. In adventure.py
modify do_consume()
#
In this section we’ll modify the do_consume()
function so that it removes the
item from inventory, modifies the player’s health, and prints a message about
what happened.
Need help?
[ ]
Callinventory_change()
with the argumentsname
and-1
.[ ]
Callitem.get()
with the arguments"health"
and0
and assign the returned value to the variablehealth
.[ ]
Callhealth_change()
with the argumenthealth
and assign the returned result to the variabledifference
.[ ]
Ifhealth
is greater than zero:[ ]
Print a message like: \"You lost POINTS point(s)."
[ ]
Otherwise, ifhealth
is less than zero:"You gained difference point(s)."
[ ]
Run your tests. They should pass.
do_consume()
787def do_consume(action, args):
788 """Eat or drink something"""
789 debug(f"Trying to {action}: {args}")
790
791 if not args:
792 error(f"What do you want to {action}?")
793 return
794
795 # get the item entered by the user and make it lowercase
796 name = " ".join(args).lower()
797
798 # make sure the item is in this place or in the players inventory
799 if not player_has(name):
800 error(f"Sorry, you don't have any {name!r} to {action}.")
801 return
802
803 # get the item dictionary
804 item = get_item(name)
805
806 # make sure it is an item you can consume
807 if f"{action}-message" not in item:
808 error(f"Sorry, you can't {action} {name!r}.")
809 return
810
811 inventory_change(name, -1)
812 health = item.get("health", 0)
813 difference = health_change(health)
814
815 print()
816 for sentence in item[f"{action}-message"]:
817 wrap(sentence)
818 print()
819 sleep(DELAY)
820
821 if health < 0:
822 write(f"You lost {abs(difference)} point(s).")
823 elif health > 0:
824 write(f"You gained {difference} point(s).")
C. In adventure.py
modify ITEMS
#
In this section we’ll modify any of the item dictionaries in ITEMS
that have
a "eat-message"
or "drink-message"
key to add a "health"
key. The value
should be the number of health points to add or remove.
Play your game and eat or drink the relevant items to test it out!
ITEMS
163ITEMS = {
164 "elixir": {
165 "key": "elixir",
166 "name": "healing elixir",
167 "description": "a magical elixir that will heal what ails ya",
168 "price": -10,
169 "drink-message": (
170 "You uncork the bottle.",
171 "The swirling green liquid starts to bubble.",
172 "You hesitatingly bring the bottle to your lips...",
173 "then quickly down the whole thing!",
174 "Surprisingly, it tastes like blueberries.",
175 "You feel an odd tingling sensation starting at the top of your head... ",
176 "...moving down your body...",
177 "...down to the tips of your toes.",
178 ),
179 "health": 20,
180 },
181 "dagger": {
182 "key": "dagger",
183 "name": "a dagger",
184 "description": "a 14 inch dagger with a double-edged blade",
185 "price": -25,
186 },
187 "water": {
188 "key": "water",
189 "name": "bottle of water",
190 "can_take": True,
191 "description": (
192 "A bottle of water."
193 ),
194 "drink-message": (
195 "You pull the cork from the waxed leather bottle.",
196 "You take a deep drink of the cool liquid.",
197 "You feel refreshed.",
198 ),
199 "health": 5,
200 },
201 "mushroom": {
202 "key": "mushroom",
203 "name": "a mushroom",
204 "can_take": True,
205 "description": (
206 "A mushroom."
207 ),
208 "eat-message": (
209 "You shove the whole mushroom in your mouth...",
210 "Things start to look swirllllly...",
211 "Your tummy doesn't feel so good.",
212 ),
213 "health": -15,
214 },
215 "desk": {
216 "key": "desk",
217 "name": "a desk",
218 "description": (
219 "A wooden desk with a large leather-bound book open on "
220 "its surface."
221 ),
222 },
223 "book": {
224 "key": "book",
225 "can_take": True,
226 "name": "a book",
227 "description": (
228 "A hefty leather-bound tome open to an interesting passage."
229 ),
230 "title": (
231 "The book is open to a page that reads:"
232 ),
233 "message": (
234 "At the edge of the woods is a cave that is home to a three "
235 "headed dragon, each with a different temperament. ",
236
237 "Legend says that if you happen upon the dragon sleeping, the "
238 "brave may pet one of its three heads. ",
239
240 "Choose the right head and you will be rewarded with great "
241 "fortunes. ",
242
243 "But beware, choose poorly and it will surely mean your doom!",
244 ),
245 },
246 "gems": {
247 "key": "gems",
248 "name": "gems",
249 "description": (
250 "A pile of sparkling gems."
251 ),
252 },
253 "dragon": {
254 "key": "dragon",
255 "name": "a three-headed dragon",
256 "description": (
257 f"A colossal dragon with heads of {', '.join(COLORS[0:-1])}, "
258 f"and {COLORS[2]}."
259 ),
260 },
261}
262
Part 15.7: Add health to examine#
In this section we’ll add health to the examine command.
Demo
A. In test_game.py
add test_examine()
#
In this section we’ll add a test for the do_examine()
function if it does not
already exist. Then we’ll modify the test to make sure that the health is
displayed.
Need help? (skip if test_do_examine is defined)
1. Import do_examine
2. Define the test_do_examine()
function and include the capsys
fixture.
…
[ ]
Define test_do_examine()
with the parameter capsys
3. [ ]
GIVEN: An item exists
…
[ ]
Create a dictionary for a fake item and assign it to the variableitem
. The dictionary should include keys for:"name"
,"description"
and"price"
.[ ]
Add theitem
to theadventure.ITEMS
dictionary
4. [ ]
AND: A place has the item
…
[ ]
Add a fake place to the adventure.PLACES
dictionary. It should include
keys for "name"
and "items"
. Make sure to add the fake item to the place.
5. [ ]
AND: The player is that place
…
[ ]
Modify adventure.PLAYER
to put the player in your fake place.
6. [ ]
WHEN: do_examine() is called
…
[ ]
Calldo_examine()
with a list containing the key to your fake item.[ ]
Assign the variableoutput
to the result of callingcapsys.readouterr().out
7. [ ]
THEN: a debug message should be printed
…
[ ]
Assert that a string like "Trying to examine: ['cookie']"
is in output
8. [ ]
AND: The item name should be printed
…
[ ]
Assert that item["name"]
is in output
9. [ ]
AND: The item description should be printed
…
[ ]
Assert that item["description"]
is in output
10. [ ]
AND: The price should be printed
…
[ ]
Assert that a string like "5 gems"
is in output
11. [ ]
Run your tests. They should pass.
Need help?
1. [ ]
Modify: GIVEN: An item exists to add "health"
…
[ ]
Modify your fake item dictionary to include a key for "health"
2. [ ]
Under THEN add : AND: The health points should be printed
…
[ ]
Assert that a string like "+5 health"
is in output
3. [ ]
Run your tests. They should fail.
test_do_examine()
365def test_do_examine(capsys):
366 # GIVEN: An item exists
367 item = {
368 "name": "a cookie",
369 "description": "A chocolate chip cookie.",
370 "health": 5,
371 "price": 5,
372 }
373 adventure.ITEMS["cookie"] = item
374
375 # AND: A place has the item
376 adventure.PLACES["bakery"] = {
377 "name": "bakery",
378 "items": ["cookie"],
379 }
380
381 # AND: The player is that place
382 adventure.PLAYER["place"] = "bakery"
383
384 # WHEN: do_examine() is called
385 do_examine(["cookie"])
386
387 output = capsys.readouterr().out
388
389 # THEN: a debug message should be printed
390 assert "Trying to examine: ['cookie']" in output, \
391 "Debug message should be in output"
392
393 # AND: The item name should be printed
394 assert item["name"] in output, \
395 "The item name should be printed"
396
397 # AND: The item description should be printed
398 assert item["description"] in output, \
399 "The item description should be printed"
400
401 # AND: The price should be printed
402 assert "5 gems" in output, \
403 "The price should be printed"
404
405 # AND: The health points should be printed
406 assert "+5 health" in output, \
407 "The health points should be printed"
B. In adventure.py
modify do_examine()
#
In this section we’ll modify the do_examine()
function to print the health
gained or lost from an item, if any.
In my examine display I have the item price, inventory quantity, and health all on the same line, right aligned.
To accomplish this I put them all in a list named stats
, then joined them
with the |
character, then called .rjust()
on the result to print it.
Depending how you want to format your examine output, you may or may not want
to do it the same way.
Need help?
[ ]
Create an empty list assigned to the variablestats
.[ ]
If the place can"shop"
and the place has the item and the item is for sale:[ ]
Append a string like"Price: PRICE gems"
tostats
.
[ ]
Otherwise, if the item is in the player’s inventory:[ ]
Append a string like"(x QUANTITY)"
tostats
.
[ ]
If the item has a"health"
key:[ ]
Append a string like"+HEALTH health"
tostats
[ ]
If thestats
list is not empty:[ ]
Assign the variabletext
to thestats
list joined by a string like:
" | "
.[ ]
Call the.rjust()
method with the argumentWIDTH - MARGIN
and print it.[ ]
Print a blank line.
[ ]
Run your tests. They should pass.
do_examine()
539def do_examine(args):
540 """Look at an item in the current place."""
541
542 debug(f"Trying to examine: {args}")
543
544 # make sure the player said what they want to examine
545 if not args:
546 error("What do you want to examine?")
547 return
548
549 # get the item entered by the user and make it lowercase
550 name = args[0].lower()
551
552 # make sure the item is in this place or in the players inventory
553 if not (place_has(name) or player_has(name)):
554 error(f"Sorry, I don't know what this is: {name!r}.")
555 return
556
557 # get the item dictionary
558 item = get_item(name)
559
560 # variable for the health/price/inventory line
561 stats = []
562
563 # the price if we're in the market
564 if place_can("shop") and place_has(name) and is_for_sale(item):
565 stats.append(f"Price: {abs(item['price'])} gems")
566
567 # the quantity if the item is from inventory
568 elif player_has(name):
569 stats.append(f"(x{PLAYER['inventory'][name]})")
570
571 # add the health if applicable
572 if "health" in item:
573 stats.append(f"{item['health']:+} health")
574
575 # print the item information
576 header(item["name"].title())
577
578 # right-justify the price/quantity
579 if stats:
580 text = " | " + " | ".join(stats) + " | "
581 print(text.rjust(WIDTH - MARGIN))
582 print()
583
584 wrap(item["description"].capitalize().rstrip(".") + ".")