Become a Pokemon Master Challenge Project (Python)

This was a fun project with a lot of opportunity to be creative. I now feel more confident using classes and object-oriented programming. Here’s how mine turned out:

hey @timothygjohnson great idea on the poketypes dictionary! I borrowed the idea and wrote my own function to implement it, and I can give you a shout out on my github repository for the dictionary idea if you like? My initial commit was the completed project but I can add a readme with your name or something, since you gave me the idea that vastly simplified the function from what would require updating EVERY new poketype to simply updating the poketypes dictionary.

Here is my code

For some reason an if statement block of code is not being executed and I can not figure out why. Could someone give me some idea?

The code of interest begins at line 93 in the method in which I switch current pokemon. I wanted to handle the argument being the pokemon name and the index as well.

So I went a little off on my own with this one. I forgot to add the reserve/active Pokemon functionality, but I did include save files, some minor computer player AI, win/loss counter, and a few other things. Ended up with almost 700 lines of code!

Doing this project really makes me want to attempt a larger game. I would need to think through the rules, user-interface, etc a lot more before I start coding. This was a lot of fun though. Thanks!

You are getting a type error “TypeError: tuple indices must be integers or slices, not str.” It needs to be an int and it’s getting a string. I am not quite sure what you are trying to do here though, why are you passing an integer or a string as an argument? Pass the instance in as a parameter instead. Try this…

 def switch_pokemon(self, new_pokemon):
  if new_pokemon in self.pokemons:
    if new_pokemon.is_knocked_out == False:
      self.current_pokemon = new_pokemon
      print("Switching to {pokemon}!".format(pokemon = new_pokemon.name))
    else:
      print("{pokemon} is knocked out, you can't switch that pokemon!".format(pokemon = new_pokemon.name))
  else:
    print("You don't own that pokemon!")
  
  return self.current_pokemon

trainer_one.switch_pokemon(pokemon2)

There is my take on it. Didn’t add to many fancy stuff but it is working as far as i can tell ^^

Feel free to comment and critique my attampt!

PS: This was fun!

If a person is calling the function, then it is more likely that the name or the index of the pokemon would be passed and not the instance itself was what I was thinking. Thanks for tip though.

I am currently going through your code and trying to get it to work using the approach you did, but I think it is a bit more complicated. When I am finished I’ll post it up. I don’t think it will matter though, as the player is not typing in the function themselves, it’s all happenning behind the scenes.

Ok this is what I have got so far

  def switch_pokemon(self, new_pokemon):
    # deal with pokemon passed as string or int
    #This variable is redundant
    #current_pokemon = new_pokemon

    #This if statement wasn't working it has been replaced with isinstance()
    #if (type(new_pokemon) == type(str)):
     if isinstance(new_pokemon, str):
       #instert a print statement to see if it's working. It is so we can remove it now
       #print(new_pokemon)

       for pokemon in self.pokemons:
        if new_pokemon == pokemon.name:
          print(new_pokemon)
          if pokemon.is_knocked_out == False:
            #another print statement to make sure that our code still works, it does so we can remove it (in the final code you can remove it completely, I am leaving it in
            # so you can see how I tested your code)
            #print(new_pokemon)
            #rename this so from new_pokemon to new_pokemon index so it's not changing our argument value
            new_pokemon_index = self.pokemons.index(pokemon)
            #hey look another print statement, it got the index correctly. Let's comment it out.
            #print(new_pokemon_index)
            self.current_pokemon = pokemon

            #print(self.current_pokemon.name)
            #what exactly are you wanting to use the index value for?
            break
          else:
            print("That Pokemon is knocked out!")
        #prints that you don't own the pokemon if there was not match to the string name. Remember to deal with upper and lowercase here (use .lower() in your function)
        #But wait!, if the pokemon isn't in the list it prints it for EVERY Pokemon in the list because of your for statement! A break statement fixes that,
        # but it might be better to rethink your approach
        else:
          print("Pokemon is not in list")


      #You don't need another if(type(new_pokemon) == type(str): statement here, you have one above. you can put it there
      #if(type(new_pokemon) == type(str)):
          #print("Pokemon is not in list")
          #return

    # checks if pokemon is knocked out
    #if(self.pokemons[new_pokemon].is_knocked_out):
      #print("{name} is knocked out! Can not switch".format(name = self.pokemons[current_pokemon]))
      #return

      #self.current_pokemon = new_pokemon

      #print("{name}'s active pokemon is now {pokemon}".format(name=self.name, pokemon = self.pokemons[self.current_pokemon]))

