Python - Get a function to overwrite a variable - python-3.x

So I'm testing out some mechanics for a text-based game I was going to make. If the player has armour it would halve the damage they take and if not they would take full damage. The problem I'm having is that whenever I try to run the functions twice, it resets the health because it has been hardcoded. So I'm wondering how can I get the function to overwrite the health variable after it runs each time?
Here is the code:
import random
inventory = ["Armour","Sword","Healing Potion"]
health=100
def randDmg():
dealtDamage = random.randint(1,10)
print("You have taken "+str(dealtDamage)+" damage.")
return dealtDamage
def dmgCheck(damage, health):
if "Armour" in inventory:
damage = damage/2
else:
damage = damage
health-=damage
return health
print("Your new health is "+str(dmgCheck(randDmg(), health)))

Defining a global at the top of your dmgCheck function would work well - then you don't need to pass it in as a local. While you're at it, if you call the randDmg function within dmgCheck you won't need to pass that in either.
import random
inventory = ["Armour","Sword","Healing Potion"]
health=100
def randDmg():
dealtDamage = random.randint(1,10)
print("You have taken "+str(dealtDamage)+" damage.")
return dealtDamage
def dmgCheck():
global health
damage = randDmg()
if "Armour" in inventory:
damage = damage/2
else:
damage = damage
health-=damage
return health
print("Your new health is" + str(dmgCheck()))
print("Your new health is" + str(dmgCheck()))
That last bit could also be simplified by using pythons' string formating syntax:
print("Your new health is %s" % dmgCheck())
To do something similar using a Python Class you could use:
import random
class Game(object):
def __init__(self, inventory, health):
self.inventory = inventory
self.health = health
def randDmg(self):
dealtDamage = random.randint(1,10)
print("You have taken "+str(dealtDamage)+" damage.")
return dealtDamage
def dmgCheck(self):
damage = self.randDmg()
if "Armour" in self.inventory:
damage = damage/2
else:
damage = damage
self.health -= damage
return self.health
def play(self):
result = self.dmgCheck()
print("Your new health is %s" % result)
game = Game(["Armour","Sword","Healing Potion"], 100)
game.play()
game.play()

Related

Unable to access parent class' global variable with Child class' object

I want to update the counter of available cars whenever someone rents the car. When I try to access this variable through child class, it shows an error as "UnboundLocalError: local variable 'available_cars' referenced before assignment"
PS: Still working on it, that's why I haven't completed all the method codes.
Parent Class
available_cars = 1000
class Car_rental():
def __init__(self):
pass
def display_available_cars(self):
print("Total avaialable cars for renting is:", available_cars)
def rent_hourly(self, cars_rented):
print("Our hourly rate is $100/hr.")
if cars_rented > available_cars:
print("Sorry! We currently do not have the number of cars requested. You can have {} cars for now if you want.".format(
available_cars))
elif cars_rented < available_cars:
print("Thank you for renting {} cars from Swift car renting portal. Enjoy you ride.".format(
cars_rented))
elif cars_rented < 0:
print ("Please provide a valid number of cars.")
def rent_weekly(self):
pass
def rent_monthly(self):
pass
def bill(self):
pass
def update_invetory(self, cars_rented):
available_cars = available_cars - cars_rented
Child Class
from carRental import *
class Customer(Car_rental):
def init(self):
Car_rental.init(self)
def rent_cars(self):
mode = int(input(
"Please select the mode of renting the car:\n1. Hourly\n2. Weekly\n3. Monthly\n"))
if mode == 1 or mode == 'hourly' or mode == 'Hourly':
cars_rented = int(input("How many cars do you wish to rent?"))
self.rent_hourly(cars_rented)
self.update_invetory(cars_rented)
elif mode == 2 or mode == 'weekly' or mode == 'Weekly':
self.rent_weekly()
elif mode == 3 or mode == 'monthly' or mode == 'Monthly':
self.rent_monthly()
else:
print("Please provide appropriate input.")
def return_cars(self):
pass
So if I follow your question correctly, you seem to have objectives with your code.
You want to define a Car_Rental class with a class-level variable available_cars which is initially set to 1000, and then as customers rent vehicles decrement the number of available cars.
You also want a second Customer class which inherits the Car_Rental class which implements the billing functionality.
While I am unsure of this, it seems that you would also like each of these class definitions to reside in their own separate python script file.
This is how I would address these issues.
# If desired could be placed in separate file Car_Rental.py
class Car_Rental():
available_cars = 1000
def display_available_cars(self):
print("Total avaialable cars for renting is:", Car_Rental.available_cars)
def isOkayToRent(self, nmbr):
return nmbr <= Car_Rental.available_cars
def rent_hourly(self, cars_rented):
print("Our hourly rate is $100/hr.")
if cars_rented < 0:
print ("Please provide a valid number of cars.")
else:
if self.isOkayToRent(cars_rented):
print("Thank you for renting {} cars from Swift car renting portal. Enjoy you ride.".format(
cars_rented))
else:
print("Sorry! We currently do not have the number of cars requested. You can have {} cars for now if you want.".format(
Car_Rental.available_cars))
def rent_weekly(self):
pass
def rent_monthly(self):
pass
def bill(self):
pass
def update_inventory(self, nmbr):
assert nmbr > 0
Car_Rental.available_cars -= nmbr
#property
def cars(self):
return Car_Rental.available_cars
# Can be placed in second file.
#Note include this next line if classes are stored in separate files
#from Car_rental import Car_Rental
class Customer(Car_Rental):
def __init__(self):
Car_Rental.__init__(self)
def rent_cars(self):
mode = int(input(
"Please select the mode of renting the car:\n1. Hourly\n2. Weekly\n3. Monthly\n"))
if mode == 1:
cars_rented = int(input("How many cars do you wish to rent?"))
self.rent_hourly(cars_rented)
self.update_inventory(cars_rented)
elif mode == 2:
self.rent_weekly()
elif mode == 3:
self.rent_monthly()
else:
print("Please provide appropriate input.")
def return_cars(self):
pass
To use:
c1 = Customer()
c1.cars
1000
c1.rent_cars()
Please select the mode of renting the car:
1. Hourly
2. Weekly
3. Monthly
How many cars do you wish to rent? 2
Our hourly rate is $100/hr.
Thank you for renting 2 cars from Swift car renting portal. Enjoy you ride.
To illustrate proper inventory reduction:
c1.cars yields 998

