How to stop a function - python

For example:
def main():
if something == True:
player()
elif something_else == True:
computer()
def player():
# do something here
check_winner() # check something
computer() # let the computer do something
def check_winner():
check something
if someone wins:
end()
def computer():
# do something here
check_winner() # check something
player() # go back to player function
def end():
if condition:
# the player wants to play again:
main()
elif not condition:
# the player doesn't want to play again:
# stop the program
# whatever i do here won't matter because it will go back to player() or computer()
main() # start the program
My problem is that if a certain condition becomes true (in the function check_winner) and function end() executes it will go back to computer() or player() because there's no line that tells the computer to stop executing player() or computer(). How do you stop functions in Python?

A simple return statement will 'stop' or return the function; in precise terms, it 'returns' function execution to the point at which the function was called - the function is terminated without further action.
That means you could have a number of places throughout your function where it might return.
Like this:
def player():
# do something here
check_winner_variable = check_winner() # check something
if check_winner_variable == '1':
return
second_test_variable = second_test()
if second_test_variable == '1':
return
# let the computer do something
computer()

In this example, the line do_something_else() will not be executed if do_not_continue is True. Control will return, instead, to whichever function called some_function.
def some_function():
if do_not_continue:
return # implicitly, this is the same as saying `return None`
do_something_else()

This will end the function, and you can even customize the "Error" message:
import sys
def end():
if condition:
# the player wants to play again:
main()
elif not condition:
sys.exit("The player doesn't want to play again") #Right here

def player(game_over):
do something here
game_over = check_winner() #Here we tell check_winner to run and tell us what game_over should be, either true or false
if not game_over:
computer(game_over) #We are only going to do this if check_winner comes back as False
def check_winner():
check something
#here needs to be an if / then statement deciding if the game is over, return True if over, false if not
if score == 100:
return True
else:
return False
def computer(game_over):
do something here
game_over = check_winner() #Here we tell check_winner to run and tell us what game_over should be, either true or false
if not game_over:
player(game_over) #We are only going to do this if check_winner comes back as False
game_over = False #We need a variable to hold wether the game is over or not, we'll start it out being false.
player(game_over) #Start your loops, sending in the status of game_over
Above is a pretty simple example... I made up a statement for check_winner using score = 100 to denote the game being over.
You will want to use similar method of passing score into check_winner, using game_over = check_winner(score). Then you can create a score at the beginning of your program and pass it through to computer and player just like game_over is being handled.

Maybe you are looking yield, its same as return but it stops function's execution rather than terminating functions, You can look at generators here.

Related

A bug disappears when I print something in pygame

I know the title is not very clear, but I don't know what else I can say.
I have a player attacking, and when he is done attacking, I start a timer for 1 second, so we have to wait one second before attacking again. It wasn't working (we could attack only once) and I didn't know why, so I added print(self.between_two_attacks())and everything worked fine, I could attack, wait one second and attack again.
Here is the program, I don't know if it is enough because I have no idea where the bug comes from.
def between_two_attacks(self):
if self.after_attack_max == 0:
self.after_attack_max = pg.time.get_ticks() + 1000
print("set timer")
else:
after_attack = pg.time.get_ticks()
print("start timer")
if after_attack >= self.after_attack_max:
print("can attack again")
self.can_attack = True
self.after_attack_max = 0
def draw(self, win):
print(self.between_two_attacks())
if (self.attackcount + 1) >= 5:
self.attackcount = 0
self.between_two_attacks()
self.action = STAND_UP
self.arme = LIGHTSABER_OUT
self.stops_attacking = True
self.can_attack = False
if self.action == ATTACKING:
win.blit...
Run = True
While Run:
for event in pg.event.get():
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE and player.can_attack == True:
player.action = ATTACKING
If anything isn't clear in this part of the program, just tell me and I'll try to explain. Thanks for your help :)
The method between_two_attacks has to be invoked, before the state of self.can_attack is retrieved. self.can_attack is set in between_two_attacks. If the method is not called, the self.can_attack will never become True.
When you do print(self.between_two_attacks()), the self.between_two_attacks() is called.
Furthermore, the method can be simplified:
self.can_attack has to be set if self.after_attack_max == 0 or if the current time is greater than self.after_attack_max.
If self.can_attack is set then compute the restart the timer. If it is not set then it has to be evaluated. Initially self.after_attack_max is 0. If the current time is greater than self.after_attack_max, attacks have to be allowed and the timer has to be started again:
def between_two_attacks(self):
current_time = pg.time.get_ticks()
if self.can_attack:
self.after_attack_max = current_time + 1000
elif current_time > self.after_attack_max:
self.can_attack = True
self.after_attack_max = current_time + 1000
Note, self.after_attack_max is only set in between_two_attacks, do not reset it anywhere else.
When you print self.between_two_attacks() the self function gets called and executed. Now, if you added the whole line it means that this function was not executed at this point before and now it is, so if you remove print and left only function at the same location you should get the same behaviour.
I do not know if I manage to explain my thought so here as a quick example:
x = 5
def change_x():
global x
x+=10
print(x)
print(change_x())
print(x)
if you remove print() you will get same result.

