Stage 7 - Victory Conditions¶
Introduction¶
We are close to finishing our text-based adventure game. We now have a dungeon in which the user can move around, collect different items, interact with different characters and have the result of those interactions determined by the character type.
In this stage we will look at establishing victory conditions for the game.
We will do this by:
keeping a count of the number of enemies
reducing the enemy count when each enemy is defeated
declare the player victorious when there are no enemies left
Class Diagram¶
Look to our class diagram for this stage and you will notice something new.
The Enemy
class has a new:
num_of_enemy
attributeget_num_of_enemy
method
Notice the num_of_enemy
attribute is underlined. This indicates that this is a class variable. This means that the variable is shared across to all instances of that class. Each instance of that class can access and modify the variable, and any changes will be shared with other classes.
In our example, the num_of_enemy
will be shared with all the Enemy
objects we create in our game.
Count Number of Enemies¶
To keep track of the number of Enemy
objects in the game, we need to add a class variable to the Enemy
class.
So we need to open the character.py file, and add the highlighted code below.
1# character.py
2
3class Character():
4
5 def __init__(self, name):
6 # initialises the character object
7 self.name = name
8 self.description = None
9 self.conversation = None
10
11 def describe(self):
12 # sends a description of the character to the terminal
13 print(f"{self.name} is here, {self.description}")
14
15 def talk(self):
16 # send converstation to the terminal
17 if self.conversation is not None:
18 print(f"{self.name}: {self.conversation}")
19 else:
20 print(f"{self.name} doesn't want to talk to you")
21
22 def hug(self):
23 # the character responds to a hug
24 print(f"{self.name} doesn't want to hug you")
25
26 def fight(self,item):
27 # the character response to a threat
28 print(f"{self.name} doesn't want to fight you")
29 return True
30
31class Friend(Character):
32
33 def __init__(self, name):
34 # initialise the Friend object by calling the character initialise
35 super().__init__(name)
36
37 def hug(self):
38 # the friend responds to a hug
39 print(f"{self.name} hugs you back.")
40
41class Enemy(Character):
42
43 num_of_enemy = 0
44
45 def __init__(self,name):
46 # initialise the Enemy object by calling the character initialise
47 super().__init__(name)
48 self.weakness = None
49 Enemy.num_of_enemy += 1
50
51 def fight(self, item):
52 # fights enemy with provided item and returns if player survives
53 if item == self.weakness:
54 print(f"You strike {self.name} down with {item}.")
55 return True
56 else:
57 print(f"{self.name} crushes you. Puny adventurer")
58 return False
Save the code, and run it to ensure there are no errors.
Investigating the code:
num_of_enemy = 0
→ creates our class variableimportant to note the location and the indent level of class variables:
need to be placed before the
__init__
methodindented once → same level as method definitions
also note that, unlike the other attributes,
num_of_enemy
does start withself
because it is a class variable
Enemy.num_of_enemy += 1
→ increasesnum_of_enemy
by one each time a newEnemy
object is created.since
__init__
runs each time a new enemy is made, the value ofnum_of_enemy
will increase
Now that we are keeping track of the number of enemies, but we can’t see what that number is. So let’s create a method to find out how many enemies are in the dungeon
Still working in character.py, add the highlighted code below:
1# character.py
2
3class Character():
4
5 def __init__(self, name):
6 # initialises the character object
7 self.name = name
8 self.description = None
9 self.conversation = None
10
11 def describe(self):
12 # sends a description of the character to the terminal
13 print(f"{self.name} is here, {self.description}")
14
15 def talk(self):
16 # send converstation to the terminal
17 if self.conversation is not None:
18 print(f"{self.name}: {self.conversation}")
19 else:
20 print(f"{self.name} doesn't want to talk to you")
21
22 def hug(self):
23 # the character responds to a hug
24 print(f"{self.name} doesn't want to hug you")
25
26 def fight(self,item):
27 # the character response to a threat
28 print(f"{self.name} doesn't want to fight you")
29 return True
30
31class Friend(Character):
32
33 def __init__(self, name):
34 # initialise the Friend object by calling the character initialise
35 super().__init__(name)
36
37 def hug(self):
38 # the friend responds to a hug
39 print(f"{self.name} hugs you back.")
40
41class Enemy(Character):
42
43 num_of_enemy = 0
44
45 def __init__(self,name):
46 # initialise the Enemy object by calling the character initialise
47 super().__init__(name)
48 self.weakness = None
49 Enemy.num_of_enemy += 1
50
51 def fight(self, item):
52 # fights enemy with provided item and returns if player survives
53 if item == self.weakness:
54 print(f"You strike {self.name} down with {item}.")
55 return True
56 else:
57 print(f"{self.name} crushes you. Puny adventurer")
58 return False
59
60 def get_num_of_enemy():
61 return Enemy.num_of_enemy
Let’s investigate that method:
def get_num_of_enemy():
→ defines the methodthere is no
self
argument → this method is not tied to a particular instance, but rather the whole class.
return Enemy.num_of_enemy
→ provides the current value ofnum_of_enemy
notice the
Enemy.
→ tells Python this is a class variable of theEnemy
class
Reduce Enemy Count¶
So we have a count of the number of enemies, and we can retrieve that value, now we need to reduce the enemy count when the player defeats an enemy.
The Enemy
class fight
method already has code that is executed when the player defeats the enemy, so we just need to add to that.
Still in character.py go to insert the highlighted line below:
1# character.py
2
3class Character():
4
5 def __init__(self, name):
6 # initialises the character object
7 self.name = name
8 self.description = None
9 self.conversation = None
10
11 def describe(self):
12 # sends a description of the character to the terminal
13 print(f"{self.name} is here, {self.description}")
14
15 def talk(self):
16 # send converstation to the terminal
17 if self.conversation is not None:
18 print(f"{self.name}: {self.conversation}")
19 else:
20 print(f"{self.name} doesn't want to talk to you")
21
22 def hug(self):
23 # the character responds to a hug
24 print(f"{self.name} doesn't want to hug you")
25
26 def fight(self,item):
27 # the character response to a threat
28 print(f"{self.name} doesn't want to fight you")
29 return True
30
31class Friend(Character):
32
33 def __init__(self, name):
34 # initialise the Friend object by calling the character initialise
35 super().__init__(name)
36
37 def hug(self):
38 # the friend responds to a hug
39 print(f"{self.name} hugs you back.")
40
41class Enemy(Character):
42
43 num_of_enemy = 0
44
45 def __init__(self,name):
46 # initialise the Enemy object by calling the character initialise
47 super().__init__(name)
48 self.weakness = None
49 Enemy.num_of_enemy += 1
50
51 def fight(self, item):
52 # fights enemy with provided item and returns if player survives
53 if item == self.weakness:
54 print(f"You strike {self.name} down with {item}.")
55 Enemy.num_of_enemy -= 1
56 return True
57 else:
58 print(f"{self.name} crushes you. Puny adventurer")
59 return False
60
61 def get_num_of_enemy():
62 return Enemy.num_of_enemy
Save the code before we Investigate it:
line
53
→ already determines if the player beats the enemyEnemy.num_of_enemy -= 1
→ reduces the value of the class variable by one.
Check for Victory¶
The player will be victorious when they have defeated all the enemies in the dungeon. When all the enemies have been defeated the Enemy.num_of_emeny
will be 0
. So we need to find the best place to check this.
Let’s look back at main.py. Line 89
is where Python decides if they player wins, so it makes sense to put out num_of_enemy
check around here.
Add the highlighted code below:
1# main.py
2
3from room import Room
4from character import Enemy, Friend
5from item import Item
6
7# create rooms
8cavern = Room("Cavern")
9cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
10
11armoury = Room("Armoury")
12armoury.description = ("The walls are lined with racks that once held weapons and armour.")
13
14lab = Room("Laboratory")
15lab.description = ("A strange odour hangs in a room filled with unknownable contraptions.")
16
17# link rooms
18cavern.link_rooms(armoury,"south")
19armoury.link_rooms(cavern,"north")
20armoury.link_rooms(lab,"east")
21lab.link_rooms(armoury,"west")
22
23
24# create characters
25ugine = Enemy("Ugine")
26ugine.description = "a huge troll with rotting teeth."
27ugine.weakness = "cheese"
28
29nigel = Friend("Nigel")
30nigel.description = "a burly dwarf with golden bead in woven through his beard."
31nigel.conversation = "Well youngan, what are you doing here?"
32
33# add characters to rooms
34armoury.character = ugine
35lab.character = nigel
36
37# create items
38cheese = Item("Cheese")
39cheese.description = "super smelly"
40
41chair = Item("Chair")
42chair.description = "designed to be sat on"
43
44elmo = Item("Elmo")
45elmo.description = "wanting to be tickled"
46
47# add items to rooms
48cavern.item = chair
49armoury.item = elmo
50lab.item = cheese
51
52'''
53# describe the rooms
54cavern.describe()
55armoury.describe()
56lab.describe()
57'''
58
59# initialise variables
60running = True
61current_room = cavern
62backpack = []
63
64# ----- MAIN LOOP -----
65while running:
66 current_room.describe()
67
68 command = input("> ").lower()
69
70 if command in ["north", "south", "east", "west"]:
71 current_room = current_room.move(command)
72 elif command == "talk":
73 if current_room.character is not None:
74 current_room.character.talk()
75 else:
76 print("There is no one here to talk to")
77 elif command == "hug":
78 if current_room.character is not None:
79 current_room.character.hug()
80 else:
81 print("There is no one here to hug")
82 elif command== "fight":
83 if current_room.character is not None:
84 weapon = input("What will you fight with? > ").lower()
85 available_weapons = []
86 for item in backpack:
87 available_weapons.append(item.name)
88 if weapon in available_weapons:
89 if current_room.character.fight(weapon):
90 current_room.character = None
91 if Enemy.get_num_of_enemy() == 0:
92 print("You have slain all the enemies. You are victorious!")
93 running = False
94 else:
95 running = False
96 else:
97 print(f"You don't have {weapon}")
98 print(f"{current_room.character.name} strikes you down.")
99 running = False
100 else:
101 print("There is no one here to fight")
102 elif command == "take":
103 if current_room.item is not None:
104 backpack.append(current_room.item)
105 print(f"You put {current_room.item.name} into your backpack")
106 current_room.item = None
107 else:
108 print("There is nothing here to take")
109 elif command == "backpack":
110 if backpack == []:
111 print("It is empty")
112 else:
113 print("You have:")
114 for item in backpack:
115 print(f"- {item.name.capitalize()}")
116 elif command == "quit":
117 running = False
118 else:
119 print("I don't understand.")
Investigating that code:
line
89
→ already established that our new code will only run when the player defeats an enemyif Enemy.get_num_of_enemy() == 0:
→ checks if the class variable is0
print("You have slain all the enemies. You are victorious!")
→ displays a victory messagerunning = False
→ readies to finish the game by exiting the main loop.
Testing¶
Now time to test our code.
Predict what you think will happen, then run your code. Make sure that you test that the player wins when they have defeated all the enemies.