Working on classes for a text based game in python - python

i'm trying to create a function to raise max hp whenever a stat is increased or decreased. i have tried changing self.maxHpCalc() player.** moving the variables = (int) into the orc class in every why i can think of and have been having no luck. depending on how i change it i get self/player have no attribute strength, maxHp or maxHpCalc is not defined.
class player:
def __init__(self, hp = 1 , maxHp = 1, strength = 4, defense = 5):
self.hp = hp
self.maxHp = maxHpCalc()
self.strength = strength
self.defense = defense
def maxHpCalc():
player.maxHp = player.strength + player.defense
class orc(player):
def __init__(self, hp, maxHp, strength , defnese):
super().__init__(hp, maxHp, strength, defense)
print(player.maxHp)
everything i change give me player/self has no attribute strength at the moment

Here's some fixed code, with a small driver program to exercise the classes.
Note the following changes
The function maxHpCalc should be a bound method, if it is to operate on data contained in a player instance. Therefore it should have a self parameter and should reference strength and defense from that self reference.
When calling _maxHpCalc you should reference self. I made it a bound method and it needs an instance to work on. I added an underscore to indicate it's a private method.
You should call maxHpCalc after setting the values of strength and defense, otherwise they are not defined at the point the function is called.
player.maxHp makes no sense. player is a class and has no static
property maxHp, you need an instance to access that property. I create an instance and reference that.
code:
class player:
def __init__(self, hp=1, maxHp=1, strength=4, defense=5):
self.hp = hp
self.strength = strength
self.defense = defense
self.maxHp = self._maxHpCalc()
def _maxHpCalc(self):
return self.strength + self.defense
class orc(player):
def __init__(self, hp, maxHp, strength , defense):
super().__init__(hp, maxHp, strength, defense)
p = player()
o = orc(1,2,3,4)
print(p.maxHp)
print(o.maxHp)
I also have to ask, why include a constructor parameter maxHp if you don't use it but calculate it from other parameters?

Related

Is there a way to better reformat my weapon and armor classes for improved readability?

I have the two classes:
class Weapon:
def __init__(self, Type, quality, material, dmg, name):
self.Type = Type
self.quality = quality
self.name = name
self.material = material
self.identify = 'Melee weapon'
def displayinvent(self):
print(f'{self.quality} quality {self.material} {self.Type} {self.name}'.center(150))
def display(self):
print(f"{self.material} {self.name}")
class Armour:
def __init__(self, quality, material, armour, name):
self.quality = quality
self.armour = armour
self.name = name
self.material = material
self.identify = 'armour'
def displayinvent(self):
print(f'{self.quality} quality {self.material} {self.name} with defense of {self.armour}'.center(150))
def display(self):
print(f"{self.material} {self.name}")
It comes up as:
<skill.Weapon object at 0x7f89696d6640>(1)
<skill.Weapon object at 0x7f89696d6220>(2)
<skill.Weapon object at 0x7f89696d65b0>(3)
<skill.Weapon object at 0x7f89695e6b20>(4)
<skill.Armour object at 0x7f89695e66a0>(5)
<skill.Armour object at 0x7f89695e6c40>(6)
<skill.Armour object at 0x7f89695e6ca0>(7)
<skill.Armour object at 0x7f89695e6d00>(8)
I am aware of the str and repr methods but do not understand them and am not sure about any other methods.
Thanks
Move the common code to a base class, to avoid repeating the code.
Put the printing method there: __str__
from abc import ABCMeta, abstractmethod
class Gear(dict): # Make Gear a dictionary, so it can store
# all kind of properties, to be printed together.
# quality, material, name are common to Armor and Weapon
def __init__(self, quality, material, name):
self.update({'quality': quality, 'material': material, 'name': name})
#property
#abstractmethod
def identify(self) -> str:
# identify() is different for armor and weapo, then here
# is declared as an abstract method, so you know that
# you have to implement identiyfy() each time you create a Gear
# type class, like Weapon or Armor
# Each inheriting class has to provide this property
# it has to return a string that identifies the gear
pass
#abstractmethod
def displayinvent(self):
# Each inheriting class has to provide this property
# it has to return a string that displays the gear
pass
def display(self):
#This method is the same in Weapon and Armor, so is better to be in Gear
print(f'{self["material"]} {self["name"]}')
# Here the code that will be printed when the object is printed
def __str__(self):
return "".join([f"{key}: {value}\n" for key, value in self.items()])
Now, Weapon and Armor can inherit from Gear:
class Weapon(Gear):
def __init__(self, Type, quality, material, dmg, name):
super().__init__(quality, material, name) # <- Will initialize as Gear
# Type is exclusive to Weapon (not to Armor), so is coded here
self["Type"] = Type
# damage is exclusive to Weapon (not to Armor), so is coded here
self["damage"] = dmg
def displayinvent(self):
print(
f'{self["quality"]} quality {self["material"]} {self["Type"]} {self["name"]}'.center(150))
def identify(self) -> str:
return 'Melee weapon'
class Armour(Gear):
def __init__(self, quality, material, armour, name):
super().__init__(quality, material, name) # <- Will initialize as Gear
# armour is exclusive to Armour (not to Weapon), so is coded here
self['armour'] = armour
def displayinvent(self):
print(
f'{self["quality"]} quality {self["name"]} with defense of {self["armour"]}'.center(150))
def identify(self) -> str:
return 'armour'
myArmor = Armour('good', 'steel', 'jacket', 'Sacred Armor of destruction')
myWeapon = Weapon('sword', 'good', 'steel', 60, 'Cutter of Poltergeist')
print(myArmor)
print(myWeapon)
myArmor.display()
myArmor.displayinvent()
myWeapon.display()
myWeapon.displayinvent()
>>> quality: good
>>> material: steel
>>> name: Sacred Armor of destruction
>>> armour: jacket
>>> quality: good
>>> material: steel
>>> name: Cutter of Poltergeist
>>> Type: sword
>>> damage: 60
>>> steel Sacred Armor of destruction
>>> good quality Sacred Armor of destruction with defense of jacket
>>> steel Cutter of Poltergeist
>>> good quality steel sword Cutter of Poltergeist

