Python: Change condition value of while loop from called module inside loop - python

I want the variable for the condition of my while loop (while repeat is True) to be changed (to repeat = False, thus not meeting the condition of the while loop) via the if statement in the action() module that is being called within the while loop itself.
Comments should explain my intentions throughout.
Note This is a simplified version of the larger code I am actually working. Hopefully I made it simple and clear enough to get my point across without additional confusing code as I have come across in other posts.
# Defining the variables
repeat = True
class monster:
hp = 5
class fighter:
damage = 1
# Defining my action module
def action():
monster.hp -= fighter.damage # Monster's hp decreases by fighter's damage
print "Monster HP is %s" % monster.hp # Print this result
if monster.hp < 1: # Check to see if monster is dead, hp less than 1
repeat = False # If monster is dead, stop repeating
else:
repeat = True # If monster is not dead, repeat attack
# Here is the while loop
while repeat is True: # Defining the condition for the while loop
print "repeat is %r" % repeat # Here it should print repeat is True
action() # Then call the action module
print "repeat is %r" % repeat # Here it should print repeat is False

You have to declare repeat as a global variable to change it from inside action(). Include this line after the def action():
def action():
global repeat

Related

I'm trying to simulate a coin flip game between two people using OOP, but for some reason my for loop through 1000 is only going through once

