patternpythonModerate
Battle a random enemy
Viewed 0 times
randombattleenemy
Problem
I am trying to write a text adventure as my first Python project. For this segment, I wanted a random enemy (part of a class with its own attributes) to appear and then enter a turn based battle with the player until either the enemies health is 0 or the player's health is 0. The player can use one of three moves to attack the enemy. The difficulty to beat the enemy is based on its strength, defence and health attribute.
Feel free to pull it apart and mention improvements or constructive criticisms that you may come up with.
```
import random, time
player_health = 100
class Enemy:
def __init__(self, name, strength, defense, health):
self.name = name
self.strength = strength
self.defense = defense
self.health = health
def attack_enemy(self):
time.sleep(1)
print ("what move would you like to make? (punch, kick or headbutt?")
print("")
answer = input()
if answer == "punch":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
elif answer == "kick":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
elif answer == "headbutt":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
else:
print("you stumble...")
time.sleep(1)
print (self.name + "'s health is now: " + str(int(self.health)))
print("")
return int(self.health)
def enemy_attack(self):
global player_health
time.sleep(1)
print ("The enemy " + self.name + " " + "attacks...")
print("")
player_health = player_health - (self.strength * random.uniform(0.1, 1.4))
player_health = int(player_health)
time.sleep(1)
print ("Yo
Feel free to pull it apart and mention improvements or constructive criticisms that you may come up with.
```
import random, time
player_health = 100
class Enemy:
def __init__(self, name, strength, defense, health):
self.name = name
self.strength = strength
self.defense = defense
self.health = health
def attack_enemy(self):
time.sleep(1)
print ("what move would you like to make? (punch, kick or headbutt?")
print("")
answer = input()
if answer == "punch":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
elif answer == "kick":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
elif answer == "headbutt":
self.health = self.health - (random.randint(1,100)/(random.uniform(0,1)* self.defense))
self.health = int(self.health)
else:
print("you stumble...")
time.sleep(1)
print (self.name + "'s health is now: " + str(int(self.health)))
print("")
return int(self.health)
def enemy_attack(self):
global player_health
time.sleep(1)
print ("The enemy " + self.name + " " + "attacks...")
print("")
player_health = player_health - (self.strength * random.uniform(0.1, 1.4))
player_health = int(player_health)
time.sleep(1)
print ("Yo
Solution
A positive to begin with - your code generally follows the style guide, which is an excellent start. Some docstrings would be nice, though (I have also omitted these, but they are generally a good idea). Also, your imports should really be on separate lines:
Having a
Note that all
The
The
This isn't perfect, because:
Perhaps you could refactor to solve these problems?
The battle logic is independent of which
Note that there is no need to
Now the overall game becomes:
Note the use of
import random
import timeHaving 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 NotImplementedErrorNote 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 methods into separate classes, which makes much more sense (the use of self is currently a bit confusing).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))
else:
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__, and the use of in to check the user's input.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
Playerrelies onotherhaving adefenseattribute, so twoPlayers can't attack each other; and
healthcan fall below zero, which doesn't make much sense.
Perhaps you could refactor to solve these problems?
The battle logic is independent of which
Characters 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:
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), Enemy("Wolf", 20, 10, 100),
Enemy("Lion", 30, 20, 100), Enemy ("Dragon", 40, 30, 130)]
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 Characters in other scripts more easily), and the line break in enemies to keep line lengths reasonable.Code Snippets
import random
import timeclass Character:
def __init__(self, health):
self.health = health
def attack(self, other):
raise NotImplementedErrorclass 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))
else:
print("you stumble...")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))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))Context
StackExchange Code Review Q#60571, answer score: 15
Revisions (0)
No revisions yet.