Хотите создать свою первую игру, но не знаете, с чего начать? «Сапёр» — идеальный проект для этого! В нём есть простая, но интересная логика, работа с пользовательским вводом и графика, что делает его отличной стартовой точкой для любого начинающего разработчика.

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

Вот наш план:

  1. Настроим рабочее окружение: создадим отдельное пространство для нашего проекта.

  2. Создадим «мозг» игры: напишем код, который будет отвечать за мины и цифры на поле.

  3. Нарисуем игровое поле: выведем нашу игру на экран.

  4. Научим игру реагировать: добавим обработку кликов мыши.

  5. Добавим условия победы и поражения: чтобы в игру было интересно играть!

В конце у вас будет готовый проект, которым можно поделиться с друзьями, и четкое понимание, как устроены простые 2D-игры.

Ну что, готовы? Начнем

Шаг 0. Подготовка рабочего места (самый важный шаг!)

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

Что это такое простыми словами? Представьте, что для каждого своего проекта вы создаете отдельную, изолированную "коробку". В эту коробку вы складываете только те инструменты (библиотеки), которые нужны именно для этого проекта. Это защищает ваши проекты от конфликтов и считается золотым стандартом в Python-разработке.

Давайте создадим такую "коробку" для нашего «Сапёра».

1. Создаем папку проекта

Сначала нам нужна папка, где будут лежать все файлы нашей игры. Для этого откройте терминал (или командную строку в Windows). Это программа, где мы можем давать компьютеру команды текстом.

Введите следующие команды по очереди, нажимая Enter после каждой:

# Создаем папку с именем minesweeper
mkdir minesweeper

# Заходим внутрь этой папки
cd minesweeper

Теперь все дальнейшие действия мы будем выполнять внутри папки minesweeper.

2. Создаем виртуальное окружение

Находясь в папке minesweeper, введите в терминал следующую команду:

python -m venv venv

Эта команда создаст внутри minesweeper новую папку с именем venv. В ней будет храниться "чистая" копия Python и все библиотеки, которые мы установим для нашей игры.

3. Активируем окружение

Мы создали "коробку", а теперь её нужно "открыть" или активировать. Команды для этого немного отличаются в зависимости от вашей операционной системы.

  • Если у вас Windows:

    venv\Scripts\activate
    
  • Если у вас macOS или Linux:

    source venv/bin/activate
    

Если все прошло успешно, вы увидите, что в начале строки терминала появилось (venv). Это наш сигнал — виртуальное окружение активно!

# Пример того, как это будет выглядеть:
(venv) C:\Users\YourName\minesweeper>

4. Устанавливаем Pygame

Теперь, когда наша "коробка" открыта, мы можем положить в неё наш главный инструмент — библиотеку Pygame. pip — это стандартный менеджер пакетов Python, который скачает и установит её для нас.

Выполните в терминале одну простую команду:

pip install pygame

pip установит Pygame именно в наше виртуальное окружение, не затрагивая основную систему.

Шаг 1. "Мозг" игры — создаем логику поля

Прежде чем мы начнем что-либо рисовать, нам нужно создать правила и внутреннее устройство нашей игры. Эту часть часто называют логикой или моделью. Представьте, что мы создаем «Сапёра», в которого можно было бы играть с закрытыми глазами, просто называя координаты.

Наш план для этого шага:

  1. Создать "кирпичик" — объект для одной ячейки поля.

  2. Собрать из этих "кирпичиков" целое игровое поле.

  3. Научить поле расставлять мины.

  4. Научить поле считать цифры вокруг мин.

Давайте начнем! Создайте в папке minesweeper новый файл с именем main.py и пишите весь код из этого шага туда.

1.1. "Кирпичик" для нашего поля: класс Cell

Каждый квадратик на поле «Сапёра» должен что-то о себе "знать": есть ли в нем мина, открыт ли он и так далее. Чтобы удобно хранить эту информацию, мы создадим для ячейки собственный класс-шаблон.

Добавьте этот код в ваш файл main.py:

class Cell:
    def __init__(self):
        self.is_mine = False
        self.is_open = False
        self.is_flagged = False
        self.adjacent_mines = 0

