python马踏棋盘( 三 )

< 1 or x > 8 or y < 1 or y > 8:# 判断x和y的值是否在[1, 8]范围内raise ValueError('x, y range [1, 8]')# 超出范围抛出值错误full_chess_board(x, y)# 执行递归查找填充路线print(chess_list)# 打印出棋子行走坐标print(times)# 打印出使用递归函数的次数row_list = [[0] * 9 for i in range(9)]# 创建一个二维列表来存放行走顺序(1~64)number = 1# 第一个落子位置序号为1for i in chess_list:# 通过循环来逐个输出落子位置row_list[i[0]][i[1]] = number# 在棋盘对应位置记录落子序号number += 1# 循环一次序号加一print('-' * 41)# 打印棋盘上边缘线for i in range(1, 9):# 通过循环逐个输出1到8的数字for j in range(1, 9):# 通过循环逐个输出1到8的数字print(f'|{row_list[i][j]:^4}', end='')# 取出二维列表中棋盘对应位置的序号,并打印出来print('|')# 棋盘右边界线print('-' * 41)# 打印棋盘底部边界线print_chess_order(1, 1)
运行结果如下:
从运行结果来看,从第一个位置(1,1)开始共尝试落子次找出了一种走完棋盘的方式 。看似很多次,但对于4的64次方来说小到忽略不计 。也算是恰巧碰到了(1,1)这个位置,我尝试了(1,2)10分钟都没找到结果,我就手动终止了(浪费电) 。你们的电脑比较好的话可以尝试一下多久出结果 。(我的电脑尝试落子次需要10秒钟,那么尝试落子4的64次方次大约需要33,282,130,589,010,443,001,514,556年,太难了!)
使用UI界面来动态打印行走方式
使用print来打印的结果,看起来密密麻麻的 。不太方便观察,看久了花眼睛 。所以我们可以使用来动态展示行走方式 。UI界面的代码如下:
import timeimport tkinter as tkimport threadingchess_list = []# 存放落子位置的公共列表times = 0# 记录递归函数被调用次数的公共变量def return_location(x: int, y: int):"""返回可移动位置有那些:param x: x坐标:param y: y坐标:return: 可移动位置列表"""l_list = []# 创建一个列表来存储可移动位置if x - 2 > 0 and y + 1 < 9:# 判断此位置是否超出棋盘l_list.append((x - 2, y + 1))# 没有超出棋盘,则放入列表中if x - 1 > 0 and y + 2 < 9:l_list.append((x - 1, y + 2))if x + 1 < 9 and y + 2 < 9:l_list.append((x + 1, y + 2))if x + 2 < 9 and y + 1 < 9:l_list.append((x + 2, y + 1))if x + 2 < 9 and y - 1 > 0:l_list.append((x + 2, y - 1))if x + 1 < 9 and y - 2 > 0:l_list.append((x + 1, y - 2))if x - 1 > 0 and y - 2 > 0:l_list.append((x - 1, y - 2))if x - 2 > 0 and y - 1 > 0:l_list.append((x - 2, y - 1))return l_list# 返回可移动位置列表def full_chess_board(x: int, y: int):"""使用递归找出能填满整个棋盘的线路:param x: x坐标:param y: y坐标:return: bool,当递归深度为64时返回True,否则返回False"""global times# 声明公共变量times += 1# 每调用一次递归函数times加一chess_list.append((x, y))# 把落子位置放入公共列表中if len(chess_list) == 64:# 判断棋盘所有位置是否都已落子(递归深度是否为64)return True# 返回Truefor i in return_location(x, y):# 使用循环把可移动位置逐个输出if i not in chess_list:# 判断该位置在列表chess_list中是否存在if full_chess_board(i[0], i[1]):# 使用递归判断向下一个可移动位置移动是否可行(不断递归下去,能否达到64的深度)return True# 返回Truechess_list.remove((x, y))# 此位置所有的分支都为绝路时,把此位置从公共列表中移除return False# 返回Falseclass ChessBoard:"""马踏棋盘动态展示使用tkinter中的方法创建UI界面界面中提供X和Y坐标的输入框,提供开始按钮使用full_chess_board函数计算出填充顺序"""canvas, coordinate_x, coordinate_y = None, None, Nonedef win(self):"""UI界面窗口初始化:return:"""root = tk.Tk()# 定义UI界面root.title('马踏棋盘')# 设置界面标题width = 605# 设置界面宽度height = 630# 设置界面高度win_width = root.winfo_screenwidth()# 获取电脑显示器宽度win_height = root.winfo_screenheight()# 获取电脑显示器高度x = win_width / 2 - width / 2# 计算出UI界面左右边距y = win_height / 2 - height / 2# 计算出UI界面上下边距root.geometry('%dx%d+%d+%d' % (width, height, x, y))# 让UI界面居中显示tk.Label(root, text='X坐标:').grid(row=0, column=0)# 设置文本信息'X坐标:'位置第0行第0列self.coordinate_x = tk.Entry(width=10)# 设置X坐标输入框,宽度10self.coordinate_x.grid(row=0, column=1)# 位置第0行第1列tk.Label(root, text='Y坐标:').grid(row=0, column=2)# 设置文本信息'Y坐标:'位置第0行第2列self.coordinate_y = tk.Entry(width=10)# 设置Y坐标输入框,宽度10self.coordinate_y.grid(row=0, column=3)# 位置第0行第3列button = tk.Button(root, text='开始', command=self.thread_ui)# 设置开始按钮,绑定点击事件触发self.thread_ui函数button.grid(row=0, column=4)# 位置第0行第4列self.canvas = tk.Canvas(root, bg="SandyBrown", width=600, height=600)# 设置Canvas画布,背景为"SandyBrown",高宽600self.canvas.grid(row=1, column=0, rowspan=10, columnspan=10)# 位置第1行第0列,跨度10行10列for i in range(9):self.canvas.create_line(60, (60 * i + 60), 540, (60 * i + 60))# 通过循环画9条间隔为60的竖线self.canvas.create_line((60 * i + 60), 60, (60 * i + 60), 540)# 通过循环画9条间隔为60的横线root.mainloop()# 持续刷新UI界面def draw_chess(self):"""绘制棋子行走过程:return:"""x = int(self.coordinate_x.get())# 获取起始位置的X坐标y = int(self.coordinate_y.get())# 获取起始位置的Y坐标full_chess_board(x, y)# 执行递归查找填充路线for i in chess_list:# 通过循环从公共列表中取出行走位置self.canvas.create_oval(15 + i[0] * 60, 15 + i[1] * 60, 45 + i[0] * 60, 45 + i[1] * 60, fill="black")# 画棋子time.sleep(1)self.canvas.create_oval(15 + i[0] * 60, 15 + i[1] * 60, 45 + i[0] * 60, 45 + i[1] * 60, fill="gray")# 画棋子def thread_ui(self):"""多线程执行任务:return:"""thread = threading.Thread(target=self.draw_chess)# 定义新线程thread.setDaemon(True)# 开启线程守护,主线程结束时子线程也会结束thread.start()# 执行新线程if __name__ == '__main__':chess_board = ChessBoard()# 实例画UI界面chess_board.win()# 调用界面窗口