Создайте игру-лабиринт с Python

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

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

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

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

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

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


Генерация лабиринта Существует множество алгоритмов для создания лабиринтов, наиболее часто используемые из них - это рекурсивный поиск с возвратом, рекурсивная сегментация и алгоритм случайного Prim. Сегодня мы используем последний алгоритм.

Основные шаги алгоритма следующие:
1. Строки и столбцы лабиринта должны быть нечетными
2. Пересечение нечетных строк и нечетных столбцов - это дорога, остальные точки - стены, а лабиринт - это дорога. окружена стенами
3. Выберите ячейку, которая является дорогой (в этом примере выберите [1,1]), а затем поместите соседнюю стену в список стен
4. Если в списке стен есть стена:
4.1. Случайно выберите стену из списка, если стена разделяет две единицы. Была посещена только одна ячейка ячейки
4.1.1, затем удалите стену из списка и одновременно откройте стену.
4.1.2, отметьте ячейку как посетил
4.1.3, установить соседа непосещенной ячейки Добавить стену в список стены
4.2. Если ячейки по обе стороны стены были посещены, удалите стену из списка

Мы определяем класс Maze, используя двумерный массив для представления карты лабиринта, где 1 представляет стену, а 0 представляет дорогу, а затем инициализируем верхний левый угол как вход, нижний правый угол как выход и, наконец, определить вектор направления вниз.

class Maze: def init (self, width, height): self.width = widthself.height = heightself.map = [[0, если x% 2 == 1 и y% 2 == 1, иначе 1 для x в диапазоне (ширина )] for y in range (height)] self.map [1] [0] = 0 # вход self.map [height-2] [width-1] = 0 # exit self.visited = [] # вправо вверх влево downself.dx = [1, 0, -1, 0] self.dy = [0, -1, 0, 1]
Следующим шагом является создание главной функции лабиринта.

def generate (self):
start = [1, 1] self.visited.append (start)
wall_list = self.get_neighbor_wall (start), а wall_list:
wall_position = random.choice (wall_list)
neighbour_road = self.get_neighbor_road (wall_position)
wall_list. remove (wall_position) self.deal_with_not_visited (neighbour_road [0], wall_position, wall_list) self.deal_with_not_visited (neighbour_road [1], wall_position, wall_list)
В этой функции есть две основные функции: get_neighbor_road (point) и deal_with_not_visited () Сосед узла входящей координатной точки, возвращаемое значение представляет собой двумерный массив, а последняя функция deal_with_not_visited () обрабатывает логику шага 4.1.

Поскольку алгоритм Prim random случайным образом выбирает все ячейки в списке случайным образом, вероятность выбора вновь добавленной ячейки и старой добавленной ячейки одинакова, поэтому у него больше ветвей, а сгенерированный лабиринт более сложен. и конечно выглядит естественнее. Сгенерированный лабиринт.
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1 , 1, 1, 1, 0, 1, 1, 1, 1, 1]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1, 1, 0 , 1, 0, 1, 0, 1, 0, 1]
[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
[1, 0, 1, 0, 1, 0 , 1, 1, 1, 1, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1]
[1, 0, 1, 0, 1, 1, 1, 0 , 1, 0, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1 , 1]

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

def dfs (self, x, y, path ,hibited = []): # outOfIndexif self.is_out_of_index (x, y): return False # посещение или стена if [x, y] в посещенном или self.get_value ([x, y]) == 1: вернуть False

visit.append ([x, y]) 
path.append ([x, y]) # end ... if x == self.width - 2 и y == self.height - 2: вернуть True # рекурсивно для i в диапазон (4): если 0 <x + self.dx [i] <self.width - 1 и 0 <y + self.dy [i] <self.height - 1 и \ self.get_value ([x + self. dx [i], y + self.dy [i]]) == 0: если self.dfs (x + self.dx [i], y + self.dy [i], путь, посещенный): вернуть True 
        elif не self.is_out_of_index (x, y), а путь [-1]! = [x, y]: 
            path.append ([x, y])

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

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

Давайте посмотрим на результаты пути, полученные нашей dfs:

[[0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1], [ 8, 1], [9, 1], [9, 1], [8, 1], [7, 1], [6, 1], [5, 1], [5, 2], [5, 3], [6, 3], [7, 3], [8, 3], [9, 3], [9, 4], [9, 5], [9, 5], [9, 4] , [9, 3], [8, 3], [7, 3], [7, 4], [7, 5], [7, 5], [7, 4], [7, 3], [ 6, 3], [5, 3], [4, 3], [3, 3], [2, 3], [1, 3], [1, 3], [2, 3], [3, 3], [3, 4], [3, 5], [2, 5], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9] , [1, 9], [1, 8], [1, 7], [1, 6], [1, 5], [2, 5], [3, 5], [3, 6], [ 3, 7], [3, 8], [3, 9], [3, 9], [3, 8], [3, 7], [3, 6], [3, 5], [3, 4], [3, 3], [4, 3], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [6, 7] , [7, 7], [8, 7], [9, 7], [9, 8], [9, 9], [10, 9]]
Визуализируйте
карту лабиринта и путь пути, остальная часть работы Он предназначен для визуализации этих координатных точек. Библиотека визуализации, которую мы используем сегодня, - это pyxel, которая представляет собой библиотеку Python для написания игр на пиксельном уровне.

Конечно, вам необходимо установить эту библиотеку перед ее использованием.