Этот простой класс — наш "строительный блок". Каждый раз, когда нам понадобится новая ячейка на поле, мы будем создавать её по этому шаблону.

1.2. Собираем поле в одно целое: класс GameBoard

Теперь, когда у нас есть "кирпичик", давайте построим из них стену — наше игровое поле. Для этого создадим еще один класс, GameBoard, который будет управлять всеми ячейками.

Добавьте этот код в main.py под классом Cell:

import random

# Класс Cell, который мы написали выше, должен быть здесь...

class GameBoard:
    def __init__(self, width, height, mines_count):
        self.width = width
        self.height = height
        self.mines_count = mines_count
        
        # Создаем сетку (список списков) и заполняем её нашими "кирпичиками"
        self.board = [[Cell() for _ in range(width)] for _ in range(height)]

Здесь мы создаем сетку (двумерный список), заполненную объектами Cell. Теперь у нас есть структура поля, но оно пока пустое. Давайте это исправим.

1.3. Расставляем мины

Нам нужно случайным образом разместить на поле заданное количество мин. Для этого добавим новый метод (функцию) _place_mines внутрь нашего класса GameBoard.

    def _place_mines(self):
        # Создаем список всех возможных координат (строка, столбец)
        all_coords = [(r, c) for r in range(self.height) for c in range(self.width)]
        
        # Выбираем из списка случайные уникальные координаты для мин
        mine_coords = random.sample(all_coords, self.mines_count)
        
        # В ячейках по этим координатам ставим мины
        for r, c in mine_coords:
            self.board[r][c].is_mine = True

Мы использовали random.sample — это удобный способ выбрать несколько случайных элементов из списка, при этом он гарантирует, что все они будут уникальными. Так мы избежим ситуации, когда две мины попали в одну и ту же ячейку.

1.4. Считаем цифры вокруг мин

Это самая важная часть логики. Нам нужно пройти по каждой ячейке поля. Если в ней нет мины, мы должны посчитать, сколько мин находится в 8 соседних ячейках.

Добавьте следующий метод _calculate_adjacent_mines в класс GameBoard:

    def _calculate_adjacent_mines(self):
        # Проходим по каждой ячейке поля
        for r in range(self.height):
            for c in range(self.width):
                # Если в ячейке уже есть мина, считать ничего не нужно
                if self.board[r][c].is_mine:
                    continue
                
                mine_count = 0
                # Проверяем всех 8 соседей
                for dr in [-1, 0, 1]:      # Смещение по строке
                    for dc in [-1, 0, 1]:  # Смещение по столбцу
                        # Пропускаем саму текущую ячейку
                        if dr == 0 and dc == 0:
                            continue
                        
                        # Вычисляем координаты соседа
                        nr, nc = r + dr, c + dc
                        
                        # ВАЖНО: Проверяем, что сосед не вышел за границы поля
                        if 0 <= nr < self.height and 0 <= nc < self.width:
                            if self.board[nr][nc].is_mine:
                                mine_count += 1
                
                # Записываем результат в ячейку
                self.board[r][c].adjacent_mines = mine_count

И последнее — давайте сделаем так, чтобы мины и цифры расставлялись автоматически сразу при создании поля. Для этого просто вызовем наши новые методы в __init__.

Обновите метод __init__ в классе GameBoard, добавив в конец две новые строчки:

    def __init__(self, width, height, mines_count):
        self.width = width
        self.height = height
        self.mines_count = mines_count
        
        self.board = [[Cell() for _ in range(width)] for _ in range(height)]
        
        # Добавляем эти две строки:
        self._place_mines()
        self._calculate_adjacent_mines()

Шаг 2. Первое окно — рисуем наше игровое поле

Итак, "мозг" нашей игры готов, но пока он работает невидимо. Пора дать ему "тело" — графическое отображение! В этом шаге мы создадим окно игры и нарисуем в нём сетку, которая представляет наше игровое поле.

Наш план:

  1. Написать базовый код для запуска окна Pygame.

  2. Задать основные параметры: размеры ячеек, цвета.

  3. Создать функцию, которая будет рисовать сетку на основе данных из нашего GameBoard.

  4. Собрать всё вместе в главный игровой цикл.

