Создание игры «Сапер» на Python с использованием Pygame.





Создание игры «Сапер» на Python с использованием Pygame

Игры занимают важное место в мире программирования, позволяя сочетать творческий подход и технические навыки. Одна из классических и популярных игр — «Сапер» — отлично подходит для освоения основ разработки игр. В этой статье мы подробно рассмотрим, как создать игру «Сапер» на языке Python с использованием библиотеки Pygame.

Pygame — удобная библиотека для создания 2D-игр, обеспечивающая инструменты для работы с графикой, звуком и событиями. «Сапер» же — логическая игра, в которой игроку нужно открыть все клетки на поле, избегая мин. В процессе реализации мы разберём основные части кода, логику игры, создание интерфейса и взаимодействие с пользователем.

Подготовка к работе: установка и настройка Pygame

Прежде чем приступать к написанию игры, необходимо убедиться, что у вас установлен Python и пакет Pygame. Если Pygame отсутствует, его легко установить через менеджер пакетов pip с помощью команды в терминале:

pip install pygame

После установки библиотеки важно проверить, что Pygame корректно подключается и работает в вашей среде разработки. Для этого можно написать небольшую программу, создающую пустое окно. Это позволит убедиться в готовности к следующему этапу — разработке игрового процесса.

Структура проекта

Для удобства работы рекомендуется организовать проект в отдельную папку, в которой будет основной файл с кодом (например, minesweeper.py) и при необходимости папка с ресурсами — изображениями, звуками и т. д. В нашем случае мы попробуем использовать базовые цветовые решения, не прибегая к внешним картинкам, чтобы сделать проект максимально простым для понимания и кастомизации.

Основные элементы игры «Сапер»

Чтобы правильно подготовить программу, нужно чётко понимать, из каких компонентов состоит игра. В «Сапере» присутствуют следующие элементы:

  • Игровое поле — двумерная сетка клеток, часть из которых содержит мины.
  • Мины — опасные клетки, при открытии которых игра заканчивается поражением.
  • Отметки флагами — пользователь может помечать клетки как подозрительные.
  • Числа на клетках — показывают количество мин на соседних клетках.
  • Счётчик оставшихся мин и таймер игры.

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

Моделирование игрового поля

Игровое поле можно представить в виде двумерного списка (матрицы), где каждый элемент — это объект или словарь с информацией о клетке. Основные атрибуты клетки могут быть следующими:

Атрибут Описание
is_mine Булево значение, указывающее наличие мины.
is_open Статус, открыта ли клетка.
is_flagged Помечена ли клетка флагом.
neighbor_mines Количество мин вокруг текущей клетки.

Такое представление упрощает процесс обновления состояния игры и отрисовку интерфейса.

Создание игрового окна и отрисовка элементов

Для начала необходимо инициализировать Pygame и создать окно с определённым размером, зависящим от размера поля и размера клетки. Например, если поле 10×10, а клетка 40×40 пикселей, то окно будет примерно 400×400 пикселей.

Далее нужно продумать, как рисовать клетки. Обычно клетки показываются в трёх состояниях:

  • Закрытая — оттенок серого цвета, либо текстура.
  • Открытая — светлая клетка с либо пустым пространством, либо цифрой количества соседних мин.
  • Помеченная — с изображением флага (можно нарисовать простым треугольником или другим символом).

Хорошей практикой является создание отдельной функции, которая получает параметры клетки и рисует её на заданных координатах. Это упрощает основной цикл отрисовки и улучшает читаемость кода.

Код инициализации и базовой отрисовки

Пример инициализации игрового окна и отрисовки базовой сетки:

import pygame
pygame.init()

CELL_SIZE = 40
GRID_SIZE = 10

screen = pygame.display.set_mode((CELL_SIZE * GRID_SIZE, CELL_SIZE * GRID_SIZE))
pygame.display.set_caption('Сапер')

def draw_cell(x, y, color):
    rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
    pygame.draw.rect(screen, color, rect)
    pygame.draw.rect(screen, (0, 0, 0), rect, 1)  # рамка

