pythonpygame

Pygame IndexError Rect displayed in middle correctly, trouble with mouse clicks


I'm trying to make a grid towards the middle of the window and can get the tiles to update however if I click where the tiles are visibly on screen I get an error.

The input seems to be stuck at the top left. How can I update this? Thanks!

line 49, IndexError: list index out of range

import pygame

pygame.init()
pygame.font.init()

colorBlack = (000, 000, 000); colorWhite = (255, 255, 255); colorRed = (255, 000, 000)

winWidth = 1280; winHeight = 720; FPS = 30
winDisplay = pygame.display.set_mode((winWidth, winHeight))
FPSClock = pygame.time.Clock()
running = True

def drawGrid():
    blockSize = 47 #Pixel count for each grid block
    gridSize = 10 #Size of grid (5x5, 10x10, etc.)
    gridFrame = 479 #Size of the grid frame (in pixels)
    gridFrameX = 501 #X position of the grid frame
    gridFrameY = 221 #Y position of the grid frame
    gridFrameRect = (gridFrameX, gridFrameY, gridFrame, gridFrame) #Grid frame rectangle
    gridSolution = [[0] * gridSize for _ in range(gridSize)] #Grid solution (2D array)
    gridPuzzle = [[0] * gridSize for _ in range(gridSize)] #Grid puzzle (2D array)

    gridPuzzle[0][0] = 1 #Set the first cell of the puzzle to 1 (black)

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    quit()
                if event.key == pygame.K_r:
                    drawGrid()
            
            if event.type == pygame.MOUSEBUTTONDOWN:
                x, y = pygame.mouse.get_pos()
                print("Mouse position: ", x, y)
                gridRow = y // blockSize
                gridCol = x // blockSize
                gridPuzzle[gridRow][gridCol] = 1 - gridPuzzle[gridRow][gridCol]
                print("Grid cell toggled at: ", gridRow, gridCol)
        
        pygame.draw.rect(winDisplay, colorBlack, (0, 0, 501, winHeight))
        pygame.draw.rect(winDisplay, colorBlack, (501, 0, 772, 221))

        for gameRow in range(gridSize):
            for gameCol in range(gridSize):
                rect = (gridFrameX + gameCol * blockSize, gridFrameY + gameRow * blockSize, blockSize, blockSize)
                pygame.draw.rect(winDisplay, colorBlack, rect, 1) #Draw the grid cell outline
                if gridPuzzle[gameRow][gameCol] == 1:
                    pygame.draw.rect(winDisplay, colorRed, rect) #Draw the filled cell
                if gridPuzzle[gameRow][gameCol] == 0:
                    pygame.draw.rect(winDisplay, colorWhite, rect) #Draw the empty cell
        
        pygame.draw.rect(winDisplay, colorBlack, gridFrameRect, 1) #Draw the grid
        pygame.display.update()
        FPSClock.tick(FPS)

def quit():
    running = False
    pygame.quit()

if __name__ == "__main__":
    drawGrid()

Solution

  • The error you're encountering (IndexError: list index out of range) is caused by this part of the code:

    gridRow = y // blockSize
    gridCol = x // blockSize
    gridPuzzle[gridRow][gridCol] = 1 - gridPuzzle[gridRow][gridCol]
    

    When you click inside the grid, it is not converting screen coordinates to grid-local coordinates properly.

    Another issue is quit() function is defined near the bottom, just before the if __name__ == "__main__": block. Here's the relevant part of your code:

    def quit():
        running = False  # This is a local variable and doesn't affect the main loop
        pygame.quit()
    

    quit() is also a built-in Python function. Rename something like def quitGame():

    Running is never defined inside drawGrid(). Therefore, make it global.

    The full code with correction and improvement is provided below:

    import pygame
    
    pygame.init()
    pygame.font.init()
    
    colorBlack = (0, 0, 0)
    colorWhite = (255, 255, 255)
    colorRed = (255, 0, 0)
    
    winWidth = 1280
    winHeight = 720
    FPS = 30
    
    winDisplay = pygame.display.set_mode((winWidth, winHeight))
    pygame.display.set_caption("Centered Grid Toggle Game")
    FPSClock = pygame.time.Clock()
    running = True
    
    def drawGrid():
        global running
    
        blockSize = 47
        gridSize = 10
        gridFrame = blockSize * gridSize
        gridFrameX = (winWidth - gridFrame) // 2
        gridFrameY = (winHeight - gridFrame) // 2
        gridFrameRect = (gridFrameX, gridFrameY, gridFrame, gridFrame)
        gridPuzzle = [[0] * gridSize for _ in range(gridSize)]
        gridPuzzle[0][0] = 1
    
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    quitGame()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        quitGame()
                    elif event.key == pygame.K_r:
                        drawGrid()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    x, y = pygame.mouse.get_pos()
                    if gridFrameX <= x < gridFrameX + gridFrame and gridFrameY <= y < gridFrameY + gridFrame:
                        gridRow = (y - gridFrameY) // blockSize
                        gridCol = (x - gridFrameX) // blockSize
                        if 0 <= gridRow < gridSize and 0 <= gridCol < gridSize:
                            gridPuzzle[gridRow][gridCol] = 1 - gridPuzzle[gridRow][gridCol]
    
            winDisplay.fill(colorBlack)
    
            for row in range(gridSize):
                for col in range(gridSize):
                    rect = (
                        gridFrameX + col * blockSize,
                        gridFrameY + row * blockSize,
                        blockSize,
                        blockSize
                    )
                    pygame.draw.rect(winDisplay, colorBlack, rect, 1)
                    color = colorRed if gridPuzzle[row][col] == 1 else colorWhite
                    pygame.draw.rect(winDisplay, color, rect)
    
            pygame.draw.rect(winDisplay, colorBlack, gridFrameRect, 2)
            pygame.display.update()
            FPSClock.tick(FPS)
    
    def quitGame():
        global running
        running = False
        pygame.quit()
    
    if __name__ == "__main__":
        drawGrid()
    

    Output:

    enter image description here