1. 程式人生 > >python3.6實現的A星演算法

python3.6實現的A星演算法

A星演算法原理:

    原理我就不再贅述,可以參考這篇部落格https://blog.csdn.net/hitwhylz/article/details/23089415

程式碼實現:

    首先新增兩個通用類Array2D和Point:

class Array2D:
    """
        說明:
            1.構造方法需要兩個引數,即二維陣列的寬和高
            2.成員變數w和h是二維陣列的寬和高
            3.使用:‘物件[x][y]’可以直接取到相應的值
            4.陣列的預設值都是0
    """
    def __init__(self,w,h):
        self.w=w
        self.h=h
        self.data=[]
        self.data=[[0 for y in range(h)] for x in range(w)]


    def showArray2D(self):
        for y in range(self.h):
            for x in range(self.w):
                print(self.data[x][y],end=' ')
            print("")

    def __getitem__(self, item):
        return self.data[item]

class Point:
    """
    表示一個點
    """
    def __init__(self,x,y):
        self.x=x;self.y=y

    def __eq__(self, other):
        if self.x==other.x and self.y==other.y:
            return True
        return False
    def __str__(self):
        return "x:"+str(self.x)+",y:"+str(self.y)

    Array2D是為了簡化二維陣列的建立,Point是為了表示一個點,並且過載等號運算子,可以判斷兩個Point座標是否相等.

    AStar類:

    
class AStar:
    """
    AStar演算法的Python3.x實現
    """
    class Node:    #描述AStar演算法中的節點資料
        def __init__(self,point,endPoint,g=0):
            self.point=point        #自己的座標
            self.father=None        #父節點
            self.g=g                #g值,g值在用到的時候會重新算
            self.h=(abs(endPoint.x-point.x)+abs(endPoint.y-point.y))*10  #計算h值

    def __init__(self,map2d,startPoint,endPoint,passTag=0):
        """
        構造AStar演算法的啟動條件
        :param map2d: Array2D型別的尋路陣列
        :param startPoint: Point型別的尋路起點
        :param endPoint: Point型別的尋路終點
        :param passTag: int型別的可行走標記(若地圖資料!=passTag即為障礙)
        """
        #開啟表
        self.openList=[]
        #關閉表
        self.closeList=[]
        #尋路地圖
        self.map2d=map2d
        #起點終點
        self.startPoint=startPoint
        self.endPoint=endPoint
        #可行走標記
        self.passTag=passTag

    def getMinNode(self):
        """
        獲得openlist中F值最小的節點
        :return: Node
        """
        currentNode=self.openList[0]
        for node in self.openList:
            if node.g+node.h<currentNode.g+currentNode.h:
                currentNode=node
        return currentNode

    def pointInCloseList(self,point):
        for node in self.closeList:
            if node.point==point:
                return True
        return False

    def pointInOpenList(self,point):
        for node in self.openList:
            if node.point==point:
                return node
        return None

    def endPointInCloseList(self):
        for node in self.openList:
            if node.point==self.endPoint:
                return node
        return None

    def searchNear(self,minF,offsetX,offsetY):
        """
        搜尋節點周圍的點
        :param minF:
        :param offsetX:
        :param offsetY:
        :return:
        """
        #越界檢測
        if minF.point.x+offsetX<0 or minF.point.x+offsetX>self.map2d.w-1 or minF.point.y + offsetY < 0 or minF.point.y + offsetY > self.map2d.h - 1:
            return
        #如果是障礙,就忽略
        if self.map2d[minF.point.x+offsetX][minF.point.y+offsetY]!=self.passTag:
            return
        #如果在關閉表中,就忽略
        if self.pointInCloseList(Point(minF.point.x+offsetX,minF.point.y+offsetY)):
            return
        #設定單位花費
        if offsetX==0 or offsetY==0:
            step=10
        else:
            step=14
        #如果不再openList中,就把它加入openlist
        currentNode=self.pointInOpenList(Point(minF.point.x+offsetX,minF.point.y+offsetY))
        if not currentNode:
            currentNode=AStar.Node(Point(minF.point.x+offsetX,minF.point.y+offsetY),self.endPoint,g=minF.g+step)
            currentNode.father=minF
            self.openList.append(currentNode)
            return
        #如果在openList中,判斷minF到當前點的G是否更小
        if minF.g+step<currentNode.g: #如果更小,就重新計算g值,並且改變father
            currentNode.g=minF+step
            currentNode.father=minF

    def start(self):
        '''
        開始尋路
        :return: None或Point列表(路徑)
        '''
        #1.將起點放入開啟列表
        startNode=AStar.Node(self.startPoint,self.endPoint)
        self.openList.append(startNode)
        #2.主迴圈邏輯
        while True:
            #找到F值最小的點
            minF=self.getMinNode()
            #把這個點加入closeList中,並且在openList中刪除它
            self.closeList.append(minF)
            self.openList.remove(minF)
            #判斷這個節點的上下左右節點
            self.searchNear(minF,0,-1)
            self.searchNear(minF, 0, 1)
            self.searchNear(minF, -1, 0)
            self.searchNear(minF, 1, 0)
            #判斷是否終止
            point=self.endPointInCloseList()
            if point:  #如果終點在關閉表中,就返回結果
                # print("關閉表中")
                cPoint=point
                pathList=[]
                while True:
                    if cPoint.father:
                        pathList.append(cPoint.point)
                        cPoint=cPoint.father
                    else:
                        # print(pathList)
                        # print(list(reversed(pathList)))
                        # print(pathList.reverse())
                        return list(reversed(pathList))
            if len(self.openList)==0:
                return None

    最後,進行程式碼測試:

    
if __name__ == '__main__':
    #建立一個10*10的地圖
    map2d=Array2D(10,10)
    #設定障礙
    map2d[4][0]= 1
    map2d[4][1] = 1
    map2d[4][2] = 1
    map2d[4][3] = 1
    map2d[4][4] = 1
    map2d[4][5] = 1
    map2d[4][6] = 1
    #顯示地圖當前樣子
    map2d.showArray2D()
    #建立AStar物件,並設定起點為0,0終點為9,0
    aStar=AStar(map2d,Point(0,0),Point(9,0))
    #開始尋路
    pathList=aStar.start()
    #遍歷路徑點,在map2d上以'8'顯示
    for point in pathList:
        map2d[point.x][point.y]=8
        # print(point)
    print("----------------------")
    #再次顯示地圖
    map2d.showArray2D()

執行效果: