本篇文章禁止转载

随机深度优先搜索算法

随机深度优先搜索介绍

递归实现

  • 1。选择初始单元格,将其标记为已访问,并将其压入堆栈
  • 2。而堆栈不是空的
    • 1。从堆栈中弹出一个单元格并使其成为当前单元格
    • 2。如果当前单元有任何未被访问的邻居
      • 1。将当前单元格压入堆栈
      • 2。选择一个未被拜访的邻居
      • 3。移除当前单元格和所选单元格之间的墙
      • 4。将选中的单元格标记为已访问的,并将其压入堆栈

生成的迷宫偏向于长胡同。

Python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/python3.7
# -*- coding: utf-8 -*-
import random
# Randomized depth-first search
# Recursive implementation
#1。Choose the initial cell, mark it as visited and push it to the stack
#2。While the stack is not empty
# 1。Pop a cell from the stack and make it a current cell
# 2。If the current cell has any neighbours which have not been visited
# 1。Push the current cell to the stack
# 2。Choose one of the unvisited neighbours
# 3。Remove the wall between the current cell and the chosen cell
# 4。Mark the chosen cell as visited and push it to the stack
#随机深度优先搜索
#递归实现
#1。选择初始单元格,将其标记为已访问,并将其压入堆栈
#2。而堆栈不是空的
# 1。从堆栈中弹出一个单元格并使其成为当前单元格
# 2。如果当前单元有任何未被访问的邻居
# 1。将当前单元格压入堆栈
# 2。选择一个未被拜访的邻居
# 3。移除当前单元格和所选单元格之间的墙
# 4。将选中的单元格标记为已访问的,并将其压入堆栈


# 墙不占用单元格
# 可以保证所有的格都是相通的
# 深度优先算法可以遍历所有的单元格。
# Randomized depth-first search
# Recursive backtracker
# 递归回溯算法
def depth_maze(rows, cols):
num_cols=cols
num_rows=rows
history = [(0,0)]
# 一个格子有四堵墙,其中有两面共有,用2个标记就够用。
# 墙0通路1。x,y是墙的坐标。
# wall[x][y][0]竖墙wall[x][y][0][1]横墙
# 初始化全为墙
wall=[[ [0,0] for i in range(num_cols)]for i in range(num_rows)]
# way用来标记已经访问过的格子
# 初始化全未访问
way=[[ 0 for i in range(num_cols)]for i in range(num_rows)]
# 设置起点
r=0
c=0
# 起点加入记录
history = [(r,c)]
# 1。选择初始单元格,将其标记为已访问,并将其压入堆栈
# 2。堆栈不是空的
while history:
way[r][c] = 1 #
check = []
# 可以移动到的位置
if c > 0 and way[r][c-1] == 0:
check.append('L')
if r > 0 and way[r-1][c] == 0:
check.append('U')
if c < num_cols-1 and way[r][c+1] == 0:
check.append('R')
if r < num_rows-1 and way[r+1][c] == 0:
check.append('D')
# 如果当前单元有任何未被访问的邻居
if len(check):
# 选择一个未被拜访的邻居
# 移除当前单元格和所选单元格之间的墙
# 将选中的单元格标记为已访问的,并将其压入堆栈
history.append((r, c))
# 随机移动
move_direction = random.choice(check)
# 打通墙壁
if move_direction == 'L':
wall[r][c][0] = 1
c=c-1
if move_direction == 'U':
wall[r][c][1] = 1
r=r-1
if move_direction == 'R':
c=c+1
wall[r][c][0] = 1
if move_direction == 'D':
r=r+1
wall[r][c][1] = 1
else:
#从堆栈中弹出一个单元格并使其成为当前单元格
r, c = history.pop()
return wall


生成过程动画演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/python3.7
# -*- coding: utf-8 -*-
import random
import pygame
#
# Randomized depth-first search
# Recursive implementation
#1。Choose the initial cell, mark it as visited and push it to the stack
#2。While the stack is not empty
# 1。Pop a cell from the stack and make it a current cell
# 2。If the current cell has any neighbours which have not been visited
# 1。Push the current cell to the stack
# 2。Choose one of the unvisited neighbours
# 3。Remove the wall between the current cell and the chosen cell
# 4。Mark the chosen cell as visited and push it to the stack
#随机深度优先搜索
#递归实现
#1。选择初始单元格,将其标记为已访问,并将其压入堆栈
#2。而堆栈不是空的
# 1。从堆栈中弹出一个单元格并使其成为当前单元格
# 2。如果当前单元有任何未被访问的邻居
# 1。将当前单元格压入堆栈
# 2。选择一个未被拜访的邻居
# 3。移除当前单元格和所选单元格之间的墙
# 4。将选中的单元格标记为已访问的,并将其压入堆栈

# pygame
pygame.init() # 初始化pygame
size = width, height = 800, 600 # 设置窗口大小
screen = pygame.display.set_mode(size) # 显示窗口
# 行列
num_cols=30
num_rows=20
# 墙0表示墙1表示通路[0]竖墙[1]横墙
wall=[[ [0,0] for i in range(num_cols)]for i in range(num_rows)]
# 已访问标记
way=[[ 0 for i in range(num_cols)]for i in range(num_rows)]
# 颜色
diamond_color_size = 6
COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_YELLOW, COLOR_BLACK, COLOR_NO_DIAMOND = list(range(
diamond_color_size))
COLOR = {
COLOR_RED: (255, 0, 0),
COLOR_BLUE: (0, 0, 255),
COLOR_GREEN: (0, 255, 0),
COLOR_YELLOW: (255, 255, 0),
COLOR_BLACK: (0, 0, 0),
COLOR_NO_DIAMOND: (100, 100, 100),
}
# 格子大小
DIAMOND_SIZE = (20, 20)
# 格子
DIAMOND=pygame.surface.Surface(DIAMOND_SIZE).convert()
DIAMOND.fill(COLOR[1])

# 访问过的格子
DIAMOND_GREEN=pygame.surface.Surface(DIAMOND_SIZE).convert()
DIAMOND_GREEN.fill(COLOR[COLOR_GREEN])
# 访问过的格子
DIAMOND_RED=pygame.surface.Surface(DIAMOND_SIZE).convert()
DIAMOND_RED.fill(COLOR[COLOR_RED])

def draw_grid(lw, surface, rgb_color):
rect = (lw, lw, DIAMOND_SIZE[0] -2*lw, DIAMOND_SIZE[1] -2*lw)
pygame.draw.line(surface, rgb_color, (rect[0], rect[1]), (rect[0], rect[3]), lw)
pygame.draw.line(surface, rgb_color, (rect[0], rect[1]), (rect[2], rect[1]), lw)
pygame.draw.line(surface, rgb_color, (rect[0], rect[3]), (rect[2], rect[3]), lw)
pygame.draw.line(surface, rgb_color, (rect[2], rect[1]), (rect[2], rect[3]), lw)
return

def draw_wall(lw, surface, rgb_color):
rect = (lw, lw, DIAMOND_SIZE[0] -2*lw, DIAMOND_SIZE[1] -2*lw)
# 左
pygame.draw.line(surface, rgb_color, (rect[0], rect[1]), (rect[0], rect[3]), lw)
# 上
pygame.draw.line(surface, rgb_color, (rect[0], rect[1]), (rect[2], rect[1]), lw)
# 下
pygame.draw.line(surface, rgb_color, (rect[0], rect[3]), (rect[2], rect[3]), lw)
# 右
pygame.draw.line(surface, rgb_color, (rect[2], rect[1]), (rect[2], rect[3]), lw)
return
# 字体
use_font = pygame.font.Font("FONT.TTF", 16)
#draw_grid(2, DIAMOND, (128, 128, 128))
# 背景
background=pygame.surface.Surface(((num_cols ) * DIAMOND_SIZE[0] + 2 , (num_rows ) * DIAMOND_SIZE[1] + 2)).convert()
background.fill(COLOR[2])

# 记录
history = [(0,0)]
# 时间
clock = pygame.time.Clock()

