"local variable referenced before assignment" but I think I need to? - python

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
...

Related

How can I code a timer to run simultaneously with my code?

import time
import threading
import random
#declare variables and constant
guessingelement = ["Hydrogen", "Magnesium", "Cobalt", "Mercury", "Aluminium", "Uranium", "Antimony"]
nicephrases = ["Nice job", "Marvellous", "Wonderful", "Bingo", "Dynamite"]
wronganswers = ["Wrong answer...", "Nope", "Try again next time.", "Wrong answer. Nice effort"]
guess = ""
guess_count = 0
guess_limit = 3
out_of_guesses = False
guess_no = 0
score = 0
def countdown():
global my_timer
my_timer = 5
for i in range(7):
randomelement = random.choice(guessingelement)
guessingelement.remove(randomelement)
time.sleep(1)
for x in range(5):
my_timer = my_timer - 1
time.sleep(1)
print("Out of time.Haiya!")
countdown_thread = threading.Thread(target = countdown)
countdown_thread.start()
while my_timer > 0:
#tips of the element
if randomelement == "Hydrogen" and not out_of_guesses:
print("Tip 1: It is the most flammable of all the known substances.")
print("Tip 2: It reacts with oxides and chlorides of many metals, like copper, lead, mercury, to produce free metals.")
print("Tip 3: It reacts with oxygen to form water.")
#test the number of tries so that it doesn't exceed 3 times if answer is wrong
while guess != randomelement and not(out_of_guesses):
if guess_count < guess_limit:
guess = input("Enter guess: ")
guess_count += 1
else:
out_of_guesses = True
#add score, praise when answer is correct and encourage when answer is wrong for 3 times
if out_of_guesses:
print(random.choice(wronganswers))
print(f"{randomelement} was the element.")
else:
print(random.choice(nicephrases), ", YOU GET IT!")
score = score + 1
if randomelement == "Magnesium" and not out_of_guesses:
print("Tip 1: It has the atomic number of 12.")
print("Tip 2: It's oxide can be extracted into free metal through electrolysis.")
print("Tip 3: It is a type of metal.")
procedure same as the first question. And so on with my questions.
However, it shows that my_timer variable is not defined. The process that I would want it to undergo is that it will countdown for 5 seconds for every questions, and when the timer reaches 0, it will print out of time and proceed to the next question.
In the code you've shown, you haven't assigned a value to my_timer.
my_timer = 5
By assigning the global within countdown() you've merely allowed countdown to change the value of my_timer. You still need to assign a value.

Python not saving variable values when they are changed inside a function

I saved my variables at the start of my program and allow the functions to access them I believe but when they run the value is not saved when the function repeats.
P1_score = 0
P2_score = 0
round_number = 0
def dice_rolling():
# P1D1 means player ones dice one value and so on with P1D2
import random
# player ones turn
print("player ones turn")
P1D1 = random.randint(1, 6)
print("your number is ", P1D1)
P1D2 = random.randint(1, 6)
print("your second number is", P1D2)
# player twos turn
print("player twos turn")
P2D1 = random.randint(1, 6)
print("your number is", P2D1)
P2D2 = random.randint(1, 6)
print("your second number is", P2D2)
score_calculation(P1D1, P1D2, P2D1, P2D2,P1_score,P2_score,round_number)
def score_calculation(P1D1, P1D2, P2D1, P2D2,P1_score,P2_score,round_number):
import random
round_number = round_number + 1
# player 1 score calculation
total_P1 = P1D1 + P1D2
P1_score = P1_score + total_P1
if total_P1 % 2 == 0:
P1_score = P1_score + 10
else:
P1_score = P1_score + 5
if P1D1 == P1D2:
P1D3 = random.randint(1, 6)
P1_score = P1_score + P1D3
# player 2 score calculation
total_P2 = P2D1 + P2D2
P2_score = P2_score + total_P2
if total_P2 % 2 == 0:
P2_score = P2_score + 10
else:
P2_score = P2_score + 5
if P2D1 == P2D2:
P2D3 = random.randint(1, 6)
P2_score = P2_score + P2D3
print("player ones score at the end of round", round_number, "is", P1_score)
print("player twos score at the end of round",round_number,"is",P2_score)
for x in range(0,5):
dice_rolling()
Any help would be appreciated and if someone could give a simple explanation as to what I'm doing wrong and what to fix would be great.
Python can read from global variables inside a function, but can't assign them without some extra work. In general, when you want to use a global variable, it's a good idea to make it explicit by using the global keyword in your function:
my_global_var = 0
def some_function():
global my_gobal_var
my_global_var = 10
print(my_global_var) # it prints 10
somefunction() # modifies the global var
print(my_global_var) # now it prints 10
Variables are defined and used locally. Consider this example.
x = 1 #initial value
def test(x):
print(x) #print what you got
x += 1
print(x) #print updated value
print(x) #Actual printouts here
test(x)
print(x)
This results in :
1
1 #value when entering the function
2 #Value changed by function
1 #value outside the function did not change
If you want the variables to be maintained in functions, consider using class variables or global variables. (I recommend avoiding globals as you get to more complex problems)
Global example:
global x
x = 1
def test():
global x
x+=1
print(x)
test()
print(x)
test()
print(x)
Results in :
1
2
3
Finally class variables:
class test_class():
def __init__(self):
self.x = 1 #variables defined like this are class variables
def test(self):
self.x += 1 #Advantages of class variables is that you can defined them in the __init__ function
def running(self):
print(self.x) # And you can access it from multiple functions without using globals and much more too.
self.test()
print(self.x)
if __name__ == '__main__':
tclass = test_class()
tclass.running()

