Why is the varible always the same when using random(python) [duplicate] - python

This question already has answers here:
Using global variables in a function
(25 answers)
Closed last year.
So i am making a small game with damage and the terminal always gets printed "Nukupana does 0 damage" Any idea why this would be?
Here is the code:
Strength = 5
Magic = 5
Speed = 5
Lifeforce =5
base_health = Lifeforce *10 +50
damage_done=0
curent_health = base_health - damage_done
##functions for stuff
def glacius():
magic_damage = 5 * random.randint(1,5)
damage_done = magic_damage
def nukapana_moves():
moves = ["Glacius"]
attack = random.choice(moves)
if attack == "Glacius" :
glacius()
print(f"nukapana uses {attack} it does {damage_done}.")

In your glacius function, damage_done is a local variable. The damage_done in global namespace doesn't see the change unless you use the global keyword in the glacius function to tell Python damage_done should be global:
import random
Strength = 5
Magic = 5
Speed = 5
Lifeforce = 5
base_health = Lifeforce * 10 + 50
damage_done = 0
curent_health = base_health - damage_done
##functions for stuff
def glacius():
global damage_done # <---- here
magic_damage = 5 * random.randint(1, 5)
damage_done = magic_damage
def nukapana_moves():
moves = ["Glacius"]
attack = random.choice(moves)
if attack == "Glacius":
glacius()
print(f"nukapana uses {attack} it does {damage_done}.")
nukapana_moves()
note: Often depending on global variables considered a bad practice. Instead you should free your code from depending on a global variable by returning values from functions. Functions should do their jobs independently.
Re-implementation using class:
import random
class Hero:
def __init__(self):
self.strength = 5
self.magic = 5
self.speed = 5
self.lifeforce = 5
self.base_health = self.lifeforce * 10 + 50
self.damage_done = 0
#property
def current_health(self):
return self.base_health - self.damage_done
def glacius(self):
self.damage_done = 5 * random.randint(1, 5)
def nukapana_moves(self, moves):
attack = random.choice(moves)
if attack == "Glacius":
self.glacius()
print(f"nukapana uses {attack} it does {self.damage_done}.")
player = Hero()
print(player.current_health)
player.nukapana_moves(['Glacius'])
print(player.current_health)
output:
100
nukapana uses Glacius it does 25.
75
note: When you hit damage you need to re-calculate the current_health, Or as I've done here use a property that gives you the correct value. Otherwise you hit damage but the self.current_health doesn't change because it calculated once in the initializer.

Related

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()

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

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

Pyglet game movement lagging behind

