基于Pygame的Mastermind游戏:网格功能异常及索引越界问题排查
IndexError in Your Pygame Mastermind Game Let's break down why you're hitting that IndexError and fix it, plus clean up some messy parts of your code to get grid clicks and text input working as expected.
Why the IndexError Happens
Your blocks list is initialized to hold array_width * array_height = 4 * 8 = 32 elements (indices 0 to 31). But right now:
- When you click anywhere on the screen, you calculate
rowandcolumnwithout checking if those values are within your grid's bounds (rows 0-3, columns 0-7). - If you click outside the 4x8 grid (like the text box area),
roworcolumnwill be larger than 3 or 7, makingrow * array_height + columnexceed 31—hence the out-of-range error. - You're also looping through all grid cells for every click, which means you're updating the
blockslist multiple times for a single click, leading to unexpected behavior.
Step-by-Step Fixes
1. Merge Duplicate Event Handlers
You have two separate if event.type == pygame.MOUSEBUTTONDOWN blocks—merge them into one to avoid conflicts and redundant code.
2. Add Bounds Checking for Grid Clicks
Before updating blocks, verify that the clicked row and column are within your grid's valid range:
if 0 <= row < array_width and 0 <= column < array_height: # Only update blocks if click is inside the grid
3. Remove Redundant Loops
You don't need to loop through all grid cells to find which one was clicked—calculate the position once, check bounds, then update the corresponding block.
4. Clean Up Text Input Logic
Merge your duplicate KEYDOWN handlers into one to handle backspace, regular input, and enter submission in a single place.
5. Simplify Grid Initialization
Your grid was initialized as 10x10 but you only use 4x8—adjust it to match your actual grid size to avoid confusion.
Optimized Code
Here's the revised code with all these fixes applied:
import pygame # Color definitions BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 255, 0) RED = (255, 0, 0) BLUE = (0, 0, 255) # Grid dimensions WIDTH = 20 HEIGHT = 20 MARGIN = 5 array_width = 4 # Number of rows in our grid array_height = 8 # Number of columns in our grid # Initialize grid and state arrays grid = [[0 for _ in range(array_height)] for _ in range(array_width)] colors = [(0, 0, 0) for _ in range(array_width * array_height)] blocks = [False for _ in range(array_width * array_height)] # Pygame setup pygame.init() WINDOW_SIZE = [300, 300] screen = pygame.display.set_mode(WINDOW_SIZE) pygame.display.set_caption("Mastermind (WIP)") clock = pygame.time.Clock() # Text input setup base_font = pygame.font.Font(None, 32) user_text = "" input_rect = pygame.Rect(10, 250, 100, 50) color_active = pygame.Color('lightskyblue3') color_passive = pygame.Color('gray15') color = color_passive active = False done = False while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # Handle mouse clicks (grid + text box) elif event.type == pygame.MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() # Check if click is on the text input box if input_rect.collidepoint(event.pos): active = True else: active = False # Calculate grid coordinates only if not clicking the text box column = pos[0] // (WIDTH + MARGIN) row = pos[1] // (HEIGHT + MARGIN) # Only update blocks if within grid bounds if 0 <= row < array_width and 0 <= column < array_height: block_index = row * array_height + column blocks[block_index] = not blocks[block_index] print(f"Click {pos} -> Grid coordinates: ({row}, {column})") print(f"Block {block_index} toggled to {blocks[block_index]}") # Handle keyboard input for text box elif event.type == pygame.KEYDOWN: if active: if event.key == pygame.K_BACKSPACE: user_text = user_text[:-1] elif event.key == pygame.K_RETURN: print(f"Submitted text: {user_text}") # Add logic here to process the guess (e.g., change grid colors) if user_text == "green": for r in range(array_width): for c in range(array_height): if blocks[r * array_height + c]: colors[r * array_height + c] = GREEN elif user_text == "blue": for r in range(array_width): for c in range(array_height): if blocks[r * array_height + c]: colors[r * array_height + c] = BLUE user_text = "" # Clear input after submission else: user_text += event.unicode # Update text box color based on active state color = color_active if active else color_passive # Draw everything screen.fill(BLACK) # Clear screen first # Draw grid for row in range(array_width): for column in range(array_height): block_index = row * array_height + column pygame.draw.rect( screen, colors[block_index], [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT] ) # Draw text input box pygame.draw.rect(screen, color, input_rect, 2) text_surface = base_font.render(user_text, True, WHITE) screen.blit(text_surface, (input_rect.x + 5, input_rect.y + 5)) clock.tick(60) pygame.display.flip() pygame.quit()
Key Improvements in This Code:
- No more
IndexErrorbecause we check if clicks are within the grid bounds before updatingblocks - Merged duplicate event handlers for cleaner, more maintainable logic
- Added text submission logic (press Enter to apply color to toggled blocks)
- Simplified grid initialization to match your actual 4x8 grid
- Clearer variable names and debug prints to help with testing
Now when you click grid cells, they'll toggle their state without errors, and typing a color name (like "green" or "blue") and pressing Enter will apply that color to all toggled blocks.
内容的提问来源于stack exchange,提问作者Luke Williams

