Desenvolvimento de Jogo Sudoku com Python e Pygame

PythonBeginner
Pratique Agora

Introdução

Neste projeto, criaremos um jogo de Sudoku usando Python e a biblioteca Pygame. O jogo gerará uma grade de Sudoku com o nível de dificuldade especificado e permitirá que os jogadores resolvam o quebra-cabeça preenchendo as células vazias com números. O jogo fornecerá recursos como seleção de dificuldade, destaque de células selecionadas e verificação se a grade está completa.

👀 Pré-visualização

Sudoku Game Preview

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como importar as bibliotecas necessárias
  • Como inicializar o PyGame
  • Como definir cores
  • Como definir as dimensões e o título da janela do jogo
  • Como criar a janela do jogo
  • Como carregar fontes
  • Como gerar uma grade de Sudoku
  • Como resolver a grade de Sudoku usando o algoritmo de backtracking
  • Como remover números da grade com base na dificuldade
  • Como desenhar a grade de Sudoku na janela do jogo
  • Como verificar se a grade está totalmente preenchida
  • Como obter as coordenadas da célula sob a posição do mouse
  • Como selecionar o nível de dificuldade
  • Como implementar o loop principal do jogo

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Usar a biblioteca Pygame para desenvolvimento de jogos em Python
  • Gerar uma grade de Sudoku com um nível de dificuldade especificado
  • Resolver uma grade de Sudoku usando o algoritmo de backtracking
  • Lidar com eventos de mouse e teclado no Pygame
  • Desenhar formas e texto na janela do jogo
  • Implementar o loop principal do jogo no Pygame

Criar os Arquivos do Projeto

Para começar, crie um arquivo chamado sudoku_game.py e abra-o no seu editor de texto ou ambiente de desenvolvimento integrado (IDE) preferido.

cd ~/project
touch sudoku_game.py
✨ Verificar Solução e Praticar

Importar as Bibliotecas Necessárias

Importe as bibliotecas necessárias no início do arquivo. Precisamos das bibliotecas pygame e random para este jogo.

import pygame
import random

Instale a biblioteca pygame usando o comando pip.

sudo pip install pygame
✨ Verificar Solução e Praticar

Inicializar PyGame

Inicialize a biblioteca Pygame para configurar a janela do jogo.

pygame.init()
✨ Verificar Solução e Praticar

Definir Cores

Defina as cores a serem usadas no jogo. Usaremos o formato RGB para as cores.

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (200, 200, 200)
BLUE = (0, 0, 255)
LIGHT_BLUE = (100, 100, 255)
✨ Verificar Solução e Praticar

Definir Dimensões e Título da Janela do Jogo

Defina as dimensões da janela do jogo e defina um título para o jogo.

WINDOW_SIZE = (550, 550)
CELL_SIZE = WINDOW_SIZE[0] // 9

pygame.display.set_caption("Sudoku Game")
✨ Verificar Solução e Praticar

Criar a Janela do Jogo

Crie a janela do jogo usando as dimensões especificadas.

screen = pygame.display.set_mode(WINDOW_SIZE)
✨ Verificar Solução e Praticar

Carregar Fontes

Precisaremos carregar fontes para exibir números na tela do jogo. Carregue duas fontes, uma para os números maiores e outra para os números menores.

font_large = pygame.font.SysFont("calibri", 50)
font_small = pygame.font.SysFont("calibri", 30)
✨ Verificar Solução e Praticar

Gerar Grelha de Sudoku

Crie uma função generate_sudoku(difficulty) que gera uma nova grelha de Sudoku do nível de dificuldade especificado. A função deve retornar a grelha gerada.

def generate_sudoku(difficulty):
    ## Function code goes here
    pass
✨ Verificar Solução e Praticar

Preencher Subgrelhas Diagonais

Dentro da função generate_sudoku, preencha as sub-grelhas diagonais da grelha com números aleatórios. Isso garante que cada sub-grelha contenha números de 1 a 9 sem repetição.

## Fill diagonal subgrids
for i in range(0, 9, 3):
    nums = random.sample(range(1, 10), 3)
    for j in range(3):
        grid[i + j][i + j] = nums[j]
✨ Verificar Solução e Praticar

Resolver Grelha de Sudoku

Crie uma função solve_sudoku(grid) que resolve a grelha de Sudoku usando o algoritmo de backtracking. A função deve retornar True se a grelha for solucionável e False caso contrário.

