I am making a basic python RPG for my daughter and looking for the best way to store character stats that can be added a and subtracted from. Right now, I am using a dictionary so that she can view a list of her stats; however, I can't see a way to automatically add or subtract from objects in the list.
For example, I have
CatAbilities = {'speed': 5, 'claw': 3}
etc. And I want to have speed go down by 2 when, for example, her cat runs to avoid a dog. Is there a better way to do this while keeping a structure that let's her view her stats easily?
Why not use classes?
class Animal:
def __init__(self):
self.Abilities = {}
def PrintStats(self):
print self.Abilities
class Cat(Animal):
def __init__(self):
self.Abilities = {'speed': 5, 'claw': 3}
def ChasedByDog(self):
self.Abilities['speed'] -= 2
def main():
Kitty = Cat()
Kitty.PrintStats()
Kitty.ChasedByDog()
Kitty.PrintStats()
if(__name__ == '__main__'):
main()
Related
I'm mainly focused on an alternative to if/else's in create_animal. If there is a more professional way to handle this.
In this case, it's a classifier based on a variable number of traits needed to figure out what the animal is. Since humans are the only animals that speak English, that property is sufficient. But if they roar instead Bear and Lion need an additional property of habitat to figure it out. I know I could group those conditionals more succinctly, but that's not what I'm trying to illustrate.
class Human:
self.family = 'Hominidae'
self.order = 'Primate'
class Bear:
self.family = 'Ursidae'
self.order = 'Carnivora'
class Lion:
self.family = 'Felidae'
self.order = 'Carnivora'
def create_animal(language, roars, habitat):
if language == 'English':
return Human()
elif roars == True:
if habitat == 'forest':
return Bear()
elif habitat == 'savannah':
return Lion()
animal1 = create_animal(None, roars=True,habitat='forest') # Will be a bear
animal2 = create_animal(language = 'English', roars=False,habitat='town') # Will be a human
animal3 = create_animal(language = None, roars=True,habitat='savannah') # Will be a lion
This will work, but for some real world complexity, I don't like how nasty the nested if/else's are getting and I figure there must a good way to do it with a classification map like this, but I'm not sure how to approach it.
species_classification_map = {
'speaks_english':Human(),
'roars':{
'forest':Bear(),
'savannah':Lion()
}}
One option that at least standardizes your function inputs is to have each animal classified by it's language and habitat and store it in a dictionary
class Human():
def __init__(self):
self.family = 'Hominidae'
self.order = 'Primate'
class Bear():
def __init__(self):
self.family = 'Ursidae'
self.order = 'Carnivora'
class Lion():
def __init__(self):
self.family = 'Felidae'
self.order = 'Carnivora'
def create_animal(language, habitat):
#dict of language, habitat
animals={('english','civilization'):Human(),
('roars','forest'):Bear(),
('roars','savannah'):Lion()}
#human habitat could also be None for simplicity
return animals[(language,habitat)]
b=create_animal('roars','forest')
b
<__main__.Bear at 0x210c8795460>
some_dude=create_animal('english','civilization')
some_dude
<__main__.Human at 0x210c87953a0>
You can define a matrix as a pandas.DataFrame object, whose columns are your animal characteristics, including your animal's name and each row is a record of animal species. Then, when you need to create a new animal with some characteristics, you can easily locate the columns that have a positive value.
Does this satisfy your requirements?
I'm relatively new with a decent amount of experience and I'm trying to make a text based adventure, I'm making a fighting system and wish to have enemy's that have different abilities. Instead of recreating the fight for a different enemy every time, I'm trying to use interchangeable dictionaries for each enemy. My goal is to create a function call that varies depending on what enemy is in the fight without getting into objects. I have an example below and would like to know if there is a way to do something similar.
wolf = {'ability': 'bite'}
bear = {'ability': 'claw'}
enemy = {}
def claw():
print('stuff')
def bite():
print('different stuff')
def use_ability():
enemy = wolf
enemy['ability']()
use_ability()
In python functions are first class objects. You can just use them as values in your dictionary.
wolf = {'ability': bite}
bear = {'ability': claw}
However be careful as there is no forward referencing in python. So make sure you define your functions before you assign them to a dictionary.
def claw():
print('stuff')
def bite():
print('different stuff')
wolf = {'ability': bite}
bear = {'ability': claw}
def use_ability():
enemy = wolf
enemy['ability']()
use_ability()
You can do it:
def claw():
print('stuff')
def bite():
print('different stuff')
wolf = {'ability': bite}
bear = {'ability': claw}
def use_ability(enemy):
enemy['ability']()
use_ability(wolf)
# different stuff
It really doesn't mean you should do it this way, though.
Use Object-Oriented programming. If you only want to use dicts and functions, you probably should write Javascript instead.
I can't help myself but to make a little program explaining how it should be done in an Object Orientated Language:
You should look up some guides how OOP-Languages work, because when making a game it will be really helpfull if you do it that way
http://www.python-course.eu/object_oriented_programming.php
# This is the SUPERCLASS it holds functions and variables
# that all classes related to this object use
class Enemy(object):
# Here we initialise our Class with varibales I've given an example of how to do that
def __init__(self, HP, MAXHP, ability):
self.HP = HP
self.MAXHP = MAXHP
self.ability = ability
# This function will be used by both Bear and Wolf!
def use_ability(self):
print(self.ability)
# This is our Wolf Object or Class
class Wolf(Enemy):
# Here we init the class inheriting from (Enemy)
def __init__(self, ability, HP, MAXHP):
super().__init__(HP, MAXHP, ability)
# Here we call the superfunction of this Object.
def use_ability(self):
super().use_ability()
# Same as Wolf
class Bear(Enemy):
def __init__(self, ability, HP, MAXHP):
super().__init__(HP, MAXHP, ability)
def use_ability(self):
super().use_ability()
# How to init Classes
wolf_abilities = 'bite'
w = Wolf(wolf_abilities, 10, 10)
bear_abilities = 'claw'
b = Bear(bear_abilities, 10, 10)
# How to use methods from Classes
b.use_ability() # This will print 'bite'
w.use_ability() # This will print 'claw'
I am making a basic RPG style game. I have made different classes for the various parts of the code, one for each of the main items involved (hero, door, monsters etc.)
For both the hero and door, i assign them random locations, shown below in the code, but for the door I run a while loop which makes sure that the door is a certain distance from the hero (using pythagorus).
However the while loop in the door class won't work as it always uses a value of 0 for both heroC and heroR (row and column of the hero). I am relatively new to using classes, but it doesnt seem to make sense as in HeroLocation I assign a random integer to these variables, and HeroLocation is called before DoorLocation.
Any help would be greatly appreciated!!
class Hero(Character):
def __init__(self):
super(Hero, self).__init__(10, 10, 1, 1, 0, 1)
self.herolocations = list(range(1,6)) + list(range(10,14))
self.heroC = 0
self.heroR = 0
def HeroLocation(self):
#place hero
self.heroC = random.choice(self.herolocations)
self.heroR = random.choice(self.herolocations)
class Door:
def __init__(self):
self.hero = Hero()
self.doorC = 0
self.doorR = 0
def DoorLocation(self):
while ((self.hero.heroC-self.doorC)**2+(self.hero.heroR-self.doorR)**2) <= 128:
self.doorC = random.randint(1, 13)
self.doorR = random.randint(1, 13)
class game:
def __init__(self, parent):
self.hero = Hero()
self.door = Door()
def MakeMap(self):
self.hero.HeroLocation()
self.herol = self.Main_game.create_image(15+30*self.hero.heroC,15+30*self.hero.heroR, image = self.heroimage)
self.door.DoorLocation()
self.doorl = self.Main_game.create_image(15+30*self.door.doorC,15+30*self.door.doorR, image = self.exitdoor)
NB there is a lot more code, but i have only posted what i felt was the relevant stuff, if you need more to crack the puzzle message me!
You are not calling the good Hero instance in Door.DoorLocation.
Btw I really advice you to change class & methods name following Pep 8.
In Door.__init__, first line:
self.hero = Hero()
Here, you are instantiating a new Hero's instance. But, in game.MakeMap you are calling self.hero.HeroLocation().
This self.hero instance is not the same, because it was instantiated in game.__init__ and not in Door.__init__.
I didn't try, but check what behaviour gives this update:
class game:
def __init__(self, parent):
self.door = Door()
self.hero = self.door.hero
With this you now are calling the instance defined in Door.__init__, so when doing self.hero.HeroLocation() in game and (self.hero.heroC-self.doorC [...] in Door you are pointing the same instance.
Last thing, this solution may works, but is surely not what you really wants, I think a door should not store a hero, a hero should not store a door too, but here is more complex question about patterns.
I am creating a simple game that contains classes called 'Player' and 'Strategy'. I want to assign a Strategy instance to the Player instance when the Player is created.
class Player(object):
def __init__(self):
self.Strategy = None
def Decision(self, InputA, InputB):
Result = self.Strategy(InputA, InputB)
return Result
def SetStrategy(self):
# Sets a strategy instance to the Player instance
class Strategy(object):
def Strategy1(self, InputA, InputB):
return InputA * InputB
def Strategy2(self, InputA, InputB):
return (InputA - InputB) / 2
def Strategy3(self, InputA, InputB):
return 0
What I'm trying to achieve:
in[0] Player1 = Player()
in[1] Player2 = Player()
in[2]: Player1.SetStrategy('Strategy1')
in[3]: Player2.SetStrategy('Strategy3')
in[4]: Player1.Decision(2,5)
out[0]: 10
in[5]: Player2.Decision(3,6)
out[1]: 0
Searching here and via google shows me ways of doing it with monkey patching but the approach looks a little inelegant (and although I'm a beginner I think there's a better way to do it) - is there a way to do this with inheritance that I'm not seeing?
def strategy1(inputA, inputB): # 2
return inputA * inputB
def strategy2(inputA, inputB):
return (inputA - inputB) / 2
def strategy3(inputA, inputB):
return 0
strategy = {
'mul': strategy1,
'diff': strategy2,
'zero': strategy3
}
class Player(object):
def __init__(self, strategy_name='mul'): # 1
self.strategy_name = strategy_name # 5
def decision(self, inputA, inputB): # 4
result = strategy[self.strategy_name](inputA, inputB)
return result
player1 = Player()
player2 = Player()
player1.strategy_name = 'mul' # 3
player2.strategy_name = 'zero'
print(player1.decision(2, 5))
# 10
print(player2.decision(3, 6))
# 0
Every player has a strategy, so don't allow instantiation of Player
without assigning some strategy. You could use a default strategy
(as shown below), or make strategy a mandatory argument.
The strategies could be plain functions; I don't see a reason to
bundle them as methods of a Strategy class. Always keep code as
simple as possible; don't use a class when a function would suffice;
use a class when it provides some feature (such as inheritance) which
makes the class-based solution simpler.
In Python there is no need for getters/setters like setStrategy.
You can use plain attributes for simple values, and properties to
implement more complicated behavior. Attributes and properties use
the same syntax, so you can switch from one to the other without
having to change have the class is used.
There is a convention (recommended in PEP8) that classes be named in
CamelCase, and instances, functions and variables in lowercase. The
convention is used ubiquitously, and following it will help other
understand your code more easily.
To make it easy to store the strategy in a database, you could store
the strategy_name in the database, and use a lookup dict (such as
strategy) to associate the name with the actual function.
As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.