Variable change unexpectedly on recursion? - python

Context
I'm currently attempting Reddit's /r/dailyprogrammer challenge.
The idea is to find a solution to an ASCII maze. Unfortunately the recursion is working differently than I expected. The program checks if there is space to move to the right, left, below or above the current space. If there is then the space is moved to and the function is entered again with new co-ordinaries. This continues until the end is found.
When the end is found the program exits. If a dead end is found then the recursion will back up to the previous point and check for any more directions, this continues until the end.
Problem
My program works great however the maze draws my lines (represented by '*****') even after the recursion is backed up. I don't know how to explain that so I'll use images to give a better description.
Each new color represents a new path. However I would have expected only the current recursions path to show. For example in this case I would expect only the yellow path to show. Can someone help me understand why all paths remain?
The code
import time
import sys
import os
maze = """\
###############
#S # #
### ### ### # #
# # # # #
# ##### ##### #
# # # #
# ### # ### ###
# # # # # #
# # ### # ### #
# # # # # # #
### # # # # # #
# # # # # #
# ####### # # #
# #E#
###############"""
def displayMaze(maze):
os.system("cls")
display = ""
for x in maze:
for y in x:
display = display + y
display = display + "\n"
print(display)
def findStart(maze):
#Get the maze start co-ords.
for x in range(0,len(maze[0])):
for y in range(0,len(maze)):
if maze[x][y] == "S":
return x,y
def findPath(x,y,maze):
#Look right, left, up and down, If path then move.
time.sleep(0)
if maze[y][x+1] == " ":
newMaze = maze
newMaze[y][x+1] = "*"
displayMaze(newMaze)
findPath(x+1,y,newMaze)
elif maze[y][x+1] == "E":
sys.exit("Done")
if maze[y][x-1] == " ":
newMaze = maze
newMaze[y][x-1] = "*"
displayMaze(newMaze)
findPath(x-1,y,newMaze)
elif maze[y][x-1] == "E":
sys.exit("Done")
if maze[y+1][x] == " ":
newMaze = maze
newMaze[y+1][x] = "*"
displayMaze(newMaze)
findPath(x,y+1,newMaze)
elif maze[y+1][x] == "E":
sys.exit("Done")
if maze[y-1][x] == " ":
newMaze = maze
newMaze[y-1][x] = "*"
displayMaze(newMaze)
findPath(x,y-1,newMaze)
elif maze[y-1][x] == "E":
sys.exit("Done")
if __name__ == "__main__":
maze = maze.split("\n")
newMaze = []
for line in maze:
newMaze.append(list(line))
x,y = findStart(newMaze)
findPath(x,y,newMaze)

newMaze = maze doesn't copy the list, it just creates another name pointing to the same object. To copy, you should import copy at the top of your program, then do newMaze = copy.deepcopy(maze). (You need a deep copy because maze is a list of lists, so you need to copy not only the outer list, but all the lists inside it too.)
In Python, assignment to a plain name (like blah = ...) never copies anything. If you want a copy, you must make one explicitly. The way to do that depends on what you're copying.

Related

How to go through while loop again?