Is there a way to get a random number to repopulate inside of a function after when calling the function again?

I am fairly new to Python and I know values called in a function are only there inside the function. I am trying to have a battle between a player and a boss in a small text game I am writing; however, It just keeps populating the same information each time the function is called. I feel like I am missing something. Any help would be appreciated.
The classes:
class Character:
def __init__(self, name, stats):
self.name = name
self.stats = stats
name = {
"Name": ""
}
stats = {
"Dexterity": "",
"Strength": "",
"Health": 20,
"AC": 16,
"Weapon": "",
}
damage = 2 * random.randrange(1, 7)
ability_check = random.randrange(1, 20)
initiative = random.randrange(1,20)
class Boss:
def __init__(self, name, stats):
self.name = name
self.stats = stats
name = {
"Name": "Gargamel"
}
stats = {
"AC": 16,
"Health": 15,
"Weapon": "Sword"
}
damage = random.randrange(1, 6)
initiative = random.randrange(1,20)
The functions:
def battle():
choice = input("Do you wish to continue fighting or run? F or R ")
if (choice.lower() == 'f'):
boss_battle()
if (choice.lower() == 'r'):
pass
def boss_battle():
print("The skeletal creature grabs a sword from the wall and takes a swing at you...\n")
print(f"Boss init {Boss.initiative}, Character init {Character.initiative}")
while Boss.stats["Health"] > 0 or Character.stats["Health"]:
if (Boss.initiative > Character.initiative):
boss_damage = Boss.damage
current_player_health = (Character.stats["Health"] - boss_damage)
Character.stats.update({"Health": current_player_health})
print(f"The boss did {boss_damage} damage. You now have {current_player_health} hitpoints left.")
if (Character.stats["Health"] <= 0):
print('You died!')
break
battle()
elif (Character.initiative > Boss.initiative):
player_damage = Character.damage + stat_block_str(int)
current_boss_health = Boss.stats["Health"] - player_damage
Boss.stats.update({"Health": current_boss_health})
print(f"You attacked the creature with your {Character.stats['Weapon']} and dealt {player_damage} damage.")
if (Boss.stats["Health"] <= 0):
print(f'Congratulations {Character.name["Name"]}! You have beaten the boss and claimed the treasure!')
break
battle()
You have declared classes with class variables, but have made no class instances, so the values are all fixed due to being initialize once when the class was defined.
To make a class instance, you "call" the class using parentheses, which calls your __init__ function on the instance, which sets instance variables.
Here's a small example:
import random
class Character:
def __init__(self,name):
self.name = name
self.health = 20
self.damage = 2 * random.randrange(1,7)
def attack(self,target):
print(f'{self.name} attacks {target.name}...')
target.health -= self.damage
print(f'{target.name} has {target.health} health remaining.')
# defines how to display the class instance when printed.
def __repr__(self):
return f"Character(name={self.name!r}, health={self.health}, damage={self.damage})"
fred = Character('Fred')
george = Character('George')
print(fred)
print(george)
print(f'{fred.name} can deliver {fred.damage} damage.')
fred.attack(george)
Output:
Character(name='Fred', health=20, damage=4)
Character(name='George', health=20, damage=10)
Fred can deliver 4 damage.
Fred attacks George...
George has 16 health remaining.
While true, that your using class variables when you should better use instance variables (see Instance variables vs. class variables in Python amongst others for details) I don't think it is enough to achieve your goal.
What you describe in the comment happens because the class is only setup once and from there on the class variables like damage etc. are static, meaning the random call is only executed once for the whole program. I would suggest to convert the damage variables to functions like this:
class Character:
def __init__(self,name):
self.name = name
self.health = 20
# just as a simple example
self.base_damage = 2
def damage(self):
return self.base_damage * random.randrange(1,7)
This way you will obtain a new random value for call to the function (battle round) and not just have a static value for damage inside the class.
Assuming you made instances for the character and the boss the call can look like this:
current_boss_health = bossInstance.stats["Health"] - characterInstance.damage()
If you want to take this on step further and make this a bit more pythonic you can use the #property decorator for damage:
#property
def damage(self):
return self.base_damage * random.randrange(1,7)
This way the usage of the function will look even more like your original and it hides the function call:
current_boss_health = bossInstance.stats["Health"] - characterInstance.damage

