• A*算法实现寻找较优路径


        【qboy】原创 2013年2月2日

        好久没回到这里来写了,回家过年之前再写一篇吧。这是在2012年11月到12月之间做的一个游戏中所采用的算法。跟大家分享一下。

    一、A*算法的简介

        在大学时,在一个人工智能的选修课,我第一次接触了A*算法,也采用这个算法实现课堂上一个作业8数码问题。

        简单的说A*算法的核心就是F=G+H;G为到第i步经过的步数,H为到达目的地预计还需要通过多少步,即是一个可能值(或者排除一切障碍的最优值,视问题情况而定),F即是两者相加,对当前情况下的所有F值进行排序,得到F的最优的一步进行第i+1步,直到H值为0。通过这个公式我们可以看出A*算法照顾了过去,也预测着未来

    二、问题背景

          我们游戏的背景是在一个有限的海洋中(可以定义为一个二维数组),敌方船向我方船进行逼近,直到撞到我方船为止。在海洋中,有一些零散的岛屿或者其他是在海洋中的障碍物,有些敌方船会以直线的方式逼近(即不考虑障碍物,撞到障碍会死亡),而另一些船AI较高会绕开障碍物向我方逼近。

            如下图所示,五角形是我方船位置,即目标位置,菱形和五边形是敌方船,菱形是直线逼近,无智能,五边形是有AI,每艘船可以沿着八个方向进行行走,黄色方块是障碍物。那么菱形会右下撞向障碍物,五边形以实线方向逼近我方船,而不是走最近虚线。当然游戏是回合制,一次只能走特定的步数。

            

    三、实现步骤

            为了解决以上问题,我们采用了A*算法(由于游戏是IOS上,所以很多都是ObjC,不过此方法主要还是采用C语言来实现)。

            1、我们设定了一个即二维数组(世界环境),描述各船和障碍物所在的位置。

            2、定义在寻路过程中的数据结构:

    typedef struct ShipPointAStar

    {

        ShipPoint point;//所在位置

        int GValue;//已经过的步数,G

        int HValue;//H值

        int FValue;//=G+H

        struct ShipPointAStar *prePoint;//前一步,这是为了逆着找到要走的点

    }ShipPointAStar;

    3、定义H函数和G函数:

    //A star 算法中的H 

    // 输入:curPoint为当前所在点,destPoint为目标点

    //输出:H值

    //原理是:假设没有障碍的情况下两点之间的最短距离数

    CG_INLINE int H(ShipPoint curPoint,ShipPoint destPoint)

    {

        ShipPoint prePoint = curPoint;

        int numStep = 0;//预测的步数

        while (!EqualShipPoint(prePoint, destPoint)) {//当前点是否与目标点重合

            prePoint = getNextShipPoint(prePoint, destPoint);//获取下一步

            numStep++;

        }

        return numStep;

    }

    G的求法:

    定义:

    G(i)=G(i-1)+1;

    G(0)=0;

    根据数学知识可以推算出G(i)=i;其中i为分解步数。

    4、构造可选区域的,由于船最多往8个方向移动,则把当前步下的所有可能方向,并计算出相应的H值和G值,求出F值。

    5、对所有的可能情况(未走过的点)进行F值排序,取出F值最小的点进行下一步展开,在这采用的是冒泡排序所以就不贴出来了,有机会再改成快速排序。

    当出现点与目标点重合时查找结束,或者当所有的能展开的点都已排除,则查找失败(不可达)。

    当找到路径时我们逆着把当前点下一步点返回,这已经不在我们讨论范围了。

    以下是算法的程序段:

    //通过A*算法 得到移动位置

    //输入:srcPoint源点, destPoint目标点,arrmap地点二维数组,我们定义1是不可走区域,mapW是二维数组的行数,mapH是二维数组的列数 maxFValue是扩展功能,即最大F值,当超过时不再寻路,0时没有限制。

    //输出:船的下个行动点

    CG_INLINE ShipPoint getNextShipPointByAStarAndMaxFValue(ShipPoint srcPoint,ShipPoint destPoint,int **arrmap,int mapW,int mapH,int maxFValue)

    {

        ShipPoint nextPoint = ShipPointCopy(srcPoint);

        ShipPointAStar star;

        star.GValue = 0;

        star.HValue = H(nextPoint, destPoint);

        star.FValue = star.GValue+star.HValue;

        star.point=nextPoint;

        star.prePoint = NULL;

        

        ShipPointAStar* stararr =malloc(sizeof(ShipPointAStar)*mapW*mapH);//我个人觉得最多只有地图中的一半个节点会进入数组

        int num = 0;

        stararr[num] = star;

        num ++;

        int pi =0;

        while (!EqualShipPoint(stararr[pi].point, destPoint)) //已到达目标

        {

            nextPoint = stararr[pi].point;

            

            if (maxFValue > 0 && stararr[pi].FValue>maxFValue) {

                break;

            }

            

            int minX = nextPoint.ShipX > 0 ? nextPoint.ShipX - 1 : 0;

            int maxX = nextPoint.ShipX < mapW - 1 ? nextPoint.ShipX + 1 : mapW-1;

            

            int minY = nextPoint.ShipY > 0 ? nextPoint.ShipY - 1 : 0;

            int maxY = nextPoint.ShipY < mapH - 1 ? nextPoint.ShipY + 1 : mapH-1;

            

            for (int i = minY ; i <= maxY; i++) {

                for (int j = minX; j <= maxX; j++) {

                    if (arrmap[i][j] == 1) //不可走

                    {

                        continue;

                    }

                    else

                    {

                        ShipPoint p =ShipPointMake(j, i);

                        bool blExist = NO;

                        for (int k = 0; k<num; k++) {

                            if (EqualShipPoint(p, stararr[k].point)) {

                                blExist = YES;

                                break;

                            }

                        }

                        if (!blExist) {

                            ShipPointAStar s;

                            s.GValue = stararr[pi].GValue+1;

                            s.HValue = H(p, destPoint);

                            s.FValue = s.GValue+s.HValue;

                            s.point=p;

                            s.prePoint = &stararr[pi];

                            stararr[num++] = s;

                        }

                    }

                }

            }

            SortShipPointAStar(stararr, num);

            pi++;

            if (pi>=num) {

                break;

            }

        }

        

        for (int i = 0; i<num; i++) {

            ShipPoint p = stararr[i].point;

            FCLOG(@"X = %i Y = %i,GValue = %i,FValue = %i",p.ShipX,p.ShipY,stararr[i].GValue,stararr[i].FValue);

        }

        

        FCLOG(@"Arr Num = %i",num);

        

        ShipPoint rstPoint= ShipPointNull();

        

        if (EqualShipPoint(stararr[pi].point, destPoint)) {

            FCLOG(@"循路成功");

            //往回走路径

            if (stararr[pi].prePoint) {

                ShipPoint p = stararr[pi].point;

                FCLOG(@"X = %i Y = %i,GValue = %i,FValue = %i",p.ShipX,p.ShipY,stararr[pi].GValue,stararr[pi].FValue);

                ShipPointAStar *preP = stararr[pi].prePoint;

                if (preP->prePoint!=NULL) {

                    while (preP->prePoint->prePoint!=NULL) {

                        p = preP->point;

                        CCLOG(@"X = %i Y = %i,GValue = %i,FValue = %i",p.ShipX,p.ShipY,preP->GValue,preP->FValue);

                        preP = preP ->prePoint;

                    }

                    p = preP->point;

                    FCLOG(@"X = %i Y = %i,GValue = %i,FValue = %i",p.ShipX,p.ShipY,preP->GValue,preP->FValue);

                    rstPoint = preP->point;

                }

                else

                {

                    rstPoint = p;

                }

                

            }

        }

        else

        {

            rstPoint = ShipPointNull();

            FCLOG(@"循路失败");

        }

        free(stararr);

        return rstPoint;

    }

    四、缺陷问题

    通过以上分析,我们把船的寻路给找到了。但是我们在实际发现了以下两个问题:

    1、由于可走路径比较多(8个方向),我们寻找出的路径只是好几条较优路径中的一条。如下图中两个艘船就有三条路径。

     

    2、由于在搜索时采用的是先上后下,先左后右的搜索顺序,所以在上图中敌方船的行走就不是中间那一条,而是上面那条,这样会引出船的方向可能并不是用户(玩家)所希望的,船的方向和位置会决定后续回会中的走法。

    解决以上问题的办法:

    一开始我想到的办法是将船的方向不要设定为8个方向,而是只有4个方向,上下左右。但是如果两艘船的位置是在斜线方向,则两者也有可能会出现多条路径,况且这也符合实际情况。。

    当写到这儿的时候,我突然之间想到一个办法,不知道可行不,先写出来再说,再设定每个方向的权重,比如,通过两个点之间距离来决定权重,而不是步数。例如上图中,设定每个方块宽度为1,最上一条和最下面一条的距离就应该是,而中间的一条的路径是2。这样我们就可以用距离来计算G,H,F值,而不是之前的步数了。

    【Qboy】
  • 相关阅读:
    不用写代码的框架
    bat执行python脚本,执行多条命令
    VMware-workstation-full-15.1.0-13591040安装破解-附件密钥
    w10谷歌chrome关闭自动更新
    谷歌安装提示已经安装高版本解决
    python项目三方库导出导入 requirements.txt文件
    点阴影
    goto gamedev blog
    20135315-信息安全系统设计基础第五周学习总结
    win10 +python3.6环境下安装opencv以及pycharm导入cv2有问题的解决办法
  • 原文地址:https://www.cnblogs.com/qboy/p/2890552.html
Copyright © 2020-2023  润新知