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

如何在Python Turtle GUI中实现鼠标追踪与列高亮功能?

Connect Four游戏鼠标列高亮功能实现方案

问题说明

我正在使用Python的Turtle库开发Connect Four游戏,作业要求实现鼠标追踪功能:当鼠标处于棋子占位符所在列时,高亮该占位符(如图所示),但当前代码无法正确实现此效果。

游戏需求

原代码问题分析

  • 鼠标移动事件处理引入冗余延迟和状态变量,导致高亮响应卡顿
  • 列判断仅通过x坐标差做近似比较,精准度不足
  • 状态变量管理混乱,易出现逻辑冲突导致高亮失效

修复后的完整代码

import turtle
from turtle import *

textinput("游戏说明", "你需要在任意一列放置棋子,率先在任意方向连成4枚棋子的玩家获胜。明白了吗?(输入任意内容继续)")

def init_board():
    global gameBoard, winner
    gameBoard = []
    winner = None

    global tokenBoard
    tokenBoard = []

    for i in range(8):
        gameBoard.append([0, 0, 0, 0, 0, 0, 0, 0])
        tokenBoard.append([None, None, None, None, None, None, None, None])

'''初始化游戏棋盘,创建8个灰色矩形作为棋子占位符'''
def start_game(canvas):
    global rectangleHeight, rectangleWidth, gap
    rectangleWidth = 60
    rectangleHeight = 10
    gap = (canvas.window_width() - (rectangleWidth * 8)) / 9
    global rectangleTurtles
    rectangleTurtles = []
    for i in range(8):
        rectangle = turtle.Turtle()
        rectangle.speed(0)
        rectangle.hideturtle()
        rectangle.penup()
        # 计算每个矩形的中心坐标
        x = -canvas.window_width()/2 + gap + i*(rectangleWidth + gap) + 0.5*rectangleWidth
        y = -canvas.window_height()/2 + 100 + 0.5*rectangleHeight
        rectangle.goto(x, y)
        rectangle.color("gray")
        rectangle.shape("square")
        rectangle.shapesize(1, 3)
        rectangle.fillcolor("gray")
        rectangle.showturtle()
        rectangleTurtles.append(rectangle)

'''查找指定列的第一个空行(从底部开始),列满则返回None'''
def search_horizontally(table, column):
    rowNum = 8
    for row in table[::-1]:
        rowNum -= 1
        try:
            if row[column] == 0:
                return rowNum
        except IndexError:
            pass
    return None

'''创建指定颜色的棋子,并放置在对应位置'''
def create_token(x, y, color):
    token = turtle.Turtle()
    token.speed(0)
    token.hideturtle()
    token.penup()
    token.goto(x, y)
    token.color(color)
    token.shape("circle")
    token.shapesize(3, 3)
    token.showturtle()
    return token

'''检查当前落子是否获胜,判断横向、纵向、斜向的四连子'''
def check_winner(column, row):
    global gameBoard, winner
    directions = [(-1, 0), (0, 1), (-1, 1), (1, 1)]
    player = gameBoard[row][column]
    for direction in directions:
        count = 1
        winning_tokens = [(row, column)]
        # 正向遍历
        for i in range(1, 4):
            newRow = row + direction[1] * i
            newCol = column + direction[0] * i
            if 0 <= newRow < 8 and 0 <= newCol < 8 and gameBoard[newRow][newCol] == player:
                count += 1
                winning_tokens.append((newRow, newCol))
            else:
                break
        # 反向遍历
        for i in range(1, 4):
            newRow = row - direction[1] * i
            newCol = column - direction[0] * i
            if 0 <= newRow < 8 and 0 <= newCol < 8 and gameBoard[newRow][newCol] == player:
                count += 1
                winning_tokens.append((newRow, newCol))
            else:
                break
        if count >= 4:
            winner = player
            highlight_token(winning_tokens)
            return

