Draw over a pygame rect with a smaller width

Viewed 411

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!

2 Answers

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.

I see several options. I'll start with the ones that will require the least amount of code changes.

  • Draw your selection rect only inside your tile. Problem solved.
  • when you draw a tile, draw the border around it as well.

You might also want to store the selected tile in a variable, so you can some stuff with it later on.

Related