Python: AttributeError while trying to create child from ABC

I am trying to refactor some old code from a beginner CS course I took in college
I've noticed a lot has changed with python since then (2017)
I also haven't been able to find any very informative online tutorial for writing and using abstract base classes with python (except the docs), So I have very little to go off of for what is the correct way to write and use ABCs.
Currently I'm having two problems, one is more explicit:
Traceback (most recent call last):
File "Hero.py", line 10, in <module>
from Enemy import Enemy
File "C:\Users\Ben\vscodeSCM\CS171Assignments\Zelda_CLI_game\Enemy.py", line 15, in <module>
class Enemy(metaclass = abc.ABCMeta):
File "C:\Users\Ben\vscodeSCM\CS171Assignments\Zelda_CLI_game\Enemy.py", line 32, in Enemy
def __init__(self):
AttributeError: 'function' object has no attribute '__bases__'
which comes up when I try to run 'Hero.py' a script
from Enemy import Enemy
class Hero(Enemy):
'''Class Level Docstring: Hero Object has six attributes,
most of which are nessecary for gameplay mechanisms.
Name is purely descriptive (no impact on game implementation details)
Therefore, name is the only mutable value, able to be set via constructor.
'''
def __init__(self,name):
self._name = name
self.__health = 200
self.__defense_mode = False
self.__bombs = 20
self.__elixers = 5
self.__arrows = 10
def __repr__(self):
'''Returns Hero attributes.
(health and defense_mode are implementation details)'''
return "Health: {} / 200\n\
\r\rBombs: {} / 20\n\
\r\rElixers: {} / 6\n\
\r\rArrows: {} / 10".format(self.__health, self.__bombs, self.__elixer, self.__arrows)
def __str__(self):
'''Returns Hero description'''
return "{}, a young warrior, clad in green".format(self.__name)
def health_bar(self):
'''Returns Hero health points vs. max health points.'''
return "{}/200".format(self.__health)
###Basic. Defense and Special attack for hero, overriden from Enemy.
def basic_attack(self, enemy):
'''Offensive attack that leaves Hero vulnerable.'''
self.__defense_mode = False
enemy.do_Damage(50)
def basic_name(self):
'''Provides name for Hero basic attack.'''
return "used the Master sword"
def defense_attack(self, enemy):
'''Parry attack that bolsters hero defense
while also doing small amount of damage to enemy.'''
self.__defense_mode = True
enemy.do_Damage(20)
def defense_name(self):
'''Returns the name for Hero defense attack'''
return "shield strike"
def special_attack(self, enemy):
'''A Powerful attack that does higher amount of damage
and also swaps defense mode. If defense mode is true,
does the standard amount of damage. If false, does
greater damage but also hurts the Hero.
'''
if self.__bombs:
if self.__defense_mode:
self.__defense_mode = False
enemy.do_damage(75)
return "The bomb had a Direct Hit.\nYour enemy recoils"
else:
self.__defense_mode = True
enemy.do_damage(110)
self.__health -= 20
return "The bomb was very powerful.\n\
Your enemy was badly injured,\n\
but you were damaged as well by the shrapnel"
else:
return "no bombs remaining.\nCannot use Special attack."
def special_name(self):
'''Returns the name of the special attack.'''
return "Used a bomb"
#Hero unique moves- Arrow and Elixer
def use_arrow(self, enemy):
'''Projectile attack:
highly effective, has no effect on defense mode.
but limited availability/ uses- 10 available arrows per game.'''
enemy.do_Damage(95)
self.__arrows= self.__arrows - 1
def arrow_name(self):
'''Returns the name of arrow attack.'''
return "{} fired an arrow".format(self.__name)
def arrow_count(self):
'''Returns the number of arrows remaining vs. maximum.'''
return "{} / 10".format(self.__arrows)
def elixer(self):
'''Replenishes half of the Heros max health point.
but limited uses, also turns defense mode to false.
'''
self.__defense_mode = False
self.__health + 100
self.__elixers - 1
def elixer_name(self):
'''Returns the name of the elixer move.'''
return "drank an elixer"
def elixer_count(self):
'''Returns the amount of remaining elixers vs max elixers.'''
return "{} / 5".format(self.__elixers)
def do_damage(self, damage):
'''
subtracts damage argument from Hero health.
If Defense mode is true, cuts value of damage argument in half.
'''
if(self.__defense_Mode):
self.__health -= damage // 2
else:
self.__health -= damage
def reset_health(self):
'''Sets the health points of Hero to maximum.'''
self.__health = 200
def reset_ammo(self):
'''Sets all the ammunititions to the max/initial value'''
self.__arrows = 10
self.__elixers = 5
if __name__ == "__main__":
print("Running in standby mode")
and here is Enemy abstract base class
import abc
from abc import ABCMeta
#cant figure out how to inherit from metaclass = ABCe
class Enemy(metaclass = abc.ABCMeta):
'''Class Level Docstring: creates signitures/ abstract methods
for init (constructor), __str__ (string repr.) and all helper
methods for gameplay.
NOTE: getter methods are retained from version 1, despite
this class adhering to the Descriptor protocol/ being a
new style class.
This is done in order to facilitate documentation.
When users implement the enemy class they will understand
how use of their object attributes makes gameplay/ battle possible.
'''
version = 2.0
#abc._abc_init
def __init__(self):
'''Construct enemy by passing in name, desc & health as args.'''
pass
#abc.abstractmethod
def __str__(self):
'''Returns all attributes/properties of the enemy.'''
return NotImplemented
#property
#abc.abstractmethod
def get_name(self):
'''Returns the enemy name.'''
return NotImplemented
#property
#abc.abstractmethod
def get_description(self):
'''Returns a short message describing
the enemy and providing any nessecary information.
'''
return NotImplemented
#property
#abc.abstractmethod
def get_health(self):
'''An enemy with 0 health is defeated.'''
return NotImplemented
#gamified versions of setters/mutator methods
#abc.abstractmethod
def do_damage(self, damage):
'''Positive numeric argument does damage,
and negative numeric argument heals enemy.'''
return NotImplemented
#abc.abstractmethod
def reset_health(self):
'''Returns health to starting HP.'''
return NotImplemented
# the attacks include: basic attack, defensive attack, special attack
# each of these has a corresponding method for returning the attack name.
#abc.abstractmethod
def basic_attack(self, enemy):
'''A less powerful, more commonly used attack.'''
return NotImplemented
#abc.abstractmethod
def basic_name(self):
'''returns basic attack name.'''
return NotImplemented
#abc.abstractmethod
def defense_attack(self, enemy):
'''Defensive move that counters or blocks an opponent
attack. some may infrequently do damage to opponent.
'''
return NotImplemented
#abc.abstractmethod
def defense_name(self):
'''Returns defense attack name.'''
return NotImplemented
#abc.abstractmethod
def special_attack(self, enemy):
'''Special attack which has limited availability, that may deal
extra damanage, have increased accurarcy, or some other desired effect.
'''
return NotImplemented
#abc.abstractmethod
def special_name(self):
'''Returns name of special attack,'''
return NotImplemented
The other issue is intellisense or vscode having issues finding the files for my imports, despite them all being in the same directory. It underlines the first occurence of 'Enemy' in from Enemy import Enemy
Is it a problem that my class name and the file containing the class have the same title?
I bring up this second issue, with the imports, because I suspect it may be the cause of the first error. This import line was causing errors earlier, until I edited some things in my preferences: python language specific configuration files ( commented out the line "python.jediEnabled" : false, because of a solution from a different stackoverflow post where a user was having a similar problem) After saving these changes, the Hero.py and Enemy.py scripts worked momentarily, but soon after I started getting this AttributeError mentioned above.
I think the line with the issue is
#abc._abc_init
over your init function in Enemy- where did you see you need to do that?
As a general note, things that start with underscores are considered private and should not be used when imported from other packages.

