Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I'm trying to write a simple program that defines two imaginary fighters with only a name and amount of health.
Right now this is what I have written:
import random
def main():
pass
if __name__ == '__main__':
main()
hpRed = 20
hpBlu = 20
def attack():
damage = random.randrange(1,3)
return hpRed - damage
return hpBlue - damage
def fighterRed(name, hpRed):
print(str(name) + " has " + str(hpRed) + " health left.")
def fighterBlue(name, hpBlu):
print(str(name) + " has " + str(hpBlu) + " health left.")
def battle():
fighterRed("Branden",hpRed)
fighterBlue("Alex",hpBlu)
while ((hpRed > 0) and (hpBlu > 0) is True):
attack()
else:
print("The battle is over!")
if (hpRed > 0):
return "Red Player is victorious!"
else:
return "Blue Player is victorious!"
battle()
So far I constantly receive the error "hpRed referenced before assignment". What can I change to get it to properly pass on the value of hpRed and hpBlu?
There were a couple of errors:
You were using two different names: hpBlue and hpBlu
You were returning two values instead of changing the values of the variables you defined.
Your printing functions were just executing 1 time. (Added inside the while so it prints each iteration)
Code:
import random
hpRed = 20
hpBlu = 20
def attack():
global hpRed, hpBlu
damage = random.randrange(1,3)
hpRed = hpRed - damage
hpBlu = hpBlu - damage
def fighterRed(name, hpRed):
print(str(name) + " has " + str(hpRed) + " health left.")
def fighterBlue(name, hpBlu):
print(str(name) + " has " + str(hpBlu) + " health left.")
def battle():
while (((hpRed > 0) and (hpBlu > 0)) is True):
fighterRed("Branden",hpRed)
fighterBlue("Alex",hpBlu)
attack()
else:
print("The battle is over!")
if (hpRed > 0):
return "Red Player is victorious!"
else:
return "Blue Player is victorious!"
battle()
You're not actually making any changes to either player's hp; look at your attack() function and start checking your variables.
The hpRed and hpBlue (which is misspelled in at least one place by the way) variables are defined at the module ("global") level. The names inside the functions are function-local names, which cannot see global variables unless they are explicitly assigned to globals. That would look like this:
def attack():
global hpRed, hpBlue
# rest of your function
While you could do this simply with globals, it's not very good practice. I recommend passing the value into any functions that need it, or putting it in a class so that methods of the class can operate on it.
def attack(hpr, hpb):
# calculate damage, then...
return (hpr - damage, hpb - damage)
You may notice that I changed your return statement. What you'd written wouldn't work the way you wanted it to: once you return, you can't return again from the same method call (that behavior would be more like a generator). If you want to return both the new red HP and the new blue HP, return a tuple containing them both.
This code has some other problems as well-- you're never actually changing the global variables, for instance, and you'll save yourself some headache if you put a print call inside the while loop so you can see how the hp changes.
Related
Trying to figure out why the program won't run.
This is the error code I am getting:
update_position (random_roll_1, random_roll_2)
builtins.NameError: name 'random_roll_1' is not defined
I thought I had all the correct parameters but I guess not. Any help would be really appreciated
import random
from random import randint
#both of the players are able to roll out their turs
this is the function where the players can roll their dice and receive a random number
def roll_die(p1_move,p2_move):
if p1_move is True:
input('Press enter to roll the die for player 1')
random_roll_1 = randint(1,6)
random_roll_1 = str(random_roll_1)
print('Player 1 rolled ' + random_roll_1)
random_roll_1 = int(random_roll_1)
return random_roll_1
elif p2_move is true:
input('Press enter to roll the die for player 2')
random_roll_2 = randint(1,6)
random_roll_2 = str(random_roll_2)
print('Player 2 rolled ' + random_roll_2)
random_roll_2 = int(random_roll_2)
return random_roll_2
This part updates the position on where each player is after it rolls the dice. The players have to roll exactly 8 in order to win. Anything higher than that will cause the player to stay in its position
def update_position(random_roll_1, random_roll_2):
player_1_position = 0
player_2_position = 0
max_score = 8
if player_1_position < max_score:
if player_1_position + random_roll_1 > 8:
print('The roll was too high, player 1 stays in the same spot')
else:
player_1_position += random_roll_1
print('Player 1 moved up ' + random_roll_1 + ' spots!')
return player_1_position
elif player_2_position < max_score:
if player_2_position + random_roll_2 > 8:
print(' The roll was too high, player 2 stays in the same spot')
else:
player_2_position += random_roll_2
print('Player 2 moved up ' + random_roll_2 + ' spots!')
return player_2_position
this function checks to see if one of the players hit 8 as their score
#checks to see if any of the players have managed to reach the end of the game
def check_game_over(player_1_position, player_2_position):
if player_1_position == 8:
print('Player 1 has won!')
print('Thank you for playing!')
continue_game = False
return continue_game
elif player_2_position == 8:
print('Player 2 has won!')
print('Thank you for playing!')
continue_game = False
return continue_game
This function is what controls who's turn it is. I added in the roll dice function along with the update spot function as it would be easier to include them in one whole function together. This is where I am getting my problem.
#random_roll_1,random_roll_2, player_1_position, player_2_position
#change the turn over to the next player
def opponent():
p1_move = True
p2_move = False
if p1_move is True:
roll_die (p1_move, p2_move)
update_position (random_roll_1, random_roll_2)
p1_move = False
p2_move = True
return p1_move, p2_move
elif p2_move is True:
roll_die(p1_move, p2_move)
update_position (random_roll_1, random_roll_2)
p1_move = True
p2_move = False
return p1_move, p2_move
This function basically shows the user which location they are currently sitting at.
def display_state (player_1_position, player_2_position, p1_move, p2_move):
if p1_move is True:
player_1_position = str(player_1_position)
print('Player 1 is in ' + player_1_position + ' spot')
player_1_position = int(player_1_position)
elif p2_move is True:
player_2_position = str(player_2_positon)
print('Player 2 is in ' + player_2_position + ' spot')
player_2_position = int(player_2_position)
Not entirely sure if this function is right at all because I still don't understand main functions completely so not sure if this main function works
def main():
#display instructions
continue_game = True
while continue_game:
opponent()
display_state (player_1_position, player_2_position, p1_move, p2_move)
check_game_over(player_1_position, player_2_position)
main()
So there are a few errors in your program above. The runtime error you stated above is caused when you try to pass 2 variables into the update_position function before they have been defined.
roll_die (p1_move, p2_move)
## random_roll_1 and random_roll_2 have not been defined
update_position (random_roll_1, random_roll_2)
However, before you deal with this error, there is a key programming concept to understand. Global and Local variables.
A Global variable is one which the entire program can use. They are normally defined at the top of your program:
p1_move = True
p2_move = False
def roll_die(): ...
def main(): ...
etc
A Local variable is one which can only be used inside of the function it was created in.
def foo():
x = 10
y = 5
return x + y
In this example, the variables x and y are local variables and cannot be used in other functions of your program (as they only exist inside the function foo).
One thing to note, in the case where you pass a variable as a functional argument, then modify that variable in the function without returning the modified variable. That variable will only be modified inside that function, no where else. This is true for primitive data types (variables not passed by reference).
def foo(z):
z += 1
z = 5
foo(z)
print(z)
In this example, the output would be 5, not 6 as the modified z variable in the function foo has not been returned.
For this reason alone, global variables may seem like the better option, however in general global variables are a bad idea and it is recommended that you stick to using local variables.
More information about Global and Local variables.
With this knowledge, some of your errors may seem more obvious.
For example, in your function roll_die, you define 2 variables random_roll_1 and random_roll_2. However you try to use these variables in other functions, update_position for example.
I realise that you have tried to return each random_roll variable independently from the roll_die function, however you do not store these returned values.
# Original
roll_die (p1_move, p2_move)
# Correctly returned
random_roll = roll_die(p1_move, p2_move)
In the next line, you then try to use both random_roll_1 and random_roll_2 variables, even though you only know 1 of those variables at that stage.
# Original
update_position (random_roll_1, random_roll_2)
# Possible Correction
update_position(random_roll)
You would then have to redefine the update_position function, thinking about each player's go as if it happened one after another, and not both at the same time.
I would also like to emphasize the importance of reducing the amount of duplicate code in your program.
Your opponent function could instead be written as:
def opponent(p1_move, p2_move):
random_roll = roll_die(p1_move, p2_move)
update_position(random_roll)
p1_move = not p1_move
p2_move = not p2_move
return p1_move, p2_move
This refactoring has changed a few details, most importantly, the use of the not. This operator will turn True->False and False->True. For example, if p1_move is false, not p1_move is True.
In addition, the function has parameters p1_move, p2_move, as these variables will be required in other parts of your program, you should define them outside of this functions scope, and pass them as arguments into your other functions. And don't forget to store the return values!
Just to note, this function refactoring is an example of how to reduce duplicate code, and is designed to be used with your modified program.
There are a few more modifications required to make your program run, but I will let you work them out for yourself.
background: I'm currently writing a text-based adventure and each enemy has a certain amount of turns you can attack it before it attacks back.
So to handle this the code sets an argument in the function for the fight dictating how many times you can attack.
def fight_sequence(rounds):
while rounds > 0:
attack = input()
if attack == magic:
magic("you teleport away to safety. Congratulations you have stayed alive through your journey and found a few souvenoirs. nice job!", 1, "you muster up all of your energy, chant the spell.... and nothing happens.Cthulu... seems unimpressed", 1, "")
elif attack == sword:
sword(1)
def magic(teleportmessage, teleportsuccess, firemessage, firefail, winmessage):
x = 0
while x == 0:
fightorflight = input("""you open the book to cast a spell
Do you want to try teleporting or use a fireball?""").lower()
if "teleport" in fightorflight:
if teleportsuccess = 1:
print(teleportmessage)
x = 1
else:
choice = input("You can't teleport out of this battle. Would you like to try a fireball?")
if choice == yes:
fightorflight = "fireball"
else:
x = 1
elif "fire" in fightorflight:
print(firemessage)
if firefail == 1:
choice = input("do you want to try to teleport instead?").lower()
if "yes" in choice:
fightorflight = "teleport"
else:
x = 1
else:
print(winmessage)
else:
print("Sorry not sure what you mean")
def sword(attacksuccess):
if attacksuccess == 1:
print("You pull out the sword and swing at the monster damaging it severely.")
else:
print("You pull out the sword and swing at the monster, but its immune to blunt objects.")
fight_sequence(3)
both magic() and sword() need to be able to decrease rounds by 1, originally i just did that before entering the magic or sword function. however some items to attack with allow you to attack more than once if you want so that won't work for them. Such as magic if they also choose to teleport. Is there a way to allow me to change the variable rounds while inside of another function?
I think using a return value might help but I'm not sure how to go about it
You can simply add a new argument to the magic function, and pass the 'rounds' variable through when you call it.
e.g.
def fight_sequence(rounds):
...
magic("some message", false, "fired", false, "you won", rounds)
def magic(teleportmessage, teleportsuccess, firemessage, firefail, winmessage, rounds):
Your passing through the variable (not just the value) so it will change in every context where rounds can be seen.
HTH.
I would recommend using classes to create this game rather than lots of functions, an example of a class in a hero game below.
class Hero:
def __init__(self):
self.health = 10
def eatApple(self):
self.health += 1
def takeDamage(self):
self.health -= 1
init function runs as class is initialized.
player = Hero()
print(player.health) # will print 10
player.takeDamage()
print(player.health) # will print 9
This way you can have global variables for you functions which can be changed in each function and is much more organised.
from random import randint
from time import sleep
pminatk = 0
pmaxatk = 4
playerhp = 15
def atk(minatk, maxatk):
return randint(minatk, maxatk)
def playerAtk(monsterhp):
dmg = atk(pminatk, pmaxatk)
monsterhp -= dmg
print ("Du gjorde %i skade. Monsteret har nå %i liv igjen" % (dmg, monsterhp))
sleep(1)
return monsterhp
def monsterAtk(mminatk, mmaxatk):
global playerhp
dmg = atk(mminatk, mmaxatk)
playerhp -= dmg
print ("Monsteret gjorde %i skade. Du har nå %i liv igjen" % (dmg, playerhp))
sleep(1)
def fight(monsterhp, mminatk, mmaxatk):
global playerhp
while monsterhp > 0 and playerhp > 0:
playerAtk(monsterhp)
if monsterhp > 0:
monsterAtk(mminatk, mmaxatk)
if playerhp > 0:
print ("Gratulerer! Du beseiret monsteret!")
else:
print ("Du døde!")
fight(5, 1, 2)
fight(6, 0, 2)
This is supposed to be a simple battle system in a text based adventure. Now my problem is that monsterhp always goes back to its original valye after playerAtk is executed. If I choose to have monsterhp as a global variable it will remain 0 after fight() is finished, leaving the hp of all OTHER monster equal to zero as well. Now i could have more than one variable to assign different hp-values to different monster, but is there any way I can do this using parameters to the fight() function?
Your immediate problem is in your while loop when you call playerAtk(), you return monsterhp from it, but do not actually use it in that loop and that is why you see monster's health points to go back up to the original value. Line 28 needs to read:
monsterhp = playerAtk(monsterhp)
instead of:
playerAtk(monsterhp)
As it was suggested in a comment though, It's worth looking into defining your custom classes. I've thrown together following example based on your code to whet your appetite:
from random import randint
from time import sleep
PLAYERNAME = "Player1"
PLAYERHP = 15
PMINATK = 0
PMAXATK = 4
class CharacterDied(Exception):
def __init__(self, msg):
self.msg = msg
class Character:
def __init__(self, name, initial_hp, minatk, maxatk):
self.name = name
self.hp = initial_hp
self.minatk = minatk
self.maxatk = maxatk
def take_damage(self, damage_hp):
self.hp -= damage_hp
msg = "{} takes {:d}HP damage and has {:d} left."
print(msg.format(self.name, damage_hp, self.hp))
if self.hp < 0:
msg = "{} died."
raise CharacterDied(msg.format(self.name))
def attack(self, other):
dmg = randint(self.minatk, self.maxatk)
sleep(1)
other.take_damage(dmg)
def fight(char1, char2):
try:
while True:
char1.attack(char2)
char2.attack(char1)
except CharacterDied as death_exception:
print(death_exception.msg)
if __name__ == "__main__":
player = Character(PLAYERNAME, PLAYERHP, PMINATK, PMAXATK)
fight(player, Character("Monster1", 5, 1, 2))
if player.hp > 0:
fight(player, Character("Monster2", 6, 0, 2))
There are many ways to do this. For simplicity, I've written just one generic Character class we can use for player and monsters, but even if we started customizing them with subclasses, for instance the fight is now a bit more generic and we can also have a monster attack the player as it just became function of two fighting characters.
To your original question though the benefit sought after here is that attributes are kept and persistent with their instance objects which is generally much easier to follow than to try to deal with and track global variables.
Started trying to learn python yesterday, and I have run into a wall already T.T.
I am trying to make a health function in a game in python, and I need the variable health checked constantly to make sure it does not go below 0.
health = 10
health = (health - 5)
health = (health - 6)
Here I need the program to run a completely separate line of code, since health is now equal to -1. I do not want to have
if(health <= 0):
...
because I would need to copy paste this everywhere health is changed.
Would appreciate any help, thanks!
You don't need to check health constantly. Anytime you call a function reducing health (e.g. attack(character, damage)), you could simply check if health > 0. If not, you should call game_over().
Here's some code from a related question:
class Character:
def __init__(self, name, hp_max):
self.name = name
self.xp = 0
self.hp_max = hp_max
self.hp = hp_max
# TODO: define hp_bar here
def is_dead(self):
return self.hp <= 0
def attack(self, opponent, damage):
opponent.hp -= damage
self.xp += damage
def __str__(self):
return '%s (%d/%d)' % (self.name, self.hp, self.hp_max)
hero = Character('Mario', 1000)
enemy = Character('Goomba', 100)
print(enemy)
# Goomba (100/100)
hero.attack(enemy, 50)
print(enemy)
# Goomba (50/100)
hero.attack(enemy, 50)
print(enemy)
# Goomba (0/100)
print(enemy.is_dead())
# True
print(hero.xp)
# 100
I'm not familar with the python language, but my proposal can be tranferred to python.
Create a function that decreases the health value but never returns a value lower than zero. This is the pseudo-code:
function integer decreaseHealth(parameter health, parameter loss)
{
integer newHealth = health - loss
if (health < 0)
return 0
else
return newHealth
}
So I would need to type something like
if(health <= 0)"
print("Game over")
else:
print("New health")
Shame there isn't something in python for this, wish I could put before my code:
cont if(health <= 0):
print("Game over")
This would mean that whenever the health reached 0 or below, no matter where in the code after this, Game Over would print.
Then I wouldn't need to type anything when health is taken away apart from health = health - 1
I went through this a couple of times and couldnt fine anything wrong so its probably something over my head. I also apology for what is going to be an assault on your eyes, its my first year of programming and have probably made multiple etiquette errors.
print('Befor we begin, when you are given options, I ask you to type your input as show, failure to do so will break the program and you will lose all of your progress')
def test():
print('it has worked!')
def stats():
global attack
global health
global mashealth
global agility
if race in ('human','Human'):
maxatk=4
maxagi=4
attack = lambda:random.randint(2,maxatk)
maxhealth = 20
health=20
agility = random.randint(maxagi,10)
elif race in ('Orc','orc'):
attack = random.randint(3,maxATK)
maxhealth = 25
agility = random.radint(maxAGI,10)
def main():
while True:
print('What would you like to do')
print('Rest Shop Fight')
answer=input('-')
if answer in ('Rest','rest'):
health=maxhealth
continue
def character():
global race
global name
global gender
print('What would you like the name of your character to be?')
name=input('-')
print()
print('What race will your character be?')
print('Human Orc Elf')
while True:
race = input('-')
if race in ('human','Human','Orc','orc','Elf','elf'):
break
else:
print('Not a valid response, try again')
continue
print()
print('What gender is your character')
gender=input('-')
print()
def goblin():
goblinatk=1
goblinhealth=100
while True:
print('You have encountered a goblin, what will you do?')
do=input('-')
if do == 'attack':
damage=attack()
goblinhealth=goblinhealth-damage
print('You did',damage,'damage to the goblin')
print('The goblin has',goblinhealth,'hp')
goblinatk=lambda:random.randint(3,10)
health=health-goblinatk
print('the goblin did',goblinatk,'to you')
print('you have',health,'hp')
if goblinhealth <0:
print('The goblin has died')
break
if health <0:
print('you have died')
break
character()
stats()
goblin()
test()
The error is here
Traceback (most recent call last):
File "H:\16CoFrega\Code\Python\GAME.py", line 255, in <module>
goblin()
File "H:\16CoFrega\Code\Python\GAME.py", line 238, in goblin
health=health-goblinatk
UnboundLocalError: local variable 'health' referenced before assignment
You need to specify the health as a global variable, otherwise it would be considered as a local variable, since you assign to it inside the function. Example -
def goblin():
global health
goblinatk=1
goblinhealth=100
def goblin():
............
health=health-goblinatk
............
Look at this function definition. Your function doesn't know what health is, so it won't allow you to subscript something from health.
So, somehow function has to recognize what is health. Two most common way:
First way, Declare health as a global variable. Now health can be recognized globally. But this is not the best way, as dealing with global variables are hard and error-prone and you are already handling too many global variables. So, i won't recommend it. I would rather suggest you to replace all the global variables with method 2. To understand why I am telling this, Read This Question
Second way, the recommended way, is to pass the health variable as the parameter of the function and at the end, return it from the function.
Like this:
def goblin(health):
............
health=health-goblinatk
............
return health
If you are already returning something, don't worry. With python, you can return more than one variable as a tuple.
return statement:
return a,b,c
calling statement:
a,b,c = func()
Hope this helps :)