So, my snake makes a continuous movement, but if I press any key it goes back in time and lags back and forward. Here is a video: https://youtu.be/KCesu5bGiS8
My guess would be to update the key input faster, but when I do that everything updates faster, so the snake goes faster etc.
Code (as requested in text form) here:
import pyglet
import random
pyglet.resource.path = ["resources"]
pyglet.resource.reindex()
# sets the resource path
class Snake_Window(pyglet.window.Window):
a = 0
dtx = 160
dty = 200
# sets the basic direction and snake body x and y
def __init__(self):
super(Snake_Window, self).__init__(width=1280, height=720)
# sets resolution and inherits
self.key_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.key_handler)
# sets keys
self.set_caption("Wild Snake")
# gives it a name
self.background_image = pyglet.resource.image("background.png")
self.food_image = pyglet.resource.image("food.png")
self.snake_head_image = pyglet.resource.image("snake_head.png")
self.snake_body_image = pyglet.resource.image("snake_body.png")
# makes images usable
self.center_image(self.food_image)
self.center_image(self.snake_head_image)
self.center_image(self.snake_body_image)
# centers the images using center_image
self.snake_head = pyglet.sprite.Sprite(img=self.snake_head_image, x=200, y=200)
self.snake_head.scale = 0.1
self.snake_head.rotation = 270
# sets snake_head as a image on screen
self.snake_body = pyglet.sprite.Sprite(img=self.snake_body_image, x=self.dtx, y=self.dty)
self.snake_body.scale = 0.1
self.snake_body.rotation = 90
# sets snake_body as a image on screen
self.background = pyglet.sprite.Sprite(img=self.background_image, x=0, y=0)
# sets background as a image on screen
self.food = []
# sets food
pyglet.clock.schedule_interval(self.game_tick, 0.1)
def center_image(self, image):
# sets the center of the image to the actual center
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
def update_snake_head(self):
# makes the snake head go and sets the x and y for the body
if self.a == 0:
self.snake_head.x += 40
self.dtx = self.snake_head.x - 40
self.dty = self.snake_head.y
elif self.a == 1:
self.snake_head.x -= 40
self.dtx = self.snake_head.x + 40
self.dty = self.snake_head.y
elif self.a == 2:
self.snake_head.y += 40
self.dty = self.snake_head.y - 40
self.dtx = self.snake_head.x
elif self.a == 3:
self.snake_head.y -= 40
self.dty = self.snake_head.y + 40
self.dtx = self.snake_head.x
def update_snake_body(self, dtx, dty):
# makes the snakes body go
self.snake_body.x = dtx
self.snake_body.y = dty
def game_tick(self, dt):
# updates snakes head, snakes body, key presses and sets the background
self.update_snake_head()
self.update_snake_body(self.dtx, self.dty)
self.draw_elements()
self.key_press()
print(dt)
def draw_elements(self):
# draws everything in window
self.clear()
self.background.draw()
self.snake_head.draw()
self.snake_body.draw()
def key_press(self):
# sets direction of snake upon key press and rotates his head accordingly
if self.key_handler[pyglet.window.key.RIGHT]:
if self.a == 1:
pass
else:
self.a = 0
self.snake_head.rotation = 270
elif self.key_handler[pyglet.window.key.LEFT]:
if self.a == 0:
pass
else:
self.a = 1
self.snake_head.rotation = 90
elif self.key_handler[pyglet.window.key.UP]:
if self.a == 3:
pass
else:
self.a = 2
self.snake_head.rotation = 180
elif self.key_handler[pyglet.window.key.DOWN]:
if self.a == 2:
pass
else:
self.a = 3
self.snake_head.rotation = 0
game_window = Snake_Window()
pyglet.app.run()
Converting all my comments into a answer instead. It won't solve your problem completely. But due to lack of time, I'll leave something useful at least that almost solves it.
The reason for these are a couple. One of them is that you use a scheduler to render stuff instead of using the built-in on_draw event. Can't say for sure, but a good guess is that the graphical buffer gets updated/flipped automatically in on_draw while you're doing your drawing and stuff in a side-chained render function. So moving all the rendering stuff into on_draw makes sense.
Another issue is that you don't trigger on the actual key press, but instead you need to time the key press to each tick in the scheduler you got going - which you also have mushed into the rendering function. Essentially you're doing eventhandling+rendering+updating+IO in one cluster*** of a function, heh. Instead, you should rely on on_key_press for keyboard events.
Lastly, you're doing math operations all over the place - any of which might be half way done when you're doing the actual rendering. That's why you might get ghosting or odd artifacts (some things aren't completely done updating positions etc).
But here is a almost working example of a few steps taken to get closer to what you want. If no one else (including you) haven't solved this by a few days I'll go ahead and re-write most of your code and point you in a few good directions (batches being one of them).
import pyglet
import random
pyglet.resource.path = ["resources"]
pyglet.resource.reindex()
# sets the resource path
class Snake_Window(pyglet.window.Window):
a = 0
dtx = 160
dty = 200
# sets the basic direction and snake body x and y
def __init__(self):
super(Snake_Window, self).__init__(width=1280, height=720)
# sets resolution and inherits
self.key_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.key_handler)
# sets keys
self.set_caption("Wild Snake")
# gives it a name
self.background_image = pyglet.resource.image("background.png")
self.food_image = pyglet.resource.image("food.png")
self.snake_head_image = pyglet.resource.image("snake_head.png")
self.snake_body_image = pyglet.resource.image("snake_body.png")
# makes images usable
self.center_image(self.food_image)
self.center_image(self.snake_head_image)
self.center_image(self.snake_body_image)
# centers the images using center_image
self.snake_head = pyglet.sprite.Sprite(img=self.snake_head_image, x=200, y=200)
self.snake_head.rotation = 270
# sets snake_head as a image on screen
self.snake_body = pyglet.sprite.Sprite(img=self.snake_body_image, x=self.dtx, y=self.dty)
self.snake_body.scale = 0.1
self.snake_body.rotation = 90
# sets snake_body as a image on screen
self.background = pyglet.sprite.Sprite(img=self.background_image, x=0, y=0)
# sets background as a image on screen
self.food = []
# sets food
pyglet.clock.schedule_interval(self.game_tick, 0.1)
def on_draw(self):
self.draw_elements()
def center_image(self, image):
# sets the center of the image to the actual center
image.anchor_x = image.width / 2
image.anchor_y = image.height / 2
def update_snake_head(self):
# makes the snake head go and sets the x and y for the body
if self.a == 0:
self.snake_head.x += 40
self.dtx = self.snake_head.x - 40
self.dty = self.snake_head.y
elif self.a == 1:
self.snake_head.x -= 40
self.dtx = self.snake_head.x + 40
self.dty = self.snake_head.y
elif self.a == 2:
self.snake_head.y += 40
self.dty = self.snake_head.y - 40
self.dtx = self.snake_head.x
elif self.a == 3:
self.snake_head.y -= 40
self.dty = self.snake_head.y + 40
self.dtx = self.snake_head.x
def update_snake_body(self, dtx, dty):
# makes the snakes body go
self.snake_body.x = dtx
self.snake_body.y = dty
def game_tick(self, dt):
# updates snakes head, snakes body, key presses and sets the background
self.update_snake_head()
self.update_snake_body(self.dtx, self.dty)
def draw_elements(self):
# draws everything in window
self.clear()
self.background.draw()
print('Head:', self.snake_head.x, self.snake_head.y, {0:'Right', 1:'Left', 2: 'Up', 3:'Down'}[self.a])
self.snake_head.draw()
self.snake_body.draw()
self.flip()
def on_key_press(self, symbol, modifier):
# sets direction of snake upon key press and rotates his head accordingly
if symbol == pyglet.window.key.ESCAPE:
pyglet.app.exit()
if symbol == pyglet.window.key.SPACE:
print('Here')
if symbol == pyglet.window.key.RIGHT:
if self.a == 1:
pass
else:
self.a = 0
self.snake_head.rotation = 270
elif symbol == pyglet.window.key.LEFT:
if self.a == 0:
pass
else:
self.a = 1
self.snake_head.rotation = 90
elif symbol == pyglet.window.key.UP:
if self.a == 3:
pass
else:
self.a = 2
self.snake_head.rotation = 180
elif symbol == pyglet.window.key.DOWN:
if self.a == 2:
pass
else:
self.a = 3
self.snake_head.rotation = 0
game_window = Snake_Window()
pyglet.app.run()
I'll leave you not only with this ish-working code, but a good advice.
Stop asking so many questions, and start learn ways to debug why things are happening the way they do. You're shooting in the dark right now, asking questions hoping someone will solve the problems for you so you can focus on the fun stuff - which is the game development itself.
But what you'll have a lot of use for later in life - is finding ways to debug, probe, understand and pinpoint why things are or aren't happening the way you want.
Put some print("moo") here and there, print some values, add logging/debugging all over the place until you get wiser. It's not always efficient, but it got me to this point with your code.

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.

