patternpythonMinor
Setting Pokemon stats
Viewed 0 times
settingstatspokemon
Problem
I set the stats (.ie. name, attack, defense, HP) in the
The code works as expected but I'm not sure if this proper use of classes. I believe it is and have seen some examples do similar things. I just wanted to check if I am headed in the right direction.
Edit: The reason I would like to know is because the
__init__ method. However, I have chosen to build the self.attacks dictionary with a separate method.class Pokemon(object):
def __init__(self, name, attack, defense, HP):
self.name = name
self.attack = attack
self.defense = defense
self.HP = HP
self.attacks = {}
def Moves(self,move_1,damage_1,move_2,damage_2):
self.attacks[move_1] = damage_1
self.attacks[move_2] = damage_2pikachu = Pokemon('Pikachu',40,10,50)
pidgey = Pokemon('Pidgey',50,30,60)
pikachu.Moves('tackle',50,'thunderbolt',70)
pidgey.Moves('gust',80,'tackle',50)The code works as expected but I'm not sure if this proper use of classes. I believe it is and have seen some examples do similar things. I just wanted to check if I am headed in the right direction.
Edit: The reason I would like to know is because the
Pokemon will be getting many more attributes and I need a way to assign those attributes in groups to avoid assigning fifty or so variables every time I create a new instance of Pokemon.Solution
You need to step away from programming and design your classes.
Pokemon stats aren't just one number. And so you should design for this.
First things first, Pokemon have multiple stats, the main ones are:
-
Each Pokemon of the same breed have the same base stats.
Say you have two Pikachu, their base stats are exactly the same.
And so you should define this on a Pokemon's class.
-
They have 'genes', better know as IVs - individual values.
These change for each and every Pokemon, two Pikachu are unlikely to have the same IVs.
They also don't change, ever. And so it'd be a good idea to make these immutable.
-
They learn things by fighting, and gain EVs - effort values.
These aren't immutable all the time, but have a limit, and become immutable at that point.
-
Pokemon have a nature, which also change a Pokemon's stats.
And so, if you want the program to be correct, you need to correctly handle the above.
But you need to handle all of them differently:
And so you don't need to take 50 arguments in the creation of a Pokemon. You need 4 arguments, and if you want to pass the moves, 5.
To start you off you'd need something like:
Even if you don't use the previous, then I'd still recommend that you move the data into smaller more logical datatypes.
But either way you should want to then change the way you're storing your moves.
Each move should be an object, it has a name, damage, type, miss chance, special animation. You need to contain all of this in each move.
But you also need to have an instance of each move, so that every move in the game doesn't share the same PP.
And so to create each move, you may want to do the same as above with the
After this, it'd make more sense to store these instances in a list rather than a dictionary.
And so you should instead have something like:
Going further, if this is too static, then you can use
You should want to take the moves in the constructor of the class as it normally makes saving and loading game state easier.
If you did want to keep your
Pokemon stats aren't just one number. And so you should design for this.
First things first, Pokemon have multiple stats, the main ones are:
-
Each Pokemon of the same breed have the same base stats.
Say you have two Pikachu, their base stats are exactly the same.
And so you should define this on a Pokemon's class.
-
They have 'genes', better know as IVs - individual values.
These change for each and every Pokemon, two Pikachu are unlikely to have the same IVs.
They also don't change, ever. And so it'd be a good idea to make these immutable.
-
They learn things by fighting, and gain EVs - effort values.
These aren't immutable all the time, but have a limit, and become immutable at that point.
-
Pokemon have a nature, which also change a Pokemon's stats.
And so, if you want the program to be correct, you need to correctly handle the above.
But you need to handle all of them differently:
- For base stats, you want to use Composition over inheritance.
- For IVs, you want to use a
namedtuple, or just a tuple.
- For EVs, you want to have a user defined type.
And so you don't need to take 50 arguments in the creation of a Pokemon. You need 4 arguments, and if you want to pass the moves, 5.
To start you off you'd need something like:
from collections import namedtuple
Stats = namedtuple('Stats', 'hp attack defense sp_atk sp_def speed')
# TODO: add limits on input
class EV(object):
def __init__(self, hp, attack, defense, sp_atk, sp_def, speed):
self.hp = hp
self.attack = attack
self.defense = defense
self.sp_atk = sp_atk
self.sp_def = sp_def
self.speed = speed
class Pokemon(object):
_base_stats = Stats(0, 0, 0, 0, 0, 0)
def __init__(self, iv, ev, nature, level):
# TODO: check input is within correct bounds
self._iv = Stats(*iv)
self._ev = EV(*ev)
self._nature = nature
self.level = level
# TODO: take more data, such as the pokemon's name.
@property
def max_hp(self):
base = self._base_stats.hp
iv = self._iv.hp
ev = self._ev.hp
level = self.level
return int(((2 * base + iv + int(ev/4)) * level) / 100) + level + 10
def _calculate_stat(self, stat):
base = getattr(self._base_stats, stat)
iv = getattr(self._iv, stat)
ev = getattr(self._ev, stat)
level = self.level
# TODO: correct nature
nature = 1
return int((int(((2 * base + iv + int(ev/4)) * level) / 100) + 5) * nature)
@property
def attack(self):
return self._calculate_stat('attack')
@property
def defense(self):
return self._calculate_stat('defense')
class Pikachu(object):
_base_stats = Stats(35, 55, 40, 50, 50, 90)
def pokemon(base, *args, **kwargs):
return type('Pokemon', (base, Pokemon), dict())(*args, **kwargs)
pikachu = pokemon(Pikachu, [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], None, 100)
print(pikachu.max_hp)
print(pikachu.attack)Even if you don't use the previous, then I'd still recommend that you move the data into smaller more logical datatypes.
But either way you should want to then change the way you're storing your moves.
Each move should be an object, it has a name, damage, type, miss chance, special animation. You need to contain all of this in each move.
But you also need to have an instance of each move, so that every move in the game doesn't share the same PP.
And so to create each move, you may want to do the same as above with the
Pikachu class.After this, it'd make more sense to store these instances in a list rather than a dictionary.
And so you should instead have something like:
class Tackle(object):
name = 'tackle'
damage = 70
pikachu = pokemon(..., [Tackle()])Going further, if this is too static, then you can use
type to define these classes. And allow you to off-load storing the data in a database.You should want to take the moves in the constructor of the class as it normally makes saving and loading game state easier.
If you did want to keep your
Moves function then you should change it to add_move and only take one move, not two.Code Snippets
from collections import namedtuple
Stats = namedtuple('Stats', 'hp attack defense sp_atk sp_def speed')
# TODO: add limits on input
class EV(object):
def __init__(self, hp, attack, defense, sp_atk, sp_def, speed):
self.hp = hp
self.attack = attack
self.defense = defense
self.sp_atk = sp_atk
self.sp_def = sp_def
self.speed = speed
class Pokemon(object):
_base_stats = Stats(0, 0, 0, 0, 0, 0)
def __init__(self, iv, ev, nature, level):
# TODO: check input is within correct bounds
self._iv = Stats(*iv)
self._ev = EV(*ev)
self._nature = nature
self.level = level
# TODO: take more data, such as the pokemon's name.
@property
def max_hp(self):
base = self._base_stats.hp
iv = self._iv.hp
ev = self._ev.hp
level = self.level
return int(((2 * base + iv + int(ev/4)) * level) / 100) + level + 10
def _calculate_stat(self, stat):
base = getattr(self._base_stats, stat)
iv = getattr(self._iv, stat)
ev = getattr(self._ev, stat)
level = self.level
# TODO: correct nature
nature = 1
return int((int(((2 * base + iv + int(ev/4)) * level) / 100) + 5) * nature)
@property
def attack(self):
return self._calculate_stat('attack')
@property
def defense(self):
return self._calculate_stat('defense')
class Pikachu(object):
_base_stats = Stats(35, 55, 40, 50, 50, 90)
def pokemon(base, *args, **kwargs):
return type('Pokemon', (base, Pokemon), dict())(*args, **kwargs)
pikachu = pokemon(Pikachu, [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], None, 100)
print(pikachu.max_hp)
print(pikachu.attack)class Tackle(object):
name = 'tackle'
damage = 70
pikachu = pokemon(..., [Tackle()])Context
StackExchange Code Review Q#155817, answer score: 3
Revisions (0)
No revisions yet.