Why does my function do what its supposed to do then it does not continue through the code?

I am making a choose your own adventure game and I have a function to check if what you input in the console is acceptable or not. In the beginning, you can only type "turn on light" if you type anything else it will return as an error and you will be prompted to type an actual action. The problem I have is after you type something that isn't accepted it will not let you continue after you make an error.
actions = ['help','turn light on',]
def errorcheck(player_input):
if player_input in actions:
error = False
return()
else:
error = True
while error == True:
print('i dont know what you mean by',player_input)
player_input = input('>')
if player_input in actions:
error = False
else:
error = True
print('welcome to TITLE')
print('type help at anytime to see your options')
print('">" that symbol promts you to do something')
print('')
print('you wake up, its dark')
player_input = input('>')
errorcheck(player_input)
if error == False:
if player_input == ('help'):
playerhelp = True
while playerhelp == True:
print('you can: turn light on')
playerhelp = False
errorcheck potentially modifies player_input that it accepts as an argument. It is a new local variable that has nothing to do with the global player_input.
A naive solution would be to make player_input a global variable, but that would be a bad, anti-pattern solution for several reasons:
global variables tend to lead to messy, hard to debug code
a function should preferably do one thing, and that thing should preferably be what its name suggests it does.
Instead, have errorcheck only check the input as its name suggests.
def errorcheck(player_input):
return player_input not in actions
player_input = None
while errorcheck(player_input):
player_input = input('>')
At this point having errorcheck as a function seems a bit superfluous. You do not really need it:
player_input = None
while player_input not in actions:
player_input = input('>')
First of all you should definitely never use a local variable from a function in your main code. If you want to access error you should return it as such:
def errorcheck(player_input):
if player_input in actions:
error = False
else:
error = True
while error == True:
print('i dont know what you mean by',player_input)
player_input = input('>')
if player_input in actions:
error = False
else:
error = True
return error
Secondly it is no wonder that your program stops after entering help since after that there is no more code. If you want the user to be continuously be required to enter something you must put a loop around the whole parsing logic...

Possible ways to change flag values inside a while loop in python?

So my problem is this: I'm running a while loop that will execute Function1 every time, Function2 and Function3 will only be executed when their respective flags are set to True, and I want to be able to alter those flags while the loop is running.
while brkFlag == False:
Function1.run()
if flag2 == True:
Function2.run()
else:
pass
if flag3 == True:
Function3.run()
else:
pass
if someConditions == True:
brkFlag = True
Currently I want to change those flags with a GUI while the loop is running and am trying to do this with tkinter's Buttons and Checkboxes, but no luck so far.
I also can't use multithreading or multiprocessing as they will considerably slow down the speed of the loop (which is already barely acceptable as it is right now, because there are lots of calculations in each function and I need to show the results in a real-time fashion).
Edit: So it seems that there is no way other than doing multithreading/processing after all.
A simple way is to pass a mutable container containing the flags, for example a list:
flags = [flag2, flag3, someConditions ]
while brkFlag == False:
Function1.run(flags)
if flags[0] == True:
Function2.run(flags)
else:
pass
if flags[1] == True:
Function3.run(flags)
else:
pass
if someConditions == True: # or if flags[2] == True:
brkFlag = True
In the callee you implement more or less:
...
def run(flags):
...
flags[0] = True # set flag2
...

Function called without being told to

Newish to python, working on a text adventure, testing out the use of functions.
def cell1():
loop = 1
while loop == 1:
print("ONE")
cave1 = input()
if cave1 == ("end?"):
print("\nthis should end program")
loop = 0
break
elif cave1 == ("TWO"):
global testvar
testvar = 1
option1()
else:
print("INVALID")
def option1():
print("TWO")
loop = 1
while loop == 1:
print("test1 definition")
print (testvar)
test1 = input()
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
cell1()
elif test1 == ("THREE"):
option2()
else:
print("INVALID")
def option2():
print("THREE")
loop = 1
while loop == 1:
print("This is option 3")
test2 = input()
if test2 == ("ONE"):
print("testering2")
cell1()
elif test2 == ("TWO"):
global testvar
testvar = 2014
option1()
else:
print("INVALID")
run = True
while run == (True):
print ("testing 123")
cell1()
print("restart about to activate")
cont = input("Restart? ")
if (cont) != "yes":
break
This program should allow you to go between options (what would be rooms) and eventually in cell1, the program should be end-able.
if the program is run and "end?" is typed as the first input, the program goes into the continue bit at the bottom, however, if you go between the 'rooms' then back to cell1, typing "end?" will call option 2.
Ive had a look around and it is still baffling me, am i ding something wrong?
Any help is appreciated, thank you.
The reason "end?" only quits for the player when they are within the first cell is because you're only checking for that input therein. The execution contained within option1() and option2() doesn't affect the execution of cell1(). You're not returning anything from your option functions, nor are you changing a sentinel value.
So, there's two basic ways you could go about this.
First, you could return a value from your functions:
if option1() == "END":
break
Or, you could alter your while loop:
# is_running is defined globally
while is_running:
And then just set is_running to False in any of your methods whenever the user types "end?". That'd probably be the easiest way with the design you're using now.
I'm sure you can tell, though, that in general your program is going to get exponentially more complex as you add more rooms and your function calls get further nested.
I'm pretty sure that the issue you're having is because you don't always break out of the loop in one function when you call another function. For instance, if your entries were TWO, ONE then end?, you'd find yourself still in the cell1 loop. That's because when the inner call to cell1 returns, the control flow of the program goes back to where that function was called from, which is option1, since loop is now 0, the loop ends and option1 returns, to the outer call to cell1, where the loop is still running.
Unless you want the game you're designing to have a tree structure, where you can return to where you came from with different semantics than moving to some other place, I'd suggest using a different architecture. Rather than each of your functions calling the next function when appropriate, return that function instead. Then you'd write a single top level loop that calls the function. Here's an example where the function to be called by the top level loop is saved in a variable named state:
def cell1():
print("In cell1!")
while True:
choice = input("pick 'ONE' or 'TWO' (or type 'quit' to exit):")
if choice == "ONE":
return option1
elif choice == "TWO":
return option2
elif choice == "quit":
return None
else:
print("I'm sorry, I didn't understand that.")
def option1(): # these other two functions are very basic in my example
print("In option1!") # but you can make them as complex as you want
return option2
def option2():
print("in option2!")
return cell1
def control_loop(initial_state=cell1):
state = initial_state
while state is not None:
state = state() # the next state is the return value of the previous state
The problem is you are getting deeper and deeper within nested functions. For example, changing
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
cell1()
to
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
break
will allow you to run your program, enter room two, go back to room one, and "end?" will work properly. This won't fix your issues completely though because there is a similar problem where when you go from two to three where if you simply changed
if test2 == ("ONE"):
print("testering2")
cell1()
to
if test2 == ("ONE"):
print("testering2")
break
it would break the current function and go back into option1() (if you run your program, go to room two, then to room three, then back to one) where "end?" doesn't do anything. Hopefully this gets you on the right track.

