利用递归法和pygame实现迷宫寻路的动态展示_Prj002

来源:互联网 发布:天下网商招聘php 编辑:程序博客网 时间:2024/06/07 07:02


        在《Problem Solving with Algorithms and Data Structures》一书的141页,作者给出了一个迷宫寻路的程序。程序的核心是一段递归程序,通过不断尝试,找到从起点到终点的路。虽然不是最短路径,但程序可以动态显示寻路的过程,对于理解递归过程非常有帮助。

        书中给出的程序见“http://download.csdn.net/detail/pospro/8952965”,运行之后就会发现,由于是基于turtle作图(注:turtle是python自带的一个绘图函数集),画迷宫本身就要花费太多时间,让人无法一下抓住核心。

        在这里,PosPro引入pygame取代原有的turtle,大大降低了载入迷宫所花费的时间。同时利用pygame的函数产生了可以媲美turtle的动态效果。全部程序及相关资源见:http://download.csdn.net/detail/pospro/8947365

程序运行后的效果如下:



下面对程序进行逐段分析:

首先建立一个迷宫类(Maze),包含四个函数:构造函数__init__, 实现索引取数的__getitem,判断是否是迷宫出口的isExit,以及动态显示变化的updateScreen

构造函数的详细代码如下。这一段代码完成了以下工作:

1.按照pygame的要求,对屏幕进行初始化,

2.读入迷宫的点阵文件 

3.根据读入的点阵文件,确定墙的的位置,出发点的位置。

def __init__(self, mazeDotFile): pygame.init()#为了简化,这里直接给定了迷宫的行列值,其实也可以在读入文件时动态判断mazeColumn=22mazeRow=11self.mazeColumn=mazeColumnself.mazeRow=mazeRowbrickImg = pygame.image.load("brick.png")   #每个砖块是40*40startflagImg = pygame.image.load("startflag.png") # 30*30self.mazeScreen=pygame.display.set_mode((40*mazeColumn, 40*mazeRow))self.mazeScreen.fill((255,255,255))self.startRow=0self.startCol=0self.mazeList = []  #用于保存从文件中读入的迷宫点阵maze_file = open(mazeDotFile,'r')for line in maze_file:row_list = []for ch in line: #原文是line[: -1],但报错;估计是line读入时已自动去掉最后的换行符?row_list.append(ch)self.mazeList.append(row_list)maze_file.close()for y in range(mazeRow):for x in range(mazeColumn):#print self.mazeList[y][x],if self.mazeList[y][x]==OBSTACLE:self.mazeScreen.blit(brickImg,(x*40,y*40))   #注意这里有个容易让人混淆的地方!注意屏幕向右,即Column方向为x方向,向下为y方向!!elif self.mazeList[y][x]=='S':self.startRow=yself.startCol=xself.mazeScreen.blit(startflagImg,(x*40+5, y*40+5))



迷宫文件只是个简单的文本文件,22行11列,用+表示墙的位置,用空格表示通道,用S表示起点,示例如下:

+++++++++++++++++++++++          +         ++++   ++       +      +++        +   +     +++++    +++++++++++++++                    ++          ++   +  ++++  ++++++++++++    ++++          S        +++                    +++++++++++++++++++++++



getitem和isExit函数分别只有一句话。比较有趣的是这个__getitem__函数,通过构造函数,大家可以发现MazeList实际上是个List的List,即MazeList中的每个元素实际上也是一个List,所以在实际使用时,为了找到具体某个位置的元素,我们使用Maze[row][col],这样类似于二维数组的表达,而在这个索引函数中,我们仅仅用一个元素idx,和mazeList[idx],就完成了。Python真够简洁的!!

def __getitem__(self, idx):return self.mazeList[idx]
def isExit(self, row, col):return (row==0 or row==self.mazeRow-1 or col==0 or col==self.mazeColumn-1)


最后一个,也是最核心的函数,主要思路就是在被调用时,在当前位置画一个圆,其颜色由改点的属性决定(通路?死路?已经试过的路?),还有需要调用pygame的wait和flip函数,以便让这种变化动态的和及时反映出来。

def updateScreen(self, row, col, val=None):#如果通过val传递了值,则将值填入对应位置if val:self.mazeList[row][col] = val#根据val值,确定颜色if val == PART_OF_PATH:color = (0,255,0)elif val == OBSTACLE:color = (255,0,0)elif val == TRIED:color = (0,0,255)elif val == DEAD_END:color = (255,0,0)else:color = None#之所以val控制颜色,是为了保证执行Step01时,不会在墙上画个圈if color:pygame.draw.circle(self.mazeScreen, color, (col*40+20, row*40+20), 5)#等待一段时间,以便动态显示(不然会很快完成,体现不出颜色变化)pygame.time.wait(50)pygame.display.flip()


下面就是程序的算法核心——递归法寻路,用一个单独的函数来实现(程序分析已在注释中体现):

#本函数用递归方法寻找迷宫出路,顺序是先向北,再向东,南,西def findRoute(maze, currentRow, currentCol):#Step01首先显示已到此点,也即在此处画一个圈maze.updateScreen(currentRow, currentCol)#以下设置递归(退出)的条件,肯能存在好几种可能# 1.该点为墙  2.如果该点已检查过或是死胡同 3.改点是出口  4.非1~3的情况if maze[currentRow][currentCol]==OBSTACLE:return Falseif maze[currentRow][currentCol]==TRIED or maze[currentRow][currentCol]==DEAD_END:return False#程序走到这里已经可以判定改点不是墙了,所以一旦该点的位置是边沿,则必是出口# 故该点定为路径上的一点# 若该点不是边沿,只能标记为已到过,并且继续尝试if maze.isExit(currentRow,currentCol):maze.updateScreen(currentRow,currentCol,PART_OF_PATH)return True# 若该点不是边沿,只能标记为已到过,并且继续尝试maze.updateScreen(currentRow,currentCol,TRIED)routeFound=findRoute(maze,currentRow-1,currentCol) or \findRoute(maze,currentRow,currentCol+1) or \findRoute(maze,currentRow+1,currentCol) or \findRoute(maze,currentRow,currentCol-1)if routeFound:#如果从此点递归出去找到了出口,则此点必在路径上maze.updateScreen(currentRow,currentCol,PART_OF_PATH)else:maze.updateScreen(currentRow,currentCol,DEAD_END)return routeFound



最后是一段main函数——程序执行的起点:

def main():myMaze=Maze('maze2.txt')# 以下是个保持屏幕正常显示和退出的最小循环体findRoute(myMaze, myMaze.startRow, myMaze.startCol)while 1:    for event in pygame.event.get():        if event.type == pygame.QUIT:            pygame.quit()            exit(0)    pygame.display.flip()# 执行程序main()

PS:

书中原程序下载位置(用Turtle函数实现):http://download.csdn.net/detail/pospro/8952965

经修改完善后的例子(用pygame实现,也即上文所用程序代码):http://download.csdn.net/detail/pospro/8947365



0 0
原创粉丝点击