Why does my code say NameError: name 'self' is not defined'

Sorry if this is a really stupid question since I am terrible at python and most of my knowledge consists of the very restricted things you are taught before post 16 education. Basically I'm trying to do a coding project in preparation for when my classes start in September, and so far I've managed to get by teaching myself classes using online websites and people's online forum questions. However, I've come into a bit of a roadblock because my code keeps throwing an error. I've looked on websites and forums but they seem to be in very different situations compared to me and some of them just seem to tell me what I've done is right. The exact error given is: line 34, in returnBarbarianStats
print(self.name,"the barbarian's stats:")
NameError: name 'self' is not defined
import random
def getName():
syllables = ['en','da','fu','ka','re','toh','ko','noh','tuk','el','kar']
firstName = (random.choice(syllables))
secondName = (random.choice(syllables))
thirdName = (random.choice(syllables))
global generatedName
generatedName = firstName+'-'+secondName+'-'+thirdName
# Classes-all creatures have names generated the same way and have the same amount of health.
# The way I have selected how each subclass will be randomly chosen is having the code select a random value
# from the list and depending on which is chosen it will give a subclass.
class preset():
def _init_(self, creature, name, health=100):
self.name = generatedName
self.health = 100
getName()
self.name=generatedName
#Gives different attributes to each sub-class
class barbarian(preset):
def _init_(self, name, power=70, specialAttackPower=20, speed=50):
preset._init_(self, creature, name, health=100)
self.power = power
self.specialAttackPower = specialAttackPower
self.speed = speed
self.name = name
def returnBarbarianStats():
print(self.name,"the barbarian's stats:")
print("Health:",self.health)
print("Power damage:",self.power)
print("Special attack power damage:",self.specialAttackPower)
print("Speed:",self.speed)
class elf(preset):
def _init_(self, name, power=30, specialAttackPower=60, speed=10):
preset._init_(self, creature, name, health=100)
self.power = power
self.specialAttackPower = specialAttackPower
self.speed = speed
class wizard(preset):
def _init_(self, name, power=50, specialAttackPower=70, speed=30):
preset._init_(self, creature, name, health=100)
self.power = power
self.specialAttackPower = specialAttackPower
self.speed = speed
class dragon(preset):
def _init_(self, name, power=90, specialAttackPower=40, speed=50):
preset._init_(self, creature, name, health=100)
self.power = power
self.specialAttackPower = specialAttackPower
self.speed = speed
class knight(preset):
def _init_(self, name, power=60, specialAttackPower=10, speed=60):
preset._init_(self, creature, name, health=100)
self.power = power
self.specialAttackPower = specialAttackPower
self.speed = speed
#10 randomly generated characters.
i = 0
army = []
while i < 10:
creatures = ['barbarian','elf','wizard','dragon','knight']
creatureType = (random.choice(creatures))
if creatureType == 'barbarian':
army.append(barbarian())
elif creatureType == 'elf':
army.append(elf())
elif creatureType == 'wizard':
army.append(wizard())
elif creatureType == 'dragon':
army.append(dragon())
elif creatureType == 'knight':
army.append(knight())
i = i + 1
barbarian.returnBarbarianStats()
I've just given the whole code as I don't want to miss any important details out.
you missed self in the parameters buddy :)
[line 32]
def returnBarbarianStats():
correct it to
def returnBarbarianStats(self):