'''高亮获胜的棋子,添加黄色边框'''
def highlight_token(winning_tokens):
    global tokenBoard
    for row, column in winning_tokens:
        token = tokenBoard[row][column]
        token.shapesize(3, 3, 5)
        token.pencolor('yellow')
    canvas.title(f'恭喜!玩家{3 - winner}获胜啦 (*^ - ^*)')
    canvas.update()

'''更新游戏棋盘的落子记录'''
def update_board(column, row):
    global gameBoard
    gameBoard[row][column] = currentPlayer + 1

'''处理鼠标移动事件,实现列高亮'''
def motion_handler(event, color1, color2):
    global currentPlayer, winner
    if winner is not None:
        return
    
    canvas_width = event.widget.winfo_width()
    canvas_height = event.widget.winfo_height()
    # 转换鼠标坐标为Turtle坐标系
    x = event.x - canvas_width // 2
    y = -event.y + canvas_height // 2

    current_highlight_col = -1
    # 判断鼠标所在的列
    for idx, rect in enumerate(rectangleTurtles):
        rect_x = rect.xcor()
        # 计算列的左右边界
        left = rect_x - rectangleWidth/2
        right = rect_x + rectangleWidth/2
        if left <= x <= right:
            current_highlight_col = idx
            break
    
    # 更新所有列的样式
    for idx, rect in enumerate(rectangleTurtles):
        if idx == current_highlight_col:
            # 高亮当前列:设置对应玩家颜色的边框,加粗
            rect.shapesize(1, 3, 5)
            rect.color(color1 if currentPlayer == 0 else color2, 'gray')
        else:
            # 恢复默认样式
            rect.shapesize(1, 3, 1)
            rect.color('gray', 'gray')
    canvas.update()

'''处理鼠标点击事件,执行落子逻辑'''
def on_click(event, color1, color2):
    global currentPlayer, winner
    if winner is not None:
        return
    
    canvas_width = event.widget.winfo_width()
    canvas_height = event.widget.winfo_height()
    x = event.x - canvas_width // 2
    y = -event.y + canvas_height // 2

    # 查找点击的列
    column = -1
    for idx, rect in enumerate(rectangleTurtles):
        rect_x = rect.xcor()
        left = rect_x - rectangleWidth/2
        right = rect_x + rectangleWidth/2
        if left <= x <= right:
            column = idx
            break
    
    if column == -1:
        return
    
    row = search_horizontally(gameBoard, column)
    if row is None:
        return  # 列已满
    
    # 创建棋子
    rect = rectangleTurtles[column]
    token_y = rect.ycor() + (8 - row) * 60
    if currentPlayer == 0:
        token = create_token(rect.xcor(), token_y, color1)
        currentPlayer = 1
    else:
        token = create_token(rect.xcor(), token_y, color2)
        currentPlayer = 0
    
    canvas.update()
    update_board(column, row)
    tokenBoard[row][column] = token
    check_winner(column, row)

def __main__():
    color1 = textinput('玩家1','输入除黄色外的任意颜色:')
    color2 = textinput('玩家2', '输入除黄色外的任意颜色:')

    global currentPlayer, canvas
    currentPlayer = 0
    canvas = turtle.Screen()
    canvas.title('四子棋游戏')
    canvas.tracer(0)

    init_board()
    start_game(canvas)

    # 绑定鼠标事件
    canvas.getcanvas().bind("<Motion>", lambda event: motion_handler(event, color1, color2))
    canvas.getcanvas().bind("<Button-1>", lambda event: on_click(event, color1, color2))

    turtle.mainloop()

__main__()

关键修改说明

  1. 简化鼠标移动处理:移除冗余延迟函数和状态变量,直接在motion_handler中完成列判断和样式更新,响应更流畅
  2. 精准列判断:通过计算列的左右边界确定鼠标位置,避免近似判断的误差
  3. 优化高亮逻辑:仅对当前悬停列应用高亮样式,其他列恢复默认,减少画布更新开销
  4. 统一坐标转换:点击和移动事件使用相同的坐标转换逻辑,确保判断一致性
  5. 状态判断优化:事件处理优先判断游戏是否结束,避免无效操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:47:02