def solve_sudoku(grid):
    ## Function code goes here
    pass
✨ Verificar Solução e Praticar

Encontrar Célula Vazia

Dentro da função solve_sudoku, crie uma função auxiliar find_empty_cell(grid) que retorna as coordenadas da próxima célula vazia na grelha. Se não houver células vazias, retorne None.

def find_empty_cell(grid):
    for row in range(9):
        for col in range(9):
            if grid[row][col] == 0:
                return (row, col)

    return None
✨ Verificar Solução e Praticar

Verificar Movimento Válido

Dentro da função solve_sudoku, crie uma função auxiliar is_valid_move(grid, row, col, num) que verifica se colocar um número em uma célula é um movimento válido. A função deve retornar True se o movimento for válido e False caso contrário.

def is_valid_move(grid, row, col, num):
    ## Function code goes here
    pass
✨ Verificar Solução e Praticar

Resolver a Grelha de Sudoku (Continuação)

Dentro da função solve_sudoku, use as funções auxiliares find_empty_cell e is_valid_move para implementar o algoritmo de backtracking (retrocesso). Se uma solução for encontrada, retorne True. Se o número atual levar a uma solução inválida, faça o retrocesso (backtrack) definindo a célula atual como 0.

## Tente preencher a célula vazia com números de 1 a 9
for num in range(1, 10):
    if is_valid_move(grid, row, col, num):
        grid[row][col] = num

        if solve_sudoku(grid):
            return True

        ## Se o número atual levar a uma solução inválida, faça o retrocesso
        grid[row][col] = 0

return False
✨ Verificar Solução e Praticar

Remover Números com Base na Dificuldade

Dentro da função generate_sudoku, remova números da grelha com base no nível de dificuldade especificado. Os níveis de dificuldade são: 1 (fácil), 2 (médio), 3 (difícil). O número de números a serem removidos é calculado como num_to_remove = 45 + 10 * difficulty.

## Remover números com base no nível de dificuldade
num_to_remove = 45 + 10 * difficulty
for _ in range(num_to_remove):
    row = random.randint(0, 8)
    col = random.randint(0, 8)
    grid[row][col] = 0

return grid
✨ Verificar Solução e Praticar

Desenhar a Grelha

Crie uma função draw_grid(grid, selected_cell) que desenha a grelha de Sudoku na janela do jogo. Esta função deve ser responsável por desenhar as células, os números e destacar a célula selecionada.

def draw_grid(grid, selected_cell):
    ## Código da função vai aqui
    pass
✨ Verificar Solução e Praticar

Preencher Células e Desenhar Números

Dentro da função draw_grid, itere sobre a grelha e desenhe as células e os números usando as funções Pygame pygame.draw.rect e screen.blit.

## Desenhar as células
for row in range(9):
    for col in range(9):
        cell_rect = pygame.Rect(
            col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE
        )
        pygame.draw.rect(screen, GRAY, cell_rect)

        ## Desenhar os números
        if grid[row][col] != 0:
            number = font_small.render(str(grid[row][col]), True, BLACK)
            text_rect = number.get_rect(
                center=(
                    col * CELL_SIZE + CELL_SIZE // 2,
                    row * CELL_SIZE + CELL_SIZE // 2,
                )
            )
            screen.blit(number, text_rect)
✨ Verificar Solução e Praticar

Destacar Célula Selecionada

Dentro da função draw_grid, destaque a célula selecionada se ela não for None usando a função pygame.draw.rect.

## Destacar a célula selecionada
if (row, col) == selected_cell:
    pygame.draw.rect(screen, LIGHT_BLUE, cell_rect, 3)
✨ Verificar Solução e Praticar

Desenhar Linhas da Grelha

Dentro da função draw_grid, desenhe as linhas para criar a grelha usando a função pygame.draw.line.

## Desenhar as linhas
for i in range(10):
    if i % 3 == 0:
        thickness = 4
    else:
        thickness = 1

    pygame.draw.line(
        screen,
        BLACK,
        (0, i * CELL_SIZE),
        (WINDOW_SIZE[0], i * CELL_SIZE),
        thickness,
    )
    pygame.draw.line(
        screen,
        BLACK,
        (i * CELL_SIZE, 0),
        (i * CELL_SIZE, WINDOW_SIZE[1]),
        thickness,
    )
