制作迷宫地图和行走路线(2)
共 898字,需浏览 2分钟
·
2022-04-30 11:26
说在前面
上节课我们介绍了“使用深度优先搜索算法生成不含环迷宫”的方法,该程序只完成了构造随机迷宫图案的功能,却没有记录通道行进路线。这样只能通过肉眼观察迷宫,手动绘制从起点到终点的路线,而不能让电脑自动绘制行走路线。我们可以在绘制迷宫通道的同时,把通道前进的方向也记录下来,为后期遍历迷宫做好准备工作。
今天我们就在上节课代码的基础上,增加记录迷宫路线的功能;并实现给定任意起点和终点坐标,让电脑自动绘制行走路线的功能。
第2节 使用深度优先搜索算法生成迷宫并寻找路径
第1节课介绍的程序只完成了构造随机迷宫图案的功能,却没有记录通道行进路线。这样只能通过肉眼观察迷宫,手动绘制从起点到终点的路线,而不能让电脑自动绘制行走路线。我们可以在绘制迷宫通道的同时,使用二维数组route记录迷宫路线,其中route[r][c]的值是一个列表,存储了与格子(r, c)连通的相邻格子的行列下标(i, j),有几个格子与(r, c)相邻且连通,route[r][c]就有几个元素。
'''
函数功能:使用深度优先搜索来生成不含环迷宫,并记录通道走向
函数名:dfs(r, c)
参数表:r,c -- 表示当前格子在二维数组maze中的行列下标。
返回值:把二维数组maze和route当做全局变量,直接使用画笔绘制迷宫通道,不需要返回值。
'''
def dfs(r, c):
# 标记(r, c)来过
maze[r][c] = 1
# 走到(r, c)
tt.goto(start_x+c*size, start_y-r*size)
# (r, c)的上下左右位置
d = [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]
# 打乱位置,下一个位置是随机的
random.shuffle(d)
for (i, j) in d:
# 选所有可以走的下一个点,如果此点在范围内并且没去过
if 0<=i
and 0<=jand maze[i][j]==0: route[r][c].append((i, j)) # 记录与(r, c)连通的位置(i, j)
route[i][j].append((r, c)) # 记录与(i, j)连通的位置(r, c)
# 递归去走下一个点
dfs(i, j)
# 回溯到(r, c)
tt.goto(start_x+c*size, start_y-r*size)
2.使用深度优先搜索来寻找路径
有了二维数组route记录的通道行进信息,就可以给定任意起点和终点坐标,让电脑自动绘制行走路线。我们先将maze归零,以便重新遍历地图。同时定义列表ans来存储行走路径,先将终点加入到ans中,之后逆序添加各个格子的行列下标。
那么使用什么方法来遍历地图呢?
最容易想到的方法是模仿构造迷宫地图,使用深度优先搜索算法。我们可以定义函数dfs_ans(r, c),把列表maze和ans当做全局变量(因为列表是可变对象,即使不声明为global变量,仍可以修改其元素值),若从当前格子出发能够到达终点,返回True,否则False。
函数遍历与当前格子(r, c)连通的相邻格子route[r][c],递归处理通道上的每一个格子,直到终点。若当前格子与终点连通,将当前格子行列下标添加到ans,并返回True,否则返回False。
'''
函数功能:使用深度优先搜索来寻找路径
函数名:dfs_ans(r, c)
参数表:r,c -- 表示当前格子在二维数组maze中的行列下标。
返回值:把列表maze和ans当做全局变量,若从当前格子出发能够到达终点,返回True,否则False。
'''
def dfs_ans(r, c):
if r==r2 and c==c2:
return True
for (i, j) in route[r][c]:
if maze[i][j] == 0: # 如果没有去过该点,则递归搜索该点
maze[i][j] = 1 # 标记该点已经来过
# 递归去走下一个点
if dfs_ans(i, j): #若当前格子与终点连通,将当前格子行列下标加入到ans
ans.append((r, c))
return True
return False
3.绘制使用深度优先搜索获得的路径
有了列表ans存储的路径信息,就可以绘制行走路线了。因为ans的元素值是采用回溯算法依次添加的,所以离终点越近的格子越早添加到ans中。因此我们需要逆序遍历ans,才可以顺序输出从起点到终点的路线。我们定义函数display_ans()实现该功能,参考代码如下:
'''
函数功能:根据ans的值,绘制使用深度优先搜索获得的路径
函数名:display_ans()
参数表:直接使用列表ans的元素值,不需要任何参数。
返回值:直接利用画笔绘制路线,不需要返回值。
'''
def display_ans():
tt.speed(1)
tt.pensize(size*0.5) # 路线粗细
tt.color("red")
tt.penup()
tt.goto(start_x+ans[-1][1]*size, start_y-ans[-1][0]*size)
tt.pendown()
for (r, c) in ans[-2::-1]: #逆序遍历ans,刚好可以顺序输出从起点到终点的路线
tt.goto(start_x+c*size, start_y-r*size)
4.主函数部分代码
tt.TurtleScreen._RUNNING = True
tt.ht()
tt.bgcolor('black')
tt.color("white")
tt.speed(3)
w, h = 20, 20
size = 30
tt.pensize(size*0.8)
maze = [[0] * w for i in range(h)]
route = [[[] for j in range(w)] for i in range(h)] #记录迷宫路线
start_x = -w / 2 * size
start_y = h / 2 * size
tt.penup()
tt.goto(start_x, start_y)
tt.pendown()
dfs(0, 0)
r1, c1 = [int(i) for i in input("起点行列下标:").split()]
r2, c2 = [int(i) for i in input("终点行列下标:").split()]
maze = [[0] * w for _ in range(h)] # 将maze归零,重新遍历地图
maze[r1][c1] = 1
ans = [(r2, c2)]
dfs_ans(r1, c1)
display_ans()
tt.done()
代码说明:
与第1节课的程序相比,本节课程序在主函数中增加了一个二维数组route,用来记录记录迷宫路线。还增加了使用深度优先搜索来寻找路径的功能:先从键盘依次读入起点行、列下标(用空格隔开)和终点行、列下标,然后将maze归零,以便重新遍历地图。定义列表ans,并将终点的行列下标(r2, c2)作为ans的第一个元素,之后调用函数dfs_ans(),使用深度优先搜索算法逆序添加各个格子信息到ans中。最后调用函数display_ans()绘制使用深度优先搜索获得的路径。
本节课介绍的程序是使用深度优先搜索算法来寻找路径的,除此之外,我们还可以使用广度优先搜索算法来寻找路径。请查阅广度优先搜索算法相关知识,设置自定义函数,实现给定任意起点和终点坐标,使用广度优先搜索算法来寻找路径、并绘制行走路线的功能。
需要本文PPT、源代码和课后练习答案的,可以加入“Python算法之旅”知识星球参与讨论和下载文件,“Python算法之旅”知识星球汇集了数量众多的同好,更多有趣的话题在这里讨论,更多有用的资料在这里分享。
我们专注Python算法,感兴趣就一起来!
相关优秀文章: