I'm making a game and one of the methods calculates a character's base hit numbers based on skill values. The method currently calculates each value individually, since each skill can be used at short, medium, and long range.
I originally thought I could combine the skills into a tuple and iterate over it, dynamically creating each hit number. But I don't know if it's actually possible, since I currently have each hit number assigned to it's own variable.
I also thought about creating a method for each range, and passing the tuple as an argument. I could create a new tuple or list with the resulting values and then assign them to the individual variables, but I don't see how it would be any better than do it this way, except that it won't look so copy & pasted.
Here's what I currently have:
def calcBaseHitNumbers(self, dict):
"""Calculate character's base hit numbers depending on skill level."""
self.skill_dict = dict
self.rifle = self.skill_dict.get('CRM', 0)
self.pistol = self.skill_dict.get('PST', 0)
self.big_gun = self.skill_dict.get('LCG', 0)
self.heavy_weapon = self.skill_dict.get('HW', 0)
self.bow = self.skill_dict.get('LB', 0)
#self.skill_tuple = (self.rifle, self.pistol, self.big_gun, self.heavy_weapon,
# self.bow)
#---Short range
## for skill in self.skill_tuple:
## self.base_hit_short = skill * 0.6
self.charAttribs.bhCRM_short = self.rifle * 0.6
self.charAttribs.bhPST_short = self.pistol * 0.6
self.charAttribs.bhHW_short = self.heavy_weapon * 0.6
self.charAttribs.bhLCG_short = self.big_gun * 0.6
self.charAttribs.bhLB_short = self.bow * 0.6
#---Med range
self.charAttribs.bhCRM_med = self.rifle * 0.3
self.charAttribs.bhPST_med = self.pistol * 0.3
self.charAttribs.bhHW_med = self.heavy_weapon * 0.3
self.charAttribs.bhLCG_med = self.big_gun * 0.3
self.charAttribs.bhLB_med = self.bow * 0.3
#---Long range
self.charAttribs.bhCRM_long = self.rifle * 0.1
self.charAttribs.bhPST_long = self.pistol * 0.1
self.charAttribs.bhHW_long = self.heavy_weapon * 0.1
self.charAttribs.bhLCG_long = self.big_gun * 0.1
self.charAttribs.bhLB_long = self.bow * 0.1
How would you refactor this so it's more dynamic?
Edit: I guess what I want to do is something like this:
Have a tuple (like the one I commented out) and iterate over it 3 times, each time making a new value (for each skill) based on the modifier for each particular range. The resulting value is then automatically assigned to it's respective variable.
In my head, it makes sense. But when I actually try to code it, I get lost. The problem, I think, is that this is the first "real" program I've written; all I've done before are small scripts.
This is only the 0.1 version of my program, so it's not critical to refactor it now. However, it seems very un-Pythonic to do this manually and I also want to "future-proof" this in case things change down the road.
It feels like what you really want is a class representing the weapon, with attributes to handle the base values and calculate hit values with various modifiers. Here's a simple example:
SHORT_RANGE = 'S'
MEDIUM_RANGE = 'M'
LONG_RANGE = 'L'
SHORT_RANGE_MODIFIER = 0.6
MEDIUM_RANGE_MODIFIER = 0.3
LONG_RANGE_MODIFIER = 0.1
class Weapon(object):
def __init__(self, code_name, full_name, base_hit_value,
short_range_modifier=None, medium_range_modifier=None,
long_range_modifier=None):
self.code_name, self.full_name = code_name, full_name
self.base_hit_value = base_hit_value
self.range_modifiers = {
SHORT_RANGE: short_range_modifier or SHORT_RANGE_MODIFIER,
MEDIUM_RANGE: medium_range_modifier or MEDIUM_RANGE_MODIFIER,
LONG_RANGE: long_range_modifier or LONG_RANGE_MODIFIER,
}
def hit_value(self, range, modifier=1):
return self.base_hit_value * self.range_modifiers[range] * modifier
From there, you might create instances of Weapon inside your Character object like so:
self.rifle = Weapon('CRM', 'rifle', 5)
self.pistol = Weapon('PST', 'pistol', 10)
And then if, say, the character fires the pistol at short range:
hit_value = self.pistol.hit_value(SHORT_RANGE)
The extra argument to the hit_value() method can be used to pass in character- or situation-specific modifications.
Of course, the next step beyond this would be to directly model the weapons as subclasses of Weapon (perhaps breaking down into specific types of weapons, like guns, bows, grenades, etc., each with their own base values) and add an Inventory class to represent the weapons a character is carrying.
All of this is pretty standard, boring object-oriented design procedure, but for plenty of situations this type of thinking will get you off the ground quickly and provide at least a little bit of basic flexibility.
Lets see if I understand you scenario: each weapon has its own distinct hit point so a rifle may have 1, a heavy weapon may have 2 etc. Then each character has a short, medium and long value to be multiplied by the hit point of the weapon.
You should consider using a Strategy design. That is create a weapon superclass with a hit point property. Create sub class weapons for rifle, pistol, bow etc. I am sure that the differences between the weapons are more than just the hit points.
Then the Character has one or more weapons depending on your gameplay. To calculate the hit point for a particular weapon is as simple as
current_weapon * self.medium
If you decide to add more weapons later on then you do not have to edit your Character code because your character can handle any weapon.
In Pseudo Python
class Weapon
hit = 1
#other properties of weapon
class Rifle(Weapon)
#other properties of Rifle
class Pistol(Weapon)
#other properties of Pistol
class Character
weapon = Rifle()
long=0.6
def calcHit()
return self.long*weapon.hit
john = Character()
john.weapon= Rifle()
john.calcHit
#Vinko: perhaps make calcBaseHitNumbers, do the "if not self.calculatedBase:" check internally, and just no-op if it's been done before. That said, I can't see the pressing need for precalculating this information. But I'm no Python performance expert.
What sense of dynamic do you mean? What is likely to vary - the number of skills, or the weighting factors, the number of ranges (short, med, long) or all of these?
What happens to the (e.g.) bhPST_* values afterwards - do they get combined into one number?
One thing that leaps out is that the list of skills is hardwired in the code - I would be inclined to replace the bh variables with a method
So (please take into account I don't know the first thing about Python :) )
def bh_short(self, key)
skill = self.skill_dict.get(key, 0)
return skill * 0.6
Now you can keep a list of skills that contribute to hit points and iterate over that calling bh_short etc.
Possibly also pass the range (long med short) unto the function, or return all three values - this all depends on what you're going to do next with the calculated hitpoints.
Basically, we need more information about the context this is to be used in
I would have a class for the character's attributes (so you don't have heaps of things in the character class) and a class for a weapon's attributes:
class WeaponAttribute(object):
short_mod = 0.6
med_mod = 0.3
long_mod = 0.1
def __init__(self, base):
self.base = base
#property
def short(self):
return self.base * self.short_mod
#property
def med(self):
return self.base * self.med_mod
#property
def long(self):
return self.base * self.long_mod
class CharacterAttributes(object):
def __init__(self, attributes):
for weapon, base in attributes.items():
setattr(self, weapon, WeaponAttribute(base))
Have a CharacterAttributes object in the character class and use it like this:
# Initialise
self.charAttribs = CharacterAttributes(self.skill_dict)
# Get some values
print self.charAttribs.CRM.short
print self.charAttribs.PST.med
print self.charAttribs.LCG.long
Related
I am trying to create a Python Class to calculate the Kelly Criterion formula in order to determine the precise bet size for an individual sport's investment. I am not very good with using the Class function or init function and could use some help.
The Class I am trying to create uses three functions:
Function 1 asks the user to input the estimated win percentage of the bet (saved to variable 'winP').
Function 2 asks the user to input the American odds for this particular bet. I then convert the American odds to Decimal odds to use with the Kelly Criterion formula (saved to variable 'odds').
Function 3 takes 'winP' and 'odds' and implements this data into the Kelly Criterion formula.
The Kelly Criterion formula that I am using is:
Kelly Criterion (kCrit) = ((odds - 1) * (1 - winP)) / (odds - 1)
'odds' is the Decimal form of the American odds after conversion. 'winP' in the expected winning probability of this particular bet.
I was able to get the 1st and 2nd function to work perfectly (win_percentage, convert_to_decimal), however I was unable to get the 3rd function to work (implement_kc)
Here is my code below:
class KellyCriterion:
def win_percentage(percent):
winP = int(input('What is your win percentage?: '))
return winP
def convert_to_decimal(odds):
odds = int(input('What are the American odds?: '))
if odds > 0:
odds = (odds/100) + 1
return odds
elif odds < 0:
odds = -(100/odds) + 1
return odds
def implement_kc(winP, odds):
kCrit = ((odds - 1) * (1-winP)) / (odds-1)
return kCrit
winPercent = KellyCriterion()
winPercent.win_percentage()
betSize = KellyCriterion()
betSize.convert_to_decimals()
I was not sure how to call on the 3rd function properly:
kelly = KellyCriterion()
kelly.implement_kc()
I received an error: NameError: name 'winP' is not defined.
I am a beginner with using Class functions and could use some help. I also tried using an init(self) function but not exactly sure how those work.
Any suggestions would be greatly appreciated. Thanks in advance for any help that you may offer.
Just to clarify the 1st function (win_percentage) and 2nd function (convert_to_decimal) work just fine. I am having issues with the 3rd function (implement_kc).
I would like to find a way to call on the KellyCriterion Class to: 1) ask the user what is their win percentage; 2) ask the user what are the American odds; 3) implement both of their responses into the Kelly Criterion formula to find out the appropriate bet size.
Thanks again!
If you want to write a class, you need to pass self to the functions. Moreover, the way you have winPercent = KellyCriterion() and betSize = KellyCriterion() means you have two separate instances of the KellyCriterion class, which don't communicate with one another. What you want, is a single instance so you can assign both winP and odds to that instance, otherwise any call to the implement_kc() method is going to be missing values and return an error.
As an aside, here's a post that shows a class-based implementation of the Kelly Criterion and some more background on how it's done. Could be helpful for reference.
Anyway, here's some code that will work, at least if I understand what you're trying to accomplish:
class KellyCriterion:
def win_percentage(self):
winP = int(input('What is your win percentage?: '))
self.winP = winP / 100
def convert_to_decimal(self):
odds = int(input('What are the American odds?: '))
if odds > 0:
self.odds = (odds/100) + 1
elif odds < 0:
self.odds = -(100/odds) + 1
def implement_kc(self):
kCrit = ((self.odds - 1) * (1-self.winP)) / (self.odds-1)
return kCrit
If we run it:
KC = KellyCriterion()
KC.win_percentage()
KC.convert_to_decimal()
print(f"Wager: {KC.implement_kc():.2f}%")
If we enter, say 51 and -110 when prompted for input, then we get:
Wager: 0.49%
Now each of the input functions you defined assign an attribute to the class (e.g. self.winP and self.odds) that the implement_kc() method will use later when you call it.
I'm trying to get a class to spit out a number with units. The purpose of it having units would be to be able to work with the number later, but without the units getting in the way as a string. Though I have no idea how to do this. I'm quite new with (actually understanding) classes, I'm not even sure if what I ask is possible or not.
My goal is to make a class for making atoms, based on their protons, electrons and neutons. The thing I wanna try is to be able to see the units of the mass (in kgs for my purpose) when I try printing it. Here's what I have:
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
def main():
Hydrogen = Atom(1,1,0)
print (Hydrogen.mass)
Argon = Atom(18,18,22)
print (Argon.mass)
if __name__ == "__main__":
main()
This code works completely fine, so no worries about that :p
Is there a way to even do this? If so, how can it be done? Thanks!
I'm not sure if this is exactly what you're looking for, but you could have a method inside the Atom class that prints the mass of the atom plus a unit string.
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
def print_mass_str(self):
print('{} {}'.format(self.mass, 'kg'))
Then this:
Argon = Atom(18,18,22)
print(Argon.mass)
Argon.print_mass_str()
would print:
6.64216e-26
6.64216e-26 kg
There are ways of doing it, but firstly you should ask yourself whether you want this. Having different units can get confusing. You can put conversions at the appropriate places (e.g. after reading the input from user) and have your system work solely on a given unit (that's why we have SI units, after all). Being able to support multiple units internally not only complicates the code, but introduces another source of possible confusion.
There are packages which do this for you, such as units, numericalunits or pint.
An example taken from units documentation is:
>>> from units import unit
>>> metre = unit('m')
>>> second = unit('s')
>>> print(metre(10) / second(2))
5.00 m / s
>>> print(metre(10) ** 3)
1000.00 m * m * m
See how metre creates a meter value, and it keeps track of its usage. It has also support for defining custom units.
Therefore, you could simply store the values in your class as values from units or other package, and you're all set. I looked a bit into the code of units and is quite short and I think it's a good source of learning how to handle stuff like this in your own code.
I have to cite the failure of Nasa Mars Climate Orbiter, which was due to a unit discrepancy.
To override the string representation of the number, make a wrapper class:
class FloatWrapper:
def __init__(self, float, unit):
self.float = float
self.unit = unit
def __getattr__(self, key):
try:
return object.__getattr__(self, key)
except AttributeError:
return getattr(self.float, key)
def __str__(self):
return str(self.float) + self.unit
Now in your __init__, after defining self.mass, add:
self.mass = FloatWrapper(self.mass, 'kg')
This works?
Basically I have added another property to the Atom class so that all objects of type Atom can use it. Another way of doing it would be to pass it as you are doing with the neutrons/protons/electrons. That way it you can perform your own calculations (if there was a requirement to do so) given a unit (say you wanted to show weight in grams for Hydrogen , while in KGs for Argon)
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
self.massUnit = "kgs"
def main():
Hydrogen = Atom(1,1,0)
print ("{0} {1}".format(Hydrogen.mass,Hydrogen.massUnit))
Argon = Atom(18,18,22)
print ("{0} {1}".format(Argon.mass,Argon.massUnit))
if __name__ == "__main__":
main()
Output
1.66054e-27 kgs
6.64216e-26 kgs
I am new to python, about 3 days, and I am not sure if I have worded the question properly.
I have a class:
class blue_slime:
nom = "BLUE SLIME"
img = " /\ \n( o o)"
str = 10
int = 5
dex = 5
con = 10
spd = 10
hp = ((con + str) / 2)
mp_bonus = 1
and I want to use the variables from this class in another function.
def encounter(nom, hp, img):
print(char_name + " encountered a " + nom + "!!!")
wait()
while hp > 0:
battle(hp, img)
else:
stats()
now I know that I could call this by using
encounter(blue_slime.nom, blue_slime.hp, blue_slime.img)
but I would much rather (and think it may be necessary for my program down the line) be able to just use the class name as the function argument and then in the function I can just utilize all variables without having to write them in each time. While this may sound like laziness, I am thinking about making the encounter be random, so 10% chance to encounter(blue_slime) 10% chance to encounter(green_slime).
I feel the easiest way to implement this would be to somehow condense all the variables in "class blue_slime" into one name.
please let me know if there is a way to do this, perhaps I have just not learned it yet.
You can just pass the class into the function is that's what you wanna do. That will solve your problem:
def encounter(monster):
monster.hp
monster.img
# etc.
Here's some tips:
As was already mentioned in the comments on your question, you probably want to be using instances of those classes instead of the actual classes. I'll give a sample class with a few pointers:
class BlueSlime: # Using CapCase like this is normal for class names
# You can use this space to make class variables.
# These will be the same across all the classes.
# Probably use this for initializing your instances
base_hp = 5
base_int = 10
base_dmg = 3
def __init__(self): # The "Constructor" of your instances
self.current_hp = self.base_hp # self refers to the instances
self.int = self.base_int
self.dmg = self.base_dmg
The instance thing is nice because if some of your slimes take dmg, you don't necessarily want them all to take dmg.
bs1 = BlueSlime() # Init the instance
bs2 = BlueSlime()
# bs1 takes 1 dmg (May want a method to do this)
bs1.hp -= 1
bs1.hp
# Now 4
bs2.hp
# Still 5
Getting back to your question, at this point, you can pass these instances into your encounter function.
def encounter(monster):
# Something happens here to reduce the hp, it will reduce the instance's hp
# Something happens here to deal dmg, you can look it up on the instance
player.hp -= monster.dmg # Something like this....
# etc
I'm making a simple text-based game.
My full code is 150 lines, so I'll include what I think is relevant. Let me know if you need more.
The problem is that this line:
print("You deal " + str(hero.damage) + " damage to the monster")
returns only 5 instead of 5 + level as wanted.
class Hero:
def __init__(self):
self.level = 0
self.damage = 5 + self.level #This is the problem line. Self.level seems to be treated as equal to 0 even when it is higher than 0.
self.experience = 0
def level_up(self): #triggered on monster death
xp_required = 15
if self.experience >= xp_required:
self.level += 1
hero = Hero()
I know hero.level_up() is successful because:
print(hero.level)
returns a level that gets correctly updated as appropriate.
I'm guessing either:
self.damage only gets calculated once, then stores that value even after components of it have changed.
or:
There is some kind of issue with calling __init__ values within __init__.
or:
The calculation is done on the class Hero, not the object Hero()
You are right, self.damage is only written to once: In the __init__ constructor which is itself only called once (when the object is created). And at that time, self.level is zero, so self.damage will be always 5.
You have two options:
Either you change your code to always update the damage as well whenever you change the level. In order to avoid spreading the damage calculation logic into multiple places, you would create a helper method to update the damage value:
def update_damage(self):
self.damage = 5 + self.level
def level_up(self):
if self.experience <= 15:
self.level += 1
self.update_damage()
If the damage value is only ever dependent on the level, you could also make it a update_level(new_level) method; but I opted out of that solution here since it’s not uncommon to have some other things affect damage later on as well (e.g. damage potions or whatever).
Or you change damage into a calculated property, so the damage value is calculated whenever you access it. That has the overhead that it needs to be calculated every time instead of the value being stored somewhere, but on the other hand, you have the ultimate truth about the damage value and you do not need to remember to update it elsewhere:
def __init__(self):
self.level = 0
self.experience = 0
#property
def damage(self):
return 5 + self.level
Your guess about self.damage being calculated once is correct. As #Willem Van Onsem mentions, self.damage= 5+self.level is an assignment and not an equation. You will need to manually update it each time you change self.level.
The most suitable approach to me seems to be wrap it into a property such as:
#property
def damage(self):
return 5 + self.level
The way that you are treating equality is more closely related to reactive programming which is often emulated with properties in python.
I'm making a program for calculate the electrical consumption of a building starting from the characteristics of the building, like the number of apartments and its type, I mean all not need to be of the same size for example, so I create a class called apartment, something like this:
class apartamento:
def __init__(self):
self.area = 0
self.cantidad = 0
self.altura = 0
self.personas = 0
self.area = 0
self.dotacion = 0
self.cto_alum = 0
self.cto_tug = 0
self.cto_e = 0
So I could have let's say thirty apartments fifteen of 100m^2 and others fifteen of 80m^2, the I want:
type1 = apartamento()
type2 = apartamento()
type1.area = 100
type2.area = 80
But since I dont know how many types of apartments are, I need to create them when the program is running in a loop for example
When I say I don't know how many types of apartments are, I refer to the fact that this could be used for someone else, in differents sets of buildings, so in one It could be only one type of apartment and in other could be ten, and this has to be transparent to the user, now I have a spin box to put how many types of apartments are and then I create a table to put all the data of them, their size, number of person, number of circuits that it has, the problem is that them I have to make some calculations on this data so I want to instantiated every type of apartment as a object with the atributes that are put in the table, now I dont known how many types there will be, it depends of the building.
Most complex programs solve this problem using a reload feature that they incorporate into some kind of daemon. For instance nginx
service nginx reload
Will update sites with new configuration parameters without having to actually restart the server.
In your case since this sounds like a more simple program you can always just have a python config file and read from it at a set interval.
import imp
config_module = imp.load_source("config", "/path/to/config.py")
And your configuration could look like
CONFIG = {
"type1": {
"altura": 10,
"personas": 2,
"cantidad": 5,
},
"type2": {
...
}
}
At a certain interval you can just ask your program to look up the source of the configuration module and then you won't have to worry about your configuration being up to date.
Either that or you can just stop your program and then run it again in order to pick up the latest changes without having to wait for your config to reload.
After the configuration is updated you can create new apartment objects as you desire
apartmentos = {}
for type, values in config_module.CONFIG.iteritems():
apartmentos[type] = Aparatmento(values)
Where Apartmento now accepts a new dictionary of configuration
class Apartmento(object):
def __init__(self, config):
personas = config["personas"]
altura = config["altura"]
...
Without knowing what exactly is it that you need, this is my take on it.
It's most likely the dumbest approach to it. It looks to me like all you want to create is a building with some fixed number of apartments, some of which are of type 1 some of which are type 2.
Types of apartments don't sound to me like something that is preset, but more like something that is specific to each building and its plans. I.E. some buildings have apartments split in 2 categories, 100m^2 and 80m^2 and some have 90 and 50?
`class apartment:
def __init__(self, area):
self.area = area
class building:
def __init__(self, ntypesOfapartments):
self.ntypesOfapartments = ntypesOfapartments
self.apartments = list()
for i in range(ntypesOfapartments):
area = input("Area of {0}. type of apartment: ".format(i))
n = input("Number of apartments of area {0}: ".format(area))
for j in range(int(n)):
self.apartments.append(apartment(area))
def __str__(self):
desc = "Building has {0} types of apartments \n".format(self.ntypesOfapartments)
desc += "Total number of rooms is {0}.".format(len(self.apartments))
return desc
So you can instantiate a building with n different apartment types, and then define your apartment types interactively in the interpreter.
>>> test = building(2)
Area of 0. type of apartment: 100
Number of apartments of area 100: 5
Area of 1. type of apartment: 80
Number of apartments of area 80: 10
>>> print(test)
Building has 2 types of apartments
Total number of rooms is 15.
>>>
Of course it would be a lot better to use a more sensical data structure than a list to hold the apartments, like a dictionary. That way you could access apartment type 1 in a dictionary under which you could have a list of all apartments that match your defined type 1.
This also very depends on the type of calculations you want to do with them? What are all the important data that you have to use in those calculations? In this example I only instantiate apartment area, if it turns out that number of people that live in it, is important to your calculations too, then it doesn't make sense to instantiate all of that by hand. Also what do you mean you don't know how many apartment types there are? What do you consider as a "running python program"? Do you mean interpreter, or are you writing something else?