Pause and resume a running script in Python 3.42 in Windows

I'm new to Python and have been googling for a couple of days and read all I can find on this forum. Might be that I don't understand it all but I haven't found a solution to my problem yet. Ask for forgiveness already if there's an answer already to my problem, then I haven't understood it.
I want to make a Pause function for my program Tennismatch. The program will when it's being run print the score of a tennis match like this: "15-0, 15-15 etc ongoing till the match ends. It will print the score line by line.
I want the user to be able to pause after x number of balls, games, etc. So I don't know when the user wants to pause and after the user has paused I want the user to be able to resume the tennismatch where it was.
Have seen the time.sleep() but as I have understood it you must know when you want to pause to use this and it also ain't an indefinetie pause like I want. With input() it's the same.
Am going to make a GUI later on when the code is finished. Happy for anything that leads me to solving my problem.
I use Windows and Python 3.42 and run the program in Shell.
A piece of the code (haven't written it all yet, it's more of a general situation when something is being printed line after line for some time and want to be able do pause in the CIL:
#self.__points = [0,0]
def playGame(self):
if self.server == True: #self.server is either True or False when someone calls playGame()
server = self.player_1.get_win_serve() #self.player_1 = an object of a class Player():
else:
server = self.player_2.get_win_serve() #get_win_serve() method returns the probability to win his serv (1-0)
while (0 < self.__points[0] - self.__points[1] >= 2 or 0 < self.__points[1] - self.__points[0] >= 2) and (self.__points[1] >= 4 or self.__points[0] >= 4):
x = random.uniform(0,1)
if x > 0 and x < server:
self.__points[0] += 1
else:
self.__points[1] += 1
# print('The score, by calling a score() function that I haven't written yet')
For dealing with events in main loop you need to make a separated thread which capture input or any other event.
import sys
from sys import stdin
from time import sleep
from threading import Thread
from Queue import Queue, Empty
def do_something():
sleep(1)
print 42
def enqueue_output(queue):
while True:
# reading line from stdin and pushing to shared queue
input = stdin.readline()
print "got input ", input
queue.put(input)
queue = Queue()
t = Thread(target=enqueue_output, args=(queue,))
t.daemon = True
t.start()
pause = False
try:
while True:
try:
command = queue.get_nowait().strip()
print 'got from queue ', command
except Empty:
print "queue is empty"
command = None
if command:
if command == 'p':
pause = True
if command == 'u':
pause = False
if not pause:
print pause
do_something()
except KeyboardInterrupt:
sys.exit(0)
I came up with the following.
while True:
try:
## Keep doing something here
## your regular code
print '.',
except KeyboardInterrupt:
## write or call pause function which could be time.sleep()
print '\nPausing... (Hit ENTER to continue, type quit to exit.)'
try:
response = raw_input()
if response.lower() == 'quit':
break
print 'Quitting...'
except KeyboardInterrupt:
print 'Resuming...'
continue
The Event loop might as well be the code I wrote with.
I don't see any user input so I assume that x emulates it. To pause the game if x < 0.1 and to unpause(/resume) it if x > 0.9, you could:
while your_condition(self.__points):
x = random.random()
if x < 0.1: # pause
self.pause()
elif x > 0.9: # resume
self.resume()
if self.is_paused:
continue # do nothing else only wait for input (`x`)
# assume your_condition() has no side-effects
# here's what the resumed version does:
print("...")
# change self.__points, etc
where pause(), resume(), is_paused() methods could be implemented as:
def __init__(self):
self.is_paused = False
def pause(self):
self.is_paused = True
def resume(self):
self.is_paused = False
as you can see the implementation is very simple.

Categories