Графический интерфейс TicTacToe Tkinter продолжает сбоить

#python #user-interface #tkinter #crash #tic-tac-toe

#python #пользовательский интерфейс #tkinter #сбой #крестики-нолики

Вопрос:

Я пытаюсь создать простую игру в крестики-нолики, используя Tkinter, но я не могу ее протестировать, потому что приложение продолжает сбоить. Я не могу продвинуться дальше второго раунда. Кто-нибудь может сказать мне, почему он продолжает сбоить? Кажется, после внедрения метода стало немного лучше after() , но он по-прежнему не работает должным образом.

Вот мой код:

 import tkinter as tk
from tkinter import messagebox
from random import randrange


# click function
def choose(event):
    global game_over
    if not game_over:
        if event.widget["state"] == "normal":
            event.widget.configure(text="O", disabledforeground="#0000ff", state="disabled")
            # check for winner
            window.after(1000, check)
            # let computer make its move
            if not game_over:
                window.after(1000, machine_move)
                # check again
                window.after(1000, check)
        elif event.widget["state"] == "disabled":
            messagebox.showerror("Field taken", "Please choose a different field")


# define machine move
def machine_move():
    global taken
    pick = randrange(1, 9)
    taken = True
    while taken:
        if button_dict[pick-1].cget("state") == "normal":
            button_dict[pick-1].configure(text="X", disabledforeground="#ff0000", state="disabled")
            taken = False


# check for winner function
def check():
    global game_over
    global inputs
    inputs = []
    for key in button_dict.keys():
        inputs.append(button_dict[key].cget("text"))
    # line win
    if inputs[0] == "X" and inputs[1] == "X" and inputs[2] == "X" 
    or inputs[3] == "X" and inputs[4] == "X" and inputs[5] == "X" 
    or inputs[6] == "X" and inputs[7] == "X" and inputs[8] == "X":
        messagebox.showinfo("Game over", "Line win by machine")
        game_over = True
    elif inputs[0] == "O" and inputs[1] == "O" and inputs[2] == "O" 
    or inputs[3] == "O" and inputs[4] == "O" and inputs[5] == "O" 
    or inputs[6] == "O" and inputs[7] == "O" and inputs[8] == "O":
        messagebox.showinfo("Game over", "Line win by you")
        game_over = True
    # row win
    elif inputs[0] == "X" and inputs[3] == "X" and inputs[6] == "X" 
    or inputs[1] == "X" and inputs[4] == "X" and inputs[7] == "X" 
    or inputs[2] == "X" and inputs[5] == "X" and inputs[8] == "X":
        messagebox.showinfo("Game over", "Row win by machine")
        game_over = True
    elif inputs[0] == "O" and inputs[3] == "O" and inputs[6] == "O" 
    or inputs[1] == "O" and inputs[4] == "O" and inputs[7] == "O" 
    or inputs[2] == "O" and inputs[5] == "O" and inputs[8] == "O":
        messagebox.showinfo("Game over", "Row win by you")
        game_over = True
    # check for diagonal win
    elif inputs[0] == "X" and inputs[4] == "X" and inputs[8] == "X" 
    or inputs[2] == "X" and inputs[4] == "X" and inputs[6] == "X":
        messagebox.showinfo("Game over", "Diagonal win by machine")
        game_over = True
    elif inputs[0] == "O" and inputs[4] == "O" and inputs[8] == "O" 
    or inputs[2] == "O" and inputs[4] == "O" and inputs[6] == "O":
        messagebox.showinfo("Game over", "Diagonal win by machine")
        game_over = True


# create window
window = tk.Tk()
window.title("TicTacToe")
window.geometry("450x480")


# create buttons

button_dict = {}
for i in range(9):
    new_button = tk.Button(window, text=" ", width=3, font=30)
    new_button.grid(column=i // 3, row=i % 3, ipadx=50, ipady=50)
    new_button.bind("<Button-1>", choose)
    button_dict[i] = new_button


# start game
game_over = False
button_dict[4].configure(text="X", disabledforeground="#ff0000", state="disabled")
window.mainloop()
 

Комментарии:

1. В теле цикла while в вашей machine_move функции переменная pick никогда не изменяется из своего начального состояния. Итак, если случайно выбранная кнопка оказывается отключенной, вы никогда не выходите из цикла, и программа зависает.

Ответ №1:

Как также указано @Paul M в комментариях, если pick переменная в machine_move функции относится к кнопке, которая уже отключена, ее значение останется неизменным, а цикл while будет выполняться бесконечно, что приведет к зависанию вашей программы. Чтобы преодолеть это, вы можете попробовать следующее (это одно из многих возможных решений для решения этой проблемы)

 def machine_move():
    available_buttons=[button_dict[i] for i in range(len(button_dict)) if button_dict[i].cget("state") == "normal"]
    pick = choice(available_buttons)
    pick.configure(text="X", disabledforeground="#ff0000", state="disabled")
 

Итак, в основном список available_buttons содержит кнопки, которые еще не были отключены, переменная pick выбирается случайным образом (с помощью random.choice() ) из этого списка, а затем настраивается соответствующим образом.

Обновить

Я также заметил, что вы используете after метод для вызова check() , это может привести к значительной задержке в определении победителя, а иногда и нескольких победителей, если до вызова проверки будет сделан еще один ход, чтобы преодолеть это, вы могли бы попробовать это вместо (опять же, это одно из многих решений, возможных длярешите эту проблему)

 def choose(event):
    global game_over
    if not game_over:
        if event.widget["state"] == "normal":
            event.widget.configure(text="O", disabledforeground="#0000ff", state="disabled")
            # check for winner
            check()
            # let computer make its move
            if not game_over:
                window.after(1000, machine_move)
        elif event.widget["state"] == "disabled":
            messagebox.showerror("Field taken", "Please choose a different field")


# define machine move
def machine_move():
    available_buttons=[button_dict[i] for i in range(len(button_dict)) if button_dict[i].cget("state") == "normal"]
    pick = choice(available_buttons)
    pick.configure(text="X", disabledforeground="#ff0000", state="disabled")
    check()
 

Вызов check() сразу после того, как игрок или машина сделают ход.