3
\$\begingroup\$

I took a crack at making a Pokemon-style text battle game in Python 3 to practice object-oriented programming a bit, inspired this project on Reddit, but I did not follow every instruction.

import random


Type_Monster=['ice','fire','grass']


Possible_Monster_name=['Mulu','Lume','Manda','Bomna','Kasa','Kama','Mutswe','Mako','Kela','Mate','Kasa','Mula','Matu','Kachi','Moba','Kala','Koleo','Meba','Mufo','Ilis','Sila','Sidab','Nannu','Sili','Lili','Ayabil','Meta','Nanna','Anum', 'Trump', 'Bot', 'Nevergonnagiveyouup','Bernie', "Hilliary", 'life', 'Baka', 'Kald','Aston','Óinthu','Roo','Wolftrarg', 'Stark','Bojack','Rick','Ci','Yourmom','Sartre','Nean','Xōtez','Tōxo','Darkmelswannan','Lannister','Greyjoy','Littlefinger','Spider','San','Lala','What','Sorry']

class Monsters:
  'class for monsters that fight the player'
  def __init__(self,name,health,types,defense,offense,quickness):
    '''initalizes the health, types, defense point, offense point and quickness point.'''
    Monsters.name=name
    Monsters.health=health
    Monsters.types=types
    Monsters.defense=defense
    Monsters.offense=offense 
    Monsters.quickness=quickness



class Player:
  def __init__(self,name,health,types,defense,offense,quickness,experience=0):
    '''initilzies the health, types, defense point, offense point and quickness point.'''
    Player.name=name
    Player.health=health
    Player.types=types
    Player.defense=defense
    Player.offense=offense 
    Player.quickness=quickness
    self.experience=experience
  def eat(self,food):
    ''''method that will be called if the player eats something. The player can either eat good potion or suicide pill'''
    if (food=='good_potion'):
      self.health=self.health+10
    if (food=='suicide_pill'):
      self.health=self.health-10


class Skills:
  '''class on skills, which consist of strong attacks, weak attacks, and heals'''
  def __init__(self,strong_attack_name,weak_attack_name,heal_name):
    '''this function lets the player initlize the name for their skills, which are strong attack, weak attack, heal, and dodge .'''
    Skills.strong_attack_name=strong_attack_name
    Skills.weak_attack_name=weak_attack_name
    Skills.heal_name=heal_name

  def strong_attack(self,offense):
    random_number_list=[1,1,1,1,2,2,2,2,2,2]
    maximum=offense*0.50 
    if random.choice(random_number_list)==1:
      return random.uniform(1,maximum)
    else:
      return 0
  def weak_attack(self,offense):
    random_number_list=[1,1,1,2,2,2,2,2,2,2]
    maximum=offense*0.1 
    if random.choice(random_number_list)==2:
      return random.uniform(1,maximum)
    else:
      return 0
  def heal(self,defense):
    random_number_list=[1,1,1,1,2,2,2,2,2,2]
    maximum=defense*0.2
    if random.choice(random_number_list)==1:
      return random.uniform(1,maximum)
    else:
      return 0 
  def dodge(self,quickness):
    if quickness<=10:
      if random.choice([1,1,2,2,2,2,2,2,2,2,2,2])==1:
        return True
      else:
        return False
    if quickness>10 and quickness<=20:
      if random.choice([1,1,1,1,2,2,2,2,2,2,2])==1:
        return True
      else:
        return False
    if quickness<20 and quickness<=30:
      if random.choice([1,1,1,1,1,1,2,2,2,2])==1:
        return True
      else:
        return False
    if quickness>30:
      if random.choice([1,1,1,1,1,1,1,1,2,2])==1:
        return True
      else:
        return False



print ("Welcome to this game. Please note that you have to type in the correct number when distributing your point. Also, there can be no typo when you are leveling up. Lastly, you can enter help during battle to see your opponent's stats. If you type in eat, you'll either take good potion to recover 10 health or take suicide pill to lose 10 health.")