# 墙不占用单元格
# 可以保证所有的格都是相通的
# 深度优先算法可以遍历所有的单元格。
# Randomized depth-first search
# Recursive backtracker
# 递归回溯算法
def depth_maze():
r=0
c=0
history = [(r,c)]
# 1。选择初始单元格,将其标记为已访问,并将其压入堆栈
# 2。堆栈不是空的
while history:
way[r][c] = 1 #
check = []
if c > 0 and way[r][c-1] == 0:
check.append('L')
if r > 0 and way[r-1][c] == 0:
check.append('U')
if c < num_cols-1 and way[r][c+1] == 0:
check.append('R')
if r < num_rows-1 and way[r+1][c] == 0:
check.append('D')
# 如果当前单元有任何未被访问的邻居
if len(check):
# 选择一个未被拜访的邻居
# 移除当前单元格和所选单元格之间的墙
# 将选中的单元格标记为已访问的,并将其压入堆栈
history.append((r, c))
# 随机移动
move_direction = random.choice(check)
# 打通墙壁
if move_direction == 'L':
wall[r][c][0] = 1
c=c-1
if move_direction == 'U':
wall[r][c][1] = 1
r=r-1
if move_direction == 'R':
c=c+1
wall[r][c][0] = 1
if move_direction == 'D':
r=r+1
wall[r][c][1] = 1
else:
#从堆栈中弹出一个单元格并使其成为当前单元格
r, c = history.pop()
return wall


def depth_maze_demo():
global history
global way
global wall
r=0
c=0
history = [(r,c)]

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return

if history:
way[r][c] = 1 #
check = []
if c > 0 and way[r][c-1] == 0:
check.append('L')
if r > 0 and way[r-1][c] == 0:
check.append('U')
if c < num_cols-1 and way[r][c+1] == 0:
check.append('R')
if r < num_rows-1 and way[r+1][c] == 0:
check.append('D')

if len(check):
history.append((r, c))
move_direction = random.choice(check)
if move_direction == 'L':
wall[r][c][0] = 1
c=c-1
if move_direction == 'U':
wall[r][c][1] = 1
r=r-1
if move_direction == 'R':
c=c+1
wall[r][c][0] = 1
if move_direction == 'D':
r=r+1
wall[r][c][1] = 1
else:
r, c = history.pop()
#
screen.blit(background, (0, 0))

#screen.blit(background, (x, y))
# 格子
for x in range(num_cols):
for y in range(num_rows):
px,py=1 + x * DIAMOND_SIZE[0], 1 + y * DIAMOND_SIZE[1]
# 标记走过的
if way[y][x]:
screen.blit(DIAMOND, (px, py))
else:
screen.blit(DIAMOND_GREEN, (px, py))

px,py=1 + c * DIAMOND_SIZE[0], 1 + r * DIAMOND_SIZE[1]
screen.blit(DIAMOND_RED, (px, py))

# 墙
pygame.draw.rect(screen, COLOR[COLOR_RED], (0, 0, 20*num_cols+1, 20*num_rows+1), 2)
#
for x in range(num_cols):
for y in range(num_rows):
px,py=1 + x * DIAMOND_SIZE[0], 1 + y * DIAMOND_SIZE[1]
if not wall[y][x][0]:
pygame.draw.line(screen, COLOR[COLOR_BLACK], (px, py), (px, py+20), 2)

if not wall[y][x][1]:
pygame.draw.line(screen, COLOR[COLOR_BLACK], (px, py), (px+20, py), 2)

if not history:
score_surface = use_font.render("生成完成!", True, COLOR[COLOR_BLACK], COLOR[COLOR_BLUE])
screen.blit(score_surface, (num_cols*22/10, num_rows*22))

time_passed = clock.tick(30)

pygame.display.update()
return



# main
if __name__ == "__main__":
'''main'''
depth_maze_demo()

GIF演示

在这里插入图片描述

参考

https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_depth-first_search

下一篇:迷宫生成算法(Randomized Prim‘s algorithm)

迷宫生成算法(Randomized Prim‘s algorithm)