源代碼:https://github.com/tzous/mazeastar
並查集生成迷宮參考 https://blog.csdn.net/qq_40693171/article/details/100716766
一、前半部分爲迷宮生成
import random
# 並查集生成迷宮
aa = 5
tree = [] # 節點地圖
isling = [] # 節點連通關係
for i in range(0, aa):
ta = []
for j in range(0, aa):
ta.append(-1) # 初始值爲-1
tree.append(ta)
for i in range(0, aa * aa):
tb = []
for j in range(0, aa * aa):
tb.append(-1) # 初始值爲-1
isling.append(tb)
def getnei(a): # 獲得鄰居號,上下左右四個節點 random
x = int(a / aa) # 要精確成整數
y = a % aa
mynei = [] # 儲存鄰居
if x - 1 >= 0:
mynei.append((x - 1) * aa + y) # 上節點
if x + 1 < aa:
mynei.append((x + 1) * aa + y) # 下節點
if y - 1 >= 0:
mynei.append(x * aa + y - 1) # 左節點
if y + 1 < aa:
mynei.append(x * aa + y + 1) # 右節點
ran = random.randint(0, len(mynei) - 1)
return (mynei[ran])
def search(a): # 找到根節點
if tree[int(a / aa)][a % aa] > 0: # 說明是子節點
return search(tree[int(a / aa)][a % aa])
else:
return a
def union(a, b): # 合併
a1 = search(a) # a根
b1 = search(b) # b根
if a1 != b1:
if tree[int(a1 / aa)][a1 % aa] < tree[int(b1 / aa)][b1 % aa]: # 這個是負數()
tree[int(a1 / aa)][a1 % aa] += tree[int(b1 / aa)][b1 % aa] # 個數相加 注意是負數相加
tree[int(b1 / aa)][b1 % aa] = a1 # b樹成爲a樹的子樹,b的根b1直接指向a,值>0
else:
tree[int(b1 / aa)][b1 % aa] += tree[int(a1 / aa)][a1 % aa]
tree[int(a1 / aa)][a1 % aa] = b1 # a所在樹成爲b所在樹的子樹,值>0
while search(0) != search(aa * aa - 1): # 並查集主要思路
num = int(random.randint(0, aa * aa - 1)) # 產生一個小於aa*aa-1的隨機數
neihbour = getnei(num) # 取一個鄰居
if search(num) == search(neihbour): # 檢查是否在同一個集合中
continue
else: # 不在同一個集合中則將兩個集合合併
isling[num][neihbour] = 1 # 表示 num 和 neihbour 兩節點連通
isling[neihbour][num] = 1
union(num, neihbour)
# 以下爲顯示迷宮
# 畫第一條橫線
s = "+"
for j in range(0, aa):
s = s + "-+"
print(s)
# 畫第一行至aa-1行格子及下面的橫線
for i in range(0, aa):
s = "|"
for k in range(0, aa - 1): # 防止最後一列溢出
s = s + " "
if isling[i * aa + k][i * aa + k + 1] == 1:
s = s + " "
else:
s = s + "|"
s = s + " |" # 追加畫最後一格
print(s)
# 畫格子下面的橫線,要檢測是否與下一行格子連通
s = "+"
for k in range(0, aa):
if i < aa - 1: # 防止最後一行溢出
if isling[i * aa + k][(i + 1) * aa + k] == 1:
s = s + " "
else:
s = s + "-"
s = s + "+"
else: # 追加畫最後一行橫線
s = s + "-+"
print(s)
二、A*算法尋找路徑
# AStar算法
#
class Node: # 節點
def __init__(self, x, y):
self.x = x # 節點座標
self.y = y
self.g = 0 # 到起點的長度
self.h = 0 # 到終點的長度
self.px = -1 # 父節點x
self.py = -1 # 父節點y
class AStar: # 算法類
def __init__(self, w, h, isling):
self.W = w # 地圖寬
self.H = h # 地圖高
self.Isling = isling # 節點連通關係
self.OpenSet = [] # 開放節點表
self.CloseSet = [] # 關閉節點表
def findPath(self, startNode, endNode): # 路徑查找
curNode = startNode # 將開始節點設爲當點節點
bFound = False
while (not bFound):
self.CloseSet.append(curNode) # 當前節點加入關閉節點
cura = curNode.x * self.W + curNode.y # 節點二維座標轉爲一維
arrs = []
if curNode.x - 1 >= 0:
arrs.append([curNode.x - 1, curNode.y]) # 左節點
if curNode.x + 1 < self.W:
arrs.append([curNode.x + 1, curNode.y]) # 右節點
if curNode.y - 1 >= 0:
arrs.append([curNode.x, curNode.y - 1]) # 上節點
if curNode.y + 1 < self.H:
arrs.append([curNode.x, curNode.y + 1]) # 下節點
for arr in arrs:
a = arr[0] * self.W + arr[1] # 節點二維座標轉爲一維
if self.Isling[cura][a] != 1: # 該節點與當前節點不連通,則跳過
continue
found = 0
for cnode in self.CloseSet: # 查找是否已在CloseSet
if cnode.x == arr[0] and cnode.y == arr[1]: # 在OpenSet中
found = 1
break
if found == 1: #在Closet中,則跳過
continue
node = Node(arr[0], arr[1])
node.g = curNode.g + 1 # 重新設置到起點的長度
node.h = abs(node.x - endNode.x) + abs(node.y - endNode.y) # 計算到終止節點的長度
node.px = curNode.x # 父節點改爲當前節點
node.py = curNode.y
if node.x == endNode.x and node.y == endNode.y: # 如果是終止節點,則表示已找到,返回
self.CloseSet.append(node)
bFound = True
return node
found = 0
i = -1
for onode in self.OpenSet: # 查找是否已在OpenSet
i = i + 1
if onode.x == node.x and onode.y == node.y: # 在OpenSet中
if node.g < onode.g: # 如果新g值更小,則更新節點
self.OpenSet[i].g = node.g
self.OpenSet[i].h = node.h
self.OpenSet[i].px = node.px
self.OpenSet[i].py = node.py
found = 1
break
if found == 0: # 如果不在OpenSet中,則新節點加入OpenSet
self.OpenSet.append(node)
# 在OpenSet中查找最小f=g+h值,設爲當前節點
f = 99999
i = -1
j = -1
for onode in self.OpenSet:
i = i + 1
if f > onode.g + onode.h:
f = onode.g + onode.h
j = i
if j < 0: # 找到了OpenSet爲空,表示找不到路徑
return None
else:
curNode = self.OpenSet[j]
del self.OpenSet[j]
astar = AStar(aa, aa, isling)
startNode = Node(0, 0)
endNode = Node(aa - 1, aa - 1)
node = astar.findPath(startNode, endNode)
if node == None:
print("走不通")
exit
astar.CloseSet.reverse()
for cnode in astar.CloseSet:
if cnode.x == node.x and cnode.y == node.y:
tree[node.x][node.y] = aa*aa
node.x = cnode.px
node.y = cnode.py
# 以下爲顯示迷宮解答
# 畫第一條橫線
s = "+"
for j in range(0, aa):
s = s + "-+"
print(s)
# 畫第一行至aa-1行格子及下面的橫線
for i in range(0, aa):
s = "|"
for k in range(0, aa - 1): # 防止最後一列溢出
if tree[i][k] == aa*aa:
s = s + "@"
else:
s = s + " "
if isling[i * aa + k][i * aa + k + 1] == 1:
s = s + " "
else:
s = s + "|"
if tree[i][aa-1] == aa*aa: # 追加畫最後一格
s = s + "@"
else:
s = s + " "
s = s + "|"
print(s)
# 畫格子下面的橫線,要檢測是否與下一行格子連通
s = "+"
for k in range(0, aa):
if i < aa - 1: # 防止最後一行溢出
if isling[i * aa + k][(i + 1) * aa + k] == 1:
s = s + " "
else:
s = s + "-"
s = s + "+"
else: # 追加畫最後一行橫線
s = s + "-+"
print(s)