names=input("enter your name here: ")

typess=input("enter your type here, there is ice, fire, and grass types: ")
while typess!='ice' and typess!='fire' and typess!='grass':
  typess=input("please try again: ")

offenses=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your offense point: "))

defenses=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your defense point: "))

quick=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your quickness point: "))

while offenses+defenses+quick>60:
  offenses=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your offense point: "))

  defenses=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your defense point: "))

  quick=int(input("Distribute your points between defense, offense and quickness. The total points can no excede 60. Type in your quickness point: "))

names=Player(names,20,typess,defenses,offenses,quick)

Monster=Monsters(random.choice(Possible_Monster_name)+'mon',random.randint(1,40),random.choice(Type_Monster),random.randint(1,40),random.randint(1,40),random.randint(1,40))


strong_attack=input("Name your strong_attack here: ")
weak_attack=input("Name your weak attack here: ")
healing=input("Name your healing skill here: ")


Player_Skills=Skills(strong_attack,weak_attack,healing)  


computer_turn=True
human_turn=False
gameover=False


while not gameover:
  while computer_turn:
    if names.health<=0 or Monster.health<=0:
      player_input=input("game over, would you like to play again?: ")
      if player_input=='no':
        print ('thanks for playing')
        gameover=True
        break
      if player_input=='yes':
        if names.health>0:
          names.health=20
          Monster=Monsters(random.choice(Possible_Monster_name)+'mon',random.randint(1,40),random.choice(Type_Monster),random.randint(1,40),random.randint(1,40),random.randint(1,40))
          level_up=input("what area would you like to improve on between quickness, defense and offense?: ")
          if level_up=='offense':
            names.offense+=3
            level_up=None
            print (names.offense)
            break
          if level_up=='defense':
            names.defense+=3
            level_up=None
            print (names.defense)
            break
          if level_up=='quickness':
            names.quickness+=3
            level_up=None
            print (names.quickness)
            break
      if names.health<=0:
        print ("You can't just come back from being dead!")
        gameover=True
        break
    if names.health<=4:
      if Skills.dodge(Monster,Monster.quickness)==True:
        print ("You sucessfully dodged.")
        human_skill=None
        computer_turn=False
        human_turn=True
        break
      damage_done=Skills.weak_attack(Monster,Monster.offense)
      if Monster.types=='ice' and names.types=='fire':
        damage_done+=1
      if Monster.types=='fire' and names.types=='grass':
        damage_done+=1
      if Monster.types=='grass' and names.types=='ice':
        damage_done+=1
      names.health=names.health-damage_done
      print ("{} used weak attack. {} damage was done.".format(Monster.name, damage_done))
      print ("{} has {} health now.".format(names.name,names.health))
      computer_turn=False
      human_turn=True
      break
    if Monster.health<=4:
      damage_done=Skills.heal(Monster,Monster.defense)
      Monster.health=Monster.health+damage_done
      print ("{} used healing.".format(Monster.name))
      print ("{} was healed by {}. New health is {}.".format(Monster.name,damage_done,Monster.health))
      computer_turn=False
      human_turn=True
      break
    if names.health>4:
      if Skills.dodge(Monster,Monster.quickness)==True:
        print ("You sucessfully dodged.")
        human_skill=None
        computer_turn=False
        human_turn=True
        break
      damage_done=Skills.strong_attack(Monster,Monster.offense)
      if Monster.types=='ice' and names.types=='fire':
        damage_done+=1
      if Monster.types=='fire' and names.types=='grass':
        damage_done+=1
      if Monster.types=='grass' and names.types=='ice':
        damage_done+=1
      names.health=names.health-damage_done
      print ("{} used strong attack. {} damage was done.".format(Monster.name, damage_done))
      print ("{} has {} health now.".format(names.name,names.health))
      computer_turn=False
      human_turn=True
      break
  while human_turn:
    if names.health<=0 or Monster.health<=0:
      player_input=input("game over, would you like to play again?: ")
      if player_input=='no':
        print ('thanks for playing')
        gameover=True
        break
      if player_input=='yes':
        if names.health>0:
          names.health=20
          Monster=Monsters(random.choice(Possible_Monster_name)+'mon',random.randint(1,40),random.choice(Type_Monster),random.randint(1,40),random.randint(1,40),random.randint(1,40))
          level_up=input("what area would you like to improve on between quickness, defense and offense?: ")
          if level_up=='offense':
            names.offense+=3
            level_up=None
            print (names.offense)
            break
          if level_up=='defense':
            names.defense+=3
            level_up=None
            print (names.defense)
            break
          if level_up=='quickness':
            names.quickness+=3
            level_up=None
            print (names.quickness)
            break
      if names.health<=0:
        print ("You can't just come back from being dead!")
        gameover=True
        break

    human_skill=input("what skills will be used: ")
    if human_skill==Skills.heal_name:
      damage_done=Skills.heal(names,names.defense)
      names.health=names.health+damage_done
      print ("{} used {}. {} was healed by {}. New health is {}.".format(names.name,healing,names.name,damage_done,names.health))
      computer_turn=True
      human_turn=False
      break
    if human_skill==Skills.strong_attack_name:
      if Skills.dodge(Monster,Monster.quickness)==True:
        print ("The monster sucessfully dodged.")
        human_skill=None
        computer_turn=True
        human_turn=False
        break
      damage_done=Skills.strong_attack(names,names.offense)
      if names.types=='ice' and Monster.types=='fire':
        damage_done+=1
      if names.types=='fire' and Monster.types=='grass':
        damage_done+=1
      if names.types=='grass' and Monster.types=='ice':
        damage_done+=1
      Monster.health=Monster.health-damage_done
      print ("{} used {}. {} damage was done.".format(names.name, strong_attack, damage_done))
      print ("{} has {} health now.".format(Monster.name,Monster.health))
      computer_turn=True
      human_turn=False
      break
    if human_skill==Skills.weak_attack_name:
      if Skills.dodge(Monster,Monster.quickness)==True:
        print ("The monster sucessfully dodged.")
        human_skill=None
        computer_turn=True
        human_turn=False
        break
      damage_done=Skills.weak_attack(names,names.offense)
      if names.types=='ice' and Monster.types=='fire':
        damage_done+=1
      if names.types=='fire' and Monster.types=='grass':
        damage_done+=1
      if names.types=='grass' and Monster.types=='ice':
        damage_done+=1
      Monster.health=Monster.health-damage_done
      print ("{} used {}. {} damage was done.".format(names.name, weak_attack,damage_done))
      print ("{} has {} health now.".format(Monster.name,Monster.health))
      computer_turn=True
      human_turn=False
      break
    if human_skill=='eat':
      potion=random.choice(['suicide_pill','good_potion'])
      if potion=='suicide_pill':
        names.eat('suicide_pill')
        print ("{} picked {}! {} now has {} health left.".format(names.name, 'suicide pill',names.name,names.health))
        human_skill=None
        computer_turn=True
        human_turn=False
        break
      if potion=='good_potion':
        names.eat('good_potion')
        print ("{} picked {}! {} now has {} health left.".format(names.name, 'good potion',names.name,names.health))
        human_skill=None
        computer_turn=True
        human_turn=False
        break
    if human_skill=='help':
      print ("health:{}, offense:{}, defense:{}, quickness:{} type:{}".format(Monster.health,Monster.offense,Monster.defense,Monster.quickness,Monster.types))