Im making a simple question and answer app in python , in this specific example it displays a word in simple chinese and gives two answers to pick wrong or right , i'm struggling to present a new question without restarting my code. This my go at making an app that helps me learn chinese and I wanted to use minimum help , hence the weird code.
For example :
the question is what is 1+1 and the user answered two then I want to go thourgh the code again an present the next question.
the specific section im trying the run from inside a function, so when the user answers correctly or incorrectly by pressing the button in the function I want to go through the code again and present my next question
`
# ans
def button_1(event):
if Ans_option1.text == ans_or_not["ans"]:
print('correct')
return 'correct'
else:
print("incorrect")
def button_2 (event):
if Ans_option2.text == ans_or_not["ans"]:
print('correct')
return 'correct'
else:
print("incorrect")
Ans_option1 = gp.Button(app,return_1(), button_1)
Ans_option2 = gp.Button(app,return_2(),button_2)
app.add(Ans_option1,3,2, align = 'center')
app.add(Ans_option2,3,4, align = 'center')
app.run()
`
whole code
import gooeypie as gp
import random
app = gp.GooeyPieApp ('hello')
app.set_size (1000,500)
i = 2
n =3
while True :
# use dictionary instead of lists so we c an have an answer to the questions
question_dict = {
'xihuan' : 'like',
'Wo': 'I',
'Ni': 'you'
}
# random picks a value from a list of values made here
picker = random.choice(list(question_dict.values()))
# I remake the same list here again as the indexing stays the same then find the index for the random value
ind = list(question_dict.values()).index(picker)
# I make a list of the keys and they have mathing indexes to their values and we use the index number given to us previously to find the key
final = list(question_dict.keys())[ind]
# print(final)
test = 1
def question ():
question_dict.pop(final)
print(question_dict)
# return final
return final
ans_or_not = {
# this works first before the item is popped so it can cause a they same two words to appear on the buttons
# varialbe from inside the functions arent stected outside , making this whole dictionary meaningless
'ans' : picker,
'non' : random.choice(list(question_dict.values()))
}
print(ans_or_not["non"])
print(ans_or_not["ans"])
while ans_or_not["non"] == ans_or_not["ans"]:
ans_or_not.pop('non')
print(ans_or_not)
ans_or_not['non'] = random.choice(list(question_dict.values()))
print(ans_or_not["non"])
print(ans_or_not["ans"])
nums = random.randrange(1,3)
print(nums)
def return_1():
# while anss == nons :
# anss = random.randrange(0,2)
# print(anss + ','+ nons)
if nums == 1 :
return ans_or_not["ans"]
if nums == 2:
return ans_or_not["non"]
def return_2():
# while anss == nons :
# anss = random.randrange(0,2)
# print(anss + ','+ nons)
if nums == 1 :
return ans_or_not["non"]
elif nums == 2:
return ans_or_not["ans"]
# design and layout
def menu_select (event):
pass
menu_path = ' > '.join(event.menu)
status.text = menu_path
app.add_menu_item('Menu 1', 'Item 1', menu_select)
# grid setup
app.set_grid(4,5)
question_lbl = gp.Label(app,question())
app.add(question_lbl,2,3, align = 'center')
# ans
def button_1(event):
if Ans_option1.text == ans_or_not["ans"]:
print('correct')
return 'correct'
else:
print("incorrect")
def button_2 (event):
if Ans_option2.text == ans_or_not["ans"]:
print('correct')
return 'correct'
else:
print("incorrect")
Ans_option1 = gp.Button(app,return_1(), button_1)
Ans_option2 = gp.Button(app,return_2(),button_2)
app.add(Ans_option1,3,2, align = 'center')
app.add(Ans_option2,3,4, align = 'center')
app.run()
What i've tried
i've tried using the continue function
ways to restart while loops
You have the problem wrong.
This is going to sound really complicated, but stay with me. So what app.run() does is start the program, and it displays what you have already added to it. By putting app.run() in the while loop, despite calling it multiple times, will only call once. What you need to do is draw the label and buttons, run the app, then start the while loop. In fact, I wouldn't use a while loop here at all. Instead, I would have is so you have a dictionary for the right and wrong answers, then simply modify that and the button text when you get the answer correct. I don't really understand your code, but it would look something like:
#function for choosing answer
#function for checking button one
#function for checking button two
#draw button one and two and label
app.run()
Also, you would have it so that if you got the answer right, you would choose a new one.

Why do I keep getting argument of type 'method' is not iterable?

