Extension: Player Class
On this page we will learn
- why grouping related features into one class makes a program easier to manage
- how to decide whether a feature should be an attribute or a method
- how to refactor code out of main.py and into a class
- how objects interact by calling each other's methods
Terminology
- inventory – the collection of items a player is carrying in a game, such as the backpack in Deepest Dungeon.
In this extension, we'll reorganise our code by making a Player class. This gives us a player object that can hold all the player's features, like health, items, gear and weapons. The first thing we'll move into it is the player's inventory: the backpack.
Planning
Right now, all the backpack code is in main.py. Before we create the Player class, we need to find that code and decide which parts should move into the class. Let's start with main.py from the end of Stage 8.
| # main.py
from room import Room
from character import Enemy, Friend
from item import Item
# create rooms
cavern = Room("Cavern")
cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
armoury = Room("Armoury")
armoury.description = ("The walls are lined with racks that once held weapons and armour.")
lab = Room("Laboratory")
lab.description = ("A strange odour hangs in a room filled with unknowable contraptions.")
# link rooms
cavern.link_rooms(armoury,"south")
armoury.link_rooms(cavern,"north")
armoury.link_rooms(lab,"east")
lab.link_rooms(armoury,"west")
# create characters
ugine = Enemy("Ugine")
ugine.description = "a huge troll with rotting teeth."
ugine.weakness = "cheese"
nigel = Friend("Nigel")
nigel.description = "a burly dwarf with golden beads woven through his beard."
nigel.conversation = "Well youngun, what are you doing here?"
# add characters to rooms
armoury.character = ugine
lab.character = nigel
# create items
cheese = Item("Cheese")
cheese.description = "super smelly"
chair = Item("Chair")
chair.description = "designed to be sat on"
elmo = Item("Elmo")
elmo.description = "wanting to be tickled"
# add items to rooms
cavern.item = chair
armoury.item = elmo
lab.item = cheese
# initialise variables
running = True
current_room = cavern
backpack = []
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
# move
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
print(f"You travel {command}")
# talk
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
# hug
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
# fight
elif command== "fight":
if current_room.character is not None:
weapon = input("What will you fight with? > ").lower()
available_weapons = []
for item in backpack:
available_weapons.append(item.name)
if weapon in available_weapons:
if current_room.character.fight(weapon):
if isinstance(current_room.character, Enemy):
current_room.character = None
if Enemy.get_num_of_enemy() == 0:
print("You have slain all the enemies. You are victorious!")
running = False
else:
running = False
else:
print(f"You don't have {weapon}")
print(f"{current_room.character.name} strikes you down.")
running = False
else:
print("There is no one here to fight")
# take
elif command == "take":
if current_room.item is not None:
backpack.append(current_room.item)
print(f"You put {current_room.item.name} into your backpack")
current_room.item = None
else:
print("There is nothing here to take")
# backpack
elif command == "backpack":
if backpack == []:
print("It is empty")
else:
print("You have:")
for item in backpack:
print(f"- {item.name.capitalize()}")
# help
elif command == "help":
print("Type which direction you wish to move,")
print("or use one of these commands:")
print("- Talk")
print("- Fight")
print("- Hug")
print("- Take")
print("- Backpack")
# quit
elif command == "quit":
running = False
# incorrect command
else:
print("Enter 'help' to list the commands.")
input("\nPress <Enter> key to continue")
print("Thank you for playing Deepest Dungeon")
|
There are four places where main.py uses the player's backpack:
- line 54 → creates the
backpack variable as an empty list.
- lines 83–86 → check whether the chosen weapon is in the backpack.
- lines 103–104 → add an item to the backpack.
- lines 110–115 → display the contents of the backpack.
If we move these features to a Player class, we need to think about what kind of feature each one is:
- the backpack describes part of the player → attribute
- checking for a weapon in the backpack is an action → method
- adding an item to the backpack is an action → method
- displaying the contents of the backpack is an action → method
So the class diagram looks like this:

Now that we have a plan, let's build it.
Coding
We'll change the code in small steps. This makes it easier to test as we go, and to check that we haven't accidentally created any new bugs.
Create the Player class
Create a new file called player.py in the same folder as the other files, and add the code below.
| # player.py
class Player():
def __init__(self):
self.backpack = []
|
Code explanation
- line 3 → defines the
Player class.
- line 5 → defines the dunder init method, which runs when we create the player.
- line 6 → creates the
backpack attribute as an empty list.
Replace the backpack variable
Now let's create a player object in main.py and use its backpack instead of the backpack variable. Make the highlighted changes below, and delete the old backpack = [] line under # initialise variables.
| # main.py
from room import Room
from character import Enemy, Friend
from item import Item
from player import Player
# create rooms
cavern = Room("Cavern")
cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
armoury = Room("Armoury")
armoury.description = ("The walls are lined with racks that once held weapons and armour.")
lab = Room("Laboratory")
lab.description = ("A strange odour hangs in a room filled with unknowable contraptions.")
# link rooms
cavern.link_rooms(armoury,"south")
armoury.link_rooms(cavern,"north")
armoury.link_rooms(lab,"east")
lab.link_rooms(armoury,"west")
# create characters
ugine = Enemy("Ugine")
ugine.description = "a huge troll with rotting teeth."
ugine.weakness = "cheese"
nigel = Friend("Nigel")
nigel.description = "a burly dwarf with golden beads woven through his beard."
nigel.conversation = "Well youngun, what are you doing here?"
# add characters to rooms
armoury.character = ugine
lab.character = nigel
# create items
cheese = Item("Cheese")
cheese.description = "super smelly"
chair = Item("Chair")
chair.description = "designed to be sat on"
elmo = Item("Elmo")
elmo.description = "wanting to be tickled"
# add items to rooms
cavern.item = chair
armoury.item = elmo
lab.item = cheese
# create player
player = Player()
# initialise variables
running = True
current_room = cavern
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
# move
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
print(f"You travel {command}")
# talk
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
# hug
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
# fight
elif command== "fight":
if current_room.character is not None:
weapon = input("What will you fight with? > ").lower()
available_weapons = []
for item in player.backpack:
available_weapons.append(item.name)
if weapon in available_weapons:
if current_room.character.fight(weapon):
if isinstance(current_room.character, Enemy):
current_room.character = None
if Enemy.get_num_of_enemy() == 0:
print("You have slain all the enemies. You are victorious!")
running = False
else:
running = False
else:
print(f"You don't have {weapon}")
print(f"{current_room.character.name} strikes you down.")
running = False
else:
print("There is no one here to fight")
# take
elif command == "take":
if current_room.item is not None:
player.backpack.append(current_room.item)
print(f"You put {current_room.item.name} into your backpack")
current_room.item = None
else:
print("There is nothing here to take")
# backpack
elif command == "backpack":
if player.backpack == []:
print("It is empty")
else:
print("You have:")
for item in player.backpack:
print(f"- {item.name.capitalize()}")
# help
elif command == "help":
print("Type which direction you wish to move,")
print("or use one of these commands:")
print("- Talk")
print("- Fight")
print("- Hug")
print("- Take")
print("- Backpack")
# quit
elif command == "quit":
running = False
# incorrect command
else:
print("Enter 'help' to list the commands.")
input("\nPress <Enter> key to continue")
print("Thank you for playing Deepest Dungeon")
|
Code explanation
- line 6 → imports the
Player class from player.py.
- line 53 → creates the player object and stores it in
player.
- line 86 → loops through
player.backpack instead of backpack.
- line 106 → adds the item to
player.backpack.
- line 113 → checks whether
player.backpack is empty.
- line 117 → loops through
player.backpack to display each item.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program. Test every command that uses the backpack:
take, backpack and fight.
- It should work exactly as before. Refactoring changes how the code is written, not what it does.
The add_item method
To make our code cleaner, let's move the backpack code into the Player class. We'll start with an add_item method that puts items into the backpack.
In player.py, add the highlighted code below.
| # player.py
class Player():
def __init__(self):
self.backpack = []
def add_item(self, item):
self.backpack.append(item)
print(f"You put {item.name} into your backpack")
|
Code explanation
- line 8 → defines the
add_item method, which takes the item to add.
- line 9 → adds the item to this player's backpack.
- line 10 → tells the player which item they picked up.
Now let's use the method in main.py. In the take command, replace the two lines that append the item and print the message with the highlighted line below.
| # main.py
from room import Room
from character import Enemy, Friend
from item import Item
from player import Player
# create rooms
cavern = Room("Cavern")
cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
armoury = Room("Armoury")
armoury.description = ("The walls are lined with racks that once held weapons and armour.")
lab = Room("Laboratory")
lab.description = ("A strange odour hangs in a room filled with unknowable contraptions.")
# link rooms
cavern.link_rooms(armoury,"south")
armoury.link_rooms(cavern,"north")
armoury.link_rooms(lab,"east")
lab.link_rooms(armoury,"west")
# create characters
ugine = Enemy("Ugine")
ugine.description = "a huge troll with rotting teeth."
ugine.weakness = "cheese"
nigel = Friend("Nigel")
nigel.description = "a burly dwarf with golden beads woven through his beard."
nigel.conversation = "Well youngun, what are you doing here?"
# add characters to rooms
armoury.character = ugine
lab.character = nigel
# create items
cheese = Item("Cheese")
cheese.description = "super smelly"
chair = Item("Chair")
chair.description = "designed to be sat on"
elmo = Item("Elmo")
elmo.description = "wanting to be tickled"
# add items to rooms
cavern.item = chair
armoury.item = elmo
lab.item = cheese
# create player
player = Player()
# initialise variables
running = True
current_room = cavern
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
# move
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
print(f"You travel {command}")
# talk
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
# hug
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
# fight
elif command== "fight":
if current_room.character is not None:
weapon = input("What will you fight with? > ").lower()
available_weapons = []
for item in player.backpack:
available_weapons.append(item.name)
if weapon in available_weapons:
if current_room.character.fight(weapon):
if isinstance(current_room.character, Enemy):
current_room.character = None
if Enemy.get_num_of_enemy() == 0:
print("You have slain all the enemies. You are victorious!")
running = False
else:
running = False
else:
print(f"You don't have {weapon}")
print(f"{current_room.character.name} strikes you down.")
running = False
else:
print("There is no one here to fight")
# take
elif command == "take":
if current_room.item is not None:
player.add_item(current_room.item)
current_room.item = None
else:
print("There is nothing here to take")
# backpack
elif command == "backpack":
if player.backpack == []:
print("It is empty")
else:
print("You have:")
for item in player.backpack:
print(f"- {item.name.capitalize()}")
# help
elif command == "help":
print("Type which direction you wish to move,")
print("or use one of these commands:")
print("- Talk")
print("- Fight")
print("- Hug")
print("- Take")
print("- Backpack")
# quit
elif command == "quit":
running = False
# incorrect command
else:
print("Enter 'help' to list the commands.")
input("\nPress <Enter> key to continue")
print("Thank you for playing Deepest Dungeon")
|
Code explanation
- line 106 → calls the player's
add_item method with the room's item, which adds it to the backpack and prints the message.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program, and check that we can still add items to the backpack.
The display_contents method
Next, let's move the code that shows what's in the backpack. Go back to player.py and add the highlighted code below.
| # player.py
class Player():
def __init__(self):
self.backpack = []
def add_item(self, item):
self.backpack.append(item)
print(f"You put {item.name} into your backpack")
def display_contents(self):
if self.backpack == []:
print("It is empty")
else:
print("You have:")
for item in self.backpack:
print(f"- {item.name.capitalize()}")
|
Code explanation
- line 12 → defines the
display_contents method.
- lines 13–14 → if the backpack is empty, tells the player…
- line 15 → …otherwise…
- lines 16–18 → …prints a heading, then the name of each item with a capital letter.
Then go back to main.py. In the backpack command, replace the six lines under elif command == "backpack": with the highlighted line below.
| # main.py
from room import Room
from character import Enemy, Friend
from item import Item
from player import Player
# create rooms
cavern = Room("Cavern")
cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
armoury = Room("Armoury")
armoury.description = ("The walls are lined with racks that once held weapons and armour.")
lab = Room("Laboratory")
lab.description = ("A strange odour hangs in a room filled with unknowable contraptions.")
# link rooms
cavern.link_rooms(armoury,"south")
armoury.link_rooms(cavern,"north")
armoury.link_rooms(lab,"east")
lab.link_rooms(armoury,"west")
# create characters
ugine = Enemy("Ugine")
ugine.description = "a huge troll with rotting teeth."
ugine.weakness = "cheese"
nigel = Friend("Nigel")
nigel.description = "a burly dwarf with golden beads woven through his beard."
nigel.conversation = "Well youngun, what are you doing here?"
# add characters to rooms
armoury.character = ugine
lab.character = nigel
# create items
cheese = Item("Cheese")
cheese.description = "super smelly"
chair = Item("Chair")
chair.description = "designed to be sat on"
elmo = Item("Elmo")
elmo.description = "wanting to be tickled"
# add items to rooms
cavern.item = chair
armoury.item = elmo
lab.item = cheese
# create player
player = Player()
# initialise variables
running = True
current_room = cavern
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
# move
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
print(f"You travel {command}")
# talk
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
# hug
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
# fight
elif command== "fight":
if current_room.character is not None:
weapon = input("What will you fight with? > ").lower()
available_weapons = []
for item in player.backpack:
available_weapons.append(item.name)
if weapon in available_weapons:
if current_room.character.fight(weapon):
if isinstance(current_room.character, Enemy):
current_room.character = None
if Enemy.get_num_of_enemy() == 0:
print("You have slain all the enemies. You are victorious!")
running = False
else:
running = False
else:
print(f"You don't have {weapon}")
print(f"{current_room.character.name} strikes you down.")
running = False
else:
print("There is no one here to fight")
# take
elif command == "take":
if current_room.item is not None:
player.add_item(current_room.item)
current_room.item = None
else:
print("There is nothing here to take")
# backpack
elif command == "backpack":
player.display_contents()
# help
elif command == "help":
print("Type which direction you wish to move,")
print("or use one of these commands:")
print("- Talk")
print("- Fight")
print("- Hug")
print("- Take")
print("- Backpack")
# quit
elif command == "quit":
running = False
# incorrect command
else:
print("Enter 'help' to list the commands.")
input("\nPress <Enter> key to continue")
print("Thank you for playing Deepest Dungeon")
|
Code explanation
- line 112 → calls the player's
display_contents method to show what's in the backpack.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program, and check that we can still see everything in the backpack.
The check_item_in method
Finally, we need to change how the fight command uses the backpack. This time it's more than a simple swap. We'll make the backpack give us the whole item, not just item.name. This will make it easier later to add things like weapon damage and health points.
In player.py, add the highlighted code below.
| # player.py
class Player():
def __init__(self):
self.backpack = []
def add_item(self, item):
self.backpack.append(item)
print(f"You put {item.name} into your backpack")
def display_contents(self):
if self.backpack == []:
print("It is empty")
else:
print("You have:")
for item in self.backpack:
print(f"- {item.name.capitalize()}")
def check_item_in(self, item_name):
for item in self.backpack:
if item.name == item_name:
return item
return None
|
This code is different, so let's investigate it.
Code explanation
- line 20 → defines the
check_item_in method, which takes the name of the item as a string.
- line 21 → loops through every item object in the backpack.
- line 22 → checks whether this item's name matches the name we're looking for…
- line 23 → …and if it does, returns that item object.
return also ends the method straight away.
- line 24 → returns
None if no item in the backpack has that name.
In main.py, change the fight command to match the highlighted lines below.
| # main.py
from room import Room
from character import Enemy, Friend
from item import Item
from player import Player
# create rooms
cavern = Room("Cavern")
cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
armoury = Room("Armoury")
armoury.description = ("The walls are lined with racks that once held weapons and armour.")
lab = Room("Laboratory")
lab.description = ("A strange odour hangs in a room filled with unknowable contraptions.")
# link rooms
cavern.link_rooms(armoury,"south")
armoury.link_rooms(cavern,"north")
armoury.link_rooms(lab,"east")
lab.link_rooms(armoury,"west")
# create characters
ugine = Enemy("Ugine")
ugine.description = "a huge troll with rotting teeth."
ugine.weakness = "cheese"
nigel = Friend("Nigel")
nigel.description = "a burly dwarf with golden beads woven through his beard."
nigel.conversation = "Well youngun, what are you doing here?"
# add characters to rooms
armoury.character = ugine
lab.character = nigel
# create items
cheese = Item("Cheese")
cheese.description = "super smelly"
chair = Item("Chair")
chair.description = "designed to be sat on"
elmo = Item("Elmo")
elmo.description = "wanting to be tickled"
# add items to rooms
cavern.item = chair
armoury.item = elmo
lab.item = cheese
# create player
player = Player()
# initialise variables
running = True
current_room = cavern
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
# move
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
print(f"You travel {command}")
# talk
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
# hug
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
# fight
elif command== "fight":
if current_room.character is not None:
choice = input("What will you fight with? > ").lower()
weapon = player.check_item_in(choice)
if weapon:
if current_room.character.fight(weapon.name):
if isinstance(current_room.character, Enemy):
current_room.character = None
if Enemy.get_num_of_enemy() == 0:
print("You have slain all the enemies. You are victorious!")
running = False
else:
running = False
else:
print(f"You don't have {choice}")
print(f"{current_room.character.name} strikes you down.")
running = False
else:
print("There is no one here to fight")
# take
elif command == "take":
if current_room.item is not None:
player.add_item(current_room.item)
current_room.item = None
else:
print("There is nothing here to take")
# backpack
elif command == "backpack":
player.display_contents()
# help
elif command == "help":
print("Type which direction you wish to move,")
print("or use one of these commands:")
print("- Talk")
print("- Fight")
print("- Hug")
print("- Take")
print("- Backpack")
# quit
elif command == "quit":
running = False
# incorrect command
else:
print("Enter 'help' to list the commands.")
input("\nPress <Enter> key to continue")
print("Thank you for playing Deepest Dungeon")
|
PRIMM
- Predict what you think will happen. Be specific.
- Run the program. Check that every fight option still works: with the right weapon, the wrong weapon, and a weapon we don't have.
- Time to investigate the code. What does each line do?
Code explanation
- line 84 → stores what the player typed in
choice. We changed the variable name because weapon will now hold an item object.
- line 85 → calls
check_item_in, so weapon holds the item object if it's in the backpack, or None if it isn't.
- line 86 → checks whether
weapon is an item object (truthy) or None (falsy).
- line 87 → passes
weapon.name to the fight method, because fight still expects a string.
- line 96 → prints what the player typed, because when
weapon is None it doesn't have a name.