美文网首页
A*寻路初学-_-

A*寻路初学-_-

作者: OLD沈3 | 来源:发表于2019-05-15 23:13 被阅读0次

    A星寻路是一种非常有趣的寻路算法。

    相比较bfs与dfs它更具体更科学。

    个人推荐讲的最清楚的一篇文章

    http://www.gamedev.net/reference/articles/article2003.asp

    以下是我学习时的一些理解

    *地图中的每个节点 都有自己对于终点、起点的移动代价。

    F = G + H

    F 是成本总和

    G = 从起点移动到指定方格的移动代价。

    H = 从指定的方格移动到终点 B 的估算成本。

    -------------------------------------------------------------------------------

    1.我们会从终点开始进行遍历,把当前的节点放到open list。

    添加节点相邻节点到open list 并且计算他们的代价。

    如果发现更少代价,则更新值。

    2.我们会从起点开始再次遍历,把每个节点再次放入open list。

    添加相邻节点到open list 并且计算他们的代价。

    如果发现更少代价,则更新值。

    最后的路径是这么产生的:反复遍历 open list ,选择 F 值最小的方格。

    -----------------------------------------------------------------------------------

    这张图看了就能理解了

    以当前节点看相邻节点(上下左右1步可达) 用 ‘+ 10’ 表示基础代价

    以当前节点看 左下 右上 右下 左上 斜角节点时,用 ‘+14’ 计算基础代价

    -为什么代价这么取值?

    之所以,是因为实际的对角移动距离是 2 的平方根,或者是近似的 1.414 倍的横向或纵向移动代价。使用 10 和 14 就是为了简单起见。比例是对的,我们避免了开放和小数的计算。

    -我的python代码

    # 定义节点类,节点类呢会有一些属性来区别自身与其他节点的区别

    class Node:

        def __int__(self):

            self.unable = False

            self.distanceFromDes = -1  # 距离终点的距离

            self.distanceFromOri = -1  # 距离起点的距离

            self.allDistance = -1 # 所有距离总和

            self.added = False    # 是否添加过

            self.closed = False  # 是否已经走过

            self.parent = None    # 上层节点是什么

            self.x = -1          # 坐标位置 x轴

            self.y = -1

    #  2.生成地图 m长 n宽

    def GenerateMap(m, n):

        map = list()

        for j in range(m):

            nodeRow = list()

            map.append(nodeRow)

            for i in range(n):

                node = Node()

                node.y = j

                node.x = i

                node.unable = False

                node.distanceFromDes = -1  # 距离终点的距离

                node.distanceFromOri = -1  # 距离起点的距离

                node.allDistance = -1

                node.added = False

                node.closed = False

                node.parent = None

                nodeRow.append(node)

        return map

    # 放置障碍物

    def SetUnableMapNode(map, ls=()):  # 要求一个坐标队列,里边的点上的Node的unable == True

        for index in ls:

            map[index[0]][index[1]].unable = True

        return map

    # # 添加终点,并计算每个节点与终点的距离

    def GetDistanceFromDes(map, mapSize, endNode):  # map二维数组,mapsize(m,n),desIndex终点坐标

        # 这里再次对所有节点初始化 屬性為未添加

        for ls in map:

            for node in ls:

                node.added = False

        desNode = map[endNode[0]][endNode[1]]

        # 終點與終點的距離肯定0

        desNode.distanceFromDes = 0

        # TODO ...

        addedList = list()  # 已经加入的队列,已有值distanceFromDes

        needList = list()  # 待加入的队列,需要评估值distanceFromDes

        addedList.append(desNode) # 将终点节点加入到已添加列表

        desNode.added = True      # 更改终点节点的属性为已添加

        while(len(addedList) != 0):  # 当地图上所有可以遍历的点还没全确定

            while(len(addedList) != 0):  # 当一个大循环中,addedList还没被needList取代

                # 从addedList中选出来的一个点,找needList中的needNode

                mainNode = addedList.pop(0)

                #print("main node:",mainNode)

                # 主节点的距离是从addedList中拿出来的node距离终点的距离

                mainDistanceFromDes = mainNode.distanceFromDes

                y = mainNode.y

                x = mainNode.x

                print("mainDistanceFromDes:",mainDistanceFromDes)

                print("y:",y)

                print("x:",x)

                for needNodey in (y + 1, y, y - 1):

                    # 超出的情况就忽略了

                    #print("needNodey:",needNodey)

                    if needNodey < 0 or needNodey >= mapSize[0]:

                        continue

                    for needNodex in (x + 1, x, x - 1):

                        #print("inner needNodex:",needNodey)

                        if needNodex < 0 or needNodex >= mapSize[1]:

                            continue

                        needNode = map[needNodey][needNodex]  # 坐标不出界

                        if needNode.unable == True or needNode.added == True:

                            continue  # 坐标也满足add的要求

                        yOffset = needNodey - y

                        xOffset = needNodex - x

                        allOffset = yOffset + xOffset

                        print("yOffset:",yOffset,"xOffset:",xOffset,"allOffset=",allOffset) 

                        if allOffset == 1 or allOffset == -1:

                            distanceFromDes = mainDistanceFromDes + 1

                        elif allOffset == -2 or allOffset == 0 or allOffset == 2:

                            distanceFromDes = mainDistanceFromDes + 1.4

                        else:

                            print("终点代价计算错误")

                        if needNode in needList:  # 设置needNode的距离,要求最小

                            if distanceFromDes < needNode.distanceFromDes:

                                needNode.distanceFromDes = distanceFromDes

                        else:  # needNode 不在needList中 distanceFromDes一定是-1

                            needNode.distanceFromDes = distanceFromDes

                            needList.append(needNode)

                        # print(needNode.y,needNode.x,needNode.distanceFromDes)

            # needList 装满了 addedList排空了

            addedList = needList

            for node in addedList:

                node.added = True

            needList = list()

        return map

    # # 添加起点,并生成起点到终点的节点队列

    def GetMinDistanceNodeList(map, mapSize, oriIndex, desIndex):

        for ls in map:

            for node in ls:

                node.added = False

        openedList = list()

        node = map[oriIndex[0]][oriIndex[1]]

        node.distanceFromOri = 0

        node.allDistance = 0

        openedList.append(node)

        node.added = True

        while len(openedList) != 0:

            # 从openlist拿出一个节点

            node = openedList.pop(0)

            # 标示为close过

            node.closed = True

            if node.y == desIndex[0] and node.x == desIndex[1]:

                finalListNeedReverse = list()

                while node != None:

                    finalListNeedReverse.append(node)

                    node = node.parent

                finalListNeedReverse.reverse()

                return finalListNeedReverse

            neighboursList = list()

            y = node.y

            x = node.x

            parentDistanceFromOri = node.distanceFromOri

            for needNodey in (y + 1, y, y - 1):

                if needNodey < 0 or needNodey >= mapSize[0]:

                    continue

                for needNodex in (x + 1, x, x - 1):

                    if needNodex < 0 or needNodex >= mapSize[1]:

                        continue

                    needNode = map[needNodey][needNodex]  # 坐标不出界

                    if needNode.unable == True or needNode.closed == True or needNode.added == True:

                        continue  # 坐标也满足add的要求

                    yOffset = needNodey - y

                    xOffset = needNodex - x

                    allOffset = yOffset + xOffset

                    if allOffset == 1 or allOffset == -1:

                        distanceFromOri = parentDistanceFromOri + 1

                    elif allOffset == -2 or allOffset == 0 or allOffset == 2:

                        distanceFromOri = parentDistanceFromOri + 1.4

                    else:

                        print("终点代价计算错误")

                    if needNode in neighboursList:  # 设置needNode的距离,要求最小

                        if distanceFromOri < needNode.distanceFromOri:

                            needNode.distanceFromOri = distanceFromOri

                    else:  # needNode 不在needList中 distanceFromDes一定是-1

                        needNode.distanceFromOri = distanceFromOri

                        neighboursList.append(needNode)  # 距离计算完成

            for needNode in neighboursList:  # 开始添加至openedList

                needNode.parent = node

                needNode.allDistance = needNode.distanceFromOri + needNode.distanceFromDes

                needNode.added = True

                openedList.append(needNode)

                # print(needNode.x,needNode.y,needNode.allDistance)

            openedList.sort(key=lambda x: x.allDistance)  # 最小距离的排在前边

        print("无路可循!")

        return None

    # 打印具体的方向

    def TestGetMinDistanceNodeList(map, mapSize, oriIndex, desIndex):

        finalList = GetMinDistanceNodeList(

            map, mapSize, oriIndex, desIndex)  # 添加起点,并生成起点到终点的节点队列

        # 行动路线

        directions = (('↘', '↓', '↙'), ('→', "S", '←'), ('↗', '↑', '↖'))

        print('行动路线示意')

        for nodeRow in map:

            for node in nodeRow:

                if node in finalList:

                    #print('  *  ',end ='')

                    parent = node.parent

                    if parent != None:

                        if node.y != desIndex[0] or node.x != desIndex[1]:

                            (y, x) = (parent.y - node.y+1, parent.x - node.x+1)

                            print('  '+directions[y][x]+'  ', end='')

                        else:

                            print('Final', end='')

                    else:

                        print('Start', end='')

                else:

                    if node.allDistance != -1:

                        print('{:^4.1f}'.format(node.allDistance), end=" ")

                    else:

                        print('  X  ', end=" ")

            print()

        print()

    def aStartGo(startNode=(4,6),endNode=(0,0)):

        """

        两个参数分别为起点和终点节点的坐标

        """

        m = 5  # 设置地图的长

        n = 7  # 设置地图的宽

        map = GenerateMap(m, n)  # 2.生成地图节点 

        # for nodeRow in map:

        #    for node in nodeRow:

        #        print("(",node.y,",",node.x,")",end=" ")

        # 放的障碍物位置       

        obstacleList = [(4, 3),(3,3),(2,3),(0,1),(1,1),(2,1)] 

        map = SetUnableMapNode(map, obstacleList)  # 在地图中添加障碍

        # 添加终点,并计算节点与终点的距离(代价)

        GetDistanceFromDes(map, (m, n), endNode) 

        print("-------------------------------------")

        # 先打印一下所有节点的到终点的代价,总所周知终点自己到自己是没有代价的,上下左右的一次代价为10

        for nodeRow in map:

            for node in nodeRow:

                if node.distanceFromDes != -1:

                    print('{:^5.1f}'.format(node.distanceFromDes), end=" ")

                    #print('{:.2f}'.format(node.distanceFromDes), end=" ")

                else:

                    print('  X  ', end=" ")

            print()

        TestGetMinDistanceNodeList(

            map, (m, n), startNode, endNode)  # 终点距离测试完了,演示行动路线

    #运行a*

    aStartGo()

    我的示例代码中,地图是 5 * 7 的,其中 startNode=(4,6),endNode=(0,0)

    看看我打印的结果把

    最后附上一个视频https://video.tudou.com/v/XNDE1NjMyMTUwMA==.html

    相关文章

      网友评论

          本文标题:A*寻路初学-_-

          本文链接:https://www.haomeiwen.com/subject/kfevaqtx.html