Продолжаем работать в нашем файле main.py.

2.1. Константы и базовый запуск Pygame

Хорошая практика в программировании — выносить значения, которые могут часто меняться (вроде цветов или размеров), в отдельные переменные в начале файла. Их называют константами (и часто пишут ЗАГЛАВНЫМИ_БУКВАМИ).

Добавьте этот код в самый низ вашего файла main.py, после всех классов.

import pygame

# --- Константы ---
# Размеры поля в ячейках
BOARD_WIDTH = 20
BOARD_HEIGHT = 15
MINES_COUNT = 30

# Размер одной ячейки в пикселях
CELL_SIZE = 30

# Рассчитываем размер окна в пикселях
SCREEN_WIDTH = BOARD_WIDTH * CELL_SIZE
SCREEN_HEIGHT = BOARD_HEIGHT * CELL_SIZE

# Цвета (в формате RGB)
BG_COLOR = (192, 192, 192) # Серый
LINE_COLOR = (128, 128, 128) # Темно-серый

# --- Инициализация Pygame и создание окна ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Сапёр")

# --- Создание игрового поля ---
game_board = GameBoard(BOARD_WIDTH, BOARD_HEIGHT, MINES_COUNT)

Что мы здесь сделали:

  • Импортировали pygame.

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

  • Инициализировали Pygame и создали окно нужного размера.

  • Создали один экземпляр нашего "мозга" — game_board. Именно его мы и будем рисовать.

2.2. Функция для отрисовки поля

Давайте создадим отдельную функцию, которая будет отвечать только за рисование. Это делает код более чистым и организованным.

Добавьте эту функцию в main.py после блока с константами:

def draw_board(board_obj):
    # Заливаем весь экран фоновым цветом
    screen.fill(BG_COLOR)
    
    # Проходим по каждой ячейке
    for r in range(board_obj.height):
        for c in range(board_obj.width):
            # Рассчитываем координаты ячейки на экране (в пикселях)
            x = c * CELL_SIZE
            y = r * CELL_SIZE
            
            # Создаем прямоугольник для ячейки
            rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)
            
            # Рисуем контур ячейки
            pygame.draw.rect(screen, LINE_COLOR, rect, 1)

Эта функция:

  1. Заливает экран серым цветом, чтобы стереть предыдущий кадр.

  2. В двойном цикле проходит по каждой ячейке.

  3. Вычисляет, где на экране (в пикселях) нужно нарисовать текущую ячейку.

  4. Рисует для каждой ячейки прямоугольник с черным контуром толщиной в 1 пиксель.

2.3. Главный игровой цикл

Любая игра работает внутри бесконечного цикла. На каждом витке этого цикла игра:

  1. Проверяет действия игрока (нажал ли он кнопку, закрыл ли окно).

  2. Обновляет логику игры.

  3. Перерисовывает экран.

Этот цикл — сердце нашей программы. Добавьте его в самый конец файла main.py:

# --- Главный игровой цикл ---
running = True
while running:
    # 1. Обработка событий
    for event in pygame.event.get():
        # Если пользователь нажал на "крестик"
        if event.type == pygame.QUIT:
            running = False

    # 2. Отрисовка
    draw_board(game_board)

    # 3. Обновление экрана
    pygame.display.flip()

# Корректное завершение работы
pygame.quit()

Теперь, если вы запустите ваш файл main.py из терминала (python main.py), вы должны увидеть окно с серой сеткой!

(venv) ...\minesweeper> python main.py

Шаг 3. Оживляем игру — учим ее слушать мышку

Игра без управления — не игра. Сейчас наше окно просто показывает статичную картинку. Давайте научим нашу программу "слышать" клики мыши и понимать, по какой именно ячейке кликнул игрок.

Наш план на этот шаг:

  1. Найти в нашем игровом цикле место, где отлавливаются все действия игрока.

  2. Добавить код для отслеживания именно кликов мыши.

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

  4. Проверить, что всё работает, выводя результат в терминал.

Мы будем вносить изменения в главный игровой цикл, который находится в самом конце файла main.py.

3.1. Ловим клики мыши

