Subtracting objects - python

I am a beginner and I was trying to subtract the attack of the health from the dark knight from the attack of a player but when I do a print of the health after the operation it stays the same.
the class of the player looks like this.
class Player:
def __init__(self, name, health, attack, weapon):
self.name = name
self.health = health
self.attack = attack
self.weapon = weapon
def return_name(self):
return self.name
def return_health(self):
return self.health
def return_attack(self):
return self.attack
def return_weapon_damage(self):
return self.weapon.return_modifier
def damage_plr(self, damage):
self.health -= damage
def attack_enny(self, target):
target -= self.return_attack() + self.weapon.return_modifier()
and this is where I wrote the subtraction.
from player import Player
from Weapon import Weapon
import random
from Enn import Enemy
##########-Weapon-##########
knife = Weapon('Knife', 5)
blade = Weapon('Blade', 8)
shield = Weapon('Shield', 7)
rand_weapon = [knife, blade, shield]
##########-Weapon-##########
##########-Enemy-##########
drk_att = random.randint(4, 7)
drk_kng = Enemy('Dark Knight', 45, drk_att)
opp_choose = []
##########-Enemy-##########
choose_name = input("Choose your name: ")
random.shuffle(rand_weapon)
player1 = Player(choose_name, 50, 2, rand_weapon[0])
print()
print('Welcome', choose_name, '!!!\n')
print("You've come across the path of a Dark Knight. He don't seems to happy to see you.\n"
"You know that you will need to prepare for combat. Your weapon in this fight will be...")
print('A', player1.weapon.return_name(), 'that does', player1.weapon.return_modifier(), 'damage!\n')
print('##########################################################################################\n')
while drk_kng.return_health() >= 0 or player1.return_health() >= 0:
choice = input('What would you like to do.(Attack or protect): ')
if choice == 'attack' or choice == 'Attack':
player1.attack_enny(drk_kng.return_health())
print('You have delt', player1.weapon.return_modifier(), 'damage and he know have', drk_kng.return_health(), '\n')

I'm going to assume that your Enemy class is similar to you Player class, in which case then I will further assume that drk_kng.return_health() return a number, in which case the problem is simple that you don't store the result anywhere.
You might think you do on attack_enny on the Player class when you do target -= ... as that is the job of the a-=b but that is only true for mutable object, number and string are immutable objects, any attend to modify them result in a new instance of that immutable object being created and the a-=b become a=a-b which is just a reassignment, for the -= (or any of its variations) to work as you expect you need to work with object that are mutable (and of course that they have said operator defined for them)
Now lets look at a quick example
>>> def add(a,b):
a+=b
print(a)
>>> n=9
>>> add(n,1)
10
>>> n
9
>>> x=[]
>>> add(x,[42])
[42]
>>> x
[42]
>>>
the sample with n is what you're doing, n by being a number is immutable an and thus inside the function add the += fallback to a reassignment so the a inside the function is a new variable with the new result and is no longer the same n from before, and thus the n outside remain unchanged, but when you apply the same function to a mutable object like the list x you can see the change both inside the function and outside too.
So and easy fix for problem is to pass your Enemy class instance into your method like for example player1.attack_enny(drk_kng) and change attack_enny accordingly
def attack_enny(self, enemy):
enemy.health -= self.return_attack() + self.weapon.return_modifier()
Another way is, given that value you subtract depend on two other you player class can have a new method that return that value
def total_attack(self):
return self.return_attack() + self.weapon.return_modifier()
and them in your Enemy class include a new method for taking damage, like for example
def take_dmg(self, damage):
self.health -= damage
and then your player1.attack_enny(drk_kng.return_health()) become drk_kng.take_dmg(player1.total_attack())
Apart from that, all your getter functions (all your return_*) are unnesesary, you can change your Player class to just
class Player:
def __init__(self, name, health, attack, weapon):
self.name = name
self.health = health
self.attack = attack
self.weapon = weapon
def damage_plr(self, damage):
self.health -= damage
def total_attack(self):
return self.attack + self.weapon.return_modifier()
and then change all your player1.return_X() to just player1.X, in python you don't really need to define getter or setter unless said method do something else beside just get or set a value

Related

I am trying to optimise some classes but the code isn't working