counting instances of a class based on attributes

Counting the number of instances of a specific attribute value
I have created a class, named: bird. Each instance of that class has two attributes:
creed (which can be either "C" or "D" in value), and
life (which starts with a value of 100, and changes as the program executes).
The program simulates random encounters of two live birds, the birds change the value of life after each encounter. Once reaching 0, the bird is excluded from further interactions (i.e. dead).
After several iterations, I wish to know how many birds of each creed are "alive".
import random
class Bird:
Doves = 100
Crows = 100
# Initializer / Instance Attributes
def __init__(self, creed, life):
self.creed = creed
self.life = life
def slct_parties():
first = random.randint(0, (Bird.Crows + Bird.Doves -1))
second = random.randint(0, (Bird.Crows + Bird.Doves -1))
while first == second or population[first].life < 1 or population[second].life < 1:
first = random.randint(0, (Bird.Crows + Bird.Doves - 1))
second = random.randint(0, (Bird.Crows + Bird.Doves - 1))
return first, second
# initiating Population
population = []
for bird in range(0, Bird.Crows):
population.append(Bird('C', 100))
for bird in range(0, Bird.Doves):
population.append(Bird('D', 100))
for x in range(1, 1000):
Contest = slct_parties()
l1 = population[Contest[0]].life
l2 = population[Contest[1]].life
# battle
if population[Contest[0]].creed != population[Contest[0]].creed:
if population[Contest[0]].creed == 'D':
population[Contest[0]].life += 1
population[Contest[1]].life += 1
else:
population[Contest[0]].life += -5
population[Contest[1]].life += -10
elif population[Contest[0]].creed == 'C':
population[Contest[0]].life += 5
population[Contest[1]].life += -20
else:
population[Contest[0]].life += -20
population[Contest[1]].life += 5
print("The battle was between {} number {} with {} life, and {} number {} with {} life"
.format(population[Contest[0]].creed, Contest[0], population[Contest[0]].life, population[Contest[1]].creed,
Contest[1], population[Contest[1]].life))
After 1,000 battles, some birds have died. How many birds? Which creed?
Short answer
single liner (Thanks to Carcigenicate)
dead_crows = len([bird for bird in population if bird.creed == "c" and bird.life <= 0])
function (Thanks to zixuan)
def DeadConter(crd):
dead = 0
for bird in population:
if bird.life <= 0 and bird.creed == crd:
dead += 1
else:
pass
return dead
You can also define 2 values that both start at 0 before the definition of the class that count how many birds are dead, like adding one cDead += 1 when a bird becomes "dead". After the 1000 battles, subtract 100 from the first value and subtract 100 from the second value. You get then, how many bird of each creed are alive. You can also count how many birds of each creed are dead, like this.
import random
c_dead = 0
d_dead = 0
class Bird:
Doves = 100
Crows = 100
# Initializer / Instance Attributes
def __init__(self, creed, life):
self.creed = creed
self.life = life
def slct_parties():
first = random.randint(0, (Bird.Crows + Bird.Doves -1))
second = random.randint(0, (Bird.Crows + Bird.Doves -1))
while first == second or population[first].life < 1 or population[second].life < 1:
first = random.randint(0, (Bird.Crows + Bird.Doves - 1))
second = random.randint(0, (Bird.Crows + Bird.Doves - 1))
return first, second
# initiating Population
population = []
for bird in range(0, Bird.Crows):
population.append(Bird('C', 100))
for bird in range(0, Bird.Doves):
population.append(Bird('D', 100))
for x in range(1, 1000):
Contest = slct_parties()
l1 = population[Contest[0]].life
l2 = population[Contest[1]].life
# battle
if population[Contest[0]].creed != population[Contest[0]].creed:
if population[Contest[0]].creed == 'D':
population[Contest[0]].life += 1
population[Contest[1]].life += 1
else:
population[Contest[0]].life += -5
population[Contest[1]].life += -10
elif population[Contest[0]].creed == 'C':
population[Contest[0]].life += 5
population[Contest[1]].life += -20
else:
population[Contest[0]].life += -20
population[Contest[1]].life += 5
for bird in population:
if bird.life <= 0:
if bird.creed == "C":
c_dead += 1
elif bird.creed == "D":
d_dead += 1
else:
print("Dead birds failed to count") # should never happen, this is a good check for bugs
print("The battle was between {} number {} with {} life, and {} number {} with {} life"
.format(population[Contest[0]].creed, Contest[0], population[Contest[0]].life, population[Contest[1]].creed,
Contest[1], population[Contest[1]].life))
#print dead birds here
print("Number of alive birds")
print(str(100-c_dead) + ": Doves" + " " + str(100-d_dead) + ": Crows")
I added the else line because this code is untested, I don't know if there are any bugs.
EDIT: The code has been tested now and I've changed a few lines to get it working.
Try adding an external variable before the init, initialize it at zero and increment its value by one in the init (+1 for every instance).
Then create a function that checks if a bird has died or not and if so, decrease the value of the variable by one
If you want to count them after the fact and not keep running tallies, just iterate over the population:
c_dead = 0
d_dead = 0
for bird in population:
if bird.life <= 0: # The bird is dead
if bird.creed == "c":
c_dead += 1 # Figure out what creed it is and increment a counter
else
d_dead += 1
This could be cleaned up using a dictionary or some other container, but I think this is the simplest way to show the idea.
In the comments, you noted that you were looking for something along the lines of count(population, creed="c", life>0). That can't be achieved exactly, but you can use a list comprehension to filter out the birds you don't want to count.
c_dead = len([bird for bird in population if bird.creed == "c" and bird.life <= 0])
d_dead = len([bird for bird in population if bird.creed == "d" and bird.life <= 0])
I wouldn't recommend this way though. This requires two full iterations of the population, whereas the first only requires one. If this operation was frequent, and the populations was large, it could cause performance problems.