Пользователи Win могут установить его напрямую с помощью команды pip install -U pyxel.

Пользователи Mac используют следующую команду для установки:

brew install python3 gcc sdl2 sdl2_image gifsicle
pip3 install -U pyxel
Давайте сначала рассмотрим простую демонстрацию.

водяной знак, размер_16, текст_QDUxQ1RP5Y2a5a6i, цвет_FFFFFF, t_100, g_se, x_10, y_10, shadow_90, type_ZmFuZ3poZW5naGVpdGk =

import pyxelclass App: def init (self):
pyxel.init (160, 120) self.x = 0
pyxel.run (self.update, self.draw) def update (self): self.x = (self.x + 1)% pyxel.widthdef draw (self):
pyxel.cls (0)
pyxel.rect (self.x, 0, 8, 8, 9)


Логика выполнения класса App () App состоит в том, чтобы постоянно вызывать функцию обновления и функцию рисования, поэтому вы можете обновить координаты объекта в функции обновления, а затем нарисовать изображение на экране в функции рисования.

Таким образом, мы сначала рисуем лабиринт, а затем визуализируем анимацию обхода dfs.

водяной знак, размер_16, текст_QDUxQ1RP5Y2a5a6i, цвет_FFFFFF, t_100, g_se, x_10, y_10, shadow_90, type_ZmFuZ3poZW5naGVpdGk =

width, height = 37, 21
my_maze = Maze (width, height)
my_maze.generate () class App: def init (self):
pyxel.init (ширина * пиксель, высота * пиксель)
pyxel.run (self.update, self .draw) def update (self): if pyxel.btn (pyxel.KEY_Q):
pyxel.quit () if pyxel.btn (pyxel.KEY_S): self.death = Falsedef draw (self): # рисовать лабиринт для x в диапазоне (высота): для y в диапазоне (ширина):
color = road_color, если my_maze.map [x] [y] равно 0, иначе wall_color
pyxel.rect (y * пиксель, x * пиксель, пиксель, пиксель, цвет)
pyxel.rect (0, пиксель, пиксель, пиксель, start_point_color)
pyxel.rect ((ширина - 1) * пиксель, (высота - 2) * пиксель, пиксель, пиксель, end_point_color)

App ()
выглядит нормально. Я использовал сетки 37 и 21 пиксель для генерации ширины и высоты соответственно, поэтому сгенерированный лабиринт не очень сложен. Если пикселей много, это будет сложно.

Затем нам нужно изменить функцию обновления и функцию рисования для визуализации пути. Чтобы облегчить работу, мы добавили несколько атрибутов в функцию init.

self.index = 0 self.route = [] # Используется для записи пути для визуализации self.step = 1 # Длина шага, чем меньше значение, тем выше скорость, 1: каждый раз по одной сетке; 10: 1 / 10 сеток каждый раз self .color = start_point_colorself.bfs_route = my_maze.bfs_route ()
где index и step используются для управления скоростью рендеринга. В функции рисования индекс увеличивается на 1 каждый раз, а затем вычисляется оставшийся шаг чтобы получить текущий реальный индекс real_index. Другими словами, real_index будет увеличиваться на единицу каждый раз, когда индекс увеличивается на шаг, и путь рендеринга продвигается на один шаг вперед.

def draw (self): # рисовать лабиринт для x в диапазоне (высота): для y в диапазоне (ширина):
color = road_color если my_maze.map [x] [y] равно 0 else wall_color
pyxel.rect (y * pixel, x * пиксель, пиксель, пиксель, цвет)
pyxel.rect (0, пиксель, пиксель, пиксель, start_point_color)
pyxel.rect ((ширина - 1) * пиксель, (высота - 2) * пиксель, пиксель, пиксель, end_point_color) если self.index> 0: # рисовать
смещение маршрута = pixel / 2 for i in range (len (self.route) - 1):
curr = self.route [i] next = self.route [i + 1] self.color = backtrack_color if curr в self.route [: i] и next в self.route [: i] else route_color
pyxel.line (curr [0] + смещение, (curr [1] + смещение), next [0] + смещение, next [1] + смещение, self.color)
pyxel.circ (self.route [-1] [0] + 2, self.route [-1] [1] + 2, 1, head_color)
def update (self): if pyxel.btn (pyxel.KEY_Q):
pyxel.quit () if pyxel.btn (pyxel.KEY_S): self.death = False, если не self.death: self.check_death () self.update_route ( ) def check_death (self): if self.dfs_model и len (self.route) == len (self.dfs_route) - 1: self.death = True
elif not self.dfs_model и len (self.route) == len ( self.bfs_route) - 1: self.death = Truedef update_route (self):
index = int (self.index / self.step) self.index + = 1if index == len (self.route): # moveif self.dfs_model : self.route.append ([пиксель * self.dfs_route [index] [0], пиксель * self.dfs_route [index] [1]]) else: self.route.append ([пиксель * self.bfs_route [index] [0], пиксель * self.bfs_route [индекс] [1]])

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

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

Во-вторых, мы используем библиотеку pyxel для реализации визуализации пути. Сложность заключается в вычислении и обновлении координат. Детали более сложные и громоздкие. Конечно, читатели также могут использовать другие библиотеки или напрямую использовать веб-страницу для достижения этой цели. .
Источник статьи: веб - игры http://www.hp91.cn/web игра

рекомендация

отblog.51cto.com/14621511/2679085
рекомендация