#Here is the code I currently have:
class Character(object):
def __init__(self, currentHealth, maxHealth, damage, defence, agility):
self.currentHealth = currentHealth
self.maxHealth = maxHealth
self.damage = damage
self.defence = defence
self.agility = agility
class Enemy(Character):
def __init__(self, drop):
Character.__init__(self, currentHealth, maxHealth, damage, defence, agility)
self.drop = drop
def checkHealth(self):
print("The enemies health is: " + str(self.currentHealth))
def inspectStats(self):
print("The enemies health is: " + str(self.currentHealth))
print("The enemies attack is: " + str(self.damage))
print("The enemies defence is: " + str(self.defence))
print("The enemies evasiveness is: " + str(self.agility))
class Player(Character):
def __init__(self, currentHealth, maxHealth, damage, defence, agility, gold):
self.currentHealth = currentHealth
self.maxHealth = maxHealth
self.damage = damage
self.defence = defence
self.agility = agility
self.gold = gold
def checkHealth(self):
print("Your current health is: " + str(self.currentHealth))
def inspectStats(self):
print("Your current health is: " + str(self.currentHealth))
print("Your max health is: " + str(self.maxHealth))
print("Your attack is: " + str(self.damage))
print("Your defence is: " + str(self.defence))
print("Your agility is: " + str(self.agility))
print("Your gold is: " + str(self.gold))
bob = Player(15, 15, 5, 6, 7, 70)
spider = Enemy(10, 10, 2, 1, 5, 'silk')
spider.inspectStats()
I haven't been able to get this code working, I have searched online and have found mild success with this website: http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/
It appears, however, that the code still isn't working. It would be greatly appreciated if someone could tell me how I am supposed to do inheritance.
(I know that there is going to be a subclass of enemy so it would be helpful if that was taken into consideration).
Ok , so , your child class which is Enemy got in method definition that it only takes 2 arguments self and drop.
So if You want to make it able to recieve more params you must provide some placeholder for it. The naive way is just write all the needed params in place. But lats say in future you will extend the number of the params the Character class is taking. To make the solution more freindly we can add the *args varibale as param. This vairable (args isnt obligatory name the crucial here is the * operator standing before it) will "catch" all the params which you provide after the drop param. The star operator doesn't care how many params You pass. It will try to take as many as possible. These stored params can now be unpacked to the parent __init__ method like this :
class Enemy(Character):
# the *args will "catch" all the positional
# parameters passed to this class
def __init__(self, drop, *args):
# now we are using unpacking to
# apply all positional params holded in args to parent init method
Character.__init__(self, *args)
self.drop = drop
# Now You must remeber to pass the the init the
# child argument which is drop
# + all the parent class positional args (currentHealth, ...)
enemy = Enemy(True,...)
Your Enemy init function doesn't have the right number of parameters. Try this:
class Enemy(Character):
def __init__(self, currentHealth, maxHealth, damage, defence, agility, drop):
super(Enemy, self).__init__(currentHealth, maxHealth, damage, defence, agility)
self.drop = drop
For more details about the "super" keyword, you can go there

How can I automate the creation of instances of a class?