Python - Using spaces only not solving class methods 'IndentationError' on compilation?

Learning class construction, and of course I'm stuck with the 'IdentationError: unexpected indent' on the following code:
class Account:
def __init__(self, name, balance, min_balance):
self.name = name
self.balance = balance
self.min_balance = min_balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if self.balance - amount >= self.min_balance:
self.balance -= amount
else:
print("Sorry, not enough funds!")
def statement(self):
print("Account Balance: £{}".format(self.balance)
class Current(Account):
def __init__(self, name, balance):
super().__init__(name, balance, min_balance=-1000)
I believe I have exclusively used spaces for indents but I am still unable to get ahead of this one. What am I doing wrong?

Where would the best place to create Python objects be

So I'm making an OOP based program in Python and I wanted to ask: Where would be the best place to create an object? Let's say I have this object from my program.
class Player(Item):
def __init__(self,Name,Diff,Money,Experience):
self.name = Name
self.diff = Diff
self.money = Money
self.experience = Experience
But I also have a main class game. Should I create the player object inside of class game, or maybe outside of all the classes so it is global? I don't know where I should make it so I can access it whenever I need it.
You're talking about where to define a class; this is a style question. Let's see what the Style Guide for Python Code (PEP 8) has to say about this:
Surround top-level function and class definitions with two blank lines.
Ok... That's not too helpful, because now I have to use my own knowledge to answer the question. Erm...
Well, I suppose it depends on what you're trying to say. If you've got this:
class Game:
class Player:
def __init__(self, name, difficulty, money=0, experience=0):
self.name = name
self.difficulty = difficulty
self.money = money
self.experience = experience
then that suggests that if you change the Game implementation you'll have to change the Player implementation, and you can't change the Player implementation without changing the Game implementation; i.e., they're tightly coupled. So the code might carry on like this:
def attack(self, game, other):
game.hurt(other, 1000 // self.difficulty)
if other.hp == 0:
self.money += other.money
other.money = 0
def hurt(self, enemy, damage):
enemy.hp -= damage // enemy.difficulty
if enemy.hp <= 0:
enemy.hp = 0
enemy.die(self)
self.player.experience += enemy.difficulty
This is not a great way of programming; everything's tied together too much and it's a big tangled mess. What's responsible for what? Instead, I'd have something more like this:
class Game:
def __init__(self):
self._entities = set()
def register(self, entity):
if entity not in self._entities:
self._entities.add(entity)
def unregister(self, entity):
if entity in self._entities:
self._entities.remove(entity)
def end(self):
print("Game over")
raise SystemExit(0)
class Entity:
def __init__(self, *args, game, **kwargs):
super().__init__(*args, **kwargs)
self.game = game
game.register(self)
def die(self):
self.game.unregister(self)
class Fighter(Entity):
def __init__(self, *args, difficulty, money=None, **kwargs):
super().__init__(*args, **kwargs)
if money is None:
money = (difficulty ** 2 - difficulty) // 2 + 1
self.difficulty = difficulty
self.money = money
self._hp = 1000
def attack(self, other):
money = other.hit(self, self.difficulty):
self.money += money
def hit(self, other, damage):
self._hp -= damage
if self._hp <= 0:
money = self.money
self.money = 0
self.die()
return money
# Didn't kill me, so gets no money
return 0
class Player(Fighter):
def __init__(self, *args, difficulty, money=None, experience=0, **kwargs):
if money is None:
money = 1000 // difficulty # You get less money the more skilled
# you are.
self.experience = experience
super().__init__(*args, difficulty=difficulty, money=money, **kwargs)
def die(self):
self.game.end()
def attack(self, other):
# Every time you attack an enemy, you get XP
self.experience += other.difficulty
super().attack(other)
This is crude and poorly designed, but you get the general idea. Each thing is in charge of dealing with its own stuff. Really the world should be in charge of checking whether all of the players are dead, but as it doesn't store players separately I couldn't show that in the example.

Resources