Walking through walls that I shouldn't be able to in python

I am making an RPG and I have a section in my code that prevents me from walking through walls. However I have a gate that can only be entered if I have a Key.
class Item:
def __init__(self,item,x,y,picture,owner):
self.item = item
self.x = x
self.y = y
self.picture = picture
self.owner = owner
Chart.chart.create_image(x,y,image=picture,anchor=NW)
def grab(i,h):
i.owner = h
def discard(i,h):
i.owner = None
class Move:
def checkwallup(x):
global up
global down
global left
global right
global xx
global yy
global marketwall
global castlewall
global bforest2
global bmarket
global bcastle
global bforest1
global forest1wall
global forest2wall
global cave
global cave1doorR
global cave1doorL
global bcave1
global cave1wall
global key1
global Key
global hero
# global treetop
yy = yy - 30
pos = (xx,yy)
if bmarket == True:
if pos in marketwall:
yy = yy + 30
Chart.chart.itemconfigure(x,image=heroupstand)
elif pos == (150,30):
if Key.owner == hero:
Move.moveup(x)
else:
Chart.chart.itemconfigure(x,image=heroupstand)
elif pos == (150,0):
if Key.owner == hero:
castle()
else:
Chart.chart.itemconfigure(x,image=heroupstand)
else:
Move.moveup(x)
When I go to move through the gate nothing happens if I press twice, but if I press a third time I will go right through the gate as if I did not need to have the Key. Also when this happens everything else in my moving starts messing up.

Categories

Resources