迷宮與尋路可視化(一)深度優先搜索構建迷宮(DFS算法)

效果展示

在這裏插入圖片描述

基本思想

不論是DFS,BFS還是RFS,這些算法生成的迷宮本質上是一個二維矩陣網絡形式的生成樹,也就是說其中沒有迴路,同時從右上角的起點到迷宮中的每一點都有且僅有一條路徑,當然,到終點的路徑也是唯一的。

深度優先遍歷總是從當前最長的路徑的末端隨機選擇一個可擴展點進行擴展,如果出現迴路或者抵達邊界,那麼就回溯到最近的一個可擴展分枝。這種算法生成的迷路分枝相對較少,路徑也更長更曲折。

函數詳解

一、界面初始化

import pygame
import sys 
import math 
import random
#(1,1)(1,2)行列
# pygame 初始化
pygame.init()
# 設置棋盤大小,DFS規定必須是奇數,每一個奇數的格子必須是節點
chess_number=89
IF_RANDOM_START_END=0#是否隨機終點和起點
TICK=100
BG=(144,136,145)#背景色
LINECOLOR=(112,73,46)#網格色
STARTCOLOR=(253,176,36)#起點格子的顏色
ENDCOLOR=(224,90,9)#終點(224,90,9)橙色     (252,61,63 )大紅
WALLCOLOR=(33,41,48)#牆壁的顏色

#迷宮在畫布上顯示的位置
START_POS=(50,50)
START_POSX=50
START_POSY=50
CELL_LENGTH=int(600/chess_number)#每個格子的像素大小
LINE_WIDTH=3#線的寬度
BIAS=5#中心偏差,取奇數,起點和終點離對角線的距離
# 設置背景框大小等pygame初始化操作
size = width, height = 2*START_POSX+chess_number*CELL_LENGTH,2*START_POSY+chess_number*CELL_LENGTH
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Ace Cheney made")


if IF_RANDOM_START_END==1:
	# 設置起始位置
	start_posx=random.randint(0,chess_number-1)
	start_posy=random.randint(0,chess_number-1)
	# 設置終點始位置
	end_posx=random.randint(0,chess_number-1)
	end_posy=random.randint(0,chess_number-1)
else:
	# 設置起始位置
	start_posx=0+BIAS
	start_posy=0+BIAS
	# 設置終點始位置
	end_posx=chess_number-1-BIAS
	end_posy=chess_number-1-BIAS

startpos=[start_posx,start_posy]
endpos=[end_posx,end_posy]
#設置牆格子列表
wallcell=[]

二、畫網格線以及迷宮

值得注意的是,這裏要考慮到不同圖層的上下關係,因爲後畫的圖層會覆蓋之前畫的圖層。

def draw():
	global pos
	global endflag,endpath
	for i in range(chess_number+1):
		pygame.draw.line(screen, LINECOLOR, (START_POSX,START_POSY+i*CELL_LENGTH), (START_POSX+chess_number*CELL_LENGTH,START_POSY+i*CELL_LENGTH), LINE_WIDTH)#橫線
		pygame.draw.line(screen, LINECOLOR, (START_POSX+i*CELL_LENGTH,START_POSY),(START_POSX+i*CELL_LENGTH,START_POSY+chess_number*CELL_LENGTH), LINE_WIDTH)#豎線#
	#畫牆
	drawwall(wallcell)
	drawcell(start_posx, start_posy, STARTCOLOR)#起點
	drawcell(end_posx, end_posy, ENDCOLOR) #終點

三、畫具體的一個迷宮單元格

一個單元格可以處於四種狀態,牆或者路、起點或是終點,顏色也各不相同。
參數是行座標,列座標和單元格種類。

def drawcell(i,j,cellkind):
	pygame.draw.rect(screen,cellkind,[START_POSX+CELL_LENGTH*j+(LINE_WIDTH-1),START_POSY+CELL_LENGTH*i+(LINE_WIDTH-1),CELL_LENGTH-LINE_WIDTH,CELL_LENGTH-LINE_WIDTH],0)

四、⭐️DFS算法

採用DFS算法生成隨機迷宮,輸入參數很簡單,就是棋盤的大小,因爲棋盤是正方形,所有隻有一個輸入參數。返回的事一個儲存全局地圖信息的二維數組,0爲無牆,1爲有牆。