But because of the for loop you run into problems when you try to print “pokemon not in list”). rather than printing it once if the pokemon is not in the list, you are printing it for each owned pokemon that the argument doesn’t match. I don’t think this approach is going to get around that, at least not easily. Someone more experienced might be able to comment further.

So I have completed this and went ahead and tried to add user input but I am getting some errors. Also after switching to a different Pokemon I cannot attack and am getting an error. can someone explain to me where I have gone wrong

https://gist.github.com/04ace0a9dcb855378e14daf3ae771e37

Hi,

This project was really very interesting. I am just blown away, how the same program can be written in multiple ways. Below is my code:

class Pokemon:
  def __init__(self, name, types, level):
    self.name = name
    self.level = 5
    self.types = types
    self.max_health = level * 4
    self.current_health = self.max_health
    self.knocked_out = False
    
  def __repr__(self):
    return f"{self.name}, belongs to {self.types} Pokemon type and has {self.level} level health points." 

#method for reviving after knock out
  def revive(self):
        self.is_knocked_out = False
        # A revived pokemon can't have 0 health. This is a safety precaution. revive() should only be called if the pokemon was given some health, but if it somehow has no health, its health gets set to 1.
        if self.current_health == 0:
            self.current_health = 1
            print(f"{self.name} is revived!") 
    
#method for knocking out Pokemon
  def knock_out(self):
    self.knocked_out = True
    if self.current_health != 0:
        self.current_health = 0
        print(f"{self.name} Pokemon is knocked out !!")
  
#method of taking damage and loosing health points
  def lose_health(self, damage):
      self.current_health -= damage
      if self.current_health <= 0:
         self.current_health = 0
         self.knock_out()
      else:
         print(f"{self.name}'s now has {self.current_health} points of health")

 #method for gaining health points 
  def gain_health(self, healing):
      if self.current_health == 0:
          self.revive()
      self.current_health += healing
      print(f"{self.name} has gained {healing} health points and current health is {self.current_health}")


#Attack between Fire : Water, Fire : Grass and Fire : Fire  
  def attack(self, other_pokemon):
    print("\n")
    print(f"{self.name} ATTACKS {other_pokemon.name}")
    if self.types == "Fire":
      if other_pokemon.types == "Water":
        damage = self.level / 2
        print("Attack not very effective !!")
        
      elif other_pokemon.types == "Grass":
        damage = self.level * 2
        print("Attack is very effective !!")
        
      else:
        damage = self.level
        print("Attack not so very effective")
        

#Attack between Water : Fire, Water : Grass and Water : Water
   
    elif self.types == "Water":      
      if other_pokemon.types == "Fire":
        damage = (10 * self.level) * 2
        print("Attack is very effective !!")
        
      elif other_pokemon.types == "Grass":
        damage = (10 * self.level) / 2
        print("Attack is not very effective !!")
        
      else:
        damage = (10 * self.level)
        print("Attack not so very effective")

#Attack between Grass : Fire, Grass : Water and Grass : Grass
   
    elif self.types == "Grass":
      if other_pokemon.types == "Fire":
        damage = (10 * self.level) / 2
        print("Attack is not very effective !!")
        
      elif other_pokemon.types == "Water":
        damage = (10 * self.level) * 2
        print("Attack is very effective !!")
        
      else:
        damage = (10 * self.level) 
        print("Attack not so very effective") 

    else:
      print("Pokemon Type Not Existed.....") 
    other_pokemon.lose_health(damage)
    

#creating Trainer class

class Trainer:
  def __init__(self, name, pokemons_list, no_of_potions):
    
    self.name = name
    self.pokemons_list = pokemons_list
    self.no_of_potions = no_of_potions
    self.current_pokemon = 0

  
#printing name of trainer, pokemons list and current active pokemoin
  def __repr__(self):
      print("\n")
      print(f"The trainer {self.name} has the following pokemon:")
      for pokemon in self.pokemons_list:
          print(pokemon)
      return f"The current active Pokemon is {self.pokemons_list[self.current_pokemon].name} "
   
    
#use a potion on current pokemon      
  def use_potion(self):
      print(f"{self.pokemons_list[self.current_pokemon].name} has used potions.")
      if self.no_of_potions > 0:
          self.pokemons_list[self.current_pokemon].gain_health(20)
          self.no_of_potions -= 1
          print(f"Potions left: {self.no_of_potions}")      
      else:
          print(f"{self.name}, you have no potions left !!")
 
        