I do realize this kind of code has been reviewed a lot here, so I'll ask questions I think are specific to my code:

  • Is there a better way to change turns?
  • What about styling?
  • Are my classes okay?
\$\endgroup\$
  • \$\begingroup\$ Your list of possible monster names contains 'Kasa' twice. \$\endgroup\$ – Pharap Feb 7 '17 at 1:39
4
\$\begingroup\$

Your use of classes is wrong. You overwrite the class properties instead of the instance properties. This seems to be no problem now, since you always set all properties, but if you ever have a property which is only sometimes set, an instance will get the value from the last instance where that property was set (instead of being undefined).

This is dangerous as can be seen in this example:

class A:
    def __init__(self, name=None):
        if name is not None:
            A.name = name

a1 = A("foo")
a2 = A()
print a2.name
# foo

a2.name is now "foo", whereas it should not be defined at all and raise an AttributeError.

You should set the instance property by using the instance, self:

class A:
    def __init__(self, name=None):
        if name is not None:
            self.name = name

a1 = A("foo")
a2 = A()
print a2.name
# Traceback (most recent call last):
#     File "<stdin>", line 1, in <module>
#         AttributeError: 'A' object has no attribute 'name'

So basically replace all of your Monsters.foo with self.foo and similar for Player.foo and Skills.foo.