running = True
while running:
    screen.fill((192, 192, 192))  # фон
    for y in range(GRID_SIZE):
        for x in range(GRID_SIZE):
            draw_cell(x, y, (160, 160, 160))  # закрытые клетки

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

    pygame.display.flip()

pygame.quit()

Этот код создаёт окно и рисует серые квадраты как закрытые клетки. Дальше будем добавлять логику и отображение обновлённого состояния.

Реализация логики игры

Следующий важный этап — создание логики игры, включающей:

  1. Расстановку мин случайным образом.
  2. Вычисление количества мин вокруг каждой клетки.
  3. Обработку кликов игроком.
  4. Открытие клеток и автоматическое открытие соседних пустых клеток.
  5. Управление флагами.

Распределение мин

Распределять мины можно с помощью модуля random. Для этого создаём список всех координат клеток, перемешиваем и выбираем первые n, где n — количество мин. Затем отмечаем эти клетки как содержащие мины.

Подсчёт соседних мин

Для каждой клетки считаем количество мин в восьми соседних позициях (если они в пределах поля). Эта информация нужна для отображения цифр после открытия клетки.

Обработка кликов

В Pygame мышиные события можно обрабатывать через pygame.MOUSEBUTTONDOWN. Левой кнопкой обычно открываем клетку, правой — ставим или снимаем флаг. Важно корректно преобразовывать координаты клика в индексы клеток.

Открытие клеток и рекурсивное раскрытие пустых зон

Если игрок открывает клетку без мин и с нулём соседних мин, нужно автоматически открыть все соседние клетки, чтобы упростить игровой процесс. Для этого используется рекурсивный (или с использованием стека) алгоритм обхода соседей.

Работа с пользовательским интерфейсом

В дополнение к игровому полю стоит реализовать простой интерфейс, включающий:

  • Отображение количества оставшихся мин.
  • Таймер, показывающий время с момента запуска игры.
  • Кнопку перезапуска.

Для вывода текста используется модуль шрифтов Pygame — pygame.font. Таймер можно реализовать с помощью вычисления разницы времени между началом игры и текущим моментом.

Пример вывода текста

font = pygame.font.SysFont('Arial', 24)
text_surface = font.render('Мины: 10', True, (0, 0, 0))
screen.blit(text_surface, (10, 10))

Данная технология позволяет встраивать в основное окно различные вспомогательные элементы без дополнительной сложной графики.

Полный пример кода: упрощённый «Сапер»

Ниже приведён упрощённый отрывок кода, демонстрирующий основные части игры. Это не полный код, но основа для понимания работы.

import pygame
import random
import sys
from pygame.locals import *

pygame.init()

CELL_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 10
MINES_COUNT = 15

WIDTH = CELL_SIZE * GRID_WIDTH
HEIGHT = CELL_SIZE * GRID_HEIGHT + 40  # для панели статистики

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Сапер')
font = pygame.font.SysFont('Arial', 20)

class Cell:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.is_mine = False
        self.is_open = False
        self.is_flagged = False
        self.neighbor_mines = 0

def init_field():
    field = [[Cell(x, y) for x in range(GRID_WIDTH)] for y in range(GRID_HEIGHT)]

    # Расставляем мины
    coords = [(x, y) for x in range(GRID_WIDTH) for y in range(GRID_HEIGHT)]
    random.shuffle(coords)
    for i in range(MINES_COUNT):
        x, y = coords[i]
        field[y][x].is_mine = True

    # Считаем соседей
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH):
            if not field[y][x].is_mine:
                count = 0
                for ny in range(max(0, y-1), min(GRID_HEIGHT, y+2)):
                    for nx in range(max(0, x-1), min(GRID_WIDTH, x+2)):
                        if field[ny][nx].is_mine:
                            count +=1
                field[y][x].neighbor_mines = count
    return field

