#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()
сразу после того, как игрок или машина сделают ход.