Can an attributed object access its object's other attributes in python?

I am new to python and apologize in advance if this is too bad.
Suppose i dynamically make an object an attribute of another object.
Can the assigned as an attribute object access the assigned to object's other attributes without inheritance or passing as an argument?
e.g:-
class human:
def __init__(self):
self.health = 100
class fire:
def __init__(self):
self.fire = 10
def damage(self):
????.health -= self.fire #can i do anything to get bill's health?
bill = human()
bill.fired = fire()
bill.fired.damage() #the fired object wants to know the bill object's health
I know i can pass bill's health as an argument to the damage function:-
class human:
def __init__(self):
self.health = 100
class fire:
def __init__(self):
self.fire = 10
def damage(self, obj):
obj.health -= self.fire
bill = human()
bill.fired = fire()
print bill.health
bill.fired.damage(bill) #now the fired object knows bill's health
print bill.health #works fine
But is there any other way or is this a dead end? Apart from the inheritance.
(I'm using python v2.7, but of course would like to know v3 solution too)
Once again i apologize if this question is too bad or has been answered.
I tried to read this one Can an attribute access another attribute?, but i couldn't understand it, its too complex. And if i google this question the results only lead to "How to access objects attributes" e.g this https://www.geeksforgeeks.org/accessing-attributes-methods-python/. And this one How to access attribute of object from another object's method, which is one of attributes in Python? uses inheritance.
Yes, you can pass the human into fire when it is created since they seem to be linked to one another:
class Human:
def __init__(self):
self.health = 100
class Fire:
def __init__(self, human):
self.fire = 10
self.human = human
def damage(self):
self.human.health -= self.fire
bill = Human()
bill.fired = Fire(bill)
bill.fired.damage() #the fired object damages bill object's health
I'm not sure what's your goal, but as I mentioned, your issue looks like a code smell to me (an indication that something's not right).
Assuming you want the human instances to catch fire (i.e. create a fire instance) and then deduce the fire damage their health, consider the refactoring below:
class human:
def __init__(self):
self.health = 100
self.fire = None
def set_on_fire(self):
self.fire = fire()
def suffer_burn_damage(self):
if self.fire is not None:
self.health -= self.fire.damage
class fire:
def __init__(self):
self.damage = 10
bill = human()
print(bill.health) # output: 100
bill.set_on_fire()
bill.suffer_burn_damage()
print(bill.health) # output: 90
This way, you do not need the fire instances to know about the human's health in the first place. It's the human's "job" to keep track of whether it is burned or not, and when to deduce its own damage.
This makes sense in a more abstract meaning, too - which is one of the points of using OOP. A fire in real life has a certain energy. A human that catches fire will have its "health" deduced from whatever amount of energy that fire has. The fire itself has no business knowing about the human's health, or anything else for that matter.

Python, how to create specific classes under a big class

New python learner here. I'm trying to design a text rpg in python and i want the stats of all the enemy mobs to increase at a specific part of the game. Here is my code for some those enemies:
class Executioner:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 800
self.hp = self.maxhp
self.attack = 25
self.ability = 0
self.defense = 0
self.goldgain = 333
self.expgain = 250
ExecutionerIG = Executioner("Executioner the slayer")
class Goblin:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 80
self.hp = self.maxhp
self.attack = 25
self.ability = 0
self.defense = 0
self.goldgain = 5.5
self.expgain = 22
GoblinIG = Goblin("Goblin")
class Zombie:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 180
self.hp = self.maxhp
self.attack = 19
self.ability = 0
self.defense = 0
self.goldgain = 7
self.expgain = 28
ZombieIG = Zombie("Zombie")
I thought a way to do this quickly can be defining those classes into a big class called enemies and then call enemies.hp, enemies.attack etc to increase.
But i don't know how to do that either as i am quite weeb for now.
Suggestions on how to increase class stats by specifying small classes under a big class or ways to increase class stats quickly(just typing them up and increasing them won't work as i will be having lots of enemy mobs) will be greatly thanked.
What Daniel Roseman said is true, you should try to not repeat yourself when writing code. If two objects are very similar and only differ in some certain values, then really they are the same object. All bikes are bikes, but some have different wheel sizes for example. Here, your objects are all one Enemy object, and can be implemented as such.
I will however answer your question, because subclassing is very important to know. To subclass a class, you pass in the parent class in a definition, like so:
class Zombie(Enemy):
Then if the Enemy class is defined like so for example:
class Enemy:
def attack():
<your code>
You could call attack on a Zombie object. It will look for attack() in the Zombie class, and if it doesn't find it, it will look for it in the parent class. If it doesn't find it in the parent class it will look for it in that parent's class and so on, moving all the way up to the top level object. If it doesn't find it at all an Exception will be raised. If you defined attack() in the Zombie class for example, then it would overwrite attack() in the Enemy class.
Given all this though, Daniel Roseman is right. You should put all this in one class and pass in hp etc upon construction.
No, that wouldn't be a good way to do it. Nested classes are rarely useful in Python, and certainly wouldn't be helpful here.
Much better to simply put them into a list you can loop through:
enemies = []
...
ExecutionerIG = Executioner("Executioner the slayer")
enemies.append(ExecutionerIG)
and so on. Then, when necessary, just iterate:
for enemy in enemies:
enemy.hp += 1
As an aside, I would question whether you really need separate classes for Executioner, Zombie and Goblin; they only differ in the numbers for each stat, so perhaps you should just accept those values in the __init__ method for a generic Enemy class.

Class method: 'self' not being read

For some reason, in a method I make, I put self as the first parameter, but it doesn't read it. When I try to run the method, it says it needs the 'self' positional argument.
class Monster():
name = "Snake"
health = 100
def decreaseHealth(self):
if health <= 0:
print('Dead')
health -= 4
Monster.decreaseHealth()
The issue is that you're calling decreaseHealth() on the class itself, whereas you should be calling it on an instance of the class:
m = Monster()
m.decreaseHealth()
This will automatically bind self to m.
P.S. To refer to health inside the method, use self.health.
Here is a version that fixes a couple of other (mainly stylistic) issues:
class Monster(object):
def __init__(self):
self.name = "Snake"
self.health = 100
def decreaseHealth(self):
if self.health <= 0:
print('Dead')
else:
self.health -= 4
m = Monster()
m.decreaseHealth()
If you want a class method, better make it one:
#classmethod
def decreaseHealth(cls):
...
Then you can call Monster.decreaseHealth, and you can access the class variables with cls. (e.g. cls.health). But, then, you are operating on "global" state associated with the class itself.
You probably want to make an instance of your monster instead:
class Monster:
''' Base class for monsters '''
class Snake(Monster):
def __init__(self):
self.name = "Snake"
self.health = 100
def decreaseHealth(self):
self.health -= 4
if self.health <= 0:
print("dead")
mysnake = Snake()
mysnake.decreaseHealth()
That's because the way you are calling decreaseHealth() there is no self argument. self refers to the object on which a method was called. Here you have no object, you are trying to call the method on the class name.
You need to create a Monster object, then call your method:
monster = Monster();
monster.decreaseHealth()

Categories

Resources