I'm making a small text game for fun. I want to use a function which is located in a function file that I made called functionala.
The function in question, attack(), does not work and the program crashes with the error:
Traceback (most recent call last):
File "C:\Users\seanm\Desktop\Programming\The mists of Alandria\Mists_of_alandria.py", line 22, in <module>
functionala2.attack()
File "C:\Users\seanm\Desktop\Programming\The mists of Alandria\functionala2.py", line 27, in attack
variablestamina += 2
UnboundLocalError: local variable 'variablestamina' referenced before assignment
The new and improved version of the functionala file is what seems to be causing the problem:
variablestamina = 20
variablehealth = 40
variablemonsterhealth = 30
variableattacktype1 = ("Lightattack")
variableattacktype2 = ("Mediumattack")
variableattacktype3 = ("Heavyattack")
def attack():
variableattackquery = input("You can execute three types of attacks. Lightattack does 2 damage and takes no stamina. Mediumattack does 4 damage and takes 2 stamina. Heavyattack does 7 damage and takes 5 stamina. You can only do one per turn: ")
if variableattackquery == variableattacktype1:
variablemonsterhealth -= 2
variablestamina -= 2
if variableattackquery == variableattacktype2:
variablemonsterhealth -= 4
variablestamina -= 4
if variableattackquery == variableattacktype3:
variablemonsterhealth -= 7
variablestamina -= 7
variablestamina += 2
variablestamina = min(20, variablestamina)
print ("The "+monster+" has "+str(variablemonsterhealth)+" health left")
print ("You have "+str(variablestamina)+" stamina left")
monsterattack = random.randrange(4,6)
variablehealth -= monsterattack
print ("The "+monster+" attacks you for "+str(monsterattack))
print ("You have "+str(variablehealth)+" health left")
print()
This seems a cleaner way of doing it, all in a single file. you may want to look at using classes.
From console, call game() to start the game, that's it. The game will end when either monster or you have health <= 0.
Code:
from random import randrange
def game():
stamina = 20
health = 40
monsterhealth = 30
monster = 'orc'
attacks = {'light':(-2,0),'medium':(-4,-2),'heavy':(-7,-4)}
while True:
a = input('you can execute 3 types of attacks, light, medium or heavy... pick one.')
a = a.lower().strip()
if a in attacks:
stamina, health, monsterhealth = attack(stamina, health, monsterhealth, monster, attacks[a])
if stamina <= 0:
print 'you have died...'
break
elif monsterhealth <= 0:
print 'the {} has died...'.format(monster)
break
else:
break
def attack(stamina, health, monsterhealth, monster, att):
monsterhealth += att[0]
stamina += att[1]
stamina = min(20, stamina)
print('the {} has {} health remaining'.format(monster,monsterhealth))
print('you have {} stamina remaining'.format(stamina))
ma = randrange(4,6)
health -= ma
print('the {} attacks you for {}'.format(monster,ma))
print('you have {} health left'.format(health))
return stamina, health, monsterhealth
NB: Even doing this in a single file, you need to scope the variables to the "main" procedure (game), and pass them to the attack function. Otherwise, referring to these names will raise the same error, and you can reproduce this like so:
m = 1
def foo():
m += 1 '## m doesn't exist within scope of foo, so it will raise the same error
HOWEVER, and this may be confusing, the following will not raise an error:
m = 1
def foo():
print m
Nor will this:
m = 1
def foo():
a = m
print a
But both of those seem kind of hack-y and it's usually better to pass values from the main procedure to called functions/methods/etc and return appropriate values to the caller.
Related
I am getting TypeError: 'int' object is not callable with (my_car.time())
After run my class, I put the input "O" but it can not read the time().
The program:
class Car:
def __init__(self,speed = 0):
self.speed = speed
self.odometer = 0
self.time = 0
def say_state(self):
print("I'm going {} kph!".format(self.speed))
def accelerate(self):
self.speed += 5
def brake(self):
self.speed -= 5
def step(self):
self.odometer += self.speed
self.time += 1
def average_speed(self):
if self.time != 0:
return self.odometer / self.time
else:
pass
if __name__ == '__main__':
my_car = Car()
print("I'm a car!")
while True:
action = input("What should I do? [A]ccelerate, [B]rake, "
"show [O]dometer, or show average [S]peed?").upper()
if action not in "ABOS" or len(action) != 1:
print("I don't know how to do that")
continue
if action == 'A':
my_car.accelerate()
elif action == 'B':
my_car.brake()
elif action == 'O':
print("The car has driven {} kilometers".format(my_car.odometer))
print("The time taken is".format(my_car.time()))
elif action == 'S':
print("The car's average speed was {} kph".format(my_car.average_speed()))
my_car.step()
my_car.say_state()
Output:
I'm a car!
What should I do? [A]ccelerate, [B]rake, show [O]dometer, or show average [S]peed?a
I'm going 5 kph!
What should I do? [A]ccelerate, [B]rake, show [O]dometer, or show average [S]peed?a
I'm going 10 kph!
What should I do? [A]ccelerate, [B]rake, show [O]dometer, or show average [S]peed?o
The car has driven 15 kilometers
Traceback (most recent call last):
File "D:/PyCharm/New Folder/venv/Car.py", line 42, in <module>
print("The time taken is".format(my_car.time()))
TypeError: 'int' object is not callable
Process finished with exit code 1
time in your program is a variable, not method of a class so you can't call it, change line 42 to
print("The time taken is {} h".format(my_car.time)), it should fix the error.
I'm receiving "local variable 'var_1_mon' referenced before assignment" and "local variable 'var_0_mon' referenced before assignment" when entering 0 or 1 for racer = str(input(">")). But to my knowledge I've already assigned their values, and if I assigned them after def game() it will reset my variables to those values after every turn.
I've tried putting the initial variable values after "def game()" and removing the Game loss and Victory conditions, but then choosing var_0 ended the program and var_1 didn't loop back to game().
import random
import time
def start():
var_0_mon = 100
var_1_mon = 100
def game():
var_0 = 0
var_1 = 0
racer = str(input(">"))
if racer in ("0"):
player_mon = var_0_mon
enemy_mon = var_1_mon
elif racer in ("1"):
player_mon = var_1_mon
enemy_mon = var_0_mon
while var_0 <= 100 or var_1 <= 100:
var_0 = var_0 + random.randint(1,2)
var_1 = var_1 + random.randint(0,3)
print ("Var 0:", var_0, "Var 1:", var_1)
if var_0 >= 100 or var_1 >= 100:
break
if var_0 >= 100 and var_1 >= 100:
print ("Tie!")
elif var_0 >= 100:
var_0_mon = var_0_mon + 25
var_1_mon = var_1_mon - 25
print ("Var 0 Match Victory!")
elif var_1 >= 100:
var_0_mon = var_0_mon - 25
var_1_mon = var_1_mon + 25
print ("Var 1 Match Victory!")
game()
if player_mon <= 0:
print ("Game Loss")
elif enemy_mon <= 0:
print ("Game Victory!")
start()
I expected def start() to define the beginning of the game, so the player could choose a racer = str(input(">")) to choose between making their player_mon var_0_mon or var_1_mon. Then the game would proceed with var_0 or var_1 receiving random integers (1 - 2 and 0 - 3 respectively), upon which one would reach 100 before the other, or they would tie. Upon a tie, their the player_mon and enemy_mon would remain untouched, however upon the player winning, whichever of the two variable racers they've chosen (var_0 or var_1.) They would receive 25 to their player_mon and the enemy would lose 25 of their enemy_mon, and vice versa. game() would then return them to choose a racer, (var_0 or var_1). This would continue until one variable racer (var_0 or var_1) lost all of their money, if player_mon <= 0: or elif enemy_mon <= 0: printing "Game Loss" or "Game Victory" respectively, and then looping back to the very beginning of the program using start()
As soon as you assign to var_1_mon inside game, you create a new local variable that shadows the one defined inside start. You need to declare it non-local so that assignments affect start's variable rather than create a new one.
def start():
var_0_mon = 100
var_1_mon = 100
def game():
nonlocal var_0_mon, var_1_mon
...
I set damage to 3 at the top but when I type damage += 3 at the bottom it says damage is not referenced, why does this happen?
import random, time
inventory = ['dagger','torch']
hp = 50
maxhp = 50
damage = 3
ac = 4
gold = 10
in_combat = False
def choice():
choose = input(">> ")
if choose == "stats":
print("Health:",hp,", Damage:",damage,", Armour: ",ac,", Gold:",gold)
elif choose == "backpack":
print(inventory)
elif choose == "help":
print("Keywords:\nstats | view your stats\nbackpack | view your inventory\nhelp | view keywords to input\nattack | attack an enemy when in combat")
else:
print("Invalid Input")
class Enemy:
def __init__(self, name, attack, armour, health):
self.name = name
self.attack = attack
self.armour = armour
self.health = health
def attack_enemy(self):
time.sleep(1)
print("What action do you want to make?\nType 'help' for a list of actions\n")
answer = input(">> ")
if answer == "attack":
self.health = self.health - damage
print(self.name,"health is: ",self.health)
def main():
while hp > 0:
if 'dagger' in inventory:
damage += 3
print(damage)
choice()
main()
also if I change the code to dagger = 6 at the bottom it will print 6 but when I type stats it will say damage = 3
You can read global variables, but if you wish to assign (rebind) to them, you'll need to tell Python you mean to using the global keyword.
In this case there's no need to make those variable global at all.
You should move those attributes into a class. In this example I called it Player
import random, time
class Player:
def __init__(self):
self.inventory = ['dagger','torch']
self.hp = 50
self.maxhp = 50
self.damage = 3
self.ac = 4
self.gold = 10
self.in_combat = False
def choice(self):
choose = input(">> ")
if choose == "stats":
print("Health:", self.hp, ", Damage:", self.damage,
", Armour:", self.ac, ", Gold:", self.gold)
elif choose == "backpack":
print(inventory)
elif choose == "help":
print("Keywords:\nstats | view your stats\nbackpack | view your inventory\nhelp | view keywords to input\nattack | attack an enemy when in combat")
else:
print("Invalid Input")
class Enemy:
def __init__(self, name, attack, armour, health):
self.name = name
self.attack = attack
self.armour = armour
self.health = health
def attack_enemy(self):
time.sleep(1)
print("What action do you want to make?\nType 'help' for a list of actions\n")
answer = input(">> ")
if answer == "attack":
self.health = self.health - damage
print(self.name,"health is: ",self.health)
def main():
pl = Player()
while pl.hp > 0:
if 'dagger' in pl.inventory:
pl.damage += 3
print(pl.damage)
pl.choice()
main()
Aside: Since you are probably going to be printing lots of multiline blocks, look up the dedent function in textwrap. You could use it something like this:
from textwrap import dedent
def deprint(s):
print(dedent(s))
...
elif choose == "help":
deprint("""
Keywords:
stats | view your stats
backpack | view your inventory
help | view keywords to input
attack | attack an enemy when in combat""")
I'm trying to make a text based game, and to make my fighting mechanics I have to repeat an if statement until you or the monster dies.
Here it is --
Ph would stand for Player health, str is player strength, mdam is monster damage, mstr is monster strength, monsterh is monsterhealth, the random.random() is the attack bonus you receive (Adding onto this, is there any way to round the number to one or no decimal places?) Thank you! :D
if fight == "A" and ph > 0:
damage = (str) + (random.random())
monsterh = 6
mstr = 0.9
monsterh = (monsterh) - (damage)
print(damage)
print(damage - str)
print('You attack the monster. You deal %s damage.' % damage )
time.sleep(3)
if monsterh > 0:
mstr = 0.9
mdam = (mstr) + (random.random())
print('The monster attacks you!')
ph = ph - mstr
print("You get hit %s" % mdam )
else:
xpgain = damage + 5
print("You won! You've gained %s XP!" % xpgain)
I would wrap your combat seen in a while loop. For example:
while (ph > 0 or monsterh > 0)
# combat rules
# Check who died and move on from there
if (ph > 0)
# next thing the player does
else
# print "You died"
In regards to rounding, there are multiple options depending on exactly what you want to do: python: getting only 1 decimal place
To round the value you get from
random.random()
you can do
round(random.random(),1)
to round to 1 decimal place, or if you want to get rid of decimals, replace
random.random()
with
int(random.random())
As for repeating the if statement, you can put it into a loop which will break when a condition is met
while true:
if fight == "A" and ph > 0:
damage = (str) + (int(random.random()))
monsterh = 6
mstr = 0.9
monsterh = (monsterh) - (damage)
print(damage)
print(damage - str)
print('You attack the monster. You deal %s damage.' % damage )
time.sleep(3)
elif ph<=0 or fight!="A":
print("You lose!")
break
if monsterh > 0:
mstr = 0.9
mdam = (mstr) + (int(random.random()))
print('The monster attacks you!')
ph = ph - mstr
print("You get hit %s" % mdam )
else:
xpgain = damage + 5
print("You won! You've gained %s XP!" % xpgain)
break
Although you wouldn't need the fight!="A" since I don't think you change the value of fight at all.
Also you don't have to do
mdam = (mstr) + (int(random.random()))
You can just do
mdam = mstr+int(random.random())
This above ^ applies to other places in your code as well. If you have any questions please feel free to ask.
I am new to programming in python and, I started writing a simple text based adventure game. I came across a problem when passing an object from one function to another. Here is my code:
import random
import time
num = random.randint(0,2)
xp1 = random.randint(1,2)
class player:
def __init__ (self, name, health, strength, defense, potion, xp, level):
self.__health = health
self.__strength = strength
self.__defense = defense
self.__name = name
self.__potion = potion
self.__xp = xp
self.__level = level
def getName(self):
return self.__name
def getHealth(self):
return self.__health
def getStrength(self):
return self.__strength
def getDefense(self):
return self.__defense
def getPotion(self):
return self.__potion
def getXP(self):
return self.__xp
def getLevel(self):
return self.__level
def setHealth(self):
self.__health = 10
def setLevel(self):
self.__level = 1
def subHealth(self, num):
self.__health -= num
def subPotion(self):
self.__potion -= 1
return self.__health
def addPotion(self, num1):
self.__potion += num1
def addHealth(self):
self.__health +=2
def addStrength(self):
self.__strength += 1
def addDefense(self):
self.__defense += 1
def addXP(self):
self.__xp += xp1
def addLevel(self):
self.__level += 1
self.__addHealth += 1
self.__defense += 1
self.__strength += 1
def battle(enemy, player1, name1):
player1 = player(name1, player1.getHealth(), player1.getStrength(), player1.getDefense(), player1.getPotion(), player1.getXP(), player1.getLevel())
enemy = player('Dongus', enemy.getHealth(), enemy.getStrength(), enemy.getDefense(), enemy.getPotion(), enemy.getXP(), enemy.getLevel())
s = 0
while s == 0:
time.sleep(1)
attack =int(input("Type 1 to attack, type 2 to use a potion."))
if attack == 1:
time.sleep(1)
print("Dongus's health is", enemy.subHealth(num))
print("Dongus hit you and your health is now at", player1.subHealth(num-player1.getDefense()))
elif attack == 2:
time.sleep(1)
print("You used a potion.")
player1.addHealth(), player1.subPotion()
if player1.getHealth() > 10:
player1.setHealth()
print("Dongus hit you and your health is now at", player1.subHealth(num-player1.getDefense()))
if enemy.getHealth()<=0:
print("Congratulations, you won! You recieved", xp1, "xp!")
player.addXP()
s = 2
def main():
name1 = input("What would you like your name to be?")
time.sleep(1)
print("Hello,", name1, "you are on a quest to save otis from the evil Dongus. You must slay him, or Otis will poop.")
time.sleep(2)
player1 = player(name1, 10, 2, 1, 0, 0, 1)
enemy = player('Dongus', 8, 4, 0, 0, 0, 0)
print("Your stats are, health:", player1.getHealth(), "strength:", player1.getStrength(), "and defense:", player1.getDefense())
time.sleep(2)
print("Fight!")
pick = input("You found a health potion! Press 'p' to pick it up.")
p = 0
while p == 0:
if pick == "p":
print("You added a potion to your inventory.")
player1.addPotion(1)
p = 2
else:
print("You have no potions, you should probably pick this one up.")
player1.addPotion(1)
p = 2
battle(enemy, player1, name1)
if self.__getXP() == 1:
print("You leveled up. You are now level 2.")
player1.addLevel()
print("Your stats are, health:", player1.getHealth(), "strength:", player1.getStrength(), "and defense:", player.getDefense())
loot1 = int(input("Type ''1'' to loot the enemy chest."))
if loot1 == 1:
print("You recieved two potions!")
player1.__addPotion(2)
enemy.setHealth(10)
battle(enemy, player1, name1)
main()
Now the problem is when I run the game, I get to a point where I type "1" to attack the enemy, but it says, for some reason, that after attacking the enemy, the enemies health is at "None". This is the same case when the enemy attacks player1, it says player1's health is at "None". I assume that "None" is the default value in python 3.4.1, so my thinking is that the player1's object from def main() are not being transferred over to def battle() and I cannot see the reason why this is happening. I most likely am missing something here, or it is something I do not already know about Python that is causing the issue. Does anybody know what I can do to fix this, and why it is doing this?
BTW some of the terms I am using may be wrong, so please correct me if they are... I have only been coding for 2 weeks :p.
Thanks!!!
First, received not recieved
2nd yes, If you have a Python function that does not return a value, the result is None
# dummy will return "Positive" or None
def dummy(x):
if X > 0:
return "Positive"
So, you probably want to change
def subHealth(self, num):
self.__health -= num
to
def subHealth(self, num):
self.__health -= num
return self.__health
Your question re: the "player" classes from def main() are not being transferred over to def battle() does not really make sense to me.
But, I see that in the first 2 lines of battle, you are replacing the formal parameters player1 and enemy with local variables, this seems like odd usage in your program.