值得注意的是,起點和終點的座標並不影響,由於DFS算法+完美迷宮的特性,某些特定單元格一定是路,而不是牆,那麼只需要任意在路單元格里選兩個點,令其爲起點或是終點也就可以了。

def DFScreatwall(chess_number):
	'''
	生成迷宮,有路爲0,牆爲1
	param:chess_number
	return:一個儲存全局地圖信息的二維數組,0爲無牆,1爲有牆
	'''
	neighborcell=[]
	maincell=[]
	#初始狀態下,迷宮內所有點都是牆壁,只有滿足條件,節點纔會由牆壁變成通路,而且節點和相鄰選中非節點之間的阻礙打破
	wallcell=[[1]*chess_number for i in range(chess_number)]
	wallcell[start_posx][start_posy]=0
	neighborcell.append(startpos)
	con=1
	#對一個節點進行選擇拓展,規則:選擇的拓展節點一定是孤立的
	while con:
		#在鄰居中選擇最後的一個,因爲最後一個儲存的是最深的
		[x,y]=neighborcell[-1]
		nextcell=[]
		#一個潛在的鄰居,如果他的上下左右的牆都是未打通的,那麼他是符合要求的
		if x-2>=1 and wallcell[x-2][y] ==1 and wallcell[x-1][y] == 1 and wallcell[x-3][y]==1 and wallcell[x-2][y+1]==1 and wallcell[x-2][y-1]==1:
			nextcell.append([x-2,y])
		if x+2<=chess_number-2 and wallcell[x+2][y] ==1 and wallcell[x+1][y] == 1 and wallcell[x+3][y]==1 and wallcell[x+2][y+1]==1 and wallcell[x+2][y-1]==1:
			nextcell.append([x+2,y])
		if y-2>=1 and wallcell[x][y-2]==1 and wallcell[x][y-3] ==1 and wallcell[x][y-1] == 1 and wallcell[x-1][y-2]==1 and wallcell[x+1][y-2]==1:
			nextcell.append([x,y-2])
		if y+2<=chess_number-2 and wallcell[x][y+2] ==1 and wallcell[x][y+1] == 1 and wallcell[x][y+3]==1 and wallcell[x-1][y+2]==1 and wallcell[x+1][y+2]==1:
			nextcell.append([x,y+2])
		try:
			#隨機選一個符合要求的鄰居作爲真正的鄰居
			num=random.randint(0,len(nextcell)-1)	
			neighborcell.append([nextcell[num][0],nextcell[num][1]])
			#鄰居和牆均需要被打通
			wallcell[nextcell[num][0]][nextcell[num][1]]=0
			if nextcell[num]==[x-2,y]:
				wallcell[x-1][y]=0
			elif nextcell[num]==[x+2,y]:
				wallcell[x+1][y]=0
			elif nextcell[num]==[x,y-2]:
				wallcell[x][y-1]=0
			elif nextcell[num]==[x,y+2]:
				wallcell[x][y+1]=0
		except:
			#數組越界
			#該路徑沒有合適的鄰居,那就把這個流程再來一次,在此之前必須先去除最後一個不滿足要求的
			neighborcell.pop()
		#當所有的關鍵點全聯通,結束遍歷
		odd1=0
		con=0
		for y in wallcell:
			if odd1%2==1:
				odd2=0
				for x in y: 
					if odd2%2==1:
						if x==1:
							con=1
							break
					odd2+=1
			if con==1:
				break
			odd1+=1
	return wallcell

五、畫牆

輸入參數是儲存迷宮的全局狀態信息,也就是DFS的返回值

def drawwall(wallcell):
	for i in range(chess_number):
		for j in range(chess_number):
			if wallcell[i][j]==1:
				drawcell(i, j, WALLCOLOR)			

源碼鏈接

深度優先搜索構建迷宮

敬請期待:基於Astar和Markov算法的迷宮尋路

Markov尋找最短路徑
在這裏插入圖片描述
Astar尋找最短路徑
在這裏插入圖片描述

Astar走迷宮
在這裏插入圖片描述
Markov走迷宮
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章