#swtiching pokemon
  def switch_pokemon(self, new_pokemon):
      if new_pokemon < len(self.pokemons_list) and new_pokemon >= 0:
          if self.pokemons_list[new_pokemon].knocked_out == True:
              print("{self.name} Pokemon is knocked out !!")
          elif new_pokemon == self.current_pokemon:
              print(f"{self.name} is already a active pokemon")
          else:
              self.current_pokemon = new_pokemon
              print(f"Now it is {self.name} Pokemon turn ")
              
  def attack_other_trainer(self, other_trainer):
      my_pokemon = self.pokemons_list[self.current_pokemon]
      other_pokemon = other_trainer.pokemons_list[other_trainer.current_pokemon]
      my_pokemon.attack(other_pokemon)
 
# Three classes that are subclasses of Pokemon. Charmander is a fire type, Venusaur is a Grass type and Misty is a Water type.
class Charmander(Pokemon):
    def __init__(self, level = 5):
        super().__init__("Charmander", "Fire", level)

class Venusaur(Pokemon):
    def __init__(self, level = 5):
        super().__init__("Venusaur", "Grass", level)

class Misty(Pokemon):
    def __init__(self, level = 5):
        super().__init__("Misty", "Water", level)    


# Fire = Pokemon("Charmander", "Fire", 10)
# Grass = Pokemon("Venusaur", "Grass", 5)
# Water = Pokemon("Misty", "Water", 6)   


#Pokemons with different levels
a = Charmander(9)
b = Venusaur(5)
c = Misty(8)

trainer1 = Trainer("Erica", [c, b], 10)
trainer2 = Trainer("Pikachu", [a], 14)

print(repr(trainer1))
print(repr(trainer2))


#Testing attack , potion and switch

trainer1.attack_other_trainer(trainer2)
trainer2.attack_other_trainer(trainer1)

trainer1.use_potion()

trainer2.switch_pokemon(1)


Happy coding :slightly_smiling_face:

Here is my Pokemon solution. I still haven’t added the evolution function yet but soon to come if there is a demand in it.

Here is the link:

Working on this one right now… does anybody know how I could print the messages slowly like they do on the game??

Unfortunately the because the way our output terminal works on your web browser, it will just throw all of the output to you at once. If you’ve set up your computer to run Python outside of codecademy, you could investigate both the input() function to get input from the user (rather than putting it in your code directly). You could also look up the time.sleep() function to make the input come in slowly.

If you need help setting up Python on your own computer, this article might help

I only just started this project and I am ALREADY stuck on the task 1 (well, technically it’s 2), which is kind of frustrating.

I’ve been reading on the internet about classes a lot, but it doesn’t help me much to understand this:
our first init method has different attributes, right? But why the solution provided by Codecademy (and other smart learners here) doesn’t have hp, max hp and is_knocked_out inside the brackets?
I fail to understand when we have to write

def init(self, 1, 2):
self.1 = 1
self.2 = 2
and when we also add
self.3 = something
which isn’t in the brackets. Please, educate me! And thank you!

You’d be using the parameters of __init__ for items that you would potentially change for each different instance. As far as I know most of the attributes for this project are set to the same value e.g. max_health is 100 for each and every instance of the Pokemon class.

They’re not added as parameters in the method simply because they don’t need to be. We can add them as attributes without making an overly complex call requirement for __init__

# If for example every instance is initialised with several attributes but..
# we use the same values (per instance) except for the first two arguments
# It can get rather complex quickly-
eample_inst2 = Example2(10, 'red', range(50), maplotlib.pyplot.figure, [x for x in range(20)], ...     )

# Why not simplify the method with just the two attributes that change-
example_inst = Example(10, 'red')
# and set self.span = range(50) etc. within __init__ ...
# since it is indentical for every instance anyway

If you wanted a more advanced code you could add different values for the intial max_health and so on but I don’t think it is one of the requirements. You can always add it later on if you so wish.

1 Like

Putting up my WIP Pokemon Master Challenge Project solution thus far. Went a fair bit into the rabbit hole and began splitting it across multiple files too. I’ve already sunk a lot of hours into this and could sink even more. Feedback very much appreciated!

Thank you…a lot! It’s crystal-clear to me now!