如何在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.
核心问题分析
- Forced Turn Switch: In your main loop, you're executing
turn = (turn + 1) % 2every time aMOUSEBUTTONDOWNevent is triggered—even if the player clicked an invalid spot. That's why it's switching to Player 2 immediately after an invalid move. - Deadlock-Prone While Loops: The
while turn == 0andwhile turn != 0blocks insidedraw_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) ifmove_validisTrueand 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 passingevent, turn, boardproperly.
内容的提问来源于stack exchange,提问作者Ibrahim Bholat
相关产品推荐
相关产品推荐