Наш игровой цикл while running: уже содержит цикл for event in pygame.event.get():. Этот цикл — "уши" нашей программы. Он ловит все события: движение мыши, нажатие клавиш, закрытие окна и, конечно, клики.

Давайте добавим проверку на событие клика мыши.

Найдите свой игровой цикл и добавьте в него блок if, как показано ниже:

# --- Главный игровой цикл ---
running = True
while running:
    # 1. Обработка событий
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        # ДОБАВЛЯЕМ ЭТОТ БЛОК:
        # Если произошло событие "кнопка мыши нажата"
        if event.type == pygame.MOUSEBUTTONDOWN:
            # Тут будет наша логика
            print("Клик!")

    # 2. Отрисовка
    # ... (код отрисовки остается без изменений) ...

Если вы сейчас запустите игру и покликаете по окну, вы увидите, что в терминале, из которого вы запускали скрипт, при каждом клике появляется сообщение "Клик!". Отлично, мы научились их ловить!

3.2. Превращаем пиксели в ячейки

Теперь самое интересное. Pygame сообщает нам, где был клик, в пикселях (например, "клик был в точке X=152, Y=95"). Но нашей игре нужны координаты ячейки (например, "клик был в ячейке: строка 3, столбец 5").

Как это посчитать? Очень просто! Нужно разделить координату в пикселях на размер одной ячейки.

Например, если размер ячейки CELL_SIZE у нас 30 пикселей:

  • Клик в X=70 пикселей. 70 // 30 = 2. Значит, это 3-й столбец (считая с нуля: 0, 1, 2).

  • Клик в Y=95 пикселей. 95 // 30 = 3. Значит, это 4-я строка (считая с нуля: 0, 1, 2, 3).

