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


        在《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

程序运行后的效果如下:

利用递归法和pygame实现迷宫寻路的动态展示_Prj002_第1张图片


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

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

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

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

2.读入迷宫的点阵文件 

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

def __init__(self, mazeDotFile): 
		pygame.init()

		#为了简化,这里直接给定了迷宫的行列值,其实也可以在读入文件时动态判断
		mazeColumn=22
		mazeRow=11

		self.mazeColumn=mazeColumn
		self.mazeRow=mazeRow

		brickImg = pygame.image.load("brick.png")   #每个砖块是40*40
		startflagImg = pygame.image.load("startflag.png") # 30*30
		self.mazeScreen=pygame.display.set_mode((40*mazeColumn, 40*mazeRow))
		self.mazeScreen.fill((255,255,255))

		self.startRow=0
		self.startCol=0

		self.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=y
					self.startCol=x
					self.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 False
	if 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



你可能感兴趣的:(python,pygame,编程实例,递归法,迷宫寻路)