I am trying to make a GUI for the Knight's Tour problem. I already have the solution of the problem in the form of a sorted dictionary. Now I just want the Knight's chess piece to follow the path shown by the saved dictionary and trace it in the GUI. No user input is required, apart from running the script.
# a solution to the Knight's Tour problem with initial position (7,7) stored in a dictionary
# with keys as the order of points to visit and values as the coordinates of the points.
board_path_dict = {0: [(7, 7)], 1: [(6, 5)], 2: [(5, 7)], 3: [(7, 6)], 4: [(6, 4)], 5: [(7, 2)], 6: [(6, 0)],
7: [(4, 1)], 8: [(2, 0)], 9: [(0, 1)], 10: [(1, 3)], 11: [(0, 5)], 12: [(1, 7)], 13: [(3, 6)], 14: [(1, 5)],
15: [(0, 7)], 16: [(2, 6)], 17: [(4, 7)], 18: [(6, 6)], 19: [(7, 4)], 20: [(6, 2)], 21: [(7, 0)],
22: [(5, 1)], 23: [(3, 0)], 24: [(1, 1)], 25: [(0, 3)], 26: [(2, 2)], 27: [(1, 0)], 28: [(0, 2)],
29: [(1, 4)], 30: [(0, 6)], 31: [(2, 7)], 32: [(4, 6)], 33: [(6, 7)], 34: [(7, 5)], 35: [(5, 6)],
36: [(3, 7)], 37: [(4, 5)], 38: [(5, 3)], 39: [(3, 4)], 40: [(5, 5)], 41: [(6, 3)], 42: [(7, 1)],
43: [(5, 0)], 44: [(4, 2)], 45: [(6, 1)], 46: [(7, 3)], 47: [(5, 4)], 48: [(3, 5)], 49: [(4, 3)],
50: [(3, 1)], 51: [(2, 3)], 52: [(4, 4)], 53: [(5, 2)], 54: [(4, 0)], 55: [(3, 2)], 56: [(2, 4)],
57: [(1, 6)], 58: [(0, 4)], 59: [(2, 5)], 60: [(3, 3)], 61: [(2, 1)], 62: [(0, 0)], 63: [(1, 2)]}
I also have a basic code implemented using Tkinter for the GUI.
import tkinter as tk
class GameBoard(tk.Frame):
def __init__(self, parent, rows=8, columns=8, size=64, color1="#a7ab90", color2="#0e140c"):
'''size is the size of a square, in pixels'''
self.rows = rows
self.columns = columns
self.size = size
self.color1 = color1
self.color2 = color2
self.pieces = {}
canvas_width = columns * size
canvas_height = rows * size
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0,
width=canvas_width, height=canvas_height, background="khaki")
self.canvas.pack(side="top", fill="both", expand=True, padx=2, pady=2)
# this binding will cause a refresh if the user interactively
# changes the window size
self.canvas.bind("<Configure>", self.refresh)
def addpiece(self, name, image, row=0, column=0):
'''Add a piece to the playing board'''
self.canvas.create_image(0,0, image=image, tags=(name, "piece"), anchor="c")
self.placepiece(name, row, column)
def placepiece(self, name, row, column):
'''Place a piece at the given row/column'''
self.pieces[name] = (row, column)
x0 = (column * self.size) int(self.size/2)
y0 = (row * self.size) int(self.size/2)
self.canvas.coords(name, x0, y0)
def refresh(self, event):
'''Redraw the board, possibly in response to window being resized'''
xsize = int((event.width-1) / self.columns)
ysize = int((event.height-1) / self.rows)
self.size = min(xsize, ysize)
self.canvas.delete("square")
color = self.color2
for row in range(self.rows):
color = self.color1 if color == self.color2 else self.color2
for col in range(self.columns):
x1 = (col * self.size)
y1 = (row * self.size)
x2 = x1 self.size
y2 = y1 self.size
self.canvas.create_rectangle(x1, y1, x2, y2, outline="black", fill=color, tags="square")
color = self.color1 if color == self.color2 else self.color2
for name in self.pieces:
self.placepiece(name, self.pieces[name][0], self.pieces[name][1])
self.canvas.tag_raise("piece")
self.canvas.tag_lower("square")
if __name__ == "__main__":
root = tk.Tk()
root.title("Knight's Tour Problem")
board = GameBoard(root)
board.pack(side="top", fill="both", expand="true", padx=10, pady=10)
# knight = tk.PhotoImage(file = r"C:\Gfg\knight.png")
knight = tk.PhotoImage(file = "chess_knight.png")
board.addpiece("knight", knight, 7,7) # initial position of Knight is (7,7) here, but I plan to change that manually as the solution changes
for i in range(len(board_path_dict)-1):
cell_1 = board_path_dict[i][0]
cell_2 = board_path_dict[i 1][0]
board.canvas.create_line(cell_1[0], cell_1[1], cell_2[0], cell_2[1], width=4.3, fill='red')
board.canvas.update()
root.mainloop()
I am not able to update the position of the Knight chess piece with the for loop. The output in the GUI window is this:
I want the result to be something like this:
Is it possible to do so?
TIA!
CodePudding user response:
You can use board.placepiece()
to move the chess piece. Also it is better to use .after()
instead of for loop in a tkinter application.
First add a function to GameBoard
class to draw a line between cells:
class GameBoard(tk.Frame):
...
def draw_line(self, x1, y1, x2, y2, width, fill):
x1 *= self.size
y1 *= self.size
x2 *= self.size
y2 *= self.size
half = self.size / 2
self.canvas.create_line(x1 half, y1 half, x2 half, y2 half, width=width, fill=fill)
Then use .after()
to call a function repeatedly instead of a for loop:
if __name__ == '__main__':
...
board.addpiece("knight", knight, 7,7) # initial position of Knight is (7,7) here, but I plan to change that manually as the solution changes
def move_piece(i=0):
if i < len(board_path_dict)-1:
cell_1 = board_path_dict[i][0]
cell_2 = board_path_dict[i 1][0]
# draw the line
board.draw_line(cell_1[0], cell_1[1], cell_2[0], cell_2[1], width=4.3, fill='red')
# move the chess piece
board.placepiece("knight", cell_2[1], cell_2[0])
board.after(500, move_piece, i 1)
# start the chess piece moving
board.after(500, move_piece)
root.mainloop()