Данный код реализует простую игру в лабиринты с использованием библиотеки Pygame. Код генерирует случайные лабиринты и позволяет игроку перемещаться по ним с помощью стрелок на клавиатуре. Цель игры — достичь зеленой конечной точки на каждом уровне, сложность лабиринта увеличивается по мере продвижения игрока по уровням.
-
Импорты:
- Модуль
enum
используется для создания класса перечислений. - Модуль
random
используется для генерации случайных чисел и выборов. - Модуль
typing
используется для подсказок типов. - Библиотека
pygame
используется для создания графики.
- Модуль
-
Константы:
width
иheight
: Размеры игрового окна.cell_size
: Размер каждой ячейки в лабиринте.- Кортежи RGB, представляющие используемые цвета в игре.
-
Перечисление - Direction (Направление):
- Класс перечислений
Direction
определяет возможные направления движения (вверх, вниз, влево, вправо).
- Класс перечислений
-
Класс - Player (Игрок):
- Представляет позицию игрока в лабиринте.
- Конструктор инициализирует позицию игрока.
- Метод
move()
обновляет позицию игрока на основе указанного направления, если новая позиция допустима в лабиринте. - Метод
draw()
отображает персонажа игрока на экране. - Метод
reset_player()
позволяет сбросить позицию игрока на заданные координаты.
-
Класс - Maze (Лабиринт):
- Представляет структуру лабиринта и логику его генерации.
- Конструктор инициализирует размеры лабиринта, структуру лабиринта и координаты конечной точки.
- Метод
initialize_maze()
инициализирует начальное состояния лабиринта. - Метод
generate()
генерирует лабиринт со стенами и путями на основе плотности и текущего уровня. - Метод
reset_maze()
сбрасывает состояние лабиринта к начальному перед генерацией нового уровня. - Метод
carve_paths()
рекурсивно прокладывает пути в лабиринте. - Метод
draw()
отображает лабиринт на экране.
-
Главная Функция:
- Инициализирует Pygame и настраивает игровое окно.
- Определяет уровни сложности и текущий уровень.
- Создает экземпляр класса
Maze
и генерирует начальный лабиринт. - Создает экземпляр класса
Player
. - Создает объект
reset_button_rect
для кнопки Reset. - Игровой цикл выполняется, пока игрок не достигнет конечной точки или не закроет окно игры.
- Цикл обрабатывает пользовательский ввод, обновляет состояние игры и отображает лабиринт и игрока на экране.
- Когда игрок достигает конца уровня, игра переходит к следующему уровню или завершается, если все уровни пройдены.
- При нажатии кнопки Reset лабиринт и игрок отрисовываются заново, сохраняя текущий уровень.
- Игровой цикл завершается, когда игрок завершает все уровни или закрывает окно игры.
- Очищает ресурсы Pygame перед завершением.
-
Исполнение:
- Код выполняется только при прямом запуске скрипта (не при импорте как модуля).
- Для запуска игры вызывается функция
main()
.
Соответствует PEP8 и принципу KISS.
Метод
__init__()
выполняет инициализацию объекта игрока при его создании. Он определяет начальные координаты игрока в лабиринте.
-
Параметры метода:
x
(int): Начальная координата x игрока.y
(int): Начальная координата y игрока.
-
Инициализация параметров:
- Метод начинает с сохранения переданных параметров
x
иy
как атрибуты класса. self.x
иself.y
инициализируются значениями, указанными в аргументах.
- Метод начинает с сохранения переданных параметров
-
Итог:
- Метод
__init__()
при создании объекта классаPlayer
инициализирует начальные координаты игрока в лабиринте. Эти координаты будут использоваться для отслеживания позиции игрока во время игры.
- Метод
Метод
move()
в классеPlayer
отвечает за перемещение игрока в лабиринте в указанном направлении.
-
Параметры метода:
direction
(Direction): Объект перечисленияDirection
, представляющий выбранное направление (UP, DOWN, LEFT, RIGHT).maze
(List[List[int]]): Двумерный список, представляющий структуру лабиринта.
-
Определение смещения:
- Метод получает смещение
(dx, dy)
из объекта направленияdirection
с помощью выраженияdirection.value
.
- Метод получает смещение
-
Проверка допустимости перемещения:
- Метод проверяет, что новые координаты после перемещения не выходят за границы лабиринта.
Для этого он проверяет, что
self.x + dx
находится в допустимом диапазоне от 0 до ширины лабиринта минус 1 (maze_width - 1
), и аналогично дляself.y + dy
и высоты лабиринта.
- Метод проверяет, что новые координаты после перемещения не выходят за границы лабиринта.
Для этого он проверяет, что
-
Проверка проходимости клетки:
- Далее метод проверяет, что клетка, в которую игрок пытается переместиться, является проходимой.
Он использует значение в матрице лабиринта
maze[self.y + dy][self.x + dx]
. Если значение равно 0, это означает, что клетка проходима.
- Далее метод проверяет, что клетка, в которую игрок пытается переместиться, является проходимой.
Он использует значение в матрице лабиринта
-
Обновление координат игрока:
- Если обе проверки проходят успешно, то метод обновляет координаты игрока
self.x
иself.y
, добавляя к текущим координатам значения смещения(dx, dy)
. Это фактически перемещает игрока в указанном направлении.
- Если обе проверки проходят успешно, то метод обновляет координаты игрока
-
Итог:
- Метод
move()
в классеPlayer
позволяет игроку перемещаться внутри лабиринта в зависимости от выбранного направления. Он проверяет допустимость перемещения и проходимость клетки перед обновлением координат игрока. Это важная часть игрового процесса, которая обеспечивает взаимодействие игрока с окружающим миром.
- Метод
Метод
draw()
в классеPlayer
отвечает за отображение игрока на экране с использованием библиотеки Pygame.
-
Параметры метода:
screen
(Any): Экран Pygame, на котором будет отображаться игрок.
-
Отображение игрока:
- Метод использует функцию
pygame.draw.rect()
для отрисовки прямоугольника, который будет представлять игрока на экране. - Аргументы функции
pygame.draw.rect()
задают следующие параметры:screen
: Экран, на котором будет производиться отрисовка.red
: Цвет прямоугольника (красный) в формате RGB.(self.x * cell_size, self.y * cell_size)
: Координаты верхнего левого угла прямоугольника, рассчитанные на основе текущих координат игрокаself.x
иself.y
, а также размера клеткиcell_size
.cell_size x cell_size
: Размеры прямоугольника в пикселях, соответствующие размеру одной клетки.
- Метод использует функцию
-
Итог:
- Метод
draw()
в классеPlayer
выполняет отрисовку игрока на экране. Он использует функциюpygame.draw.rect()
для создания красного прямоугольника, который представляет игрока в текущей позиции в лабиринте. Это позволяет визуально отображать позицию игрока и его перемещение по лабиринту во время игры.
- Метод
Метод
reset_player()
в классеPlayer
позволяет сбросить позицию игрока на заданные координаты.
-
Параметры метода:
x
(int): Новая координата X, на которую будет сброшена позиция игрока.y
(int): Новая координата Y, на которую будет сброшена позиция игрока.
-
Сброс позиции игрока:
- Метод принимает два аргумента,
x
иy
, они представляют новые координаты, на которые нужно переместить игрока. - Внутри метода происходит присвоение переданных значений
x
иy
атрибутамself.x
иself.y
соответственно. - Это обновление значений координат
x
иy
на новые значения, что изменяет текущую позицию игрока на экране.
- Метод принимает два аргумента,
-
Использование метода:
- Обычно этот метод вызывается после события "сброс" (например, нажатие кнопки "Reset") для перемещения игрока на заданные начальные координаты. Это может быть полезно, чтобы вернуть игрока в исходное положение при начале нового уровня или после сброса состояния игры.
-
Итог:
- Метод
reset_player()
классаPlayer
позволяет изменить текущую позицию игрока на новые координатыx
иy
. Этот метод полезен для переноса игрока на определенные позиции, например, в начальное положение после сброса игры или начала нового уровня.
- Метод
Метод
__init__()
выполняет инициализацию объектов этого класса при их создании. В данном методе определяются начальные параметры и состояния объектаMaze
.
-
Параметры метода:
maze_width
(int): Ширина лабиринта в клетках.maze_height
(int): Высота лабиринта в клетках.
-
Инициализация параметров:
- Метод начинает с сохранения переданных параметров
maze_width
иmaze_height
в атрибуты класса для дальнейшего использования. self.width
иself.height
инициализируются значениями, указанными в аргументах.
- Метод начинает с сохранения переданных параметров
-
Инициализация структуры лабиринта:
- Создается двумерный список
self.maze
, который представляет матрицу лабиринта. Изначально он будет заполнен значениями 1, представляющими стены.
- Создается двумерный список
-
Инициализация конечной точки:
- Инициализируются атрибуты
self.end_x
иself.end_y
нулевыми значениями. Они будут использованы для хранения координат конечной точки лабиринта.
- Инициализируются атрибуты
-
Итог:
- Метод
__init__()
при создании объекта классаMaze
устанавливает начальные значения атрибутов, которые будут использоваться при генерации и отображении лабиринта. Это обеспечивает базовую конфигурацию объекта класса перед его дальнейшим использованием.
- Метод
Метод
initialize_maze()
используется для инициализации начального состояния лабиринта, создания пустой сетки с определенными размерами.
-
Логика инициализации:
- Внутри метода создается двумерный список
maze
размеромself.height
наself.width
, который представляет сетку лабиринта. - Каждый элемент списка
maze
инициализируется значением1
, что означает стену в лабиринте.
- Внутри метода создается двумерный список
-
Использование метода:
- Этот метод может быть вызван при начале игры или перед генерацией лабиринта. Он создает пустую сетку, которая будет использоваться для генерации структуры лабиринта.
-
Итог:
- Метод
initialize_maze()
классаMaze
создает начальное состояние лабиринта в виде двумерного списка с определенными размерами. Этот метод создает пустую сетку, которая будет использоваться в дальнейшем для генерации и отображения лабиринта.
- Метод
Метод
generate()
в классеMaze
отвечает за генерацию лабиринта с использованием случайных стен и путей. Он создает сложные структуры лабиринта, которые игрок должен будет пройти для достижения конечной точки.
Используется комбинация алгоритмов, в основном случайных перемещений и рекурсивного поиска в глубину, для создания лабиринта.
- Depth-First Search
- Поиск с возвратом (Backtracking)
- Maze generation algorithm
- Алгоритм Краскала
- Алгоритм Прима
-
initialize_maze()
: Заполняет всю сетку лабиринта стенами (значение 1). -
Цикл по нечетным индексам (как по x, так и по y) для создания начальных путей:
- Этот шаг инициализирует каждую вторую ячейку лабиринта как открытый путь (значение 0). Таким образом, создается сетка стен с сеткой путей между ними.
-
Корректировка плотности:
- Основываясь на
density
иcurrent_level
, добавляются пути между начальными путями. Цель здесь — создать более сложный лабиринт, добавляя пути в определенных направлениях.
- Основываясь на
-
Добавление корректированной плотности случайных путей:
- Для каждой ячейки с нечетными индексами добавляется ряд случайных путей (в зависимости от
adjusted_density
) к соседним ячейкам. - Выбранные направления могут быть вверх, вниз, влево или вправо.
- Эти пути создаются путем установки значений соседних ячеек в пути (0), но условные проверки гарантируют, что эти пути не пересекают существующие пути и не выходят за границы лабиринта.
- Для каждой ячейки с нечетными индексами добавляется ряд случайных путей (в зависимости от
-
Размещение выхода из лабиринта:
- Выбирается случайная ячейка с нечетными индексами, которая становится выходом из лабиринта. Эйтет ячейке присваивается значение 0 для обозначения открытого пути.
-
carve_paths(x, y)
: Этот метод, вероятно, представляет собой рекурсивный алгоритм "backtracking" (обратного отслеживания) для создания путей через лабиринт.- Для каждой ячейки выбирается случайное направление (вверх, вниз, влево или вправо).
- Если перемещение в этом направлении возможно без нарушения границ лабиринта и без создания нового пути поверх существующего, ячейка между текущей ячейкой и целевой ячейкой устанавливается как путь (0), и рекурсия продолжается из целевой ячейки.
В целом данное решение объединяет начальное создание путей с нечетными индексами с процессом случайной генерации путей,
который избегает пересечения с существующими путями. Рекурсивный метод "backtracking" (carve_paths
) добавляет
дополнительную сложность и связность к лабиринту, обеспечивая наличие одного пути от начала до выхода.
-
Параметры метода:
density
(int): Плотность генерации путей в лабиринте. Чем выше значение, тем больше путей будет создано.current_level
(int): Текущий уровень сложности. Используется для настройки плотности генерации на разных уровнях.
-
Инициализация матрицы лабиринта:
- Метод начинает с создания матрицы размером
height
xwidth
, заполняя ее значениями 1, представляющими стены.
- Метод начинает с создания матрицы размером
-
Генерация основных путей:
- Для каждой второй строки
y
и каждой второй колонкиx
в матрице, метод устанавливает значение 0 (проходимая клетка) для создания начальных путей в лабиринте.
- Для каждой второй строки
-
Создание случайных путей:
- Для каждой второй строки
y
и каждой второй колонкиx
в матрице, метод случайным образом выбирает направление движения(dx, dy)
из четырех возможных (верх, низ, влево, вправо). - Вычисляются новые координаты
(nx, ny)
для следующей клетки, добавляя к текущим координатам(x, y)
смещение(dx, dy)
. - Проверяется, что новые координаты
(nx, ny)
находятся внутри границ лабиринта и что клетка в выбранном направлении еще не посещена. - Если условия выполняются, метод устанавливает значение 0 для клетки
(nx, ny)
и создает путь между текущей и следующей клеткой, устанавливая значение 0 для клетки(x + dx // 2, y + dy // 2)
. - Чем выше уровень сложности, тем меньше путей будет добавлено за одну итерацию, так как плотность уменьшается.
- Для каждой второй строки
-
Создание конечной точки:
- Выбираются случайные координаты
(end_x, end_y)
для конечной точки, гарантированно находящейся на нечетных строках и столбцах. - Значение 0 (проходимая клетка) устанавливается для конечной точки
(end_x, end_y)
.
- Выбираются случайные координаты
-
Вызов
carve_paths()
:- Метод вызывает метод
carve_paths()
для прокладывания дополнительных путей между существующими путями, чтобы сделать лабиринт более сложным.
- Метод вызывает метод
-
Итог:
- Метод
generate()
создает случайный лабиринт, содержащий сложные структуры путей и стен. Путем управления параметромdensity
и изменения уровня сложности, можно достичь разнообразных конфигураций лабиринта.
- Метод
Этот метод является основной частью процесса генерации лабиринта, гарантирующей создание интересных и увлекательных игровых пространств.
Метод
reset_maze()
предназначен для сброса состояния лабиринта к начальному состоянию перед генерацией нового уровня.
-
Сброс состояния:
- Внутри метода происходит сброс состояния лабиринта к начальному состоянию. Это достигается вызовом метода
initialize_maze()
, который заполняет сетку лабиринта стенами.
- Внутри метода происходит сброс состояния лабиринта к начальному состоянию. Это достигается вызовом метода
-
Использование метода:
- Обычно этот метод вызывается перед началом генерации нового уровня лабиринта. Он позволяет обнулить состояние текущего лабиринта и подготовить его для генерации новой структуры.
-
Итог:
- Метод
reset_maze()
классаMaze
позволяет сбросить текущее состояние лабиринта к начальному, подготавливая его для генерации новой структуры на следующем уровне игры.
- Метод
Метод
carve_paths()
в классеMaze
выполняет рекурсивное прокладывание путей в лабиринте, создавая дополнительные пути между существующими путями и клетками. Это помогает сделать лабиринт более сложным и интересным для игрока.
-
Параметры метода:
x
(int): Текущая координата x, представляющая столбец в лабиринте.y
(int): Текущая координата y, представляющая строку в лабиринте.
-
Основная идея:
- Метод начинает с текущей клетки
(x, y)
и выбирает случайным образом одно из четырех направлений (вверх, вниз, влево, вправо). - Если выбранное направление можно использовать для прокладывания пути (т.е. клетка в выбранном направлении еще не посещена и находится внутри границ лабиринта), то метод прокладывает путь, добавляя клетку между текущей и выбранной клеткой.
- Метод начинает с текущей клетки
-
Прокладывание путей:
- Для каждой итерации процесса выбирается случайное направление
(dx, dy)
из четырех доступных (верх, низ, влево, вправо). - Вычисляются новые координаты
(nx, ny)
для следующей клетки, добавляя к текущим координатам(x, y)
смещение(dx, dy)
. - Проверяется, что новые координаты находятся внутри границ лабиринта и что клетка в выбранном направлении еще не посещена.
- Если все условия выполнены, то метод изменяет статус клетки
(nx, ny)
на проходимую (0) и клетку между текущей и следующей клеткой также делает проходимой, путем изменения статуса(x + dx // 2, y + dy // 2)
на проходимую (0). - После этого, метод вызывает сам себя рекурсивно для новых координат
(nx, ny)
, чтобы продолжить прокладывать пути.
- Для каждой итерации процесса выбирается случайное направление
-
Ограничения и выход из рекурсии:
- Рекурсия завершается, когда для текущей клетки не удается выбрать новое направление для прокладывания пути.
- Это может произойти, если все направления вокруг текущей клетки уже использованы или находятся за границами лабиринта.
-
Итог:
- Метод
carve_paths()
создает дополнительные проходы между существующими путями в лабиринте, делая его более интересным для игры. Рекурсивный процесс прокладывания путей происходит до тех пор, пока есть возможность продолжать создавать пути.
- Метод
Этот метод является ключевой частью генерации сложных и разнообразных лабиринтов, что в свою очередь способствует увлекательности игры.
Метод
draw()
в классеMaze
отвечает за отображение лабиринта на экране, используя библиотеку Pygame. Он рисует стены и пути лабиринта, а также отображает конечную точку.
-
Параметры метода:
screen
(Any): Экран Pygame, на котором будет отображаться лабиринт.
-
Очистка экрана:
- Сначала метод заполняет экран черным цветом с помощью функции
screen.fill(black)
. Это позволяет стирать предыдущий кадр и подготовить поверхность для новой отрисовки.
- Сначала метод заполняет экран черным цветом с помощью функции
-
Отображение стен и путей:
- С помощью вложенных циклов
for
метод проходит через каждую клетку лабиринта. - Если значение в текущей клетке
self.maze[y][x]
равно 1, это означает, что клетка представляет стену. В этом случае метод рисует белый прямоугольник на экране в соответствующей позиции(x * cell_size, y * cell_size)
с размерамиcell_size x cell_size
.
- С помощью вложенных циклов
-
Отображение конечной точки:
- Затем метод рисует зеленый прямоугольник, представляющий конечную точку лабиринта, на экране в позиции
(self.end_x * cell_size, self.end_y * cell_size)
с размерамиcell_size x cell_size
.
- Затем метод рисует зеленый прямоугольник, представляющий конечную точку лабиринта, на экране в позиции
-
Обновление экрана:
- После отрисовки всех элементов, метод вызывает
pygame.display.flip()
, что приводит к обновлению отображения на экране. Таким образом, пользователь видит новый кадр, включая отрисованный лабиринт и конечную точку.
- После отрисовки всех элементов, метод вызывает
-
Итог:
- Метод
draw()
выполняет отрисовку лабиринта на экране, включая стены, пути и конечную точку. Он использует информацию о структуре лабиринта, передаваемую через атрибутself.maze
, и преобразует ее в графическое представление с помощью функций Pygame. Это позволяет игроку визуально взаимодействовать с игровой средой.
- Метод
Является главной точкой входа в программу. Она инициализирует игровое окно, управляет основным игровым циклом и обеспечивает взаимодействие игрока с игрой. В этой функции создаются экземпляры объектов классов
Maze
иPlayer
, генерируется и отображается лабиринт, а также обрабатываются события, такие как движение игрока и завершение игры.
-
Инициализация Pygame и создание окна:
- Функция начинает с инициализации библиотеки Pygame вызовом
pygame.init()
. - Затем создается игровое окно с помощью
pygame.display.set_mode()
, устанавливая размер окна в соответствии сwidth
иheight
, определенными в константах. Также устанавливается заголовок окна с названием игры.
- Функция начинает с инициализации библиотеки Pygame вызовом
-
Инициализация уровней сложности:
- Определяется список словарей
levels
, представляющий разные уровни сложности. Каждый словарь содержит имя уровня и значение плотности генерации лабиринта.
- Определяется список словарей
-
Инициализация лабиринта и игрока:
- Создается экземпляр класса
Maze
с указанием размеров лабиринтаwidth // cell_size
иheight // cell_size
. - Генерируется лабиринт с использованием метода
generate()
объектаmaze
, передавая плотность генерации и текущий уровень сложности. - Создается экземпляр класса
Player
с начальными координатами(1, 1)
.
- Создается экземпляр класса
-
Инициализация Pygame Clock и переменных:
- Создается экземпляр
clock
для отслеживания частоты кадров. - Инициализируется переменная
running
в значенииTrue
, чтобы начать игровой цикл.
- Создается экземпляр
-
Игровой цикл:
- Внутри цикла происходит отслеживание событий Pygame и обработка игровой логики.
- Вложенный цикл
for event in pygame.event.get()
перебирает все события, происходящие в игре. - Если событие - закрытие окна (
pygame.QUIT
), переменнаяrunning
устанавливается вFalse
, что приведет к завершению игрового цикла.
-
Обработка времени:
current_time = pygame.time.get_ticks()
: получает текущее время в миллисекундах с использованием функцииpygame.time.get_ticks()
. Это позволяет отслеживать, сколько времени прошло с момента последнего изменения состояния игры.elapsed_time = current_time - last_move_time
: рассчитывает разницу между текущим временем и временем последнего изменения состояния игры (запоминается в переменнойlast_move_time
). Это дает представление о том, сколько времени прошло с последнего движения игрока.if elapsed_time >= 1000 / speed
: сравнение прошедшего времени с интервалом, который определяется как1000 / speed
, гдеspeed
- это скорость движения игрока. Если прошедшее время больше или равно этому интервалу, то это означает, что прошло достаточно времени для выполнения следующего шага игрока.
-
Обработка движения игрока:
pygame.key.get_pressed()
получает состояние с клавиш клавиатуры.- Если клавиша
pygame.K_UP
нажата, тоdy
устанавливается на -1, что будет двигать игрока вверх. - Аналогично, если нажата клавиша
pygame.K_DOWN
, тоdy
устанавливается на 1, двигая игрока вниз. - Если нажата клавиша
pygame.K_LEFT
, тоdx
устанавливается на -1, двигая игрока влево. - Если нажата клавиша
pygame.K_RIGHT
, тоdx
устанавливается на 1, двигая игрока вправо. - Если нажатие клавиши соответствует направлению, игрок выполняет перемещение с использованием метода
move()
.
-
Расчет новой позиции:
- Вычисление новой координаты
new_x
для горизонтальной позиции игрока, добавляяdx
к текущей позиции игрока. - Аналогично, вычисление новой координаты
new_y
для вертикальной позиции игрока, добавляяdy
к текущей позиции игрока.
- Вычисление новой координаты
-
Проверка допустимости новой позиции:
- Проверка границ лабиринта и наличия пути (
maze.maze[new_y][new_x] == 0
) для новой позицииnew_x
,new_y
. - Если все условия выполняются, то игрок перемещается на новую позицию, обновляя значения
player.x
иplayer.y
.
- Проверка границ лабиринта и наличия пути (
-
Проверка завершения уровня и игры:
- Если игрок достигает конечной точки лабиринта (
player.x == maze.end_x
иplayer.y == maze.end_y
), проверяется, есть ли еще уровни сложности для прохождения. - Если уровни сложности остались, увеличивается переменная
current_level
, и лабиринт генерируется с новой плотностью и начальной позицией игрока. В противном случае игровой цикл завершается.
- Если игрок достигает конечной точки лабиринта (
-
Отрисовка элементов на экране:
- Экран заполняется черным цветом.
- Лабиринт и игрок отрисовываются на экране с помощью методов
draw()
.
-
Обновление экрана:
- После отрисовки всех элементов, вызывается
pygame.display.flip()
, чтобы обновить отображение на экране.
- После отрисовки всех элементов, вызывается
-
Завершение игры:
- По завершении игрового цикла вызывается
pygame.quit()
, что завершает работу Pygame и закрывает окно.
- По завершении игрового цикла вызывается
-
Итог:
- Функция
main()
объединяет все элементы игровой логики, включая инициализацию, генерацию лабиринта, обработку событий и отображение элементов на экране. Она создает интерактивное окружение для игрока и обеспечивает геймплей.
- Функция
Файл readme.md
сгенерирован с помощью ChatGPT / OpenAI ChatGPT-3.5