Разработка игры Судоку с использованием Python и Pygame

PythonPythonIntermediate
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом проекте мы создадим игру Судоку с использованием Python и библиотеки Pygame. Игра сгенерирует сетку Судоку заданного уровня сложности и позволит игрокам решать головоломку, заполняя пустые клетки цифрами. Игра будет предоставлять функции, такие как выбор уровня сложности, подсветка выбранных клеток и проверка, заполнена ли сетка.

👀 Предпросмотр

Предпросмотр игры Судоку

🎯 Задачи

В этом проекте вы научитесь:

  • Как импортировать необходимые библиотеки
  • Как инициализировать PyGame
  • Как определить цвета
  • Как установить размеры и заголовок игрового окна
  • Как создать игровое окно
  • Как загрузить шрифты
  • Как сгенерировать сетку Судоку
  • Как решить сетку Судоку с использованием алгоритма обратного хода
  • Как удалить цифры из сетки в зависимости от сложности
  • Как нарисовать сетку Судоку на игровом окне
  • Как проверить, заполнена ли сетка полностью
  • Как получить координаты клетки под позицией мыши
  • Как выбрать уровень сложности
  • Как реализовать главный игровой цикл

🏆 Достижения

После завершения этого проекта вы сможете:

  • Использовать библиотеку Pygame для разработки игр на Python
  • Генерировать сетку Судоку заданного уровня сложности
  • Решать сетку Судоку с использованием алгоритма обратного хода
  • Обрабатывать события мыши и клавиатуры в Pygame
  • Рисовать фигуры и текст на игровом окне
  • Реализовать главный игровой цикл в Pygame

Создайте файлы проекта

Для начала создайте файл с именем sudoku_game.py и откройте его в предпочитаемом текстовом редакторе или интегрированной среде разработки (IDE).

cd ~/project
touch sudoku_game.py
✨ Проверить решение и практиковаться

Импортируйте необходимые библиотеки

Импортируйте необходимые библиотеки в начале файла. Для этой игры нам нужны библиотеки pygame и random.

import pygame
import random

Установите библиотеку pygame с помощью команды pip.

sudo pip install pygame
✨ Проверить решение и практиковаться

Инициализируйте PyGame

Инициализируйте библиотеку PyGame для настройки игрового окна.

pygame.init()
✨ Проверить решение и практиковаться

Определите цвета

Определите цвета, которые будут использоваться в игре. Мы будем использовать формат RGB для цветов.

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (200, 200, 200)
BLUE = (0, 0, 255)
LIGHT_BLUE = (100, 100, 255)
✨ Проверить решение и практиковаться

Задайте размеры игрового окна и его заголовок

Установите размеры игрового окна и задайте заголовок для игры.

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

pygame.display.set_caption("Sudoku Game")
✨ Проверить решение и практиковаться

Создайте игровое окно

Создайте игровое окно с использованием указанных размеров.

screen = pygame.display.set_mode(WINDOW_SIZE)
✨ Проверить решение и практиковаться

Загрузите шрифты

Нам понадобится загрузить шрифты, чтобы отображать цифры на игровом экране. Загрузите два шрифта, один для больших цифр и другой для маленьких цифр.

font_large = pygame.font.SysFont("calibri", 50)
font_small = pygame.font.SysFont("calibri", 30)
✨ Проверить решение и практиковаться

Генерируйте сетку Судоку

Создайте функцию generate_sudoku(difficulty), которая генерирует новую сетку Судоку заданного уровня сложности. Функция должна возвращать сгенерированную сетку.

def generate_sudoku(difficulty):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Заполните диагональные подсетки

Внутри функции generate_sudoku заполните диагональные подсетки сетки случайными числами. Это гарантирует, что в каждой подсетке содержатся числа от 1 до 9 без повторений.

## 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]
✨ Проверить решение и практиковаться

Решите сетку Судоку

Создайте функцию solve_sudoku(grid), которая решает сетку Судоку с использованием алгоритма обратного хода. Функция должна возвращать True, если сетка разрешима, и False в противном случае.

