I'm creating a basic game to help understand classes, where by the Player() instance (Ben), owns a number of buildings and units. Each player, such as ben will have various buildings such as a Plane factory or Bot Factory.
class Player:
def __init__(self):
self.money = 200
self.score = 0
print('Created ')
def increase_money(self, amount):
self.money += amount
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building(Player):
def __init__(self):
self.level = 0
self.upgrade_cost = 100
print('created building')
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
ben = Player()
I have realised that if I call the Building() class to create an instance, it actually creates an building instance that inherit the properties of player (i.e. each building would have its own money).
How would I have each player contain various buildings and units with different properties? For example, Ben and Bill have different buildings. In this case, would I use a single Player() class and then use functions inside of Player()?
First of all, Building inheriting from Player does not make much sense, a is-a relationship between Building and Player seems weird and like a semantic error.
Just don't inherit from Player, i.e.:
class Building(object):
# code for the class
Now for your other question, if you want each Player to be able to aggregate multiple instances of Building objects, you could make each instance of Player have a collection attribute (a list or a set) to store the Building instances.
To achieve this, you can add the line
self.buildings = []
to the __init__ method of Player. Initially, this list will be empty. To add a building for a specific player, append to its buildings attribute, for example:
p = Player()
p.buildings.append(Building())
p.buildings.append(Building())
p.buildings[1].upgrade_building()
The player p now has two buildings, and the second one is upgraded once. This is just a general demo of how you can do it. I can't say much more without more information about the direction you want your program to take.
Your player constructions don't have an attribute to track buildings owned. Your buildings don't have an owner, either. So you need:
class Player:
def __init__(self):
self.money = 200
self.score = 0
self.buildings = []
print('Created ')
def increase_money(self, amount):
self.money += amount
def add_building(self, Building):
#Here's what to call when we construct a new building with the owner
self.buildings.append(Building):
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building:
def __init__(self, Owner, Type)
#Constructor now requires an Owner and Type:
self.level = 0
self.owner = Owner
self.Type = Type
self.upgrade_cost = 100
print('created building')
Owner.add_building(self) #And now we will add this to the owner's buildings set
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
To create a building, you'll call it like so:
P = player()
B1 = Building(P, "Building type")
and this will add to the player's list of owned buildings. In addition, you can look at each building for an owner ("How many Bot Factories are built?"). You can also look at the player for their buildings.
So this is answer is more for reference purposes - someone who may be trying to do the same sort of thing.
I actually found the best way to do it, and cleanest was to call the Building class from the Player class:
class Player:
def __init__(self):
self.money = 200
self.score = 0
print('Created ')
self.bot_factory = Building('Bot Factory') # [1]
self.vehicle_factory = Building('Vehicle Factory')
def increase_money(self, amount):
self.money += amount
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building(Player):
def __init__(self):
self.level = 0
self.upgrade_cost = 100
print('created building')
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
ben = Player()
This way, each player, such as Ben has their own versions of the bot_factory and vehicle_factory. I then intend to upgrade the buildings by a call such as ben.building_upgrade('bot','upgrade'), which calls the Building functions from the Player class.
Related
How do I set a boolean from a method to create "chance" with a poison status, or use later for dodge and other reusable.
Just now learning Simple Inheritance with Super() and not yet gotten to Complex Inheritance. Not sure if that would make a difference in how I could of coded this but using what I know, here is what I have.
What I am trying to do is create a method inside the Hero Class, and make the hero only be poisoned if a condition is met or a boolean is passed. I keep getting an issue with the logic. Still new to programming.
I've re-written the code multiple times using Idle for speed and it just keeps giving me problems, even when i place the for-loop inside the hero.PoisionedCheck(True) I only get the displayed text and not the forloop activated. Then I will get an error that "amount" parameter is not defined or called before assignment, then when I fix that I will get that scorpion.health and scorpion.energy is not defined. I'm running in circles.
Thank you for reading.
import time
class Monster:
name= 'Monster'
def __init__(self,health, energy):
self.health = health
self.energy = energy
def attack(self, amount):
print('Monster has attacked')
print(f'{amount} damage was delt!')
self.energy -= 20
def move(self,speed):
print(f'{self.name} has moved, at {speed} speed')
class Scorpion(Monster):
name='Scorpion'
def __init__(self, health, energy):
super().__init__(health, energy)
def attack(self, amount):
print('Scorpion has attacked')
print('Poison has take effect!')
# Loops Poison display repeated with time delay.
# for repeater amount of times.
repeater = 5
for poison in range(repeater):
poison = 0
amount = poison + amount
print(f'Poisoned: {amount} damage')
time.sleep(0.5)
poison = repeater
amount *= poison
print(f'Poison did a total damage of {amount} to {hero.name}')
hero.health -= amount
class Hero(Monster):
name = 'Rockwood'
def __init__(self, health, energy) -> None:
self.health = health
self.energy = energy
# How do I use this in a boolean to check Scorpion attack?
# Lets say later adding *import random* later to determine chances of poison.
# def PoisonedCheck(self, posioned):
# self.poisoned = posioned
# if posioned == True:
# print("Testing if become posioned- For loop goes after here to create damage.")
# posioned = True
# else:
# print('Became else.. ')
# posioned = False
monster = Monster(health=100, energy=100)
scorpion = Scorpion(health = 200, energy = 150)
hero = Hero(health=100, energy=100)
scorpion.attack(3)
print()
scorpion.move(110)
hero.move(50)
print(f"{hero.name} has {hero.health} health remaining. ")
print()
To be honest, I am not really sure what you are trying to achieve, hopefully this answers your question or gets you going.
Firstly I would suggest adding a target parameter to Scorpion's attack behaviour and modifying it's behaviour. That way you can define multiple heroes and have them combat the scorpion, for example:
scorpion = Scorpion(health = 200, energy = 150)
hero_one = Hero(health=100, energy=100)
hero_two = Hero(health=200, energy=100)
scorpion.attack(hero_one)
scorpion.attack(hero_two)
Secondly, Scorpion doesn't seem to lose energy after attacking and doesn't deal initial damage. You could fix that by changing Scorpion attack behaviour to first call parent attack function:
class Scorpion(Monster):
...
def attack(self, amount, target):
super().attack(amount, target)
print('Poison has take effect!')
...
But Monster attack behaviour doesn't accept second argument, so my suggestion is to modify Monster's attack function with second parameter to also deal initial damage to target (and correctly subtract target's health with damage amount):
class Monster:
...
def attack(self, amount, target):
print(f'{self.name} has attacked')
print(f'{amount} damage was delt!')
target.health -= amount
self.energy -= 20
To track poisoned status, easiest you can do is to introduce state in Monster class constructor called poisoned and set it to False (initial state is that monster and every child object is not poisoned).
Then you can change target's poisoned state in Scorpion's attack method, like so:
class Scorpion(Monster):
...
def attack(self, amount, target):
...
target.poisoned = True
...
This way you can see if one of Heroes is poisoned, like so:
print(hero_one.poisoned) --> False or True (if attacked by scorpion)
To calculate a chance (lets say 50%) if Scorpion's attack will poison the target or not, extract poison application behaviour into separate private function and modify it like this:
class Scorpion(Monster):
...
def attack(self, amount, target):
super().attack(amount, target)
self.__poison_target(amount, target)
def __poison_target(self, amount, target):
# A coin toss to decide if target will be poisoned or not.
should_apply_poison = random.randint(0, 1) == 1
if should_apply_poison:
print('Poison has take effect!')
target.poisoned = True
# Loops Poison display repeated with time delay.
# for repeater amount of times.
repeater = 5
for poison in range(repeater):
poison = 0
amount = poison + amount
print(f'Poisoned: {amount} damage')
time.sleep(0.5)
poison = repeater
amount *= poison
print(f'Poison did a total damage of {amount} to {target.name}')
target.health -= amount
...
Or you can delegate damage dealing behaviour from Scorpion to Hero by creating abstract class Poisonable which you can use to check if the target is Poisonable (in your case - Hero inherits from Poisonable). This way Scorpion class doesn't care if target is successfuly poisoned or not.
Poisonable class is contract which all classes that inherit from it must abide by (e.g. implement their own version of resistance, damage taking from poison). This way every poisoned creature can react differently.
class Poison:
def __init__(self):
self.damage_per_tick = 5
self.ticks = 3
class Scorpion(Monster):
name = 'Scorpion'
def __init__(self, health, energy):
super().__init__(health, energy)
self.poison = Poison()
def attack(self, amount, target):
super().attack(amount, target)
self.__poison_target(target)
def __poison_target(self, target):
if isinstance(target, Poisonable):
target.suffer_poison_damage(self.poison)
class Poisonable(ABC):
#abc.abstractmethod
def resist_poison(self) -> bool:
pass
#abc.abstractmethod
def suffer_poison_damage(self, poison: Poison) -> None:
pass
class Hero(Monster, Poisonable, ABC):
name = 'Rockwood'
def __init__(self, health, energy) -> None:
super().__init__(health, energy)
def resist_poison(self) -> bool:
# resist poison chance
return random.randint(0, 1) == 1
def suffer_poison_damage(self, poison: Poison) -> None:
if self.resist_poison():
pass
else:
print('Poison has take effect!')
# Loops Poison display repeated with time delay.
# for repeater amount of times.
total_amount = 0
for poison_tick in range(poison.ticks):
total_amount += poison.damage_per_tick
print(f'Poisoned: {poison.damage_per_tick} damage')
time.sleep(0.5)
print(f'Poison did a total damage of {total_amount} to {self.name}')
self.health -= total_amount
I am trying to learn Python OOP and have been struggling to understand scope, passing values and how to encapsulate. My earlier attempts have rapidly become spaghetti code (likely because my only programming experience was on 8 bit BASIC 40 years ago), and thus, I am trying classes and objects.
Here it is:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
player.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
player.check_dead()
player = Player(1, 15)
player.fight()
The calculate_damage function is flagged as being 'static' and the check_dead function seemingly has no effect at all.
I need some hints to get my footing. Thanks.
There are a few errors in your code.
First, you are using the player variable in the fight function. This works, because you've called the instance of the Player class player, but it wouldn't if you would give it any other name. To fix your issue, use self instead ofplayer in all class functions. The self argument is automatically passed in by python and does always refer to the object on which the the function is called.
Second, the check_deadfunction does nothing because it only returns a value. There is no attribute set or value printed out in this function. In order to make it work, you need to print the value, either inside the function or outside, with the return value of the function.
This code should work:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
print(self.check_dead())
player = Player(1, 15)
player.fight()
In this code, we print the aliveness of the player directly instead of returning it:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
print("Player not dead")
else:
print("Player dead")
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
self.check_dead()
player = Player(1, 15)
player.fight()
The problem that you are having is probably due to the way you refer to the functions of check_dead(), reduce_health() and the variable health in fight(). You are calling out the player class instance, instead of the instance the function is running in, that meant that, if you were to change the name of the player class instance to something different, it would spill out an error that "player is not defined".
Why the check_dead() is not working could also be because it doesn't print out the values, it just returns them, and it you don't do anything with them when it returns them to the calling function it just scraps them.
I recommend checking the code if it is spelled right and also replace every reference in the class to self. And also make the check_dead() function wrapped in print in the fight() function. That would end up looking something like this:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(self.health)
print(self.check_dead())
player = Player(1, 15)
player.fight()
If this still doesn't work it's propablly of your running environment or your ide/editor.
Also:
Static means, that the function standalone
If a function is static it doesn't use any of the functions/variables the class gives it (which are commonly gotten by using self.[function/variable name]). By that it is independent and it could be written outside of the class and it would still work as intended.
I am currently developing a short text-based adventure so I can learn how to use Classes within Python. As part of this, I am trying to create a combat system where the player could choose an NPC to attack.
The aim is that the player can enter the name of the NPC and the weapon they want to use. A method in the target's class will then be called, to lose health based on the damage of the weapon.
My current code is below:
class npc:
def __init__(self, name, alliance):
self.name = name
self.alliance = alliance
def loseHealth(self, health, dmg):
self.dmg = dmg
self.health = self.health - dmg
def usePotion(self, health, pType):
if pType == "great":
self.health = min(self.health + 50,self.maxHealth)
elif pType == "normal":
self.health = min(self.health + 25,self.maxHealth)
else:
pass
def attack(self, target, weaponDmg):
if target in npcList:
target.loseHealth(self.health, weaponDmg)
class human(npc):
maxHealth = 100
health = 100
def __init__(self, name, alliance):
super().__init__(name, alliance)
class orc(npc):
maxHealth = 200
health = 200
def __init(self, name, alliance):
super().__init__(name, alliance)
weaponDmg = {'sword':10,'axe':20}
alice = human("alice","good")
bob = orc("bob","evil")
npcList = [alice, bob]
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(target,weaponDmg[weapon]) #using alice temporarily until I have a person class sorted
print(target.health)
The simple and pythonic answer is to use a dict of NPCs keyed by name, the same way you’re already doing it with weapons:
npcs = {‘alice’: alice, ‘bob’: bob}
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(npcs[target], weaponDmg[weapon])
print(target.health)
And if you want to look up the attacking NPC by user-supplied name as well as the attackee, you can do the same thing there:
npcs[attacker].attack(npcs[target], weaponDmg[weapon])
If you really want to do this inside the attack method you can keep passing in target as a name (string) and do this:
if target in npcs:
npcs[target].loseHealth(self.health, weaponDmg)
... but that probably isn’t a very good design. It means you’re sharing a global variable, and your NPC objects all “know” about that global dict and all the NPCs in it, which doesn’t seem like part of their responsibility.
You can make this a little less repetitive by creating the dict with a comprehension:
npcs = {npc.name: npc for npc in (alice, bob)}
... or by just creating them directly in the dict instead of in variables that you’re probably never going to otherwise use:
npcs = {}
npcs[‘alice’] = human("alice","good")
npcs[‘bob’] = orc("bob","evil")
You can call a method on an instance by using getattr, here is an example:
>>> class Test:
... def my_method(self, arg1, arg2):
... print(arg1, arg2)
...
>>> t = Test()
>>> getattr(t, 'my_method')('foo', 'bar')
foo bar
I already asked something similar on the site, but I can't get over this error. The code that I'm using is:
class Enemy:
# Base class to create all future enemies
def __init__(self, name, hp, dmg, desc):
self.name = name # Enemy name
self.hp = hp # Enemy hp
self.dmg = dmg # Enemy damage
self.desc = desc # Enemy description (Will make sense later)
def is_alive(self):
return self.hp > 0
The code above is used to define all future enemies, as I stated on with #. Then, I created three enemies for testing, as my game will generate random encounters from a list. However, I'm only going to list one because the error happens with any enemy:
class Goblin(Enemy):
def __init__(self):
super().__init__(name = "Goblin",
hp = 40,
dmg = 33,
desc = "An ordinary goblin runs in your direction!")
Then, as the enemies do, the rooms of the dungeon also have a template class and a list for random purposes, but as I did before, I will only list one because it's the one that is causing errors to me:
class Room:
# Template to define all future rooms
def __init__(self, intro):
self.intro = intro
class VsRoom(Room):
# Room with an enemy
duelist = None
def __init__(self):
self.duelist = duelist = random.choice(Enemy_List)
super().__init__(intro = "There's an enemy here! \n%s" %(duelist.desc))
And then there's the "def" to run the game (currently just for testing):
def print_room():
# Tells the player the type of room they are
print("You enter the next room...")
print()
gen_room = random.choice(Rooms)() # ("Rooms" is a list)
print(gen_room.intro)
if gen_room == VsRoom:
combat() # No need to list the combat() definition, I think.
But when I run, I get this:
AttributeError: type object 'Goblin' has no attribute 'desc'
I know that AttributeError is quite easy to find here, but it seems that none of the existing questions suits me. If anybody knows why the code fails, please tell me.
I have recently started my end of the year project in computer science (self proposed, also my first year taking it so only have about 4 months of python experience) and decided I wanted to do a simple Pokemon simulator in it.
I've recently run into a roadblock: Let's say the user chooses Pikachu as their starter. I don't want the user to input their health, attack, speed, and moves. Is their a way to create a Pikachu class that already has that data in it?
I tried something like this:
class Pikachu(object):
def __init__(self):
self.data = []
def health(self):
self.health = 22
print self.health
def attack(self):
self.attack = 7
def speed(self):
self.speed = 6
Pikachu.health()
When I do this (in an attempt to check and see if the health is stored and will be returned on demand), it gives the error:
TypeError: unbound method health() must be called with Pikachu instance as first argument (got nothing instead)
I'm confused as to what the error means as well. Usually this would work and you wouldn't need an argument in the parentheses, but in this instance it doesn't.
Is there some different way to create a class and store data in it? Is there some way to just create an object or do something like this:
def Pikachu:
health = 22
attack = 7
speed = 6
it is because your variable names are the same as your method names. and also why don't you initialise everything in the constructor:
class Pikachu(object):
def __init__(self):
self.data = []
self.attack = 7
self.health = 22
self.speed = 6
def get_health(self):
return self.health
def get_attack(self):
return self.attack
def get_speed(self):
return self.speed
if __name__ == '__main__':
p = Pikachu()
print p.get_health()