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

如何在Pygame开发的Connect4游戏中循环玩家回合直至其做出有效选择?

Hey there! Let's tackle that frustrating turn-switching bug in your Pygame Connect4 game. I see exactly what's going wrong here—your current code is switching turns regardless of whether the move was valid, and those nested while loops were causing deadlocks because they weren't playing nice with Pygame's event loop. Let's fix this step by step.

核心问题分析

  1. Forced Turn Switch: In your main loop, you're executing turn = (turn + 1) % 2 every time a MOUSEBUTTONDOWN event is triggered—even if the player clicked an invalid spot. That's why it's switching to Player 2 immediately after an invalid move.
  2. Deadlock-Prone While Loops: The while turn == 0 and while turn != 0 blocks inside draw_drop_piece() are dangerous. They lock up the program because they don't allow Pygame to process new events (like another mouse click or even the quit event) while they're running.

解决方案

We'll restructure the code to:

  • Let the main event loop handle turn persistence instead of nested loops
  • Only switch turns after a valid move is made
  • Simplify the draw_drop_piece() function to handle a single click attempt and return whether the move was valid + if the game is over

修正后的完整代码

import pygame
import math
import sys

# Define required constants (add these if missing in your original code!)
ROW_COUNT = 6
COLUMN_COUNT = 7
SQUARESIZE = 100
BLUE = (0,0,255)
BLACK = (0,0,0)
RED = (255,0,0)
YELLOW = (255,255,0)

def create_board():
    board = [[0 for _ in range(COLUMN_COUNT)] for _ in range(ROW_COUNT)]
    return board

def print_board(board):
    for row in reversed(board):
        print(row)

def draw_board(board):
    for c in range(COLUMN_COUNT):
        for r in range(ROW_COUNT):
            pygame.draw.rect(screen, BLUE, (c*SQUARESIZE, r*SQUARESIZE+SQUARESIZE, SQUARESIZE, SQUARESIZE))
            pygame.draw.circle(screen, BLACK, (int(c*SQUARESIZE+SQUARESIZE/2), int(r*SQUARESIZE+SQUARESIZE+SQUARESIZE/2)), int(SQUARESIZE/2 - 5))
    
    for c in range(COLUMN_COUNT):
        for r in range(ROW_COUNT):
            if board[r][c] == 1:
                pygame.draw.circle(screen, RED, (int(c*SQUARESIZE+SQUARESIZE/2), height - int(r*SQUARESIZE+SQUARESIZE/2)), int(SQUARESIZE/2 - 5))
            elif board[r][c] == 2:
                pygame.draw.circle(screen, YELLOW, (int(c*SQUARESIZE+SQUARESIZE/2), height - int(r*SQUARESIZE+SQUARESIZE/2)), int(SQUARESIZE/2 - 5))
    pygame.display.update()

def next_open_row(board, col):
    for r in range(ROW_COUNT):
        if board[r][col] == 0:
            return r

def drop_piece(board, row, col, piece):
    board[row][col] = piece

def is_valid(board, col):
    return board[ROW_COUNT - 1][col] == 0

def winning_move(board, piece):
    # Check horizontal wins
    for c in range(COLUMN_COUNT - 3):
        for r in range(ROW_COUNT):
            if board[r][c] == piece and board[r][c+1] == piece and board[r][c+2] == piece and board[r][c+3] == piece:
                return True
    # Check vertical wins
    for c in range(COLUMN_COUNT):
        for r in range(ROW_COUNT - 3):
            if board[r][c] == piece and board[r+1][c] == piece and board[r+2][c] == piece and board[r+3][c] == piece:
                return True
    # Check positive diagonal wins
    for c in range(COLUMN_COUNT - 3):
        for r in range(ROW_COUNT - 3):
            if board[r][c] == piece and board[r+1][c+1] == piece and board[r+2][c+2] == piece and board[r+3][c+3] == piece:
                return True
    # Check negative diagonal wins
    for c in range(COLUMN_COUNT - 3):
        for r in range(3, ROW_COUNT):
            if board[r][c] == piece and board[r-1][c+1] == piece and board[r-2][c+2] == piece and board[r-3][c+3] == piece:
                return True

def track_mouse():
    # Add your mouse tracking logic here if needed
    pass

def draw_drop_piece(event, turn, board):
    game_over = False
    move_valid = False
    posx = event.pos[0]
    col = int(math.floor(posx / SQUARESIZE))

    # Clear previous status messages
    pygame.draw.rect(screen, BLACK, (0, 0, width, SQUARESIZE))

    if is_valid(board, col):
        move_valid = True
        row = next_open_row(board, col)
        drop_piece(board, row, col, turn + 1)  # Player 1 = 1, Player 2 = 2

        # Check for winning move
        if winning_move(board, turn + 1):
            label_text = "PLAYER 1 WINS!" if turn == 0 else "PLAYER 2 WINS!"
            label_color = RED if turn == 0 else YELLOW
            label = FONT.render(label_text, 1, label_color)
            screen.blit(label, (40, 10))
            game_over = True
    else:
        # Show invalid move prompt
        label = FONT.render("Enter Valid Spot", 1, BLUE)
        screen.blit(label, (40, 10))
    
    pygame.display.update()
    return game_over, move_valid

if __name__ == '__main__':
    pygame.init()
    board = create_board()
    print_board(board)
    game_over = False
    turn = 0  # 0 = Player 1's turn, 1 = Player 2's turn
    FONT = pygame.font.SysFont("monospace", 75)
    
    width = COLUMN_COUNT * SQUARESIZE
    height = (ROW_COUNT + 1) * SQUARESIZE
    size = (width, height)
    screen = pygame.display.set_mode(size)
    draw_board(board)
    pygame.display.update()

    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            
            track_mouse()

            if event.type == pygame.MOUSEBUTTONDOWN:
                # Handle the drop and get results
                game_over, move_valid = draw_drop_piece(event, turn, board)
                print_board(board)
                draw_board(board)

                # Only switch turns if the move was valid and game isn't over
                if move_valid and not game_over:
                    turn = (turn + 1) % 2

        if game_over:
            pygame.time.wait(5000)

关键改进点

  • Removed Nested While Loops: The draw_drop_piece() now handles one click at a time, letting Pygame's main event loop process subsequent clicks until a valid move is made.
  • Conditional Turn Switch: We only switch turns (turn = (turn + 1) % 2) if move_valid is True and the game isn't over—no more accidental turn skips.
  • Clearer Return Values: The function returns two values: whether the game is over, and whether the move was valid—giving the main loop full control over turn progression.
  • Fixed Parameter Mismatch: The original code had a mismatch between draw_drop_piece()'s parameters and the call. We fixed this by passing event, turn, board properly.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 23:02:33