Having a global
is generally a bad sign. Why not make your Player
a class too? This would be very similar to Enemy
, so we can do some inheritance:
class Character:
def __init__(self, health):
self.health = health
def attack(self, other):
raise NotImplementedError
Note that all Characters
will have health
and be able to attack
, although at this point we don't supply an implementation for the latter. This separates the enemy_attack
and attack_enemy
into separate classes, which makes much more sense.
class Player(Character):
def __init__(self, health=100):
super().__init__(health)
def attack(self, other):
answer = input("What move would you like to make (punch, kick or headbutt)? ")
if answer.lower() in ('punch', 'kick', 'headbutt'):
other.health -= int(random.randint(1, 100) /
(random.uniform(0, 1) * other.defense))
print("you stumble...")
The Player
will only have health
, and its attack
is based on user input. Note the use of super
to call the parent __init__
.
class Enemy(Character):
def __init__(self, name, strength, defense, health):
super().__init__(health)
self.name = name
self.strength = strength
self.defense = defense
def attack(self, other):
print("The {0.name} attacks...".format(self))
other.health -= int(self.strength * random.uniform(0.1, 1.4))
The Enemy
has some additional attributes, and the attack is entirely random. Note the use of str.format
to provide output.
This isn't perfect, because:
- a
Player
relies on other
having a defense
attribute, so two Player
s can't attack each other; and
health
can fall below zero, which doesn't make much sense.
Perhaps you could refactor to solve these problems?
The battle logic is independent of which Character
s are involved, so should become a separate function rather than an instance method:
def battle(player, enemy):
print ("An enemy {0.name} appears...".format(enemy))
# Combat loop
while player.health > 0 and enemy.health > 0:
player.attack(enemy)
print("The health of the {0.name} is now {0.health}.".format(enemy))
if enemy.health <= 0:
break
enemy.attack(player)
print("Your health is now {0.health}.".format(player))
# Display outcome
if player.health > 0:
print("You killed the {0.name}.".format(enemy)
elif enemy.health > 0:
print("The {0.name} killed you.".format(enemy)
Note that there is no need to break
for the player.health
, as the loop will end there automatically.
Now the overall game becomes:
if __name__ == '__main__':
enemies = [Enemy("Boar", 10, 5, 100), ...]
player = Player()
battle(player, random.choice(enemies))
Note the use of if __name__ == '__main__'
to allow e.g. import
elsewhere without running the loop (so you can use the Character
s in other scripts more easily.