8人の女王 tkinterアニメーションで人間の試行のように見せる
再起呼び出しを使わないバックトラックアルゴリズムで解く
Python3(3.10)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-14)Python3.10で動作確認済み
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
# -*- coding: utf-8 -*- """ 8 queens animation """ import tkinter as Tk import time Q_font = ("Times", 14) Timer = 1 # ミリ秒;アニメ更新間隔(1以上を設定) def move_queen(now, next): return [(i, z1-z0) for i, (z0, z1) in enumerate(zip(now, next)) if z0 != z1] class Cboard(Tk.Canvas): cell_size = 46 margin = 5 q_images = [] q_figure = [] def __init__(self, master): cwidth = 8*self.cell_size Tk.Canvas.__init__(self, master, relief=Tk.RAISED, bd=4, bg='white', width=cwidth, height=cwidth) self.put_next = [0, 0, 0, 0, 0, 0, 0, 0] self.put_now = [0, 0, 0, 0, 0, 0, 0, 0] self.row_put = -1 self.col_put = 0 self.rowfull = [0 for i in range(8)] self.upfull = [0 for i in range(15)] self.downfull = [0 for i in range(15)] for i in range(8): for j in range(8): bcolor = (i-j)%2==0 and "#E8B77D" or "#A06040" x0 = i*self.cell_size + self.margin y0 = j*self.cell_size + self.margin self.create_rectangle(x0, y0, x0+self.cell_size, y0+self.cell_size, fill=bcolor, width=0) self.q_images.append(Tk.PhotoImage(file="queen.png")) x = self.cell_size*(i+0.5) + self.margin y = 28.0 self.q_figure.append(self.create_image (x, y, image=self.q_images[i], tags="queen")) def check_full(self, row, col): return self.rowfull[row] == 0 and \ self.upfull[col - row + 7] == 0 and \ self.downfull[col + row] == 0 def set_full(self, row, col): self.rowfull[row] = 1 self.upfull[col - row + 7] = 1 self.downfull[col + row] = 1 def reset_full(self, row, col): self.rowfull[row] = 0 self.upfull[col - row + 7] = 0 self.downfull[col + row] = 0 def refresh(self, num_sol): """クィーンを動かす """ # まず最初にクィーンを下に動かす self.row_put += 1 self.put_next[self.col_put] = self.row_put if self.row_put == 8: # クィーンが下にはみ出したら列を戻す self.col_put -= 1 self.row_put = self.put_now[self.col_put] self.reset_full(self.row_put, self.col_put) return False # print self.put_next, self.put_now # debug用 # print self.row_put, self.col_put, '-' # # クィーンを置く for i, j in move_queen(self.put_now, self.put_next): self.move(self.q_figure[i], 0, j*self.cell_size) # 今の位置を次の位置に更新する for k in [0,1,2,3,4,5,6,7]: self.put_now[k] = self.put_next[k] if self.check_full(self.row_put, self.col_put): # クィーンを置くことができるなら self.put_next[self.col_put] = self.row_put self.set_full(self.row_put, self.col_put) if self.col_put == 7: # 解発見 print(self.put_now) # 解出力 self.reset_full(self.row_put, self.col_put) self.col_put = 6 self.row_put = self.put_now[self.col_put] self.reset_full(self.row_put, self.col_put) return True else: # 次の列を始める self.col_put += 1 self.row_put = -1 return False class Queen(Tk.Frame): def __init__(self, master=None): Tk.Frame.__init__(self, master) self.master.title("8 Queens") # title l_title = Tk.Label(self, text='8 Queens', font=('Times', '24', ('italic', 'bold')), fg='#191970', bg='#E8B77D', width=12) l_title.pack(padx=10, pady=10) # chess board self.f_board = Cboard(self) self.f_board.pack(padx=10, pady=10) # buttons and a counter self.q_counter = 0 self.f_footer = Tk.Frame(self) self.f_footer.pack() self.s_counter = Tk.StringVar() self.s_counter.set("%d/92" % (1 + self.q_counter)) self.a_button = Tk.Button(self.f_footer, text="next", font=Q_font, command = self.show_next) self.a_button.pack(side=Tk.LEFT, padx=5,pady=5) self.f_label = Tk.Label(self.f_footer, textvariable = self.s_counter, font=Q_font) self.f_label.pack(side=Tk.LEFT, padx=5, pady=5) def show_next(self): sol = self.f_board.refresh(self.q_counter) if sol: self.change_counter(1) return self.after(Timer, self.show_next) def change_counter(self, i): self.q_counter += i self.s_counter.set("%d/92" % (1 + self.q_counter)) if __name__ == "__main__": app = Queen() app.pack() app.mainloop()