2

I'm a beginner practicing python by making a pokemon battle simulator, and I'm in the process of creating a damage modifier function.

The idea is:

If the pokemon's "attack type" is found in "attack_type_2x_dmglist", the pokemon deals twice the damage. "Very effective"

If the pokemon's "attack type" is found in "attack_type_half_dmglist", the pokemon deals only half the damage. "Isn't very effective"

I've used print to make it easy to debug for now.

To verify this, I've done conditionals for type vs type.

normal_2x_dmglist = []
normal_half_dmglist = ["Rock","Dragon"]

rock_2x_dmglist = ["Fire","Ice","Fly","Bug",]
rock_half_dmglist = ["Fighting","Ground","Steel"]

water_2x_dmglist = ["Fire","Ground","Rock"]
water_half_dmglist = ["Water","Grass","Dragon"]

grass_2x_dmglist = ["Water","Ground","Rock"]
grass_half_dmglist = ["Fire","Grass","Poison","Fly","Bug","Dragon","Steel"]

fighting_2x_dmglist = ["Normal","Ice","Rock","Dark","Steel"]
fighting_half_dmglist = ["Poison","Fly","Psychic","Bug","Fairy"]

ground_2x_dmglist = ["Fire","Electric","Poison","Rock","Steel"]
ground_half_dmglist = ["Grass","Bug"]

electric_2x_dmglist = ["Water","Fly"]
electric_half_dmglist = ["Electric","Grass","Dragon"]

def dmg_modifier(attack_type, receiver_pokemon_type):
    if attack_type == "Normal" and receiver_pokemon_type in normal_2x_dmglist:
        print "Normal deals 2x damage!"
    elif attack_type == "Normal" and receiver_pokemon_type in normal_half_dmglist:
        print "Normal deals 1/2 damage!"
    elif attack_type == "Rock" and receiver_pokemon_type in rock_2x_dmglist:
        print "Rock deals 2x damage!"
    elif attack_type == "Rock" and receiver_pokemon_type in rock_half_dmglist:
        print "Rock deals 1/2x damage!"
    elif attack_type == "Water" and receiver_pokemon_type in water_2x_dmglist:
        print "Water deals 2x damage!"
    elif attack_type == "Water" and receiver_pokemon_type in water_half_dmglist:
        print "Water deals 1/2x damage!"
    elif attack_type == "Grass" and receiver_pokemon_type in grass_2x_dmglist:
        print "Grass deals 2x damage!"
    elif attack_type == "Grass" and receiver_pokemon_type in grass_half_dmglist:
        print "Grass deals 1/2x damage!"
    elif attack_type == "Fighting" and receiver_pokemon_type in fighting_2x_dmglist:
        print "Fighting deals 2x damage!"
    elif attack_type == "Fighting" and receiver_pokemon_type in fighting_half_dmglist:
        print "Fighting deals 1/2x damage!"
    elif attack_type == "Ground" and receiver_pokemon_type in ground_2x_dmglist:
        print "Ground deals 2x damage!"
    elif attack_type == "Ground" and receiver_pokemon_type in ground_half_dmglist:
        print "Ground deals 1/2x damage!"
    elif attack_type == "Electric" and receiver_pokemon_type in electric_2x_dmglist:
        print "Electric deals 2x damage!"
    elif attack_type == "Electric" and receiver_pokemon_type in electric_half_dmglist:
        print "Electric deals 1/2x damage!"
    else:
        print "Type deals 1x damage - stays the same"

This works perfectly, it's just very repetitive and dirty.

To automate this, I planned to attach the declared "attack type" to the "_2x_dmglist" so that it will automatically call the list.

First I lowercase the declared attack type, for example "Rock", to make it into rock. Then I attach it to _2x_dmglist so that it comes out as rock_2x_dmglist. Then it can find the list.

def dmg_modifier_2(attack_type, receiver_pokemon_type):
    lower_case_attack_type = attack_type
    lower_cased_attack_type = lower_case_attack_type.lower()
    if attack_type == attack_type and receiver_pokemon_type in lower_cased_attack_type+_2x_dmglist:
        print attack_type+"deals 2x damage"
    elif attack_type == attack_type and receiver_pokemon_type in lower_cased_attack_type+_2x_dmglist:
        print attack_type+"deals 1/2 damage"
    else:
        print "Not working"

Enter this:

dmg_modifier_2("Rock","Rock")

Output:

Traceback (most recent call last):
  File "poke_game_test.py", line 98, in <module>
    dmg_modifier_2("Rock","Rock")
  File "poke_game_test.py", line 90, in dmg_modifier_2
    finder = lower_case_attack_type.lower() + _2x_dmglist
NameError: global name '_2x_dmglist' is not defined

I understand the traceback, but how do I concatenate the two variable names so that the whole thing can be automated? In PHP, I was able to do this by just using the + sign, which I tried here. I also understand that the lower cased "Rock" is also a string, while _2x_dmglist is not. I'm just quite confused really if variable concatenation works in python cause I can't find a question similar to it here.

Or is there something fundamentally wrong with my code?

Thanks!

myleschuahiock
  • 303
  • 1
  • 3
  • 10
  • You are using `lower_cased_attack_type+_2x_dmglist`, which is of type String. However, you want to retrieve the name of the list (which is not a string). You can use [`exec`, but it is better to find another way](http://stackoverflow.com/questions/19122345/to-convert-string-to-variable-name-in-python). – Nander Speerstra Feb 03 '16 at 08:10
  • @NanderSpeerstra yeah been reading that one should avoid using exec at all. :( – myleschuahiock Feb 03 '16 at 08:11
  • Why don't you make a pokemon class, with sets of attack types which they are weak against and strong against as instance attributes? – timgeb Feb 03 '16 at 08:14
  • @timgeb In truth, I'm learning about classes right now, so I don't have extensive knowledge to understand how to do that. My pokemons are made up of classes, but their attributes are only related to their stats such as HP, Attack Stat, Def Stat,etc. – myleschuahiock Feb 03 '16 at 08:25

3 Answers3

4

I think it's time to introduce classes, but you you can also try dictionaries:

dict_2x_dmg = {
    "Normal":set([]),
    "Rock":{"Fire","Ice","Fly","Bug"},
    "Water":{"Fire","Ground","Rock"},
    "Grass":{"Water","Ground","Rock"},
    "Fighting":{"Normal","Ice","Rock","Dark","Steel"},
    "Ground":{"Fire","Electric","Poison","Rock","Steel"},
    "Electric":{"Water","Fly"}
}

dict_half_dmg = {
    "Normal":{"Rock","Dragon"},
    "Rock":{"Fighting","Ground","Steel"},
    "Water":{"Water","Grass","Dragon"},
    "Grass":{"Fire","Grass","Poison","Fly","Bug","Dragon","Steel"},
    "Fighting":{"Poison","Fly","Psychic","Bug","Fairy"},
    "Ground":{"Grass","Bug"},
    "Electric":{"Electric","Grass","Dragon"}
}

def dmg_modifier(attack_type, receiver_pokemon_type):
    if receiver_pokemon_type in dict_2x_dmg[attack_type]:
        dmg = "2x"
    elif receiver_pokemon_type in dict_half_dmg[attack_type]:
        dmg = "1/2x"
    else:
        dmg = "1x"

    print "{0} deals {1} damage".format(attack_type, dmg)


>>> dmg_modifier("Water", "Ground")
Water deals 2x damage

Another thing you can do - is to use dictionary of dictionaries:

dict_dmg = {
    "Normal":{"Rock":0.5,"Dragon":0.5},
    "Rock":{"Fighting":0.5,"Ground":0.5,"Steel":0.5,"Fire":2,"Ice":2,"Fly":2,"Bug":2},
    "Water":{"Water":0.5,"Grass":0.5,"Dragon":0.5,"Fire":2,"Ground":2,"Rock":2},
    "Grass":{"Fire":0.5,"Grass":0.5,"Poison":0.5,"Fly":0.5,"Bug":0.5,"Dragon":0.5,"Steel":0.5,"Water":2,"Ground":2,"Rock":2},
    "Fighting":{"Poison":0.5,"Fly":0.5,"Psychic":0.5,"Bug":0.5,"Fairy":0.5,"Normal":2,"Ice":2,"Rock":2,"Dark":2,"Steel":2},
    "Ground":{"Grass":0.5,"Bug":0.5,"Fire":2,"Electric":2,"Poison":2,"Rock":2,"Steel":2},
    "Electric":{"Electric":0.5,"Grass":0.5,"Dragon":0.5,"Water":2,"Fly":2}
}

def dmg_modifier2(attack_type, receiver_pokemon_type, dict_dmg):
    dmg = dict_dmg[attack_type].get(receiver_pokemon_type, 1)

    print "{0} deals {1}x damage".format(attack_type, dmg)

Or you can even use pandas DataFrame:

>>> import pandas as pd
>>> df_dmg = pd.DataFrame.from_dict(dict_dmg)
>>> df
          Electric  Fighting  Grass  Ground  Normal  Rock  Water
Bug            NaN       0.5    0.5     0.5     NaN   2.0    NaN
Dark           NaN       2.0    NaN     NaN     NaN   NaN    NaN
Dragon         0.5       NaN    0.5     NaN     0.5   NaN    0.5
Electric       0.5       NaN    NaN     2.0     NaN   NaN    NaN
Fairy          NaN       0.5    NaN     NaN     NaN   NaN    NaN
Fighting       NaN       NaN    NaN     NaN     NaN   0.5    NaN
Fire           NaN       NaN    0.5     2.0     NaN   2.0    2.0
Fly            2.0       0.5    0.5     NaN     NaN   2.0    NaN
Grass          0.5       NaN    0.5     0.5     NaN   NaN    0.5
Ground         NaN       NaN    2.0     NaN     NaN   0.5    2.0
Ice            NaN       2.0    NaN     NaN     NaN   2.0    NaN
Normal         NaN       2.0    NaN     NaN     NaN   NaN    NaN
Poison         NaN       0.5    0.5     2.0     NaN   NaN    NaN
Psychic        NaN       0.5    NaN     NaN     NaN   NaN    NaN
Rock           NaN       2.0    2.0     2.0     0.5   NaN    2.0
Steel          NaN       2.0    0.5     2.0     NaN   0.5    NaN
Water          2.0       NaN    2.0     NaN     NaN   NaN    0.5

def dmg_modifier3(attack_type, receiver_pokemon_type, df_dmg):
    dmg = df_dmg[attack_type][receiver_pokemon_type]

    print "{0} deals {1}x damage".format(attack_type, dmg)
Roman Pekar
  • 107,110
  • 28
  • 195
  • 197
  • 1
    May I humbly suggest `{'set', 'literals'}`? :) – Ben Graham Feb 03 '16 at 10:05
  • @RomanPekar Awesome! I'm just amazed by the amount of effort and content you put here! Thanks Roman! With everyone's input, I didn't know dictionaries could be so versatile! Also, this is my first time encountering the "panda DataFrame" so I'm not really sure how that's used! :) – myleschuahiock Feb 03 '16 at 13:38
2

Although you can do it like

globals().get(lower_cased_attack_type+'_2x_dmglist')

where globals is python builtin method that returns dict of all the global variables accessible in the calling method. But I would suggest to store your damage lists in dictionary that would be a better approach.

You should do

damages = {
  'rock_2x_dmglist': ["Fire","Ice","Fly","Bug",],
  'rock_half_dmglist': ["Fighting","Ground","Steel"],

  'water_2x_dmglist': ["Fire","Ground","Rock"],
  'water_half_dmglist': ["Water","Grass","Dragon"],
}

then you can do damages.get(lower_cased_attack_type+'_2x_dmglist')

Muhammad Tahir
  • 5,006
  • 1
  • 19
  • 36
1

You could do the following:

damageDict = {}
damageDict['normal']: {'2x':[], 'half':["Rock","Dragon"]}
damageDict['rock']: {'2x':["Fire","Ice","Fly","Bug",], 'half':["Fighting","Ground","Steel"]}
damageDict['water']: {'2x':["Fire","Ground","Rock"], 'half':["Water","Grass","Dragon"]}
damageDict['grass']: {'2x':["Water","Ground","Rock"], 'half':["Fire","Grass","Poison","Fly","Bug","Dragon","Steel"]}
damageDict['fighting']: {'2x':["Normal","Ice","Rock","Dark","Steel"], 'half':["Poison","Fly","Psychic","Bug","Fairy"]}
damageDict['ground']: {'2x':["Fire","Electric","Poison","Rock","Steel"], 'half':["Grass","Bug"]}
damageDict['electric']: {'2x':["Water","Fly"], 'half':["Electric","Grass","Dragon"]}

# test case
attack_type = 'normal'
receiver_type = 'grass'

if receiver_type in damageDict[attack_type]['2x']:
    print ("%s deals 2x damage!" % (attack_type)
elif receiver_type in damageDict[attack_type]['half']:
    print ("%s deals 0.5x damage!" % (attack_type)
else:
    print ("%s deals 1x damage!" % (attack_type)
Nander Speerstra
  • 1,496
  • 6
  • 24
  • 29