Lastly, Python has an official style-guide, PEP8, which you should adhere to. In addition to what TemporalWolf noted in his answer, this means using lower_case_with_underscores as variable and function names and PascalCase only for class names (and not for class instances, these are just normal variables).

\$\endgroup\$
6
\$\begingroup\$

The class Monstersonly stores data, so a namedtuple would be a better alternative:

from collections import namedtuple
Monster = namedtuple("Monster", "name health types defense offense quickness")

monster = Monster("a", 100, "b", 100, 100, 100)

The dodge-method of your Skill class can be simplified a lot:

def dodge(self, quickness):
  if quickness <= 10:
    return random.choice([1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) == 1
  if quickness > 10 and quickness <= 20:
    return random.choice([1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2]) == 1
  ...

It seems that human_turn is always equal to not computer_turn. You can drop computer_turn and alter your loop conditions to:

while not human_turn:
  ...
while human_turn:
  ...

As long as a function only return booleans you don't need to compare its return value to True:

if Skills.dodge(Monster, Monster.quickness):

does the same as your:

if Skills.dodge(Monster,Monster.quickness)==True:

The two conditions if potion=='suicide_pill': and if potion=='good_potion': do actually the same. You can combine them to:

names.eat(potion)
print ("{} picked {}! {} now has {} health left.".format(names.name,  potion, names.name, names.health))
human_skill = None
computer_turn = True
human_turn = False
break
\$\endgroup\$
  • \$\begingroup\$ Note that one disadvantage of usiing a namedtuple is that you could never put verification of the monster name in the class. \$\endgroup\$ – jpmc26 Feb 7 '17 at 4:01
4
\$\begingroup\$
while typess!='ice' and typess!='fire' and typess!='grass':

can be simplified to

while typess not in Type_Monster:

PEP-8 styling should be followed... your code is missing a lot of spaces (I fixed this example):

def strong_attack(self, offense):
random_number_list = [1, 1, 1, 1, 2, 2, 2, 2, 2, 2]
maximum = offense * 0.50 
if random.choice(random_number_list) == 1:
    return random.uniform(1, maximum)

Also, these sections

def strong_attack(self,offense):
    random_number_list=[1,1,1,1,2,2,2,2,2,2]
    maximum=offense*0.50 
    if random.choice(random_number_list)==1:
      return random.uniform(1,maximum)
    else:
      return 0

seem to be giving a % chance to succeed. Simplified:

def strong_attack(self, offense):
    return random.uniform(1, offense * 0.50) if random.random() < 0.4 else 0

The dodge skill can be simplified to:

def dodge(self,quickness):
    return random.random() < ((int(min(quickness, 40)) - 1) / 10 * 0.2 + 0.2)

Both of these are just using random.random() to get a random value between 0 & 1, then calculating the percentage they want to succeed and comparing the two values. min(quickness, 40) prevents quickness above 40 from giving 100% dodge.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.