I am trying to make a very simple python script that pits two characters together, but while running the script, it executes the first two functions that the script uses to define the two characters stats, but when it gets to the third function, it just hangs.
Here's the code:
#STAPS: Strength Toughness Agility Perception weapon Skill
#A comparative simulator
import random
#Functions used to define character parameters
#Character 1's parameter function
def char1():
global s1
global t1
global a1
global p1
global dam1
global dt1
global dr1
global ac1
global ws1
s1 = int(input("Char1's Strength? "))
t1 = int(input("Char1's Toughness? "))
a1 = int(input("Char1's Agility? "))
p1 = int(input("Char1's Perception? "))
dam1 = int(input("Char1's Damage? "))
dt1 = int(input("Char1's Damage Threshold? "))
dr1 = int(input("Char1's Damage Resistance? "))
ac1 = int(input("Char1's Armor Class? "))
ws1 = int(input("Char1's Weapon Skill? "))
#Character 2's paramter function
def char2():
global s2
global t2
global a2
global p2
global dam2
global dt2
global dr2
global ac2
global ws2
s2 = int(input("Char2's Strength? "))
t2 = int(input("Char2's Toughness? "))
a2 = int(input("Char2's Agility? "))
p2 = int(input("Char2's Perception? "))
dam2 = int(input("Char2's Damage? "))
dt2 = int(input("Char2's Damage Threshold? "))
dr2 = int(input("Char2's Damage Resistance? "))
ac2 = int(input("Char2's Armor Class? "))
ws2 = int(input("Char2's Weapon Skill? "))
#Main battle function. Ordo Xenos calls this "complex and easy to misuse"
#Jury-rigged way of getting names, why did I include them anyways?
def stapsbatt(c1n,c2n,hp1,hp2):
while hp1 > 0 or hp2 > 0:
#determines original raw acc
char1rawacc = ws1 - ac2
#if statement settles it to minimum 95% acc
if char1rawacc > 95:
char1rawacc = 95
#random int used to determine whether it's a hit or not
char1hitnum = random.randint(0, 100)
if char1rawacc > char1hitnum:
moddam1 = dam1 - dt2
if moddam1 < 0:
moddam1 = 0
rawdam1 = moddam1 * (100 - dr2)
hp2 = hp2 - rawdam1
#Now we move on to doing char2's batt calcs
char2rawacc = ws2 - ac1
if char2rawacc > 95:
char2rawacc = 95
char2hitnum = random.randint(0, 100)
if char2rawacc > char2hitnum:
moddam2 = dam2 - dt1
if moddam2 < 0:
moddam2 = 0
rawdam2 = moddam2 * (100 - dr1)
hp1 = hp1 - rawdam2
if hp1 == 0:
print(c2n, "has won!")
else:
print(c1n, "has won!")
char1()
char2()
stapsbatt("Character 1", "Character 2",400,30)
input("Press enter to exit. ")
And yes, this code is completely unedited, I realize my comments aren't very good.
First, your comments must be at the same indentation level as the code.
The problem you're probably looking for:
while hp1 > 0 or hp2 > 0:
#determines original raw acc
char1rawacc = ws1 - ac2
This loop never ends, because whatever you do inside never changes its condition. Probably you wanted an if.
The rest: OMG. Seeing this code hurts. Let's make it a bit better.
Don't use global unless you have a damn good reason to do so. For now, consider this a matter of discipline; as you progress as a programmer you will see why separation of scopes matters.
Use functions to describe similar things once. It's the whole point: removing repeating parts, replacing them with names, with new 'words' that makes your language closer to the problem you're solving.
def get_character_description(name):
print "Describe character %s" % name
# input values here...
return description
char1 = get_character_description('Liu Kang')
char2 = get_character_description('Sub Zero')
Use data structures. In this case, combine stats of a character into a single entity.
# Use a dict to describe character stats
def get_character_description(name):
description = {}
description['name'] = name
print "Describe character %s" % name
description['strength'] = int(input("%s's strength?"))
# input other values here...
return description
char1 = get_character_description('Pikachu')
if char1['strength'] > 100: ...
Consider creating a custom class to describe characters when you learn about classes:
class Character(object):
def __init__(self, name):
self.name = name
def input(self):
print "Let's define stats of %s" % self.name
self.strength = int(input("Strength?"))
# and so on
char1 = Character('J.C. Denton')
if char1.strength > 100: ...
After that, your code might look like this:
char1 = get_character_description('Liu Kang')
char2 = get_character_description('Sub Zero')
if char1.wins_over(char2):
print char1.name, "has won"
else:
print char2.name, "has won"
There are a few problems with your code that are causing it to hang. I would suggest getting familiar with a python debugger, such as pdb. This will allow you to look at values during runtime, stepping through the program execution line-by-line.
Indentation issues aside, here are the problem areas I found:
In your while loop, you use while hp1 > 0 and hp2 > 0 as your condition. You don't want to use or here, or it will keep looping until both chars have < 0 hp, so you should change it to and.
When calculating rawacc (for both chars), you used if char1rawacc > 95, which actually enforces a maximum on the char1rawacc. You did the same thing for char2rawacc. Switch these to if char1rawacc < 95 and if char2rawacc < 95.
As a style note, if you are executing this as a script, you should put the function calls outside the function definitions themselves, a good way to do this is to put a block like this at the end of your script:
if __name__ == "__main__":
# the following only gets executed if the file is run as a script
char1()
char2()
stapsbatt("Character 1", "Character 2", 400, 30)
Hopefully this gets you out of your infinite loop! I got the game working on my computer with these changes.
Now, as Oz123 mentions, you are really abusing global variables. Instead, you should look into creating and passing objects (see 9000's answer). For example, you could define a class for your character, and create two instances of this class (char1 and char2) that get passed to your battle function. This will also save you from a lot of redundant code. This will require some significant restructuring, but it will be worth it. If you run into problems with that, feel free to open a new question.
Two immortal characters (with "or->and" fix) => infinite loop
moddam1 = dam1 - dt2
if moddam1 < 0:
moddam1 = 0 # dam1 <= dt2: moddam1 = 0
rawdam1 = moddam1 * (100 - dr2) # rawdam1 == 0
hp2 = hp2 - rawdam1 # hp2 == const: immortality
If it makes you feel better, it doesn't seem to be an issue with your program. (Nevermind yes it is, it's the while statement)
Traceback (most recent call last):
File "C:\Python32\staps.py", line 89, in <module>
stapsbatt("Character 1", "Character 2",400,30)
File "C:\Python32\staps.py", line 76, in stapsbatt
char2hitnum = random.randint(0,100)
File "C:\Python32\lib\random.py", line 215, in randint
return self.randrange(a, b+1)
File "C:\Python32\lib\random.py", line 191, in randrange
return istart + self._randbelow(width)
File "C:\Python32\lib\random.py", line 221, in _randbelow
getrandbits = self.getrandbits
KeyboardInterrupt
It's hanging inside the random module, and despite trying several other functions (random.randrage(0,100), and random.choice(range(0,100))) the problem still manifests. Oddly, there's no issues calling the functions outside your program.
Try cleaning up your code a bit and see if it improves. Generally, your globals should be declared at the beginning. Also, I think you mean to say "and" instead of "or" in this line: while hp1 > 0 or hp2 > 0:. Using an OR in this case means if char1 or char2 is alive. You only want to go until the fight is over (unless of course, you like beating a dead horse).
Related
This is my code:
import random
Mine_time = 0
Mining = 0
Diamond = 0
def Mine(Mining):
if Doing == "mine" or Doing == "Mine":
Mining = random.randint(0, 10)
print("you mined", Mining,"diamonds ")
def Stash(Mining, Diamond):
if Doing == "Stash" or Doing == "stash":
Diamond = Diamond + Mining
print("You have", Diamond, "diamonds")
def Time(Mine_Time):
if Doing == 'Time' or Doing == 'time':
print('Your Time is:', Mine_time)
def MT(Mine_Time):
if Mine_time > 0:
print("this action is on cooldwon please wait", Mine_time, "seconds")
while True:
Doing = input("\n")
Mine(Mining)
Stash(Mining, Diamond)
Time(Mine_time)
MT(Mine_time)
The possible commands are Stash, Mine, and Time, but time doesn't do anything yet. It would be very helpfull if every time I run Stash it didn't show "You have 0 Diamonds"
Your code has multiple issues, starting with concept and style and ending with the actual fulfillment of the task. I reworked your code so Mine and Stash work logically (leastwise for me).
Disclaimer: the code below is still wrong on multiple points, but I want to post it in this form in order to be understandable for OP.
import random
Mine_time = 0
Mining = 0
Diamond = 0
def Mine():
global Mining
if Doing == "mine" or Doing == "Mine":
Mined = random.randint(0, 10)
print("you mined", Mined, "diamonds ")
Mining += Mined
def Stash():
global Diamond, Mining
if Doing == "Stash" or Doing == "stash":
Diamond = Diamond + Mining
Mining = 0
print("You have", Diamond, "diamonds")
def Time():
if Doing == 'Time' or Doing == 'time':
print('Your Time is:', Mine_time)
def MT():
if Mine_time > 0:
print("this action is on cooldown please wait", Mine_time, "seconds")
while True:
Doing = input("\n")
Mine()
Stash()
Time()
MT()
What I've changed:
Your global variables are now global inside your functions. So they keep their values between cycle's loops.
Mine now firstly obtain random Mined value, this value shown to the user, and then it's added to Mining. So if you mined several times, you lose nothing.
Stash now resets Mining to 0, so mined once wouldn't be added to stash several times.
Not sure how Time and MT should work, so I left them untouched.
What you should change (and also learn):
General structure. If you have some data united with some logic, most probably it's a class. You have to learn what it is and how you should use it for tasks like this, and you wouldn't have such strange situations with variable scopes.
Function concept. Maybe you feel some things, but you definitely don't understand them.
Python code style. Here is quite easy to read intro.
I'm creating a simple program to take in time and distance to then state the speed, but I want to do this with classes to learn about oop in python. I'm not figuring out how to set the loop to keep going until the user decides to not go again.
y=True
while y:
class Timer:
def __init__(self,speed):
self.speed=speed
def log(self):
print(mph)
again=input('Go again? y or n: ')
if again=='y':
y=True
else:
print('Thank you')
y=False
m=float(input('Enter the minutes: '))
s=float(input('Enter the seconds: '))
d=float(input('Enter distance: '))
x=(m*60)+s
x_tot=(x/3600)
mph=d/x_tot
t=Timer(mph)
t.log()
You need following code:
y=True
while y:
class Timer:
def __init__(self,speed):
self.speed=speed
def log(self):
print(mph)
global y
again=input('Go again? y or n: ')
if again=='y':
y=True
else:
print('Thank you')
y=False
if y:
m=float(input('Enter the minutes: '))
s=float(input('Enter the seconds: '))
d=float(input('Enter distance: '))
x=(m*60)+s
x_tot=(x/3600)
mph=d/x_tot
t=Timer(mph)
t.log()
else:
break
The y variabel inside log function should be global else it won't change global y referred inside if-else. We need if-else with y so that we can break out of loop if user chooses n. The t=Timer(mph) has to be inside while loop because class is not known outside the loop. Same applies for t.log function call.
Honestly to make your code easier to debug and track where changes are occuring, you should pull the class out of the loop and then reference it inside the loop when you need to use it.
In the init, I would pull out the assignment of the speed variable and just initialize it as none.
def __init__(self):
self.speed = None
Then you can add a separate private setter function to set the speed with user input and do error checking around it. Note, I have set the program to exit with a 0 code if the user inputs something wrong, but you can easily make another loop here that will wait until the user finally does input valid values for all the inputs. The __ double underscore in front of the function name makes it private to the class.
def __set_mph(self):
try:
m = float(input('Enter the minutes: '))
s = float(input('Enter the seconds: '))
d = float(input('Enter distance: '))
x = (m * 60) + s
x_tot = (x / 3600)
self.mph = d / x_tot
except (ValueError, ArithmeticError) as e:
print(f'Invalid user input: {e}')
exit(0)
except Exception as e:
print(f'Unexpected error: {e}')
exit(0)
Now you can update the log function to not even worry about the y variable by changing it to this:
def log(self):
self.__set_mph()
print(mph)
again = input('Go again? y or n: ')
if again == 'y':
return True
else:
print('Thank you')
return False
Now we just initialize the class before the loop and clean it up to be make it more manageable.
t = Timer()
while True:
if not t.log():
break
Final Code:
class Timer:
def __init__(self):
self.speed = None
self.mph = None
def __set_mph(self):
try:
m = float(input('Enter the minutes: '))
s = float(input('Enter the seconds: '))
d = float(input('Enter distance: '))
x = (m * 60) + s
x_tot = (x / 3600)
self.mph = d / x_tot
except (ValueError, ArithmeticError) as e:
print(f'Invalid user input: {e}')
exit(0)
except Exception as e:
print(f'Unexpected error: {e}')
exit(0)
def log(self):
self.__set_mph()
print(self.mph)
again = input('Go again? y or n: ')
if again == 'y':
return True
else:
print('Thank you')
return False
t = Timer()
while True:
if not t.log():
break
OOP is all about modeling your real world objects into programmatic objects that maintain the features and functionality of the real world object to its programatic counterpart's attributes and features, respectively.
Also, those objects should be separated on its own. Creating and calling a class from within a while loop is pretty bad practice. I would encourage you to separate the code based on its purpose. for example, I would have a file called timer.py to handle the Object that matches the timer like so:
# The timer class
class Timer:
def calculate_speed(self, minutes, seconds, distance):
hours = (minutes * 60) + seconds
tot_time = (hours / 3600)
return distance / tot_time
def get_speed(self):
minutes = float(input('Enter the minutes: '))
seconds = float(input('Enter the seconds: '))
distance = float(input('Enter distance: '))
return self.calculate_speed(minutes, seconds, distance)
Then in your main file:
from timer import Timer
timer = Timer()
while True:
user_response = input('Go again? y or n: ').lower()
if user_response == 'n':
break
elif user_response == 'y':
speed = timer.get_speed()
print(f'Your speed is {speed}')
else:
print('Not a valid response')
This makes it easier on the backend too. In other words, if you have an error that relates to the calculations, you know where to start looking.
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
def perd():
Personaldetails_file = open("Personaldetails_file.txt", "w")
Personaldetails_file = open("Personaldetails_file.txt", "a")
pd = input("Are you a new user?")
if pd == "yes":
print ("Add your details")
####################################
age = int(input("how old are you?"))
DOB = input("Date of birth:")
gender = input("Gender:")
weight = int(input("weight in kg:"))
height = int(input("height in cm:"))
Personaldetails = (age, DOB, gender, weight, height)
Personaldetails_file.write(str(Personaldetails))
Personaldetails_file.close()
#######################################
def td():
choice = input("which trainning event would you like to access?\n1.swimming\n2.cycling\n3.running\nplease type in the number before the event of which you want to choose\n")
if choice == "1":
Swimming_file= open("Swimming_file.txt", "w")
totaldistance = input("what was the total distance you swam in meters?")
totaltime = input("how long did you swim for in minutes?")
speed = totaldistance/totaltime
print ("on average you where running at a speed of", speed, "mps\nyou can look at the tables to see how many calouries you have burnt")
total = (totaldistance, totaltime, speed)
Swimming_file.write(str(total))
Swimming_file.close()
elif choice == "3":
Running_file= open("Running_file.txt", "w")
totaldistanceR = int(input("what was the total distance you ran in KM?"))
totaltimeR = int(input("how long did you run for in minutes?"))
totaltimeR1 = 60/totaltimeR
speedR1 = totaldistanceR/totaltimeR1
calburn = (speedR1 * 95)/(60/totaltimeR1)
print ("\nThe records have been saved")
print ("\non average you where running at a speed of", speedR1, "KMph\nyou burnt",calburn," calouries\n")
totalR = (totaldistanceR, totaltimeR, speedR1, calburn)
Running_file.write(str(totalR))
Running_file.close()
##############################################################
elif choice == "2":
Cycling_file= open("Cycling_file.txt", "w")
with open("Personaldetails_file.txt", "r") as f:
content = [x.strip('\n') for x in f.readlines()]
lines = f.readlines()
for line in lines:
words = line.split(",")
age = (line.split)(0)
weight = (line.split)(3)
height = (line.split)(4)
################################################################
totaldistancec = int(input("what was the total distance you cycled in KM?"))
totaltimec = int(input("how long did you cycle for in minutes?"))
calburn1 = (13.75 * weight) + (5 * height) - (6.67 * age)
speedc = totaldistancec/totaltimec
print ("on average you where running at a speed of", speedc, "KMph\nyou burnt", calburn1, " calouries")
totalc = (totaldistancec, totaltimec, speedc)
Cycling_file.write(str(totalc))
Cycling_file.close()
Personaldetails_file.close()
when I un the program an error appears.
line 84, in td
calburn1 = (13.75 * weight) + (5 * height) - (6.67 * age)
UnboundLocalError: local variable 'weight' referenced before assignment
does anyone know how I can fix this error?
the code which is relevant to the question surrounded by '#'
You declare weight here
def perd():
...
gender = input("Gender:")
weight = int(input("weight in kg:"))
height = int(input("height in cm:"))
...
but you try to use it here in this other function:
def tr():
....
calburn1 = (13.75 * weight) + (5 * height) - (6.67 * age)
....
this is a separate function that does not know about the variables within the perd() function, and why should it? It does not need any of them to operate as functions should isolate pieces of code and be able to be used by themselves. In other words once perd() runs its local variables go out of scope. Take a look at this question, Short Description of the Scoping Rules?. To solve this you either need to make the input taken in perd() global, not advisable because it is not very pythonic. Or you can pass the value in as a function parameter like this:
td(weight, height, age):
#code here
Now you have access to the value passed in as weight within the td function. But you need to realize that these are not the same variables, they may share the same name but they are NOT the same. You will need to pass in all values that you want to use from the perd function. This is preferable because you are designing your functions in a way that they can be used modularity, so one can handle getting input from the user while the other can perform calculations on data that it already knows are valid, this allows for easier to read code, which is what python is all about
This program is about ordering stuff from a "mail order" house thing.
They sell five products. P1 = 2.98, P2 = 4.5, P3 = 9.98, P4 = 4.49, P5 = 6.87
So this is my code so far.
#Imports: None
#Defs:
def userInput1():
x = int(input("Product Number?"))
return x
def userInput2():
q = int(input("Quantity?"))
return q
def calculations(x,q):
p1 = 2.98
p2 = 4.5
p3 = 9.98
p4 = 4.49
p5 = 6.87
subtotal = q * x
return subtotal
def final(subtotal):
print ("Total:", subtotal)
def loop():
cont = input("Do you want to continue? (yes/no)")
if cont == 'yes':
main()
return
else:
final()
def main():
x = userInput1()
subtotal = calculations(x,q)
final(subtotal)
#Driver:
main()
I have to use a sentinel controlled loop.
Here's my traceback.
Traceback (most recent call last):
File "C:\Documents and Settings\user1\My Documents\Downloads\az_mailorder3.py", line 49, in <module>
main()
File "C:\Documents and Settings\user1\My Documents\Downloads\az_mailorder3.py", line 46, in main
subtotal = calculations(x,q)
NameError: name 'q' is not defined
Here's how it should work.
Product number?: 1
Quantity?: 2
Continue?(yes/no): yes
Product number?: 2
Quantity?: 5
Continue?(yes/no): yes
Product number?: 3
Quantity?: 3
Continue?(yes/no): yes
Product number?: 4
Quantity?: 1
Continue?(yes/no): no
Total: $62.89
There's probably deeper problems.
Assuming you are a newbie to programming (I can feel that from your code) and also this problem seems a school homework which is quite easy and also you don't want to try hard debugging it (You need to debug a lot to be a good programmer)
Here I am presenting your code to you with proper comments why your code is going wrong.
Start reading from the main function (not from the first line)
def userInput1():
x = int(input("Product Number?"))
return x
def userInput2():
q = int(input("Quantity?"))
return q
def calculations(x,q):
p1 = 2.98
p2 = 4.5
p3 = 9.98
p4 = 4.49
p5 = 6.87
subtotal = q * x
#here you are multiplying the item number by quantity
#not the price by quantity (use proper if else to check price for item number
# return to main function again for more comments
return subtotal
def final(subtotal):
print ("Total:", subtotal)
#nothing wrong here but since we have declared subtotal as global (remember that?)
#so no need to pass subtotal by argument.
def loop():
cont = input("Do you want to continue? (yes/no)")
if cont == 'yes':
main()
return
else:
final()
#you are calling final() but you declared with argument (are you mad?)
#now check final function
def main():
x = userInput1()
#Where you are taking input from user for quantity??
#first take quantity as input q = userInput2()
subtotal = calculations(x,q)
#now every time you call main function your subtotal will be revised
#remember teacher talking about global and local variables?
#subtotal needs to be global (declare it somewhere outside the functions
#now even if subtotal is global you need to increment it (not reassign)
# use subtotal += calculations(x,q)
#now check your calculations function
final(subtotal)
#you are calling final subtotal but where the hell you calling your loop?
#call it before calling final and now check your loop function
main()
hopefully by now you are able to write correct code
but even if you are not able to write correct code by now here is something for your help. But try to write code by yourself first. Happy Debugging :)