✨ Verificar Solução e Praticar

Atualizar a Exibição

Dentro da função draw_grid, atualize o display usando a função pygame.display.update.

pygame.display.update()
✨ Verificar Solução e Praticar

Verificar se a Grelha Está Cheia

Crie uma função is_grid_full(grid) que verifica se a grelha do Sudoku está completamente preenchida. Isso pode ser feito iterando sobre a grelha e verificando se alguma célula contém 0.

def is_grid_full(grid):
    ## Código da função vai aqui
    pass
✨ Verificar Solução e Praticar

Obter Célula Sob o Mouse

Crie uma função get_cell_under_mouse(pos) que retorna as coordenadas da célula sob a posição do mouse. Isso pode ser calculado dividindo a posição do mouse pelo tamanho da célula.

def get_cell_under_mouse(pos):
    ## Código da função vai aqui
    pass
✨ Verificar Solução e Praticar

Selecionar Dificuldade

Crie uma função select_difficulty() que permite ao jogador selecionar o nível de dificuldade antes de iniciar o jogo. Esta função deve exibir as opções de dificuldade e retornar o nível de dificuldade selecionado.

def select_difficulty():
    ## Código da função vai aqui
    pass
✨ Verificar Solução e Praticar

Implementar Seleção de Dificuldade

Dentro da função select_difficulty, implemente a lógica de seleção de dificuldade usando as funções Pygame pygame.mouse.get_pos e pygame.event.get.

## Exibir texto de seleção de dificuldade
title_text = font_large.render("Select Difficulty", True, BLACK)
screen.blit(title_text, (110, 200))