I am trying to make a chess program, but cannot get past a section that keeps returning an error. I've checked the code over and cannot find what's wrong with it. It keeps saying 'argument of type 'method' is not iterable'. Online says it's due to a parenthesis error, but I can't find where.
if len(userClick)==2: # if its player click number 2
move = chessEngine.Move(userClick[0],userClick[1],SoG.board)
print(move.chessNotation())
if move in legalMoves: # this is where the error occurs
SoG.makingMove(move)
moveMade = True
selectedSquare = () # reset square selected
userClick = [] # reset user clicks
I have some of the other code here,
def legalMoves(self): # possible moves
return self.possibleMoves()
def possibleMoves(self):
moves = [] #Move((6,4),(4,4),self.board)
for r in range(len(self.board)): # number of rows
for c in range(len(self.board[r])): # number of columns in a row
colourOfTurn = self.board[r][c][0]
if (colourOfTurn == 'b' and not self.whiteMove) and (colourOfTurn == 'w' and self.whiteMove):
piece = self.board[r][c][1]
if piece == 'P': # pawns
self.pawnMoves(r,c,moves)
elif piece == 'R': # rooks
self.rookMoves(r,c,moves)
#elif piece == 'N': # knights
# self.knightMoves(r,c,moves)
#elif piece == 'B': # bishop
# self.bishopMoves(r,c,moves)
#elif piece == 'Q': # queen
# self.queenMoves(r,c,moves)
#elif piece == 'K': # king
# self.kingMoves(r,c,moves)
return moves
You must call legalMoves with parenthesis, like this:
if move in legalMoves():

How do i count the number of attempts the user took to guess the correct sequence