Мы используем целочисленное деление (//), которое отбрасывает остаток — это именно то, что нам нужно.

Давайте напишем это в коде. Обновите ваш блок обработки клика:

        if event.type == pygame.MOUSEBUTTONDOWN:
            # Получаем координаты клика в пикселях
            mouse_x, mouse_y = pygame.mouse.get_pos()
            
            # Превращаем пиксели в координаты ячейки
            clicked_col = mouse_x // CELL_SIZE
            clicked_row = mouse_y // CELL_SIZE
            
            # Выводим результат в терминал для проверки
            print(f"Клик по ячейке: строка {clicked_row}, столбец {clicked_col}")

3.3. Собираем всё вместе и проверяем

Вот как теперь должен выглядеть ваш полный игровой цикл:

# --- Главный игровой цикл ---
running = True
while running:
    # 1. Обработка событий
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            
            clicked_col = mouse_x // CELL_SIZE
            clicked_row = mouse_y // CELL_SIZE
            
            print(f"Клик по ячейке: строка {clicked_row}, столбец {clicked_col}")

    # 2. Отрисовка
    draw_board(game_board)

    # 3. Обновление экрана
    pygame.display.flip()

# Корректное завершение работы
pygame.quit()

Теперь снова запустите игру (python main.py). Кликайте по разным ячейкам на поле. Вы должны видеть в терминале, как программа безошибочно определяет, на какую строку и столбец вы нажали.

Шаг 4. Собираем всё вместе — логика в действии!

Сейчас у нас есть "мозг" игры (GameBoard) и "уши", которые слышат клики мыши. Пришло время соединить их! Мы будем использовать координаты клика, чтобы вызывать методы нашего "мозга", а затем обновим отрисовку, чтобы показать результат этих действий на экране.

План на этот заключительный шаг:

  1. Дописать в класс GameBoard методы для открытия ячеек и установки флагов.

  2. Вызывать эти методы из главного игрового цикла в ответ на клики.

  3. Полностью переделать функцию draw_board, чтобы она отображала мины, цифры и флаги.

  4. Добавить логику победы и поражения.

4.1. Дописываем логику в GameBoard

Возвращаемся к нашему классу GameBoard. Нам нужно добавить ему два новых "умения": открывать ячейку и ставить/убирать флажок.

Добавьте эти два метода внутрь класса GameBoard:

    def open_cell(self, r, c):
        # Получаем ячейку, по которой кликнули
        cell = self.board[r][c]

        # Нельзя открыть уже открытую ячейку или ячейку с флагом
        if cell.is_open or cell.is_flagged:
            return

        cell.is_open = True

        # Магия "Сапёра": если ячейка пустая, открываем соседей
        if cell.adjacent_mines == 0 and not cell.is_mine:
            # Проходим по всем 8 соседям
            for dr in [-1, 0, 1]:
                for dc in [-1, 0, 1]:
                    if dr == 0 and dc == 0:
                        continue
                    
                    nr, nc = r + dr, c + dc
                    
                    # Убеждаемся, что сосед в пределах поля
                    if 0 <= nr < self.height and 0 <= nc < self.width:
                        # И рекурсивно вызываем эту же функцию для соседа!
                        self.open_cell(nr, nc)

    def toggle_flag(self, r, c):
        cell = self.board[r][c]
        # Флаг можно ставить только на закрытые ячейки
        if not cell.is_open:
            cell.is_flagged = not cell.is_flagged

Ключевой момент здесь — рекурсия в методе open_cell. Если игрок кликает по пустой ячейке (где 0 мин вокруг), функция вызывает саму себя для всех восьми соседей. Если кто-то из соседей тоже окажется пустым, он, в свою очередь, вызовет эту функцию для своих соседей. Так и получается "цепная реакция", открывающая целую область.

4.2. Обновляем главный игровой цикл

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

  • event.button == 1 — это левая кнопка (открыть ячейку).

  • event.button == 3 — это правая кнопка (поставить флаг).

Обновите блок обработки событий в вашем цикле:

        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            clicked_col = mouse_x // CELL_SIZE
            clicked_row = mouse_y // CELL_SIZE

            # Если левая кнопка мыши
            if event.button == 1:
                game_board.open_cell(clicked_row, clicked_col)
            
            # Если правая кнопка мыши
            elif event.button == 3:
                game_board.toggle_flag(clicked_row, clicked_col)

4.3. Большая переделка draw_board

Наша текущая функция draw_board рисует только серую сетку. Пора заставить ее показывать всё: открытые ячейки, цифры, флаги и, конечно, мины! Это потребует самой большой модификации кода.

Сначала нам нужен шрифт для рисования цифр. Добавьте его создание после инициализации Pygame.

# --- Инициализация Pygame и создание окна ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Сапёр")
# Добавляем создание шрифта
font = pygame.font.SysFont('Arial', CELL_SIZE // 2)

Теперь полностью замените вашу старую функцию draw_board на эту, новую версию:

def draw_board(board_obj):
    screen.fill(BG_COLOR)
    
    for r in range(board_obj.height):
        for c in range(board_obj.width):
            cell = board_obj.board[r][c]
            x = c * CELL_SIZE
            y = r * CELL_SIZE
            rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)

            # Рисуем контур ячейки
            pygame.draw.rect(screen, LINE_COLOR, rect, 1)

            # Если ячейка открыта
            if cell.is_open:
                if cell.is_mine:
                    # Рисуем мину (красный круг)
                    pygame.draw.circle(screen, (255, 0, 0), rect.center, CELL_SIZE // 3)
                elif cell.adjacent_mines > 0:
                    # Рисуем цифру
                    text = font.render(str(cell.adjacent_mines), True, (0, 0, 0))
                    text_rect = text.get_rect(center=rect.center)
                    screen.blit(text, text_rect)
            # Если стоит флаг
            elif cell.is_flagged:
                # Рисуем флаг (желтый треугольник)
                pygame.draw.polygon(screen, (255, 255, 0), 
                                    [(rect.left + 5, rect.top + 5), 
                                     (rect.right - 5, rect.centery), 
                                     (rect.left + 5, rect.bottom - 5)])

4.4. Добавляем финал: победа и поражение

Игра должна заканчиваться! Давайте добавим game_over флаг в наш GameBoard.

В метод __init__ класса GameBoard добавьте self.game_over = False.
В методе open_cell найдите место, где ячейка оказывается миной, и измените его:

        cell.is_open = True
        
        # Если это была мина - игра окончена
        if cell.is_mine:
            self.game_over = True
            return # Сразу выходим

Теперь в главном цикле мы должны перестать обрабатывать клики, если игра окончена.

        if event.type == pygame.MOUSEBUTTONDOWN and not game_board.game_over:
            # ... остальная логика клика ...

И последнее: давайте покажем сообщение о проигрыше!

# В главном цикле, после отрисовки
    draw_board(game_board)

    # Если игра окончена, показываем сообщение
    if game_board.game_over:
        # Полупрозрачный черный фон
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 128))
        screen.blit(overlay, (0, 0))
        
        # Текст
        text = font.render("Вы проиграли!", True, (255, 255, 255))
        text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
        screen.blit(text, text_rect)

