【发布时间】:2017-02-04 20:36:27
【问题描述】:
这是一个我自己无法解决的奇怪问题。基本上我正在使用 python 3.6 和 Tkinter 制作扫雷。游戏运行良好,直到大约在一场艰难的游戏进行到一半时(如果它们被拖得更久,它也会发生在其他游戏中),您单击一个棋子,然后控制台中出现错误。过了一会儿,在能够再玩几个动作后,游戏就崩溃了。对不起,“临时”代码,这不是我最好的作品,因为它从未打算被任何人看到!
(我没有包含程序中的所有代码)
from tkinter import *
import random
root = Tk()
buttons = []
images = {"X": PhotoImage(file="Mine.gif"), "X/": PhotoImage(file="MineSlash.gif"), "F": PhotoImage(file="Flag.gif")}
colors = {1:"#0008f9", 2:"#10cc00", 3:"#ff4f1e", 4:"#4b00a8", 5:"#bc0000", 6:"#00cbdd"}
board = []
mines = []
buttons = []
minesLeft = 10
numMines = 10
boardSize = 8
dead = False
minesLeftLabel = Label(font="Verdana 10 bold")
startingButtons = []
def ButtonCreate():
Button(root, text="Restart", width=8, height=1, font="Verdana 10 bold", command=Restart).grid(row=0, column=0, columnspan=3)
global minesLeftLabel
minesLeftLabel.grid(row=0, column=4, columnspan=2)
minesLeftLabel.config(text=str(minesLeft))
for y in range(boardSize):
tempList = []
for x in range(boardSize):
button = Button(root, text=board[y][x], bg="#eaeaea", width=2, height=1, font="Verdana 10 bold")
button.bind('<Button-1>', lambda event, x=x, y=y: MinePressed(x, y, False))
button.bind('<Button-3>', lambda event, x=x, y=y: MinePressed(x, y, True))
button.grid(row=y + 2, column=x)
tempList.append(button)
buttons.append(tempList)
def CheckNeighbours(x, y):
buttons[y][x].config(text=" ", fg="grey", bg="#e5e5e5", relief=SUNKEN)
for yPos in range(-1, 2):
for xPos in range(-1, 2):
if y + yPos >= 0 and y + yPos <= boardSize - 1 and x + xPos >= 0 and x + xPos <= boardSize - 1:
if mines[y + yPos][x + xPos] != 0:
board[y + yPos][x + xPos] = mines[y + yPos][x + xPos]
buttons[y + yPos][x + xPos].config(text=board[y + yPos][x + xPos], fg=colors[board[y + yPos][x + xPos]], bg="#e5e5e5", width=2, height=1, relief=SUNKEN)
elif board[y + yPos][x + xPos] == " " and mines[y + yPos][x + xPos] == 0:
board[y + yPos][x + xPos] = mines[y + yPos][x + xPos]
CheckNeighbours(x + xPos, y + yPos)
return
def MinePressed(x, y, flag):
if dead != True:
if flag:
global minesLeft
global minesLeftLabel
if board[y][x] == " ":
board[y][x] = "F"
minesLeft -= 1
minesLeftLabel.config(text=str(minesLeft))
buttons[y][x].config(image=images["F"], width=22, height=22)
elif board[y][x] == "F":
board[y][x] = " "
minesLeft += 1
minesLeftLabel.config(text=str(minesLeft))
buttons[y][x].config(text=board[y][x], image="", width=2, height=1)
else:
if board[y][x] != "F":
board[y][x] = mines[y][x]
if board[y][x] == "X":
GameOver()
buttons[y][x].config(image=images["X"], bg="red", width=21, height=21, relief=SUNKEN)
elif board[y][x] == 0:
CheckNeighbours(x, y)
else:
buttons[y][x].config(text=board[y][x], fg=colors[board[y][x]], bg="#e5e5e5", relief=SUNKEN)
root.update_idletasks()
root.mainloop()
def Restart():
ResetBoards()
global dead
dead = False
global buttons
buttons = []
CreateMines()
MineCalculations()
CreateWindow()
def ResetBoards():
global board
board = []
for y in range(boardSize):
tempList = []
for x in range(boardSize):
tempList.append(" ")
board.append(tempList)
global mines
mines = []
for y in range(boardSize):
tempList = []
for x in range(boardSize):
tempList.append(0)
mines.append(tempList)
def BoardSize(i):
global boardSize
boardSize = i
global numMines
numMines = int(boardSize * boardSize * 0.18)
global minesLeft
minesLeft = numMines
ResetBoards()
CreateMines()
MineCalculations()
CreateWindow()
def CreateWindow():
root.resizable(width=FALSE, height=FALSE)
root.geometry('{}x{}'.format(28 * boardSize, 28 * (boardSize + 1)))
for i in startingButtons:
i.destroy()
ButtonCreate()
def SelectSize():
root.resizable(width=FALSE, height=FALSE)
root.geometry('{}x{}'.format(150, 150))
global startingButtons
button1 = Button(text="Beginner", font="Verdana 10 bold", anchor=N, command=lambda i=8: BoardSize(i))
button1.place(relx=0.5, rely=0.2, anchor=CENTER)
button2 = Button(text="Intermediate", font="Verdana 10 bold", anchor=N, command=lambda i=16: BoardSize(i))
button2.place(relx=0.5, rely=0.45, anchor=CENTER)
button3 = Button(text="Hard", font="Verdana 10 bold", anchor=N, command=lambda i=24: BoardSize(i))
button3.place(relx=0.5, rely=0.7, anchor=CENTER)
startingButtons.append(button1)
startingButtons.append(button2)
startingButtons.append(button3)
SelectSize()
input()
另一个奇怪的事情是它引发了 2 个略有不同的错误,但它们仍然是同一个问题的一部分。以下是两个问题:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 137, in main
seq, request = rpc.request_queue.get(block=True, timeout=0.05)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\queue.py", line 172, in get
raise Empty
queue.Empty
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1698, in __call__
args = self.subst(*args)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1428, in _substitute
e.type = EventType(T)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\enum.py", line 291, in __call__
return cls.__new__(cls, value)
RecursionError: maximum recursion depth exceeded while calling a Python object
还有一个:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 137, in main
seq, request = rpc.request_queue.get(block=True, timeout=0.05)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\queue.py", line 172, in get
raise Empty
queue.Empty
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 108, in Restart
CreateWindow()
File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 153, in CreateWindow
ButtonCreate()
File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 35, in ButtonCreate
Button(root, text="Restart", width=8, height=1, font="Verdana 10 bold", command=Restart).grid(row=0, column=0, columnspan=3)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 2363, in __init__
Widget.__init__(self, master, 'button', cnf, kw)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 2293, in __init__
(widgetName, self._w) + extra + self._options(cnf))
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1320, in _options
v = self._register(v)
File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1356, in _register
f = CallWrapper(func, subst, self).__call__
RecursionError: maximum recursion depth exceeded
我已经尽可能多地查找了这个问题,它要么与 root.mainloop() 调用有关,要么与递归函数 (CheckNeighbours) 做错了什么有关,要么与我没有使用的事实有关类。感谢您的帮助:)
编辑 - 该程序似乎每次都会抛出一个略有不同的错误。但是,它们都以超出最大递归深度的错误结束
【问题讨论】:
-
如果您遇到最大递归错误,几乎可以肯定是因为您的递归
CheckNeighbours函数。如果您有一个 8x8 板,则板上的最大点数为 64。默认递归限制为 1000,这意味着您“检查邻居”1000 次,而要检查的邻居不可能超过 64 个。 -
我知道你要去哪里。我已经解决了这个问题(因为我已经发布了一个解决方案),但就我而言,有超过 64 个邻居需要检查。这个程序还没有优化,因此,一块检查它周围的所有 8 个邻居,如果其中一个没有值(是一个空白点),那么它对那个瓷砖做同样的事情,它将检查所有 8 个邻居(除了在该图块上调用该函数的邻居)。
-
(cont) 然而,话虽如此,它实际上从未在一次调用中超过 1000 次递归,它会随着时间的推移而累积,即使前一组邻居检查的递归已经完成。 Python中递归的奇怪集成,但正如解决方案中所说,解决这个问题的最佳方法就是将它变成一个循环(由于我制作递归函数的方式,这需要一些额外的工作)。感谢您的帮助
-
python 的实现与任何其他编程语言没有什么不同。该行为是由于您的代码,而不是 python 的递归实现。
-
就像补充一点,原来 mainloop() 是问题所在。这就是堆栈从未被删除或清除的原因。你确实是对的:)