I would like to count the number of attempts the user took to guess the correct color sequence. I have been stuck for quite a while as I tried to add a count function but it kept being stuck at 0. I'm new at python so any help is much appreciated (I had to remove some unrelated part of the code because I cant fit it in the box (at compare_list section)
import random
colours = ['PINK', 'BLUE', 'YELLOW', 'RED']
random_colours = []
colours_input = []
#handles the users input and stores in a list (colours_input)
def user_input():
i = 1
while i < 5:
a = input('Please enter your selected colours in sequence, colour ' + str(i) + ': ').upper()
while a not in colours:
print('Please only type the colours in the range (PINK, BLUE, YELLOW, RED) ')
a = input('Please enter a valid colour ' + str(i) + ': ').upper()
colours_input.append(a)
i+=1
print('Your selected colour sequence is: ' + str(colours_input))
#Automatically generate four random colours for the user to guess
def randomize():
for i in range (0,4):
random_colours.append(random.choice(colours))
#To check 2 variables: Correct colour in the correct place and correct colour but in the wrong place
def compare_list():
correct_place = 0
if random_colours[0] == colours_input[0]:
colour_1 = True
correct_place = correct_place + 1
else:
colour_1 = False
if random_colours[1] == colours_input[1]:
colour_2 = True
correct_place = correct_place + 1
else:
colour_2 = False
if random_colours[2] == colours_input[2]:
colour_3 = True
correct_place = correct_place + 1
else:
colour_3 = False
if random_colours[3] == colours_input[3]:
colour_4 = True
correct_place = correct_place + 1
else:
colour_4 = False
print('Correct colour in the correct place: ' + str(correct_place))
while correct_place == 4:
print('Congratulations! You are a master mind')
break
else:
colours_input.clear()
user_input()
compare_list()
You're going to want to have some driver code, that acts as the entry point to your program and orchestrates how functions will be called. Currently you are doing this in your compare_list() function, simply move this code (and change it a bit, there were some mistakes with the while loop structure) to a new function.
Typically this code is placed in a main function. In your case, it could look something like:
def main():
random_colours = []
colours_input = []
randomize() # you forgot to call this in your code
while True:
user_input()
if compare_list():
print('Congratulations! You are a master mind')
break # exit the program
colours_input.clear()
Then, we just need to refactor our compare_list() function so that it only compares the lists, and returns the result of the comparison (I just return a boolean representing whether the comparison was successful, but you could also return the correct_place value for example).
def compare_list():
correct_place = 0
if random_colours[0] == colours_input[0]:
colour_1 = True
correct_place = correct_place + 1
else:
colour_1 = False
if random_colours[1] == colours_input[1]:
...
print('Correct colour in the correct place: ' + str(correct_place))
return correct_place == 4 # returns True, if correct_place equals 4, False otherwise
Now, in our main function, we can count how many times we have run this main loop for each user attempt:
def main():
random_colours = []
colours_input = []
attempts = 0
while True:
attempts += 1
user_input()
randomize()
if compare_list():
print('Congratulations! You are a master mind')
print('It only took you {} attempt{}!'.format(attempts, 's' if attempts > 1 else "")
break # exit
colours_input.clear()
random_colours.clear()
Now we simply call the main() function in our file to run the driver code. Typically this is done in an if block that runs only if your script is being run directly as opposed to being imported by something else:
...
def compare_list():
...
def main():
...
if __name__ == "__main__":
main()
(You can read up about this practice here: What does if __name__ == “__main__”: do?)
A final refactor would be to reduce the use of global variables. You should aim to make your functions units of code that operate on input(s) and return output(s). Designing your functions so that they mutate global variables to produce a result is an anti-pattern.
Here's one way to refactor it:
import random
colours = ('PINK', 'BLUE', 'YELLOW', 'RED') # this is okay since it is a constant. We can make it an immutable tuple to clearly indicate that this is read-only.
# remove the following global variables
# random_colours = []
# colours_input = []
#handles the users input and stores in a list (colours_input)
def user_input():
colours_input = [] # define the variable here. We create a new list that this function will populate, then return it
for i in range(5):
...
print('Your selected colour sequence is: ' + str(colours_input))
return colours_input # return the populated list
def randomize():
random_colours = [] # define the variable here. Similar to above.
for i in range (0,4):
random_colours.append(random.choice(colours))
return random_colours # return it to the caller
# we take in the lists to compare as parameters to the function, rather then getting them from the global scope
def compare_list(colours_input, random_colours):
... # (everything else is the same)
def main():
random_colours = randomize()
# colours_input = []
attempts = 0
while True:
attempts += 1
colours_input = user_input()
if compare_list(colours_input, random_colours): # pass the returned lists in as arguments to this function call
print('Congratulations! You are a master mind')
print('It only took you {} attempt{}!'.format(attempts, 's' if attempts > 1 else "")
break # exit the program
# no need to clear this anymore. We get a new list from each call to user_input()
# colours_input.clear()

How to know who is winner in Tic Tac Toe in python using guizero

I have created a game called Tic Tac Toe. There are 2 players one of them are Xs one of them are Os all you have to do is get your symbol 3 in a row without the other person blocking you.
The Gui for the game looks like this:
Code:
from guizero import App, TextBox, PushButton, Text, info
empty = ' '
player = "X"
def clicked(z):
button = buttonlist[int(z)] # Finds out which button was pressed
global empty, player
if button.text != empty:
pass # If button already pushed do nothing
else:
# Marks button with user's go
button.text = player
# Switches players
if player == "X":
player = "O"
else:
player = "X"
return
def instructions():
info("Instructions","There are 2 players one of them are Xs one of them are Os. All you have to do is you have to try getting your symbol 3 times in a row without the other player blocking you")
def quit_game():
app.destroy()
app = App(title="Tic Tac Toe", layout="grid", width=200, height=250)
buttonlist = [] # Empty list to contain a list of Buttons
Player1_label = Text(app, text="Player 1", align="top", grid=[0, 0, 2 , 1])
Player1 = TextBox(app, text=empty, align="top", grid=[2, 0, 3, 1])# Player 1 enters the username
Player2_label = Text(app, text="Player 2", align="top", grid=[0, 1, 2 , 1])
Player2 = TextBox(app, text=empty, align="top", grid=[2, 1, 3, 1])# Player 2 enters the username
Instructions = PushButton(app, command=instructions, text="Instructions", grid=[0, 5, 3, 1])# Display the instructions
Quit = PushButton(app, command=quit_game ,text="Quit",grid=[10, 0, 3, 2])
# Create Buttons for game, in 3 rows of 3
z=0
for x in range(3):
for y in range(2, 5):
buttonlist.append(PushButton(app, text=empty, args=str(z), grid=[x, y], command=clicked))
z+=1
app.display()
The problem I have is in displaying who is the winner. I know how to make a window appear to show the winner and the line that does this is:
app.info("Tic Tac Toe Winner","The winner is" + Player1.value)
But the problem that i am having is knowing who is the winner at the end of the game so and displaying that they are the winner also I have a feeling that to find out the winner its got something to do with buttonlist list that i made but i just cannot figure out how to do it so I would appriciate if some one could help me.
Thanks
Nice game. To work with the game board, I would suggest to create an internal data representation rather than working with the gui elements all the time according to common practice.
The reason is, that if you work with the gui elements everywhere, it might be slower, as it is not the ideal data representation. You could not save the data with pickle etc and you also add a strong dependency to the gui framework you use.
E.g. if you later plan to convert it to a web application etc. you will have a very hard time.
So I just created a simple data representation in which I store the field in a two dimensional array.
# check_win checks if there are three markings
# in one row/column or diagonally
def check_win():
# the checks array is just a trick to
# reduce the amount of code,
# you could avoid it by just adding
# one nested loop where the outer iterates
# over y and the inner over x
# one with the outer on x and the inner on y
# and then two others with just one loop
# for the diagonals
# the body of the inner loops would
# esentially look the same as for the while
# loop below
checks= [(0, 0, 1, 1), (0, 2, 1, -1)]
for i in range(3):
checks.append((i, 0, 0, 1))
checks.append((0, i, 1, 0))
won= None
for y, x, incr_y, incr_x in checks:
player= field[y][x]
found= player is not None
while found and x < 3 and y < 3:
found= (player == field[y][x])
y+= incr_y
x+= incr_x
if found:
won= player
break
return won
# I also changed your clicked method a bit, so
# it calles the check_win method
# and also changed the signature, so
# it gets the y and x coordinate of the
# button which makes maintaining the
# field array inside the clicked method
# a bit simpler and also seems more natural
# to me
def clicked(y, x):
button = buttonlist[y][x] # Finds out which button was pressed
global empty, player
if button.text != empty:
pass # If button already pushed do nothing
else:
# Marks button with user's go
button.text = player
field[y][x] = player
# Switches players
if player == "X":
player = "O"
else:
player = "X"
won= check_win()
if won is not None:
print(f'Player {won} has won the game')
return
# now I initialize the field array
# it will contain None for an untouched
# cell and X/O if taken by the corresponding
# user
field= [[None] * 3 for i in range(3)]
buttonlist= list()
# to get the buttons to call the
# clicked function with the new
# signature and also maintain the
# buttons in a two dimensional array
# I changed the order of your loops
# and the args argument
for y in range(0, 3):
rowlist= list()
buttonlist.append(rowlist)
for x in range(3):
rowlist.append(PushButton(app, text=empty, args=(y, x), grid=[x, y+2], command=clicked))
z+=1
app.display()
# a plain vanilla version of check_win would look something like:
def check_win():
for start in range(3):
x= start
mark= field[0][x]
for y in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
y= start
mark= field[y][0]
for x in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
mark= field[0][0]
for x in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
mark= field[0][3]
for x in range(1, 3):
if field[y][2-x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
return None

Individual keypress bug in python

The following code is a python sprinting game. It was posted as an answer to my previous post, by #mango You have to tap 'a' and 'd' as fast as you can to run 100 meters. However, there are a few bugs...
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
Previous code:
import msvcrt
import time
high_score = 50
name = "no-one"
while True:
distance = int(0)
print("\n--------------------------------------------------------------")
print('\n\nWelcome to the 100m sprint, tap a and d rapidly to move!')
print('* = 10m')
print("\n**Current record: " + str(high_score) + "s, by: " + name)
print('\nPress enter to start')
input()
print('Ready...')
time.sleep(1)
print('GO!')
start_time = time.time()
while distance < 100:
k1 = msvcrt.getch().decode('ASCII')
if k1 == 'a':
k2 = msvcrt.getch().decode('ASCII')
if k2 == 'd':
distance += 1
if distance == 50:
print("* You're halfway there!")
elif distance % 10 == 0:
print('*')
fin_time = time.time() - start_time
fin_time = round(fin_time,2)
print('Well done you did it in...'+str(fin_time))
if fin_time < high_score:
print("Well done you've got a new high score ")
name = input("Please enter your name : ")
This is the code that I recieved as feedback, I have made a few changes, but I am struggling witht the bugs that I listed before.
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
# these are the modules we'll need
import sys
import tkinter as tk
class SprintGame(object):
# this represents the distance we'll allow our player to run
# NOTE: the distance is in meters
distance = 100
# this represents the stride length of the players legs
# NOTE: basically how far the player can travel in one footstep
stride = 1.5
# this represents the last key the user has pressed
lastKey = ""
# this represents wether or not the race has been completed
completed = False
# this function initiates as soon as the "sprint" variable is defined
def __init__(self):
# create the tk window
self.root = tk.Tk()
# set the tk window size
self.root.geometry('600x400')
# set the tk window title
self.root.title("Sprinting Game")
# bind the keypress event to the self.keypress handler
self.root.bind('<KeyPress>', self.keypress)
# center the window
self.centerWindow(self.root)
# insert the components
self.insertComponents()
# initial distance notice
self.output("{0}m left to go!".format(self.distance))
# start out wonderful game
self.start()
# this function centers the window
def centerWindow(self, window):
window.update_idletasks()
# get the screen width
width = window.winfo_screenwidth()
# get the screen height
height = window.winfo_screenheight()
# get the screen size
size = tuple(int(_) for _ in window.geometry().split('+') [0].split('x'))
# get the screen's dimensions
x = (width / 2) - (size[0] / 2)
y = (height / 2) - (size[1] / 2)
# set the geometry
window.geometry("%dx%d+%d+%d" % (size + (x, y)))
# this function replaces the old text in the textbox with new text
def output(self, text = ""):
self.text.delete('1.0', tk.END)
self.text.insert("end", text)
# this function handles key presses inside the tkinter window
def keypress(self, event):
# get the key and pass it over to self.logic
self.logic(event.char)
# this function handles game logic
def logic(self, key):
# convert key to a lower case string
key = str(key).lower()
# let us know how far we've got left
if key == "l":
self.output("{0}m left to go!".format(self.distance))
# restart the race
if key == "r":
# clear the output box
self.text.delete('1.0', tk.END)
# reset the distance
self.distance = 100
# reset the stride
self.stride = 1.5
# reset the last key
self.lastKey = ""
# set race completed to false
self.completed = False
# output restart notice
self.output("The Race Has Been Restarted.")
# don't bother with logic if race is completed
if self.completed == True:
return False
# check if distance is less than or equal to zero (meaning the race is over)
if self.distance <= 0:
# set the "self.completed" variable to True
self.completed = True
# let us know we've completed the race
self.output("Well done, you've completed the race!")
# return true to stop the rest of the logic
return True
# convert the key to lower case
key = key.lower()
# this is the quit function
if key == "q":
# lights out...
sys.exit(0)
# check if the key is a
if key == "a":
# set the last key to a so that we can decrement the "distance"
# variable if it is pressed next
self.lastKey = "a"
# only bother with "d" keypresses if the last key was "a"
if self.lastKey == "a":
# capture the "d" keypress
if key == "d":
# decrement the "distance" variable
self.distance -= self.stride
# let us know how far into the game we are
self.output("{0}m left to go!".format(self.distance))
# this function inserts the components into the window
def insertComponents(self):
# this component contains all of our output
self.text = tk.Text(self.root, background='#d6d167', foreground='#222222', font=('Comic Sans MS', 12))
# lets insert out text component
self.text.pack()
# this function opens the window and starts the game
def start(self):
self.root.mainloop()
# create a new instance of the game
Game = SprintGame()

Categories