def solve_sudoku(grid):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Найдите пустую ячейку

Внутри функции solve_sudoku создайте вспомогательную функцию find_empty_cell(grid), которая возвращает координаты следующей пустой ячейки в сетке. Если пустых ячеек нет, верните 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
✨ Проверить решение и практиковаться

Проверьте допустимый ход

Внутри функции solve_sudoku создайте вспомогательную функцию is_valid_move(grid, row, col, num), которая проверяет, является ли размещение числа в ячейке допустимым ходом. Функция должна возвращать True, если ход допустим, и False в противном случае.

def is_valid_move(grid, row, col, num):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Решите сетку Судоку (продолжение)

Внутри функции solve_sudoku используйте вспомогательные функции find_empty_cell и is_valid_move для реализации алгоритма обратного хода. Если решение найдено, верните True. Если текущее число приводит к недопустимому решению, открутите назад, установив текущую ячейку равной 0.

## Try filling the empty cell with numbers from 1 to 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

        ## If the current number leads to an invalid solution, backtrack
        grid[row][col] = 0

return False
✨ Проверить решение и практиковаться

Удалите числа в зависимости от сложности

Внутри функции generate_sudoku удалите числа из сетки в зависимости от указанного уровня сложности. Уровни сложности: 1 (легко), 2 (средне), 3 (сложно). Количество чисел для удаления вычисляется как num_to_remove = 45 + 10 * difficulty.

## Remove numbers based on difficulty level
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
✨ Проверить решение и практиковаться

Нарисуйте сетку

Создайте функцию draw_grid(grid, selected_cell), которая рисует сетку Судоку на игровом окне. Эта функция должна отвечать за рисование ячеек, цифр и выделение выбранной ячейки.

def draw_grid(grid, selected_cell):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Заполните ячейки и нарисуйте цифры

Внутри функции draw_grid пройдитесь по сетке и нарисуйте ячейки и цифры с использованием функций Pygame pygame.draw.rect и screen.blit.

## Draw the cells
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)

        ## Draw the numbers
        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)
✨ Проверить решение и практиковаться

Выделите выбранную ячейку

Внутри функции draw_grid выделите выбранную ячейку, если она не равна None, с использованием функции pygame.draw.rect.

## Highlight the selected cell
if (row, col) == selected_cell:
    pygame.draw.rect(screen, LIGHT_BLUE, cell_rect, 3)
✨ Проверить решение и практиковаться

Нарисуйте линии сетки

Внутри функции draw_grid нарисуйте линии для создания сетки с использованием функции pygame.draw.line.

## Draw the lines
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,
    )
✨ Проверить решение и практиковаться

Обновите экран

Внутри функции draw_grid обновите экран с использованием функции pygame.display.update.

pygame.display.update()
✨ Проверить решение и практиковаться

Проверьте, заполнена ли сетка

Создайте функцию is_grid_full(grid), которая проверяет, заполнена ли сетка Судоку полностью. Это можно сделать, пройдясь по сетке и проверив, содержит ли какая-либо ячейка 0.

def is_grid_full(grid):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Получите ячейку под мышью

Создайте функцию get_cell_under_mouse(pos), которая возвращает координаты ячейки под позицией мыши. Это можно вычислить, разделив позицию мыши на размер ячейки.

def get_cell_under_mouse(pos):
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Выберите сложность

Создайте функцию select_difficulty(), которая позволяет игроку выбрать уровень сложности перед началом игры. Эта функция должна отображать варианты сложности и возвращать выбранный уровень сложности.

def select_difficulty():
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Реализуйте выбор сложности

Внутри функции select_difficulty реализуйте логику выбора сложности с использованием функций Pygame pygame.mouse.get_pos и pygame.event.get.

## Display difficulty selection text
title_text = font_large.render("Select Difficulty", True, BLACK)
screen.blit(title_text, (110, 200))

## Display difficulty options
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()