Поздравляю!

Вы сделали это! Запустите скрипт python main.py и наслаждайтесь своей собственной, полностью рабочей версией «Сапёра». Вы прошли весь путь от настройки окружения до реализации сложной игровой логики и её визуализации.

Анонс новых статей, полезные материалы, а так же если в процессе написания кода возникнут сложности, обсудить их или задать вопрос по статье можно в моём Telegram-сообществе.

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

Комментарии (12)


  1. alex0008
    04.10.2025 11:39

    Автор, ждал разбора момента и не увидел как генератор минного поля учитывает, что мины могут создать кольцо, в которое невозможно будет попасть? Простая рандомная расстановка может привести к такому


    1. enamored_poc Автор
      04.10.2025 11:39

      В статье этот момент был опущен сознательно по одной причине: сохранение низкого порога входа для начинающих.

      Целью руководства было научить основам:

      • Работе с классами (ООП).

      • Базовой логике 2D-массивов.

      • Основам Pygame (игровой цикл, отрисовка, обработка событий).

      • Реализации простого рекурсивного алгоритма.

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


      1. Xiran
        04.10.2025 11:39

        Фу нейронка


      1. alex0008
        04.10.2025 11:39

        а это было бы интересно разобрать - как узнать что область не замкнута и в неё есть доступ? Когда это лучше делать, при постановке новой мины проверять не замкнёт ли она область, или пытаться сперва расставить а потом проверить и, допустим, перенести одну из них, чтобы разомкнуть контур. А ведь это коснётся и задачи и на поиск пути - такие алгоритмы используются во множестве игр.
        Понимаю, что статья для начинающих - но чуть-чуть материала на развитие тоже будет интересно


    1. HemulGM
      04.10.2025 11:39

      Что это за ситуация? И почему это вообще является проблемой?


      1. alex0008
        04.10.2025 11:39

        Если мины образуют кольцо вокруг ячейки без мины, скажем слева (мина это X, а 5 ячейка без неё) то разгадывая поле справа туда никак не попадёшь:
        XX
        5X
        XX


        1. HemulGM
          04.10.2025 11:39

          Верхний правый Х и нижний правый Х легко помогают определить, где нет мины

          221
          XX2
          5X3
          XX2
          221

          А если будет полный круг, то можно методом исключения


          1. alex0008
            04.10.2025 11:39

            Я имею ввиду, что начиная, например, справа сверху как мы узнаем, есть в центре мина или нет? Мы не можем вычислить это никак - нет просвета и информация из чисел вокруг не помогает тут


            1. HemulGM
              04.10.2025 11:39

              Это не страшно. Оставляем на будущее (под вопрос) и дальше идём. В конце будет понятно

              Проблема может возникнуть когда уже две будет такие ситуации, а не найденная мина одна


        1. HemulGM
          04.10.2025 11:39

          Кстати, можно попробовать в моем исполнении поиграть. Классическая игра, без рекламы и с разными размерами полей и сложностью.

          https://play.google.com/store/apps/details?id=com.embarcadero.MineSweeperFMX

          Написано на Delphi


    1. nvv1970
      04.10.2025 11:39

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

      При чем гпт забавно объяснял. Спрашиваю, про угадывание ли речь? Нет, говорит, ты "не можешь пройти" через мины)


      1. alex0008
        04.10.2025 11:39

        А всё решается просто, вместо случайной расстановки мин делать связную область без мин и генерацию начинать с первого клика игрока(иначе он может подорваться с первого же хода) - именно так сделан сапёр в винде

        А вот как - тут и стоит подумать, в том и интерес)