Skip to main content
added 70 characters in body
Source Link
jonrsharpe
  • 13.9k
  • 2
  • 36
  • 62
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.

if __name__ == '__main__':
    enemies = [Enemy("Boar", 10, 5, 100), Enemy("Wolf", 20, 10, 100),
               Enemy("Lion", 30, 20, 100), Enemy ("Dragon", 40, 30, 130)]
    player = battle(Player()
    battle(player, random.choice(enemies))
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__.

if __name__ == '__main__':
    enemies = [Enemy("Boar", 10, 5, 100), Enemy("Wolf", 20, 10, 100),
               Enemy("Lion", 30, 20, 100), Enemy ("Dragon", 40, 30, 130)]
    player = Player()
    battle(player, random.choice(enemies))
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.

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))
added 214 characters in body
Source Link
jonrsharpe
  • 13.9k
  • 2
  • 36
  • 62

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:

import random
import time

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:

Note that all CharactersCharacters 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).

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))
if __name__ == '__main__':
    enemies = [Enemy("Boar", 10, 5, 100), ...Enemy("Wolf", 20, 10, 100),
               Enemy("Lion", 30, 20, 100), Enemy ("Dragon", 40, 30, 130)]
    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 Characters in other scripts more easily), and the line break in enemies to keep line lengths reasonable.

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:

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.

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)
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 Characters in other scripts more easily.

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:

import random
import time

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:

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 methods into separate classes, which makes much more sense (the use of self is currently a bit confusing).

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))
if __name__ == '__main__':
    enemies = [Enemy("Boar", 10, 5, 100), Enemy("Wolf", 20, 10, 100),
               Enemy("Lion", 30, 20, 100), Enemy ("Dragon", 40, 30, 130)]
    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 Characters in other scripts more easily), and the line break in enemies to keep line lengths reasonable.

Source Link
jonrsharpe
  • 13.9k
  • 2
  • 36
  • 62

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 Players 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 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:
            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 Characters in other scripts more easily.