The point of this game is to have two people flip a coin, and if the first person gets heads and the second person gets heads, the first person wins, but if the second person gets the opposite coin they win. My code's output just displays "True" a thousand times, but I have a for loop in my method that isn't working?
import numpy as np
class Students():
def __init__(self,flip,history):
self.flip=flip
self.history=history
def flipcoin(self):
for self.flip in range (0,1000):
self.flip= np.random.random()
if (self.flip<0.5):
self.flip=0
else:
self.flip=1
print (str(self.flip))
self.history= self.flip
print(self.history)
return (str(self.flip))
student1=Students(flip=0,history=[])
student1.flipcoin()
student2=Students(flip=0,history=[])
student2.flipcoin()
for Students in range (0,1000):
if (student1==student2):
print('False')
else:
print('True')
print(student1.flip,student1.history)
So to answer your immediate question, your first problem is here:
for Students in range(0, 1000):
if student1 == student2:
print('False')
else:
print('True')
What you are comparing here are two instances of "Students". Since those are different instances, they are not equal. (And I'm not sure if you have your print statements reversed -- when that comparison returns False, the code prints 'True'.)

Range value changing when indexing a list backwards

I'm creating a chess game inside of Python (pygame) and in my validating moves function, I access a list of all possible moves.
However, as I'm removing from that list, I index it backwards. However, when I implement that change, the amount of repetitions undergone by the for loop encompassing the index changes from 20 to 1.
Here's the full function code:
def valid_move_2():
global white_to_move
possible_moves = generate_possible_moves()
print(range(len(possible_moves)))
for i in range(len(possible_moves)-1, -1, -1):
print("possible moves range")
print(str(possible_moves[i][0]))
move(possible_moves[i][0], possible_moves[i][1], possible_moves[i][2], possible_moves[i][3])
white_to_move = not white_to_move
check_check()
if check_check():
possible_moves.remove(possible_moves[i])
white_to_move = not white_to_move
undo_move()
if len(possible_moves) == 0:
if check_check():
checkmate = True
else:
stalement = True
else:
checkmate = False
stalemate = False
return possible_moves
To be specific:
print(range(len(possible_moves)))
This line returns 20.
However this line:
print("possible moves range")
only returns once, meaning the for loop repeats only once.
Where have I gone wrong?
THE ISSUE
The issue lies with this function, where my program gets stuck:
def square_under_attack(posx, posy):
print("SQUARE UNDER ATTACK FUNCTION CALLED")
global white_to_move
white_to_move = not white_to_move
enemy_moves = generate_possible_moves()
white_to_move = not white_to_move
x=0
for opponent_move in enemy_moves:
print(x)
if opponent_move[3] == posx and opponent_move[4] == posy: # if the opponent can move to the square being tested
print("returned true")
return True
x+=1
print("returned false")
return False
In this function, it gets stuck in the for loop. The x value was for troubleshooting to find out how many times the loop is iterated before not returning anything.
x is printed to have a value of 2 without either of the other 2 print functions being called at all.
What's wrong?
print(range(len(possible_moves)))
This shouldn't return 20.
It should return something like
range(0,20)
If so then it's problem of your for loop not range.
possible_moves.remove(possible_moves[i])
This line in particular is problematic.
If you are trying to remove i th element in possible_moves, use del
del possible_moves[i]
Also there are some dangerous bits in your code, one is global variable. Try to find another way to do that without using a global variable.
Another is your loop is iterated by initial length of possible_moves, and in the loop you are possibly removing something from possible_moves. This may cause index error. Easy fix would be to create a new list to keep track of what is deleted and what is not deleted.
If this doesn't solve error then there must be some issues in called method/functions
Try refactoring it with recursion since I think your iteration is very unorderly and possibly has repeated codes all over your code base.
If possible, use list comprehension instead of iteration to make things more concise.
If you want to do something like "Do until list L is empty" then use following pattern
while L:
do_something()
For your problem, chess, I would design it like this:
First define a function for each chess pieces that returns a set of grids they can go on next turn.
For check and checkmate I would look for a union of all the sets which represents all their possible next moves.
I think you are trying to implement your program by trying out every single enemy moves and then undoing it which is incredibly inefficient approach
You should make all the methods very concise and atomic. You shouldn't make them convoluted and do many implicit things under the hood. Keep them simple and logically concise as much as possible

tkinter after calling function with parameters causing function to hang

This is for a game of life app and using after to slow the animation of cells through the stages of birth, life, death, etc.
Couple issues with after:
1.I've been researching Tkinter extensively and this post captures the most clear instructions I have found regarding after with parameters, however, my application of Tkinter is not working - based on comments already received it's probably not the after but that's where the symptoms are appearing?
2.Basically the after does not appear to work at all when I don't put the parameters into parentheses inside the after (ex. widget.after(200, self.my_function, parameter 1, parameter 2, ....) doesn't iterate. However, when I do the same but enclose the parameters it iterates as intended (ex. widget.after(200, self.my_function(parameter 1, parameter 2, ....)).
3.However, when run with the parameters in parentheses, the after hangs. The list in the code below contains 81 items and not coincidentally, the function hangs for 16.2 seconds...
Code is as follows:
def color_cells(
self,
cells,
repeater,
temp_cells=0,
counter=0,
after_id=None
):
global paused
if temp_cells != 0:
self.repeat_colors(temp_cells)
temp_cells.clear()
temp_cells = 0
for key in cells:
if cells[key][0] == 'emerging':
cells[key] = 'active', colors['active'][1]
if cells[key][0] == 'dying':
cells[key] = 'inactive', colors['inactive'][1]
counter = 0
if repeater and not paused:
print("repeater is ", repeater, " and paused is ",paused)
self.id_changes(cells)
else:
self.closeoutGame()
else:
try:
print("made it to the else in color_cells and repeater is ",repeater, " and length of temp cells list is ", len(temp_cells))
except:
print("made it to the else in color_cells and repeater is ",repeater, " and temp cells is empty")
for key in cells:
color_rectangle = self.canvas_manager.find_withtag(key)
self.canvas_manager.itemconfigure(color_rectangle, fill = cells[key][1])
self.canvas_manager.update()
def repeat_colors(self, temp_cells, counter=0):
print("starting repeat colors and the temps cells len is ", len(temp_cells), " and the counter is ",counter)
if counter < len(temp_cells):
color_rectangle = self.canvas_manager.find_withtag(temp_cells[counter][0])
self.canvas_manager.itemconfigure(color_rectangle, fill = temp_cells[counter][1])
self.canvas_manager.update()
counter = counter + 1
root.after(200, self.repeat_colors(temp_cells, counter))
Bryan, you had asked for an example of the error. To show the error I added some print statements to the calling function and then explained where the 16.2 second period of inactivity starts:
starting repeat colors and the temps cells len is 81 and the counter is 0
starting repeat colors and the temps cells len is 81 and the counter is 1
starting repeat colors and the temps cells len is 81 and the counter is 2
...
starting repeat colors and the temps cells len is 81 and the counter is 79
starting repeat colors and the temps cells len is 81 and the counter is 80
starting repeat colors and the temps cells len is 81 and the counter is 81
...hangs for the 16.2 seconds, equal to 200 ms x 81 iterations
I'm a hobbyist and have no formal training and so I'm sure I'm overlooking something basic here, including how to best research on my own. But I appreciate any advice.
I would suggest neither:
root.after(200, self.repeat_colors(temp_cells, counter))
nor:
root.after(200, lambda x=counter: self.repeat_colors(temp_cells, x))
but rather:
root.after(200, self.repeat_colors, temp_cells, counter)
This is a misunderstanding of after() I see often and had myself. The definition of after():
after(ms, func=None, *args)
Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel.
The code you posted with the above fix and other tweaking -- unfortunately hard to do well without a MCVE:
def color_cells(self, cells, repeater, temp_cells=None, counter=0):
if temp_cells is not None:
self.repeat_colors(temp_cells)
temp_cells.clear()
temp_cells = None
for key in cells:
if cells[key][0] == 'emerging':
cells[key] = 'active', colors['active'][1]
if cells[key][0] == 'dying':
cells[key] = 'inactive', colors['inactive'][1]
counter = 0
if repeater and not paused:
print("repeater is", repeater, "and paused is", paused)
self.id_changes(cells)
else:
self.closeoutGame()
else:
print("Made it to the else in color_cells and repeater is", repeater, "and temp cells is empty")
for key in cells:
color_rectangle = self.canvas_manager.find_withtag(key)
self.canvas_manager.itemconfigure(color_rectangle, fill=cells[key][1])
self.canvas_manager.update()
def repeat_colors(self, temp_cells, counter=0):
print("Starting repeat colors and the temps cells len is", len(temp_cells), "and the counter is", counter)
if counter < len(temp_cells):
color_rectangle = self.canvas_manager.find_withtag(temp_cells[counter][0])
self.canvas_manager.itemconfigure(color_rectangle, fill=temp_cells[counter][1])
self.canvas_manager.update()
counter += 1
root.after(200, self.repeat_colors, temp_cells, counter)
You problem would be the infinite loop caused by root.after(200, self.repeat_colors(temp_cells, counter)). Instead you need to pass your self.repeat_colors as a lambda.
So what is happening is the self.repeat_colors(temp_cells, counter) is being called instantly instead of waiting the 200 seconds. So instead create a lambda function that will wait until the set time to activate.
Keep in mind for lambda expressions if you have a value that changes you need to define it in the lambda. So for the counter you need to do something like x=counter so the lambda is sure to use the correct updated value instead. This normally affects things like loops that create lambda expressions and probably does not matter in this particular case but is a good habit to practice for when it does matter.
Change this:
root.after(200, self.repeat_colors(temp_cells, counter))
To this:
root.after(200, lambda x=counter: self.repeat_colors(temp_cells, x))

For loop menu in python?

Good day everyone, I'm fairly new to python and I had some questions regarding creating menus...
So I recently made a menu using the while loop, and it works like it's supposed to. My question, however, is whether or not it's possible to create a menu using a for loop. I know a for loop is used when you want something to cycle for an x amount of times.
I tried searching for the answer already, but I couldn't really find anything related. If anyone can point me in the right direction, I'd appreciate it.
Here is my menu using "while":
def mainMenu():
print("SELECT A FUNCTION")
print("")
print("e1- Calculate The Area Of A Sphere")
print("e2- Calculate The Volume Of A Cube")
print("e3- Multiplication Table")
print("e4- Converting Negatives To Positives")
print("e5- Average Student Grades")
print("e6- Car Sticker Color")
print("e7- Exit")
print("")
while True:
selection = input("Enter a choice: ")
if(selection == "e1"):
e1()
break
elif(selection == "e2"):
e2()
break
elif(selection == "e3"):
e3()
break
elif(selection == "e4"):
e4()
break
elif(selection == "e5"):
e5()
break
elif(selection == "e6"):
e6()
break
elif(selection == "e7"):
print("Goodbye")
break
else:
print("Invalid choice. Enter 'e1'-'e7'")
print("")
mainMenu()
My guess is that your instructor wanted you to use a generator. Based on your question, I'm going to assume that you are not familiar with generators. If you already know about them, skip to the second section, otherwise it might be worth your time to learn about them first.
Generators
In short, a generator acts like a function but instead of using a return statement it uses a yield statement. Calling next on a generator will run it until it reaches a yield statement and then suspend the process (and save the state of the generator, as opposed to normal functions, which typically don't save state between calls) until next is called on the generator again, at which point the control flow will be resumed where the generator left off.
The following code should give you an idea of how generators work:
# Define a generator function. This function will return our generator iterator.
def test_gen():
numCalls = 0
print("Start. This code is only executed once.")
while numCalls < 10:
print("Entering while loop.")
yield("Yielding. numCalls = %d." %(numCalls))
numCalls += 1
print("Resuming control flow inside generator.")
print("End. This code is only executed once.")
yield("Last yield.")
# End test_gen.
# Test the behavior of our generator function.
def run_test_gen():
# Create generator iterator by calling the generator function.
gen = test_gen()
# Continuously get the next value from the gen generator.
while True:
# If the generator has not been exhausted (run out of values to yield), then print the next value.
try:
print(next(gen))
# Break out of the loop when the generator has been exhausted.
except StopIteration:
print("Generator has been exhausted.")
break
# End run_test_gen.
run_test_gen()
If you want to know more about generators I'd check out the Python docs and the explanation Jeff Knupp has here.
Making a Menu With a Generator and a for Loop
Now that we know about generators, we can use them to create a menu based on a for loop. By placing a while True statement inside of a generator we can create a generator that yields an infinite number of values. Since generators are iterable, we can iterate over the yielded values in a for loop. See the below code for an example.
def infinite_gen():
"""
First, create a generator function whose corresponding generator iterator
will yield values ad infinitum. The actual yielded values are irrelevant, so
we use the default of None.
"""
while True:
yield
# End infinite_gen.
def print_str_fun(string):
"""
This function simply returns another function that will print out the value
of string when called. This is only here to make it easier to create some
mock functions with the same names that you used.
"""
return(lambda : print("Calling function %s." %(string)))
# End print_str.
# Create a tuple of the function names that you used.
fun_name_tuple = ('e%d' %(ii) for ii in range(1, 7))
# Create a dictionary mapping the function names as strings to the functions themselves.
fun_dict = {fun_name: print_str_fun(fun_name) for fun_name in fun_name_tuple}
# Add the e7 function separately because it has distinct behavior.
fun_dict['e7'] = lambda : print("Goodbye.")
def mainMenu():
"""
Example mainMenu function that uses for loop iterating over a generator
instead of a while loop.
"""
# Create our infinite generator.
gen = infinite_gen()
# Iterate over the values yielded by the generator. The code in the for loop
# will be executed once for every value yielded by the generator.
for _ in gen:
# Get function name from user input.
selection = input("Enter a choice: ")
# Try to call the function selected by the user.
try:
fun_dict[selection]()
break
# Print a note to user and then restart mainMenu if they supplied an
# unsupported function name.
except KeyError:
print("Invalid choice. Enter 'e1' - 'e7'.")
print()
mainMenu()
# End mainMenu.
# Allows one to run the script from the command line.
if __name__ == '__main__':
mainMenu()

Python - Head or Tail Exercise [duplicate]

This question already has answers here:
Selection sort program python
(2 answers)
Closed 7 years ago.
I need a small tips to finished this exercise.
The program should display head or tail after the for loop generate a sequence of 0 and 1. This module contains a number of random number generators:
import random
# function to determine head or tail of printed random number
def flip(coin):
# if the coin is zero, then print head
if coin == 0:
print("heads!")
# else print tail
else:
print("Tail!")
# simple repeat loop that calls flip() 10 times to generate a random
# sequence of 10 heads and tails
def main():
for coin in range(10):
print (random.randrange(2))
main()
you need to call the flip function. Also I would recommend using random.randint()
import random
#function to determine head or tail of printed random number
def flip(coin):
#if the coin is zero, then print head
if coin == 0:
print("heads!")
#else print tail
else:
print("Tail!")
#simple repeat loop that calls flip() 10 times to generate a random
#sequence of 10 heads and tails
def main():
for coin in range(10):
flip(random.randint(0, 1))
main()
You need to call the flip function, passing it the result of that random.randrange(2) call:
print (flip(random.randrange(2)))
As Padraic points out in comments, your code will also output None, because it's printing the returned value from the called function, and no return statement produces an implicit None. You can fix this in one of two ways: either have flip return the result instead of printing it, or call it without printing.
if coin == 0:
return "heads!"
# else print tail
else:
return "Tail!"
or:
flip(random.randrange(2))

Categories

Resources