# Stage 8 - Useability
```{topic} In this lesson you will:
- Improve the code useabilty
```
## Introduction
Our dungeon is complete. We have a fully functioning game, but before we sign-off we need to improve the useability of our program and tidy up our code.
To do this we will:
- Create a **help** command
- Improve the user experience and user interface
- Add a farewell message
- Add some in-code comments
- Remove unused code
- Standardise our white-space
## Help command
We know all the commands we can use because we wrote the code. Someone else might not know what they can say. In addition, if they don't enter one of the commands, the program only says `I don't understand.`, which is far from helpful.
To make life easier for new players, we should create a **help** command which lists all the commands. But how will the player know about the **help** command, without using the **help** command? Simple, we change the catch-all event handler (under the `else:`) to inform the user about the help command.
To implement this, add the highlighted code below to **main.py**:
```{code-block} python
:linenos:
:emphasize-lines: 116-123, 127
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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
'''
# describe the rooms
cavern.describe()
armoury.describe()
lab.describe()
'''
# initialise variables
running = True
current_room = cavern
backpack = []
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
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):
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")
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")
elif command == "backpack":
if backpack == []:
print("It is empty")
else:
print("You have:")
for item in backpack:
print(f"- {item.name.capitalize()}")
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")
elif command == "quit":
running = False
else:
print("Enter 'help' to list the copmmands.")
```
By now this code should be familiar and makes sense to you.
## Improving the UI and UX
Although our program is visually very simple, the user stills interacts with it, which means we need to consider the UI and the UX.
```{admonition} UI and UX
UI stands for User Interface, which is basically what you see on your screen when you use an app or a website. This includes things like buttons, menus, icons, and colors. UI design focuses on making things look good and easy to use.
UX stands for User Experience, which is how you feel when you use an app or a website. It's about how easy it is to use, how it makes you feel, and whether it helps you achieve what you're trying to do. UX design focuses on making things easy and enjoyable to use.
```
We have already addressed some UI and UX problems by adding the **help** command. Play the game and see if you can identify anything else.
Did you notice that after entering a command, the game responds and then quickly describes the room again. It's really easy to loose the command response in this quick action. Let's change that by writing the response and then asking the user to press a key to proceed.
I implement this, add the highlighted code below.
```{code-block} python
:linenos:
:emphasize-lines: 128
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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
'''
# describe the rooms
cavern.describe()
armoury.describe()
lab.describe()
'''
# initialise variables
running = True
current_room = cavern
backpack = []
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
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):
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")
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")
elif command == "backpack":
if backpack == []:
print("It is empty")
else:
print("You have:")
for item in backpack:
print(f"- {item.name.capitalize()}")
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")
elif command == "quit":
running = False
else:
print("Enter 'help' to list the copmmands.")
input("\nPress key to continue")
```
Save the file, **predict** and then **run** the code.
How does that work? Let's **investigate**:
- `input("\nPress key to continue")` → this is a hack. That is we are using `input` in an unconventional way to achieve our desired outcome.
- the normal operation of `input` is to wait for the user response → addresses the pausing of the game
- normally the user enters their response by pressing the Enter key → stops the pausing
- the value the user enters is **not** assigned to a variable, so it just disappears
## Farewell message
When the game ends, it just stops. Whether the player won or lost it just exits the the Shell prompt. To make things a little more polite, we should add an farewell message.
To include a farewell message add the highlighted code below
```{code-block} python
:linenos:
:emphasize-lines: 129
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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
'''
# describe the rooms
cavern.describe()
armoury.describe()
lab.describe()
'''
# initialise variables
running = True
current_room = cavern
backpack = []
# ----- MAIN LOOP -----
while running:
current_room.describe()
command = input("> ").lower()
if command in ["north", "south", "east", "west"]:
current_room = current_room.move(command)
elif command == "talk":
if current_room.character is not None:
current_room.character.talk()
else:
print("There is no one here to talk to")
elif command == "hug":
if current_room.character is not None:
current_room.character.hug()
else:
print("There is no one here to hug")
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):
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")
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")
elif command == "backpack":
if backpack == []:
print("It is empty")
else:
print("You have:")
for item in backpack:
print(f"- {item.name.capitalize()}")
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")
elif command == "quit":
running = False
else:
print("Enter 'help' to list the copmmands.")
input("\nPress key to continue")
print("Thank you for playing Deepest Dungeon")
```
## In-code comments
We already have some in-code comment that helps structure our code, but we have missed much of the main loop. We should add some comment to the main loop to make our code more readable and therefore maintainable.
Let's first start with **main.py**
```{code-block} python
:linenos:
:emphasize-lines: 70, 73, 79, 85, 106, 114, 122, 131, 134
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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
'''
# describe the rooms
cavern.describe()
armoury.describe()
lab.describe()
'''
# 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)
# 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):
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 copmmands.")
input("\nPress key to continue")
print("Thank you for playing Deepest Dungeon")
```
When you create classes your should really have a comment that explains what each method does. If you look at the classes in our **room.py**, **item.py** and **character.py** files you will see that we have already done this.
## Remove unused code
Remember the code in **main.py** that we commented out. Well we no longer need it. Go ahead and **delete** the code highlighted below.
```{code-block} python
:linenos:
:emphasize-lines: 52-57
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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
'''
# describe the rooms
cavern.describe()
armoury.describe()
lab.describe()
'''
# 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)
# 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):
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 copmmands.")
input("\nPress key to continue")
print("Thank you for playing Deepest Dungeon")
```
## Final code
White space is the blank lines between our code. You can use this to help structure your code.
Finalise your code by adjusting it so to look the same as the following code:
### main.py
```{code-block} python
:linenos:
# 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 unknownable 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 bead in woven through his beard."
nigel.conversation = "Well youngan, 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):
current_room.character = None
if Enemy.num_of_enemy == 0:
print("You have slain the enemy. 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' for list of commands")
input("\nPress to continue")
print("Thank you for playing Darkest Dungeon")
```
### room.py
```{code-block} python
:linenos:
# room.py
class Room():
def __init__(self,room_name):
# initialises the room object
self.name = room_name.lower()
self.description = None
self.linked_rooms = {}
self.character = None
self.item = None
def describe(self):
# sends a description of the room to the terminal
print(f"\nYou are in the {self.name}")
print(self.description)
if self.character is not None:
self.character.describe()
if self.item is not None:
self.item.describe()
for direction in self.linked_rooms.keys():
print(f"To the {direction} is the {self.linked_rooms[direction].name}")
def link_rooms(self, room, direction):
# links the provided room, in the provided direction
self.linked_rooms[direction.lower()] = room
def move(self, direction):
# returns the room linked in the given direction
if direction in self.linked_rooms.keys():
return self.linked_rooms[direction]
else:
print("You can't go that way")
return self
```
### character.py
```{code-block} python
:linenos:
# character.py
class Character():
def __init__(self, name):
# initialises the character object
self.name = name
self.description = None
self.conversation = None
def describe(self):
# sends a description of the character to the terminal
print(f"{self.name} is here, {self.description}")
def talk(self):
# send converstation to the terminal
if self.conversation is not None:
print(f"{self.name}: {self.conversation}")
else:
print(f"{self.name} doesn't want to talk to you")
def hug(self):
# the character responds to a hug
print(f"{self.name} doesn't want to hug you")
def fight(self,item):
# the character response to a threat
print(f"{self.name} doesn't want to fight you")
return True
class Friend(Character):
def __init__(self, name):
# initialise the Friend object by calling the character initialise
super().__init__(name)
def hug(self):
# the friend responds to a hug
print(f"{self.name} hugs you back.")
class Enemy(Character):
num_of_enemy = 0
def __init__(self,name):
# initialise the Enemy object by calling the character initialise
super().__init__(name)
self.weakness = None
Enemy.num_of_enemy += 1
def fight(self, item):
# fights enemy with provided item and returns if player survives
if item == self.weakness:
print(f"You strike {self.name} down with {item}.")
Enemy.num_of_enemy -= 1
return True
else:
print(f"{self.name} crushes you. Puny adventurer")
return False
def get_num_of_enemy():
return Enemy.num_of_enemy
```
### item.py
```{code-block} python
:linenos:
# item.py
class Item():
def __init__(self,name):
# initialise the Item object
self.name = name.lower()
self.description = None
def describe(self):
# prints description of item to the terminal
print(f"You see {self.name} in the room. It is {self.description}.")
```
## Final Make
The tutorials are now over. It is time to make this dungeon yours by adding new features. The Extensions Ideas page has some suggestions.
The current code does have a couple of logical errors, but you will have to test it to find out what they are and then try and solve them. Don't forget to use your debugger to help you with this.
Good luck. May your adventure be long and fruitful.