def draw_cell(cell):
    rect = pygame.Rect(cell.x * CELL_SIZE, cell.y * CELL_SIZE + 40, CELL_SIZE, CELL_SIZE)
    if cell.is_open:
        pygame.draw.rect(screen, (200, 200, 200), rect)
        if cell.is_mine:
            pygame.draw.circle(screen, (255, 0, 0), rect.center, CELL_SIZE//4)
        elif cell.neighbor_mines > 0:
            text = font.render(str(cell.neighbor_mines), True, (0, 0, 255))
            text_rect = text.get_rect(center=rect.center)
            screen.blit(text, text_rect)
    else:
        pygame.draw.rect(screen, (100, 100, 100), rect)
        if cell.is_flagged:
            pygame.draw.polygon(screen, (255, 0, 0), [
                (rect.left + CELL_SIZE * 0.3, rect.top + CELL_SIZE * 0.2),
                (rect.left + CELL_SIZE * 0.7, rect.top + CELL_SIZE * 0.5),
                (rect.left + CELL_SIZE * 0.3, rect.top + CELL_SIZE * 0.8)
            ])
    pygame.draw.rect(screen, (0, 0, 0), rect, 1)

def open_cell(field, x, y):
    cell = field[y][x]
    if cell.is_open or cell.is_flagged:
        return
    cell.is_open = True
    if cell.neighbor_mines == 0 and not cell.is_mine:
        for ny in range(max(0, y-1), min(GRID_HEIGHT, y+2)):
            for nx in range(max(0, x-1), min(GRID_WIDTH, x+2)):
                if not field[ny][nx].is_open:
                    open_cell(field, nx, ny)

def check_victory(field):
    for row in field:
        for cell in row:
            if not cell.is_mine and not cell.is_open:
                return False
    return True

def main():
    field = init_field()
    clock = pygame.time.Clock()
    start_ticks = pygame.time.get_ticks()
    game_over = False
    victory = False

    while True:
        screen.fill((220, 220, 220))
        
        # Отрисовка панели статистики
        elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
        mines_left = MINES_COUNT - sum(cell.is_flagged for row in field for cell in row)
        info_surf = font.render(f'Мины: {mines_left}   Время: {elapsed_seconds}s', True, (0,0,0))
        screen.blit(info_surf, (10, 10))

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if not game_over and not victory:
                if event.type == MOUSEBUTTONDOWN:
                    mx, my = event.pos
                    if my >= 40:
                        gx, gy = mx // CELL_SIZE, (my - 40) // CELL_SIZE
                        if event.button == 1:  # левая кнопка
                            if not field[gy][gx].is_flagged:
                                open_cell(field, gx, gy)
                                if field[gy][gx].is_mine:
                                    game_over = True
                        elif event.button == 3:  # правая кнопка
                            cell = field[gy][gx]
                            if not cell.is_open:
                                cell.is_flagged = not cell.is_flagged
        
        for row in field:
            for cell in row:
                draw_cell(cell)

        if not game_over and check_victory(field):
            victory = True

        if game_over:
            lose_text = font.render('Вы проиграли! Нажмите R для рестарта.', True, (255, 0, 0))
            screen.blit(lose_text, (10, HEIGHT // 2))
        if victory:
            win_text = font.render('Поздравляем! Вы выиграли! Нажмите R для рестарта.', True, (0, 128, 0))
            screen.blit(win_text, (10, HEIGHT // 2))

        keys = pygame.key.get_pressed()
        if keys[K_r]:
            field = init_field()
            game_over = False
            victory = False
            start_ticks = pygame.time.get_ticks()

        pygame.display.flip()
        clock.tick(30)

if __name__ == '__main__':
    main()

Данный код показывает базовую работу с игровым полем, отрисовку клеток, открытие, постановку флагов и проверку победы/поражения.

Заключение

Создание игры «Сапер» на Python с использованием Pygame является отличным упражнением для начинающих и продвинутых разработчиков, позволяющим практиковаться в работе с графикой, событиями и алгоритмами. Мы рассмотрели основные этапы разработки: подготовку среды, построение структуры данных, логику игры, отрисовку и взаимодействие с пользователем.

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

Освоив эти базовые навыки, вы сможете создавать собственные интересные проекты и углублять знания в программировании игр.



«`html

создание игры сапер python pygame для начинающих как сделать сапер на python урок по pygame сапер инструкция создание игры сапер
пример кода игры сапер python pygame minesweeper tutorial создать игру с интерфейсом python pygame логика игры сапер python разработка игры на python с pygame

«`