Unbound error, local variable referenced before assignment

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.

Python pause while true loop until condition fulfilled

I am making a game in python. The game, so far, consists of a single class (animal) and a few variables and methods. You can press one of three keys to attack. It is in a while True loop. when you press the 1 key, it does one attack, and the same for the 2 and 3 keys. When run, however, it keeps running this statement over and over:
print("""
Use key 1 to do {} damage, key 2 for {} damage, and key 3 for
{} damage
""".format(
Animal.attack0_damage, Animal.attack1_damage, Animal.attack2_damage
))
How can I pause the while True loop until the user presses one of the keys? If it is needed, the entirety of my code is here.
from msvcrt import getch
class Animal():
health = 100
mana = 100
attack0_damage = 1
attack1_damage = 5
attack2_damage = 10
def __init__(self, name, species):
self.name = name
self.type = species
def stat_mod(self):
if self.type == "Hippo":
Animal.health += 30
Animal.mana -= 5
Animal.attack0_damage += 10
Animal.attack1_damage += 10
Animal.attack2_damage += 10
elif self.type == "Armadillo":
Animal.health += 20
Animal.mana += 10
Animal.attack0_damage += 1
elif self.type == "Crocodile":
Animal.health += 25
Animal.mana += 5
Animal.attack0_damage += 5
Animal.attack1_damage += 5
Animal.attack2_damage += 5
def attack(self, whatToAttack):
while True:
print("""
Use key 1 to do {} damage, key 2 for {} damage, and key 3 for
{} damage
""".format(
Animal.attack0_damage, Animal.attack1_damage, Animal.attack2_damage
))
key = ord(getch())
print(Animal.health)
print(Animal.mana)
print(whatToAttack.health)
if key == 49:
whatToAttack.health -= Animal.attack0_damage
print(Animal.health)
print(Animal.mana)
print(whatToAttack.health)
elif key == 50:
whatToAttack.health -= Animal.attack1_damage
mana -= 5
print(Animal.health)
print(Animal.mana)
print(whatToAttack.health)
elif key == 51:
whatToAttack.health -= Animal.attack2_damage
mana -= 10
print(Animal.health)
print(Animal.mana)
print(whatToAttack.health)
else:
print("Invalid key! You can only press the keys 1, 2, and 3!")
if whatToAttack.health <= 0:
print("You beat {}!".format(whatToAttack.name())
break
input("Press enter to exit.")
hippo = Animal("Joe", "Hippo")
hippo.stat_mod()
armadillo = Animal("Jeff", "Armadillo")
armadillo.stat_mod()
hippo.attack(armadillo)

Categories

Resources