You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免Python井字棋游戏中玩家与电脑同时获胜?

井字棋游戏问题:双胜BUG与落子延迟实现

问题背景

跟着教程开发了一款与电脑对战的井字棋,游戏能正常运行,但存在玩家与电脑同时获胜的情况。猜测原因是玩家点击后电脑立即执行落子操作。尝试用time.sleep(x)在玩家落子后、电脑落子前加延迟,但导致玩家的落子操作也出现延迟,不清楚问题出在哪,也不知道正确的代码放置位置。

原代码片段

初始化按钮网格

hasClicked = True
clickCount = 0

buttonGrid = []
for r in range(3):
    row = []
    for c in range (3):
        btn = Button(mainWindow, text = "", font=("Helvetica", 20), height=3, width=6, command=lambda row=r, col=c: btn_click(row, col))
        btn.grid(row=r, column=c, sticky="nsew")
        row.append(btn)
    buttonGrid.append(row)

玩家点击事件

def btn_click(row, col):
    global hasClicked, clickCount

    if buttonGrid[row][col]["text"] == "" and hasClicked == True:
        buttonGrid[row][col]["text"] = "X"
        computer_turn()
        clickCount += 1

电脑落子逻辑

def computer_turn():
    global clickCount
    empty_tiles = []
    
    for r in range(3):
        for c in range(3):
            if buttonGrid[r][c]["text"] == "":
                empty_tiles.append((r,c))
    if empty_tiles:
        row, col = random.choice(empty_tiles)
        buttonGrid[row][col]["text"] = "O"
        clickCount += 1

获胜判定函数

def who_wins(user):
    for i in range(3):
        if buttonGrid[i][0]["text"] == user and buttonGrid[i][1]["text"] == user and buttonGrid [i][2]["text"] == user:
            buttonGrid[i][0].config(bg="green")
            buttonGrid[i][1].config(bg="green")
            buttonGrid[i][2].config(bg="green")
            return True
        # 检查列获胜
        if buttonGrid[0][i]["text"] == user and buttonGrid[1][i]["text"] == user and buttonGrid [2][i]["text"] == user:
            buttonGrid[0][i].config(bg="green")
            buttonGrid[1][i].config(bg="green")
            buttonGrid[2][i].config(bg="green")
            return True
    # 检查左上到右下对角线获胜
    if buttonGrid[0][0]["text"] == user and buttonGrid[1][1]["text"] == user and buttonGrid [2][2]["text"] == user:
        buttonGrid[0][0].config(bg="green")
        buttonGrid[1][1].config(bg="green")
        buttonGrid[2][2].config(bg="green")
        return True
    # 检查右上到左下对角线获胜
    if buttonGrid[0][2]["text"] == user and buttonGrid[1][1]["text"] == user and buttonGrid [2][0]["text"] == user:
        buttonGrid[0][2].config(bg="green")
        buttonGrid[1][1].config(bg="green")
        buttonGrid[2][0].config(bg="green")
        return True
    return False

解决方案

1. 为什么time.sleep()会导致玩家落子延迟?

tkinter是单线程GUI框架,time.sleep()会直接阻塞主线程,包括GUI的更新渲染。也就是说,玩家点击后,X还没显示在按钮上,程序就被sleep卡住了,所以看起来玩家落子也延迟了。正确的做法是用tkinter自带的after()方法,它能在指定毫秒后异步执行函数,完全不会阻塞GUI。

2. 修复双胜BUG

双胜的核心原因是:玩家落子后没有先检查自己是否获胜,电脑就立刻落子了。正确的流程应该是:

玩家落子 → 检查玩家是否获胜 → 若未获胜,延迟触发电脑落子 → 电脑落子后检查自己是否获胜

3. 具体代码修改

修改btn_click函数

调整执行顺序,先检查玩家获胜,再用after()延迟调用电脑回合:

def btn_click(row, col):
    global hasClicked, clickCount

    if buttonGrid[row][col]["text"] == "" and hasClicked == True:
        buttonGrid[row][col]["text"] = "X"
        clickCount += 1  # 玩家落子后立即计数
        
        # 先检查玩家是否获胜
        if who_wins("X"):
            hasClicked = False  # 获胜后禁止继续操作
            return
        
        # 检查是否平局
        if clickCount == 9:
            hasClicked = False
            # 可添加平局提示,比如用messagebox
            from tkinter import messagebox
            messagebox.showinfo("平局", "双方均未获胜!")
            return
        
        # 延迟1000毫秒(1秒)执行电脑落子,不会阻塞GUI
        mainWindow.after(1000, computer_turn)

修改computer_turn函数

电脑落子后同样检查获胜与平局:

def computer_turn():
    global clickCount, hasClicked
    empty_tiles = []
    
    for r in range(3):
        for c in range(3):
            if buttonGrid[r][c]["text"] == "":
                empty_tiles.append((r,c))
    if empty_tiles:
        row, col = random.choice(empty_tiles)
        buttonGrid[row][col]["text"] = "O"
        clickCount += 1
        
        # 检查电脑是否获胜
        if who_wins("O"):
            hasClicked = False
            return
        
        # 检查是否平局
        if clickCount == 9:
            hasClicked = False
            from tkinter import messagebox
            messagebox.showinfo("平局", "双方均未获胜!")
            return

4. 关键说明

  • mainWindow.after(1000, computer_turn)中的1000是毫秒数,可根据需求调整(比如2000就是2秒)
  • hasClicked = False用于在获胜或平局后禁止后续的落子操作,避免无效点击
  • 平局检查可以根据需求选择是否添加,若不需要可直接删除相关代码

内容的提问来源于stack exchange,提问作者twigMURR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 21:54:53