## Wait for difficulty selection
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:  ## Left mouse button
                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
✨ Проверить решение и практиковаться

Основной игровой цикл

Создайте основной игровой цикл, определив функцию main().

def main():
    ## Function code goes here
    pass
✨ Проверить решение и практиковаться

Выберите сложность и сгенерируйте сетку

Внутри функции main выберите уровень сложности, вызвав select_difficulty(), и верните, если сложность равна None. Затем сгенерируйте сетку Судоку с использованием выбранного уровня сложности, вызвав generate_sudoku(difficulty).

## Select the difficulty level
difficulty = select_difficulty()
if difficulty is None:
    return

## Generate a new Sudoku grid
grid = generate_sudoku(difficulty)
✨ Проверить решение и практиковаться

Переменные игрового цикла

Внутри функции main создайте переменные игрового цикла selected_cell и running. Установите selected_cell в None и running в True.

selected_cell = None
running = True
✨ Проверить решение и практиковаться

Включите повтор нажатия клавиш

Внутри функции main включите повтор нажатия клавиш с использованием функции pygame.key.set_repeat. Мы устанавливаем задержку и интервал в 100 миллисекунд, чтобы сделать проще для игроков заполнять клетки.

## Enable key repeat
pygame.key.set_repeat(100, 100)
✨ Проверить решение и практиковаться

Обработка событий

Внутри игрового цикла обрабатывайте события с использованием функции pygame.event.get. Проверяйте на события выхода, нажатия кнопок мыши и нажатия клавиш.

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
✨ Проверить решение и практиковаться

Обработка событий нажатия кнопок мыши

Внутри обработки событий нажатия кнопок мыши проверьте, нажата ли левая кнопка мыши или правая кнопка мыши.

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
✨ Проверить решение и практиковаться

Обработка нажатия левой кнопки мыши

Внутри обработки нажатия левой кнопки мыши получите координаты клетки под позицией мыши, вызвав get_cell_under_mouse(pos).

## Get the cell under the mouse position
pos = pygame.mouse.get_pos()
row, col = get_cell_under_mouse(pos)
✨ Проверить решение и практиковаться

Обработка нажатия левой кнопки мыши (продолжение)

Внутри обработки нажатия левой кнопки мыши выберите клетку, если она пуста, проверив, равно ли grid[row][col] 0.

## Select the cell if it's empty
if grid[row][col] == 0:
    selected_cell = (row, col)
✨ Проверить решение и практиковаться

Обработка нажатия правой кнопки мыши

Внутри обработки нажатия правой кнопки мыши очистите выбранную клетку, если она не равна None и она пуста, проверив, равно ли 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
✨ Проверить решение и практиковаться

Обработка событий клавиатуры

Внутри обработки событий клавиатуры проверьте, какая цифровая клавиша нажата, и установите значение выбранной клетки соответственно.

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
✨ Проверить решение и практиковаться

Отрисовка сетки и проверка завершения

Внутри игрового цикла отрисуйте сетку, вызвав draw_grid(grid, selected_cell). Затем проверьте, завершена ли сетка, вызвав 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.")
✨ Проверить решение и практиковаться

Обновление отображения и выход

После игрового цикла обновите отображение с использованием функции pygame.display.update и выйдите из игры с использованием pygame.quit().

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

## Quit the game
pygame.quit()
✨ Проверить решение и практиковаться

Запуск игры

Наконец, добавьте условие для проверки, является ли текущий файл точкой входа в программу. Если да, запустите игру, вызвав функцию main().

if __name__ == "__main__":
    main()

Запустите игру с использованием команды python.

python sudoku_game.py
Sudoku game execution screenshot
✨ Проверить решение и практиковаться

Резюме

Поздравляем! Вы успешно создали игру Судоку с использованием Python и библиотеки Pygame. В игре игроки могут выбрать уровень сложности, заполнять пустые клетки цифрами и проверять, завершена ли сетка. Приятной игры и решения пазлов Судоку!