So in python 3 I am having trouble creating multiple instances of a class automatically. I am trying to make monopoly and here is the code sample that is giving me problems.
def numplayer():
numplayer = int(input('How many players would you like? (up to four)'))
while numplayer > 4 or numplayer < 1:
numplayer = int(input('How many players would you like? (up to
four)'))
for i in range(numplayer):
PlayerMoney.append(1500)
What I want to do is also add something that will create the number of players that numplayers equals to in the for i in range(numplayer) function. I have the player as a class but I don't want to manually create every single class for every player. If there is a solution to this, please do tell. Thanks!
EDIT: So I think this might be bad wording in the title but I'm trying to create multiple instances of a single class (the player).
Here is the code for the player class:
class Player:
def __init__(self, name, money, position):
self.name = name
self.money = money
self.position = position
def DiceRoll(self):
x = random.randint(1, 6)
y = random.randint(1, 6)
sum = x + y
return [sum, x, y]
def getName(self):
return sef.name
def getMoney(self):
return self.money
def getPosition(self):
return self.position
# Create Class
class Player:
def greating(self):
print 'Hello!'
# List to store instanses
l = []
for i in range(4):
l.append(Player())
# Call Instance #1 methods
l[0].greating()
Here we have a player class and 4 instances from this class stored in l list.
I would advise you structure your code as below. It's usually a good idea for your function to return something.
def setup():
n = int(input('How many players would you like? (up to 4)'))
names = [input('Give name #{0}'.format(i)) for i in range(1, n+1)]
return [Player(name, 1500, 0) for name in names]
players = setup()

Class Enemy Has Function combat_roll() but can't seem to access it Python 2.7

I really never bothered to learn classes in Python because all the docs I find are a bit too technical for me. So, yet again I have sat down to try to conquer them. I would love to be able to use classes for the text based games that I make, so that is how I try every time.
My class code (its messy now because I have been trying everything I can think of) is
class enemy:
def __init__(self, name, weapon, hp, attack, defense, xp):
self.self = self
self.name = name
self.hp = hp
self.attack = attack
self.defense = defense
self.xp = xp
#self.attack_text1 = attack_text1
self.attack_text1 = name, " attacks you with ", weapon, "."
#self.attack_damage = attack_damage
self.attack_damage = random.randint(0,2) + attack
#self.total_damage = total_damage
self.total_damage = self.attack_damage - player.defense
if self.total_damage < 1:
self.total_damage = 0
#self.attack_text2 = attack_text2
self.attack_text2 = name, " deals ", self.total_damage, " to you."
#staticmethod #tried this from a solution found on stack overflow
def combat_roll(self):
self.roll = random.randrange(0,20) + attack
combat_roll(self)
I have a function called combat() that pits the player against an instance of the enemy class.
The instance is called like in that function:
def combat():
goblin_weapon = weapon("goblin dagger", 1, 5)
goblin = enemy("ugly goblin", goblin_weapon, 3,2,1,150)
goblin_damage = goblin.attack - player.defense
print "a ", goblin.name, " attacks you with ", goblin_weapon, "."
print goblin.combat_roll
if goblin.roll > player.defense:
print goblin.name, "does ", goblin_damage, " damage."
else:
print goblin.name, "misses."
My goal is essentially somewhat of a d20 feel to the combat. attack bonus + d20 rolls vs armor class. I was hoping that the enemy class could have its own function that could handle its own dice rolls.
I've lost the original state of my code. I have checked google and searched here for solutions but none of them worked. I am sure it is because I haven't seen the solution in a way that works with my brain, because this can't be that complicated. Why can't I just call the function like I reference class variables?
enemy.combat_roll()
you know?
Anyway, thanks for the help!
You're declaring the method only within __init__, instead of within enemy. To my understanding, it will only be available if you declare it within the enemy class itself.
class enemy:
def __init__(self, name, weapon, hp, attack, defense, xp):
self.self = self
self.name = name
self.hp = hp
self.attack = attack
self.defense = defense
self.xp = xp
#self.attack_text1 = attack_text1
self.attack_text1 = name, " attacks you with ", weapon, "."
#self.attack_damage = attack_damage
self.attack_damage = random.randint(0,2) + attack
#self.total_damage = total_damage
self.total_damage = self.attack_damage - player.defense
if self.total_damage < 1:
self.total_damage = 0
#self.attack_text2 = attack_text2
self.attack_text2 = name, " deals ", self.total_damage, " to you."
combat_roll(self)
def combat_roll(self):
self.roll = random.randrange(0,20) + attack
As you say, your code is a little all over the place, and it isn't entirely complete. I've tried to rewrite and fill it out a bit in the spirit of what I think you were going for:
import random
#dice roll for dealing damage as a global function
def get_damage(dice_size, bonus_damage):
return random.randrange(dice_size) + bonus_damage
#class for a player
class Player:
#initialises the player's name, defense value and hp
def __init__(self, name, defense, hp):
self.name = name
self.defense = defense
self.hp = hp
#when something wants a string representation of a player (like format()
#might) it just uses the player's name
def __str__(self):
return self.name
#a method where a player takes damage from some enemy (if it quacks like an
#enemy..). This is where your attack_text2 has gone.
def take_damage(self, enemy, dmg):
self.hp -= dmg
print("{} deals {} damage to {}".format(enemy, dmg, self))
#class for a weapon
class Weapon:
#__init__ taking 4 arguments, like in your code, except it wasn't clear what
#the last two arguments were.
def __init__(self, name, foo, bar):
self.name = name
self.foo = foo
self.bar = bar
#as with Player
def __str__(self):
return self.name
#class for an enemy
class Enemy:
#this has changed: init only needs to *init*ialise the object not do any
#more, so all it does is stored relevant info as a field.
def __init__(self, name, weapon, hp, attack, defense, xp):
self.self = self
self.name = name
self.weapon = weapon
self.hp = hp
self.attack = attack
self.defense = defense
self.xp = xp
#as with Weapon
def __str__(self):
return self.name
#this is roughly where the second half of your init has gone. Engages in
#combat with a player, generating a random amount of damage, and doing all
#of the defense/attack malarkey. The damage is not stored as a field (using
#self.) as it is only needed for the duration of the combat
def combat(self, player):
print("{} attacks {} with {}.".format(self, player, self.weapon))
damage = get_damage(20, self.attack)
total_damage = damage - player.defense
if total_damage < 0:
total_damage = 0
player.take_damage(self, total_damage)
#function constructing a couple of objects and using them to do a 'fight'
#I've used some key word arguments with the goblin and the player. I'd advise
#you to keep doing the same, as it helps a lot with clarity.
def play():
dagger = Weapon("a goblin dagger", 1, 5)
goblin = Enemy("an ugly goblin", dagger, hp=3, attack=2, defense=1, xp=150)
aragorn = Player("Aragorn", defense=3, hp=100)
goblin.combat(aragorn)
print("{} now has {} hp".format(aragorn, aragorn.hp))
play()
I've included some comments trying to explain what's changed and what does what, and why, hopefully.
I hope it's stuck to your vision of the code. The main changes were to just refactor your bits of code around a bit. In your __init__ for the enemy, you originally had the code that's now ended up in Enemy.combat, for example. You also hadn't included a Weapon or Player class, although your code constructed a weapon and referred to player, so I've added those in. Let me know if you'd like any more clarification.
Here is a sample of this code in action:
an ugly goblin attacks Aragorn with a goblin dagger.
an ugly goblin deals 10 damage to Aragorn
Aragorn now has 90 hp

print object/instance name in python

I was wondering if there is a way to print the object name in python as a string. For example I want to be able to say ENEMY1 has 2 hp left or ENEMY2 has 4 hp left. Is there a way of doing that?\
class badguy:
def __init__(self):
self.hp = 4
def attack(self):
print("hit")
self.hp -= 1
def still_alive(self):
if self.hp <=0:
print("enemy destroyed")
else :
print (str(self.hp) + " hp left")
# creating objects
enemy1 = badguy()
enemy2 = badguy()
enemy1.attack()
enemy1.attack()
enemy1.still_alive()
enemy2.still_alive()
A much better design principle is not to rely on the specific name of the object as shown below:
class badguy(object):
def __init__(self):
pass
b = badguy()
print b
>>> <__main__.badguy object at 0x7f2089a74e50> # Not a great name huh? :D
This can lead to a whole wealth of issues with assignment binding, referencing, and most importantly does not allow you to name your objects per user or program choice.
Instead add an instance variable to your class called self._name (9.6 Classes - Private Variables) or self.name if you want to allow access outside the scope of the class (in this example, you can name it anything). Not only is this more Object-Oriented design, but now you can implement methods like __hash__ to be able to create a hash based on a name for example to use an object as a key (there are many more reasons why this design choice is better!).
class badguy(object):
def __init__(self, name=None):
self.hp = 4
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
def attack(self):
print("hit")
self.hp -= 1
def still_alive(self):
if self.hp <=0:
print("enemy destroyed")
else :
print ("{} has {} hp left.".format(self.name, self.hp))
Sample output:
b = badguy('Enemy 1')
print b.name
>>> Enemy 1
b.still_alive()
>>> Enemy 1 has 4 hp left.
b.name = 'Enemy One' # Changing our object's name.
b.still_alive()
>>> Enemy One has 4 hp left.
You'd have to first give them names. E.g.
class badguy:
def __init__(self, name):
self.hp = 4
self.name = name
def attack(self):
print("hit")
self.hp -= 1
def still_alive(self):
if self.hp <=0:
print("enemy destroyed")
else :
print (self.name + " has " + str(self.hp) + " hp left")
# creating objects
enemy1 = badguy('ENEMY1')
enemy2 = badguy('ENEMY2')
enemy1.attack()
enemy1.attack()
enemy1.still_alive()
enemy2.still_alive()
I have posted a complete solution here:
https://stackoverflow.com/a/49331683/7386061
It works without parameters. For example you could just do:
class badguy(RememberInstanceCreationInfo):
def __init__(self):
super().__init__()
self.hp = 4
def attack(self):
print("hit")
self.hp -= 1
def still_alive(self):
if self.hp <=0:
print("enemy destroyed")
else :
print (self.creation_name + " has " + str(self.hp) + " hp left")
enemy1 = badguy()
enemy2 = badguy()
enemy1.attack()
enemy1.attack()
enemy1.still_alive()
enemy2.still_alive()
out: hit
out: hit
out: enemy1 has 2 hp left
out: enemy2 has 4 hp left

Python - Compare two lists and find matching values and their positions in the list

I am trying to make a turn based RPG and I am currently working on the turn system.
To keep it simple for now I am trying to order each character/enemy by their speed and then highest speed goes first. Below is the start of my code for the turn system.
An example of what I want to do is read Order[0] (which will be the fasted character/enemy) and find out which character/enemy that relates to. Speed[0] to Speed[3] gives the speed of my 4 characters and enemySpeed[0] to enemySpeed[4] gives the speed of the 5 enemies.
def Turn():
Turn = [int(Speed[0]), int(Speed[1]), int(Speed[2]), int(Speed[3]), int(enemySpeed[0]), int(enemySpeed[1]),int(enemySpeed[2]), int(enemySpeed[3]), int(enemySpeed[4])]
Order = sorted(Turn, key = int, reverse = True)
Edit: Here is some information regarding player stats.
In main():
name = ['Ben','Ellis','Curt','Jen']
HP = [100,100,100,100]
exp = [0,0,0,0]
lvl = [1,1,1,1]
player1 = [name[0],HP[0],exp[0],lvl[0]]
player2 = [name[1],HP[1],exp[1],lvl[1]]
player3 = [name[2],HP[2],exp[2],lvl[2]]
player4 = [name[3],HP[3],exp[3],lvl[3]]
PLAYERS = [player1, player2, player3, player4]
REGION = 'start'
POS = '[x, y]'
ITEMS = None
SIGEVENTS = False
gameData = [PLAYERS, REGION, POS, ITEMS, SIGEVENTS]
Out of main():
def playerStats():
global Attack, Defense, Speed, MaxHP
Attack = [0,0,0,0]
Defense = [0,0,0,0]
Speed = [0,0,0,0]
MaxHP = [0,0,0,0]
for i in range(0,4):
Attack[i] = lvl[i] * 1
Defense[i] = lvl[i] * 2
Speed[i] = lvl[i] * 3
MaxHP[i] = lvl[i] * 4
return Attack, Defense, Speed, MaxHP
Instead of looking at just the speeds, look at your players and enemies, and devise a key to determine what speed goes with what player.
You didn't share any details on how you define players and speeds; I'll use a number here for now:
players_and_enemies = range(8)
fastest = max(players_and_enemies, key=lambda p: int(Speed[i] if i < 4 else enemySpeed[i - 4]))
It may well be that you can simplify this based on your data structures; if you have objects per player and enemy that have a speed attribute, for example, you can access that attribute in the key function instead.
max() returns the highest value by key; no need to sort all speeds if all you need is the fastest driver.
Martijn Pieters already pointed out the most important issues. Try to use objects. Try to remodel the objects of your game and don't manage their attributes seperately.
I will elaborate a bit on this and maybe I can give you so some ideas.
A Character, be it a NPC or a PC, is one object of a class that could look something like this:
class Character:
def __init__ (self, name, xp = 0, level = 1, hp = 4, speed = 3, atk = 1, def_ = 2):
self.name = name
self.xp = xp
self.level = level
self.hp = self.curHp = hp
self.speed = speed
self.atk = atk
self.def_ = def_
self.inventory = []
Now using this class you can instantiate your PCs and NPCs:
npcs = [Character (name) for name in ['Baldur', 'Lord Never', 'Ashyra'] ]
Adding methods to your class lets you interact with it, for example:
class Character:
[....]
def takeDamage (self, dmg):
self.curHp -= min (0, dmg - self.def_)
def gainXP (self, xp):
self.xp += xp
#check for level up
def takeItem (self, item):
self.inventory.append (item)
def useItem (self, item):
item.activate ()
self.inventory.remove (item)
Or in order to get all your characters ordered by their speed, you can use:
charactersBySpeed = sorted (characters, key = -character.speed)
And so further and so on. The idea is really to use object oriented programming for modelling the reality (even the made-up reality of your game) in your code.

Categories

Resources