並查集生成迷宮及AStar算法自動尋找路徑

源代碼: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)

 

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