## Exibir opções de dificuldade
option_y = 300
for difficulty, label in difficulties.items():
    option_text = font_small.render(f"{difficulty}. {label}", True, BLACK)
    text_rect = option_text.get_rect(center=(WINDOW_SIZE[0] // 2, option_y))
    screen.blit(option_text, text_rect)
    option_y += 70

pygame.display.update()

## Aguardar a seleção da dificuldade
difficulty_selected = False
difficulty = 1

while not difficulty_selected:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            return
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:  ## Botão esquerdo do mouse
                pos = pygame.mouse.get_pos()
                if 180 <= pos[0] <= 380:
                    if 300 <= pos[1] <= 350:
                        difficulty = 1
                        difficulty_selected = True
                    elif 370 <= pos[1] <= 420:
                        difficulty = 2
                        difficulty_selected = True
                    elif 440 <= pos[1] <= 490:
                        difficulty = 3
                        difficulty_selected = True

return difficulty
✨ Verificar Solução e Praticar

Loop Principal do Jogo

Crie o loop principal do jogo definindo uma função main().

def main():
    ## Código da função vai aqui
    pass
✨ Verificar Solução e Praticar

Selecionar Dificuldade e Gerar Grade

Dentro da função main, selecione o nível de dificuldade chamando select_difficulty() e retorne se a dificuldade for None. Em seguida, gere uma grade de Sudoku usando o nível de dificuldade selecionado, chamando generate_sudoku(difficulty).

## Selecionar o nível de dificuldade
difficulty = select_difficulty()
if difficulty is None:
    return

## Gerar uma nova grade de Sudoku
grid = generate_sudoku(difficulty)
✨ Verificar Solução e Praticar

Variáveis do Loop do Jogo

Dentro da função main, crie as variáveis do loop do jogo selected_cell e running. Defina selected_cell como None e running como True.

selected_cell = None
running = True
✨ Verificar Solução e Praticar

Habilitar Repetição de Teclas

Dentro da função main, habilite a repetição de teclas usando a função pygame.key.set_repeat. Definimos o atraso (delay) e o intervalo (interval) para 100 milissegundos para facilitar o preenchimento das células pelos jogadores.

## Habilitar repetição de teclas
pygame.key.set_repeat(100, 100)
✨ Verificar Solução e Praticar

Gerenciar Eventos

Dentro do loop do jogo, lide com eventos usando a função pygame.event.get. Verifique eventos de saída (quit events), eventos de botões do mouse (mouse button events) e eventos de teclas (key events).

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        running = False
    elif event.type == pygame.MOUSEBUTTONDOWN:
        ## Handle mouse button events
        pass
    elif event.type == pygame.KEYDOWN:
        ## Handle key events
        pass
✨ Verificar Solução e Praticar

Gerenciar Eventos de Botões do Mouse

Dentro do tratamento de eventos para eventos de botões do mouse, verifique se o botão esquerdo do mouse foi clicado ou se o botão direito do mouse foi clicado.

if event.button == 1:  ## Left mouse button
    ## Handle left mouse button click
    pass
elif event.button == 3:  ## Right mouse button
    ## Handle right mouse button click
    pass
✨ Verificar Solução e Praticar

Gerenciar Clique do Botão Esquerdo do Mouse

Dentro do tratamento do clique do botão esquerdo do mouse, obtenha as coordenadas da célula sob a posição do mouse chamando get_cell_under_mouse(pos).

## Get the cell under the mouse position
pos = pygame.mouse.get_pos()
row, col = get_cell_under_mouse(pos)
✨ Verificar Solução e Praticar

Gerenciar Clique do Botão Esquerdo do Mouse (Continuação)

Dentro do tratamento do clique do botão esquerdo do mouse, selecione a célula se ela estiver vazia, verificando se grid[row][col] == 0.

## Select the cell if it's empty
if grid[row][col] == 0:
    selected_cell = (row, col)
✨ Verificar Solução e Praticar

Gerenciar Clique do Botão Direito do Mouse

Dentro do tratamento do clique do botão direito do mouse, limpe a célula selecionada se ela não for None e estiver vazia, verificando se grid[selected_cell[0]][selected_cell[1]] == 0.

if selected_cell:
    ## Clear the selected cell if it's empty
    if grid[selected_cell[0]][selected_cell[1]] == 0:
        grid[selected_cell[0]][selected_cell[1]] = 0
✨ Verificar Solução e Praticar

Gerenciar Eventos de Tecla

Dentro do tratamento de eventos para eventos de tecla, verifique qual tecla numérica foi pressionada e defina o valor da célula selecionada de acordo.

if selected_cell:
    ## Get the key pressed
    if event.key == pygame.K_1:
        grid[selected_cell[0]][selected_cell[1]] = 1
    elif event.key == pygame.K_2:
        grid[selected_cell[0]][selected_cell[1]] = 2
    elif event.key == pygame.K_3:
        grid[selected_cell[0]][selected_cell[1]] = 3
    elif event.key == pygame.K_4:
        grid[selected_cell[0]][selected_cell[1]] = 4
    elif event.key == pygame.K_5:
        grid[selected_cell[0]][selected_cell[1]] = 5
    elif event.key == pygame.K_6:
        grid[selected_cell[0]][selected_cell[1]] = 6
    elif event.key == pygame.K_7:
        grid[selected_cell[0]][selected_cell[1]] = 7
    elif event.key == pygame.K_8:
        grid[selected_cell[0]][selected_cell[1]] = 8
    elif event.key == pygame.K_9:
        grid[selected_cell[0]][selected_cell[1]] = 9
✨ Verificar Solução e Praticar

Desenhar Grade e Verificar Conclusão

Dentro do loop do jogo, desenhe a grelha chamando draw_grid(grid, selected_cell). Em seguida, verifique se a grelha está completa chamando is_grid_full(grid).

## Draw the grid
draw_grid(grid, selected_cell)

## Check if the grid is complete
if is_grid_full(grid):
    print("Congratulations! You solved the Sudoku puzzle.")
✨ Verificar Solução e Praticar

Atualizar Exibição e Sair

Após o loop do jogo, atualize a exibição usando a função pygame.display.update e saia do jogo usando pygame.quit().

## Update the display
pygame.display.update()

## Quit the game
pygame.quit()
✨ Verificar Solução e Praticar

Executar o Jogo

Finalmente, adicione uma condição para verificar se o arquivo atual é o ponto de entrada principal do programa. Se for, execute o jogo chamando a função main().

if __name__ == "__main__":
    main()

Execute o jogo usando o comando python.

python sudoku_game.py
Captura de tela da execução do jogo Sudoku
✨ Verificar Solução e Praticar

Resumo

Parabéns! Você criou com sucesso um jogo de Sudoku usando Python e a biblioteca Pygame. O jogo permite que os jogadores selecionem um nível de dificuldade, preencham as células vazias com números e verifica se a grade está completa. Divirta-se jogando e resolvendo quebra-cabeças de Sudoku!