Search code examples
pythonpygamedrawing

Draw over a pygame rect with a smaller width


I'm making a Sudoku Solver via pygame and I've been able to draw the whole board, however, while programming the section of code that deals with clicking on a tile, I made it so that the current tile would "light up" green so the user can know the current tile. However, I'm stuck figuring out how to draw over the green highlighted section when a user decides to click on a different tile. I'd like to remove that part entirely but, since it's a rect with a thickness of 4 and the base tiles have a thickness of 1, all I accomplish is having an ugly black line over the thick green line. I tried redrawing the whole board but I obtain similar results. Any help?

class Tile:
    '''Represents each white tile/box on the grid'''
    def __init__(self, value, window, x1, x2):
        self.value = value #value of the num on this grid
        self.window = window
        self.active = False
        self.rect = pygame.Rect(x1, x2, 60, 60) #dimensions for the rectangle

    def draw(self, color, thickness):
        '''Draws a tile on the board'''
        pygame.draw.rect(self.window, color, self.rect, thickness)
        pygame.display.flip()

def main():
    board = Board(screen)
    tiles = board.draw_board() #store the locations of all the tiles on the grid

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

                        elif event.type == pygame.MOUSEBUTTONUP:
                mousePos = pygame.mouse.get_pos()
                #board.draw_board() or (see next comment)
                for i in range(9):
                    for j in range (9): #look for tile we clicked on
                        #tiles[i][j].draw((0,0,0),1)
                        #yield same results
                        if tiles[i][j].is_clicked(mousePos):
                            tiles[i][j].draw((50,205,50),4) #redraws that tile but with a highlighted color to show it's been clicked
                            break

main()

Drawing over doesn't work!


Solution

  • You could simply store the highlighted tile in a variable. I also don't think you need a Tile class at all, since a sudoku game state is basically just a list of list of numbers.

    Here's a simple example. Note the comments:

    import random
    import pygame
    
    TILE_SIZE = 64
    
    # function to draw the grid
    def draw_board():
        board_surface = pygame.Surface((9*TILE_SIZE, 9*TILE_SIZE))
        board_surface.fill((255, 255, 255))
        for x in range(9):
            for y in range(9):
                rect = pygame.Rect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE)
                pygame.draw.rect(board_surface, (0, 0, 0), rect, 1)
        pygame.draw.line(board_surface, (0, 0, 0), (0, 3*TILE_SIZE), (9*TILE_SIZE, 3*TILE_SIZE), 5)
        pygame.draw.line(board_surface, (0, 0, 0), (0, 6*TILE_SIZE), (9*TILE_SIZE, 6*TILE_SIZE), 5)
        pygame.draw.line(board_surface, (0, 0, 0), (3*TILE_SIZE, 0), (3*TILE_SIZE, 9*TILE_SIZE), 5)
        pygame.draw.line(board_surface, (0, 0, 0), (6*TILE_SIZE, 0), (6*TILE_SIZE, 9*TILE_SIZE), 5)
        return board_surface
    
    def main():
    
        # standard pygame setup
        pygame.init()
        clock = pygame.time.Clock()
        screen = pygame.display.set_mode((9*TILE_SIZE, 9*TILE_SIZE))
        font = pygame.font.SysFont(None, 40)
        
        # seperate the game state from the UI
        # create a dummy 9x9 sudoku board
        state = [[None for _ in range(10)] for _ in range(10)]
        for _ in range(15):
            x = random.randint(0, 9)
            y = random.randint(0, 9)
            state[y][x] = random.randint(1, 9)
    
        # a variable to hold the selected tile's position on the board 
        # (from 0,0 to 8,8)
        selected = None
    
        # create the grid surface ONCE and reuse it to clear the screen
        board_surface = draw_board()
        
        while True:
    
            for event in pygame.event.get():
                pos = pygame.mouse.get_pos()
                if event.type == pygame.QUIT:
                    return
                    
                # when the player clicks on a tile
                # we translate the screen coordinates to the board coordinates
                # e.g. a pos of (140, 12) is the tile at (2, 0)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    w_x, w_y = event.pos
                    selected = w_x // TILE_SIZE, w_y // TILE_SIZE
    
            # clear everything by blitting the grid surface to the screen
            screen.blit(board_surface, (0, 0))
            
            # print all numbers in the state to the screen
            # we use a Rect here so we can easily center the numbers
            rect = pygame.Rect(0, 0, TILE_SIZE, TILE_SIZE)
            for line in state:
                for tile in line:
                    if tile != None:
                        tmp = font.render(str(tile), True, (0, 0, 0))
                        screen.blit(tmp, tmp.get_rect(center=rect.center))
                    rect.move_ip(TILE_SIZE, 0)
                rect.x = 0
                rect.move_ip(0, TILE_SIZE)
    
            # if a tile is selected, we calculate the world coordinates from the board coordinates
            # and draw a simple green rect
            if selected:
                rect = pygame.Rect(selected[0] * TILE_SIZE, selected[1] * TILE_SIZE, TILE_SIZE, TILE_SIZE)
                pygame.draw.rect(screen, (0, 200, 0), rect, 5)
    
            clock.tick(30)
            pygame.display.flip()
    main()
    

    enter image description here

    Also, you shouldn't call pygame.display.flip() outside your main loop. It can lead to effects like flickering or images not showing up correctly.