Python: Using Variable From Different Function Without Renaming - python-3.x

I'm working on creating a short battle from a game (for fun). I have several variables that need to change throughout the sequence, such as m_hp (monster health). Here is the code I have (Forgive me if I formatted incorrectly, this is my first time using this website):
def battle(p_lvl):
print("A goblin engages you in battle!")
p_hp = p_lvl * 25 + 25
p_dmg = p_lvl * 7 + 5
print("Player Stats (Level: " + str(p_lvl) + " Health: " + str(p_hp) + " ATK: " + str(p_dmg) + ")")
m_lvl = p_lvl - 1
m_hp = m_lvl * 20 + 15
m_dmg = m_lvl * 6 + 3
scr_dmg = m_hp / 4
dus_eft = p_hp / 5
print("Goblin Stats (Level: " + str(m_lvl) + " Health: " + str(m_hp) + " ATK: " + str(m_dmg) + ")")
act()
def act():
menu = 'Attack','Block','Use Item'
print("What will you do?")
print(menu)
action = input()
if action == ‘Attack':
m_hp = m_hp - p_dmg
print("You dealt " + str(p_dmg) + "damage to the goblin!")
p_hp = p_hp - m_dmg
print("The goblin struck back with " + str(m_dmg) + " damage!”)
act()
I get problems after act() initiates. If I enter "attack" for the input(), I get this error:
UnboundLocalError: local variable 'm_hp' referenced before assignment
To my understanding, this is because I have m_hp placed under battle(), but act() cannot reach it. I want to be able to change m_hp without writing it in act(). I believe if I do so, then m_hp will reset each time act() initiates, and m_hp cannot drop. I've searched through questions on here, but the cases I've seen don't seem to work. Also, I am admittedly new to coding, so I couldn't understand some of the solutions.
Tl;dr I'm looking for a simple solution as to how to use variables from a different function without putting them into the function.

You're seeing that error message because the 'm_hp' variable is local to the scope of the 'battle' function. It is created inside the function, and ceases to exist after that function exits.
It doesn't look like battle actually gets called anywhere though...
It also looks like you call the act() function before you define it.
There are a number of other reasons why the code would not run if that error was fixed as well.
The feature you're probably looking for where you have access to variables inside multiple functions without passing them in is some kind of global variable. In general, global variables are strongly discouraged anywhere they can be reasonably avoided.
I've retooled your code a bit so that you have something working to mess around with. I've used classes to try to group related things together.
There is now a 'Player' object with all the player's stats and a 'Monster' object with all the monster's stats.
Try running the code below, then making incremental changes and seeing if it continues to run.
class Player(object):
def __init__(self, level):
self.level = level
self.stats_for_level()
def stats_for_level(self):
self.attack_damage = (self.level * 7) + 5
self.hitpoints = (self.level * 25) + 25
def take_damage(self, damage):
self.hitpoints -= damage
def print_stats(self):
print("Player Stats (Level: " + str(self.level) +
" Health: " + str(self.hitpoints) +
" ATK: " + str(self.hitpoints) + ")")
class Monster(object):
def __init__(self, level, name="Goblin"):
self.level = level
self.name = name
self.stats_for_level()
def stats_for_level(self):
self.attack_damage = (self.level * 6) + 3
self.hitpoints = (self.level * 20) + 15
def take_damage(self, damage):
self.hitpoints -= damage
def print_stats(self):
print(self.name + " Stats (Level: " + str(self.level) +
" Health: " + str(self.hitpoints) +
" ATK: " + str(self.hitpoints) + ")")
class Game(object):
def __init__(self, starting_level=1):
self.player = Player(starting_level)
# create monster
self.monster = Monster(level=self.player.level - 1, name='Goblin')
def battle(self, player, monster):
print("A {} engages you in battle!".format(monster.name))
monster.take_damage(player.attack_damage)
print("You dealt " + str(player.attack_damage) + " damage to the goblin!")
self.player.take_damage(monster.attack_damage)
print("The goblin struck back with " + str(monster.attack_damage) + " damage!")
def act(self):
# print stats
self.player.print_stats()
self.monster.print_stats()
# show menu
menu = 'Attack', 'Block', 'Use Item'
print(menu)
print("\nWhat will you do?: ")
action = input()
# take an action
if action == 'Attack':
self.battle(self.player, self.monster)
def main():
game = Game(starting_level=3)
while(True):
game.act()
if __name__ == '__main__':
main()

Related

Improving in function clarity and efficiency in python

I am very new in programming so I need help in "cleaning up my code" if the code is a mess if not then please recommend some better commands that accomplish the same purpose of any line or multiple lines.
Here is my program.
import random
def random_num():
lower_bound = input("what is your lower bound: ")
upper_bound = input("What is your upper bound: ")
trials = input("How many time would you like to try: ")
random_number = random.randint(int(lower_bound),
int(upper_bound))
user_input = input(
"What is the number that I am thinking in the range "
+ lower_bound + "-" + upper_bound + " (only " + trials + " tries): ")
ending = "Thanks For Playing"
continue_game = "That's it. Do you want to continue the game?
(Yes/No): "
count = 0
while True:
answer = int(user_input)
if count == int(trials) - 1:
lost = input("Sorry you have lost " + continue_game).lower()
if lost in ["yes"]:
random_num()
else:
return ending
else:
if answer == random_number:
win = input(continue_game).lower()
if win in ["yes"]:
random_num()
else:
return ending
elif answer >= random_number:
user_input = input("The number is smaller than that: ")
else:
user_input = input("The number is bigger than that: ")
count += 1
print(random_num())

Keep getting else/elif invalid syntax error

Currently I'm writing a code where I want to ask motor values from motor controller using raspberry pi. However my code is throwing InvalidSyntax Error in else and elif statements. I've already read about if and elif statements in python, but I can't figure out mistake by myself. The code is below:
def motor_values():
while 1:
command_1 = '?A '+str(1)+' \r' #Asking first motor current
command_2 = '?A '+str(2)+' \r' #Asking second motor current
counter = 0
#First motor current
if counter = 0:
ser.write(command_1.encode()) #string to bytes
data_1 = ser.readline().decode().strip() #bytes to string
#Checking if data is received and extracting current value
if 'A=' in data_1:
value_1 = int(data_1.split('=')[1])
print("Motor Current(Channel 1): " + str((value_1) + " Ampers")
counter += 1
else:
print("Message is not received")
#Second motor current
elif counter == 1:
ser.write(command_2.encode()) #string to bytes
data_2 = ser.readline().decode().strip() #bytes to string
if 'A=' in data_2:
value_2 = int(data_2.split('=')[1])
print("Motor Current(Channel 2): " + str((value_2) + " Ampers")
counter += 1
else:
print("Message is not received")
else:
counter = 0
A few syntax errors here:
use == in the if clause condition
#First motor current
if counter == 0: #
remove one of the two ( in str((value_2)
print("Motor Current(Channel 1): " + str(value_1) + " Ampers")
print("Motor Current(Channel 2): " + str(value_2) + " Ampers")
you missed closing brace in print function
print("Motor Current(Channel 1): " + str(value_1) + " Ampers")

Timer in Guizero/ TKinter

Right now I am busy with making a GUI for my project. It also needs a test driven by time. However when I press the start button, the counter starts counting but the Gui freezes, so you cannot press the stop button. And eventually the Program stall and shut his self down.
Take a look at my code :
###import libaries ###
from guizero import *
import tkinter as tk
import time
timeLoop = False
###Variabelen###
Sec = 0
Min = 0
Hour = 0
test_stat = 0
###Define Loops###
def verlaat_settings():
window2.hide()
window3.hide()
window4.hide()
window.show(wait=True)
def stop_test():
info("test_result","Test stopped at" + str(Hour) + " Hour " + str(Min) + " Mins " + str(Sec) + " Sec ")
text_test.value = "Test Stopped..."
timeLoop: False
def test_loopt():
global Sec
global Min
text_test.value = "Test is running....."
timeLoop = True
while timeLoop:
Sec +=1
print(str(Min) + " Mins " + str(Sec) + " Sec ")
time.sleep(1)
if Sec == 60:
Sec = 0
Min += 1
app= App(title="Profiberry",layout="",width=480, height=272)
window3 = Window(app,title="Profiberry-Tester", layout="grid",width=480, height=272)
window3.show
###Window3###
welkom_tester= Text(window3, text="Profibus Tester",grid=[2,0,1,1])
Plaatje_profi= Picture(window3,image="logoprofi.gif",grid=[2,1,1,1])
lege_ruimte1 = Text(window3, text="", grid=[2,2,1,1])
text_test= Text(window3,text=" Waiting for input..... ",grid=[2,3,1,1])
timer_test= Text(window3,text=(""),grid=[2,4,1,1] )
lege_ruimte2 = Text(window3, text="", grid=[2,5,1,1])
lege_ruimte2 = Text(window3, text="", grid=[1,6])
Start_knop= PushButton(window3,text="Start",command=test_loopt,padx=50, pady=10, grid=[1,6] )
Start_knop.tk.config(foreground="white",background="green")
Stop_knop= PushButton(window3,text="Stop",command=stop_test,padx=50,pady=10,grid=[3,6])
Stop_knop.tk.config(foreground="white",background="red")
Exit_setting = PushButton(window3,command=verlaat_settings,text="Exit to Main Menu(F2)",padx=30,pady=10,grid=[2,6])
I will talk you trough this part of my program:
Import the libraries used for this purpose.
Give timeLoop, our while variable, a false state.
Give our variables value.
Below that are our Def loops verlaat_settings is used to move trough windows in the GUI Stop_test is used to preform actions when stop is pressed (also to reset the while state) test_loopt is the actual test, the counter has to run by here what it does in the shell.
Below that we open the window and place the widgets.
This answer is to help over users encountering a similar situation.
Although the proposed answer is imaginative, it is a bit long winded.
In GuiZero, the .display() function creates an infinite loop. Therefore, any use of normal python time functions will prevent the display loop from executing.
When using GuiZero, do not use sleep, or while loops.
It is simpler and easier to use .repeat(duration, command)
For instance the code above should work if this line:
Start_knop= PushButton(window3,text="Start",command=test_loopt,padx=50, pady=10, grid=[1,6] )
is changed into those:
Start_knop= PushButton(window3,text="Start",padx=50, pady=10, grid=[1,6])
Start_knop.repeat(1000, test_loopt)
and the original test_loopt function:
def test_loopt():
global Sec
global Min
text_test.value = "Test is running....."
timeLoop = True
while timeLoop:
Sec +=1
print(str(Min) + " Mins " + str(Sec) + " Sec ")
time.sleep(1)
if Sec == 60:
Sec = 0
Min += 1
if Min == 60:
Sec = 0
Min = 0
Hour = 1
if stop_test():
timeLoop = False
is modified as:
def test_loopt():
global Secs, Mins, Hrs
text_test.value = "Test is running....."
Secs +=1
print(str(Hrs) + " Hours " + str(Min) + " Mins " + str(Sec) + " Sec ")
if Secs%60 == 0:
Secs = 0
Mins += 1
if Min%60 == 0:
Secs, Mins = 0, 0
Hrs += 1
If must be noted that the result is not displayed on the GUI but in the IDE and that there is no end to this loop.
It is also necessary to modify the stop_test function from:
def stop_test():
info("test_result","Test stopped at" + str(Hour) + " Hour " + str(Min) + " Mins " + str(Sec) + " Sec ")
text_test.value = "Test Stopped..."
timeLoop: False
to
def stop_test():
info("test_result","Test stopped at" + str(Hrs) + " Hour " + str(Mins) + " Mins " + str(Secs) + " Sec ")
text_test.value = "Test Stopped..."
Start_knop.after(pause_loop)
def pause_loop():
pass
or something along those lines
so after searching for a while I found a page on here with someone on python 2.7 having the exact same problem.
the solution to this is that everything runs in the main loop and the main loop waits for ever on this test_loopt the solution was making a Thread, which in my case was :
def test_loopt():
global Sec
global Min
text_test.value = "Test is running....."
timeLoop = True
while timeLoop:
Sec +=1
print(str(Min) + " Mins " + str(Sec) + " Sec ")
time.sleep(1)
if Sec == 60:
Sec = 0
Min += 1
if Min == 60:
Sec = 0
Min = 0
Hour = 1
if stop_test():
timeLoop = False
def start_test_loopt_thread():
global test_loopt_thread
test_loopt_thread = threading.Thread(target=test_loopt)
test_loopt_thread.deamon = True
test_loopt_thread.start()
There's a basic answer to this in GuiZero's Documentation. The problem is that, because of the loop, GuiZero's display loop is being blocked, like you said, stalling it and eventually crashing it. This is because GuiZero is an event-driven loop, which is why it uses the .display() loop to check for events.
You need to use the .repeat method (shown below) for the application to repeat that action.
.repeat( [enter delay here in milliseconds] , [command to execute] )
This means the loop in the function is not needed.
I understand I'm a bit late to answer this question, but I hope it helped.

If/ Elses statement not executing properly

I've altered the if/else statements several times and either only the if statement will be executed even if it's not true or only the else statement will be executed even if it's not true.
def interview():
""" NoneType -> NoneType
Interview the user. """
name = input("What is your name?")
unit = input("What is you preferred choice of unit - standard or imperial?")
weight = float(input("What is your weight?"))
height = float(input("What is your height?"))
a = float(bmi_std(weight, height))
b = float(bmi_std_prime(weight, height))
c = float(bmi_imp(weight, height))
d = float(bmi_imp_prime(weight, height))
e = str(category(weight, height, 's'))
f = str(category(weight, height, 'i'))
print("So," + name + ", your preferred unit is" + unit + ".")
print("Your weight", weight)
print("Your height", height)
if str(unit == 'imperial'):
print("BMI = " + str(c) + " and your BMI prime = " + str(d))
print("Category = " + f)
else:
print("BMI = " + str(a) + " and your BMI prime = " + str(b))
print("Category = " + e)
This is just part of my code. Is there something wrong in the way its written because it looks fine to me. I'm only an intro to programming.
I can see an issue right away
if str(unit == 'imperial'): should be if str(unit) == 'imperial':

How do I make my python code so that only text can be entered?

import random
def diceRoll(number):
roll = random.randint(1,number)
print("Rolling a ",number," sided die.")
return roll
def newAccount(playername):
print("Greetings ", playername,"! We'll generate your charecter's attributes by rolling some die")
skillroll = 10 + (int(round((diceRoll(12)/diceRoll(4)))))
strengthroll = 10 + (int(round((diceRoll(12)/diceRoll(4)))))
print("You have ",skillroll," skillpoints and ",strengthroll," strength!")
class newplayer:
name = playername
skill = skillroll
strength = strengthroll
save(newplayer)
def save(newplayer):
"""Saves all details"""
file = open("accounts.txt","a")
file.write("CharacterName = '" + newplayer.name + "'\n")
file.write("Strength = " + str(newplayer.strength) + '\n')
file.write("Skill = " + str(newplayer.skill) + '\n')
file.write("\n")
print("Saved account")
def newPlayer():
try:
player1 = input("What is your 1st player's name? ")
newAccount(player1)
except:
print("Please only enter letters for your character name ")
newPlayer()
newPlayer()
print(" ")
player2 = input("What is your 2nd player's name? ")
newAccount(player2)
The part in bold is the part of the code I have attempted to alter to add some sort of error handling. Someone help me out here please, I know it's probably simple but I can't find an answer to this question anywhere.
Pythons RegEx would be a quite easy solution:
import re
if not re.match("[a-z]+$",inputString):
#your exception handling

Resources