热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

A星(A-Star)寻路算法

unitaStarSearchPath;interfaceusesClasses,SysUtils;typepAStarPathNode^tAStarPathNode;tAStarP

unit aStarSearchPath;

interface
 uses Classes,SysUtils;
 
type
  pAStarPathNode=^tAStarPathNode;
  tAStarPathNode=record
      x,y:word;
      F:word;
      G:word;
      H:word;
      father:pAStarPathNode; //地图方格的父方格
      prev  :pAStarPathNode; //链表上的上个节点
      next  :pAStarPathNode; //链表上的下个节点
  end;
  tAStarMapBuf=array of byte;

  pAStarArea=^tAStarArea; //区域
  tAStarArea=record
      headNode:pAStarPathNode;
      endNode:pAStarPathNode;
  end;

var
  mapWidth,mapHeight:integer;
  mapToX,mapToY:integer;
  AStarOpenArea:pAStarArea;
  AStarCloseArea:pAStarArea;
  pAStarMapBuf:pByte;
  nearGBuf:array [0..2,0..2] of byte;
 
  function ArrayOf(buf:pByte;x,y,xSize:word):byte;
  function AStarFindPath(var returnPath:tstringlist;fromX,fromY,toX,toY,mapW,mapH:word;mapBuf:pByte):boolean;
  function AStarGetHValue(fX,fY:word):word;
  function AStarGetGValue(fX,fY,tX,tY:word):word;
  procedure AStarInitNearG(x,y:word);
  procedure AStarInsertByF(pHead:pAStarArea;thisNode:pAStarPathNode);
  function AStarSearchMinF(fatherNode:pAStarPathNode):pAStarPathNode;

implementation

  //数组与地图XY的对应关系: ary[y,x] 这样就能对应到地图的(x,y)
  //因此传入的地图数组数据,第1个是Y,第2个是X,要注意与数据的位置对应

  function AStarFindPath(var returnPath:tstringlist;fromX,fromY,toX,toY,mapW,mapH:word;mapBuf:pByte):boolean;
  var
    p:pAStarPathNode;
    i:integer;
    s:string;
  begin
     result:=false;
     pAStarMapBuf:=mapBuf;
     mapToX:=toX;
     mapToY:=toY;
     mapWidth:=mapW;
     mapHeight:=mapH;

     new(AStarCloseArea);    //建立开放区
     AStarCloseArea.headNode:=nil;
     AStarCloseArea.endNode:=nil;

     new(AStarOpenArea);     //建立封闭区
     AStarOpenArea.headNode:=nil;
     AStarOpenArea.endNode:=nil;

     new(p);                //起点加入开放区
     p.x:=fromX;
     p.y:=fromY;
     p.G:=0;
     p.H:=AStarGetHValue(fromx,fromY);
     p.F:=p.G+p.H;
     p.next:=nil;
     p.father:=nil;
     AStarOpenArea.headNode:=p;
     AStarOpenArea.endNode:=p;

     while true do
       begin
        if (p.x=tox) and (p.y=toy) then break; //找到终点就退出
        p:=AStarSearchMinF(p);
        if p=nil then exit;
       end;
     result:=true;
     while true do //从终点开始延者父节点返回到起点就是路径
       begin
         if p=nil then break;
         s:=inttostr(p.x)+','+inttostr(p.y);
         returnPath.Insert(0,s);
         p:=p.father;
       end;
  end;

  //计算到目标点的H值
  function AStarGetHValue(fX,fY:word):word;
  var
    dx,dy:integer;
  begin
    dx:=abs(fX-mapToX);
    dy:=abs(fY-mapToY);
    result:=dx*10+dy*10;
  end;

  //计算周围8个方向的G值
  function AStarGetGValue(fX,fY,tX,tY:word):word;
  var
    dx,dy:integer;
  begin
    dx:=abs(fX-tX);
    dy:=abs(fY)-(tY);
    if (dx=0) and (dy=0) then result:=0
     else
      if dx*dy=0 then result:=10 else result:=14;
  end;

  //通过指针访问数组
  function ArrayOf(buf:pByte;x,y,xSize:word):byte;
  var
    j:integer;
  begin
     j:=y*xSize+x;
     inc(buf,j);
     result:=buf^;
  end; 

  //设置x,y周围8个各自的G值
  procedure AStarInitNearG(x,y:word);
  var
   i,j,v,x1,y1:integer;
   p:pbyte;
  begin
    p:=pAStarMapBuf; //全局地图指针
    fillChar(nearGBuf,9,1);
    for i:=-1 to 1 do  //Y
      begin
        y1:=y+i;
        for j:=-1 to 1 do //X
         begin
           if nearGBuf[i+1,j+1]=0 then continue; //由于路障被预先处理了
           x1:=x+j;
           //中间的点,超出地图范围的点不可用
           if ((i=0) and (j=0)) or (x1<0) or (y1<0) or (x1>mapWidth) or (y1>mapHeight) then v:=-1
             else
               begin
                  v:=ArrayOf(p,x1,y1,mapWidth); //看地图数据里这格是否是路障
                  v:=AStarGetGValue(x,y,x1,y1) * v; //计算临近的格G值
               end;

           if (v=0) and ((x1=x) or (y1=y))  then //格子平行方向不可行或竖向不可行,上下或左右的也不可行
             begin
                if y1=y then  //左右边右路障,因方格有角,路障下面和上面的格也不可过
                  begin
                     nearGBuf[i,j+1]:=0;
                     nearGBuf[i+1,j+1]:=v; //自己=0
                     nearGBuf[i+2,j+1]:=0;
                  end;
                if x1=x then
                  begin
                     nearGBuf[i+1,j]:=0;
                     nearGBuf[i+1,j+1]:=v;
                     nearGBuf[i+1,j+2]:=0;
                  end;
                continue;
             end;
           if v<0 then v:=0; //非超出范围的位置
           nearGBuf[i+1,j+1]:=v;
         end;
      end;
  end;

  //根据thisNode进行F值降序调整链接节点位置或者插入
  procedure AStarInsertByF(pHead:pAStarArea;thisNode:pAStarPathNode);
  var
    p,p2:pAStarPathNode;
    F:word;
  begin
    p:=pHead.headNode;
    while true do
      begin
        if thisNode.F          begin
             p2:=p.prev; //上个节点
             if p2=nil then //这个节点是头节点
               begin
                 thisNode.prev:=nil;//变成头属性
                 pHead.headNode:=thisNode; //插入第一个节点作为头
               end
             else
               begin
                p2.next:=thisNode; //跟上一个点对接
                thisNode.prev:=p2;
               end;

             p.prev:=thisNode; //插入后对接
             thisNode.next:=p;
             exit;
          end;
        if p.next=nil then //末尾
          begin
             if p=thisNode then exit; //就是自己啦
             p.next:=thisNode;
             thisNode.prev:=p;
             thisNode.next:=nil;
             pHead.endNode:=thisNode;
             exit;
          end;         
        p:=p.next;
      end;
  end;

  //从开发区中选出F值最小的点
  //原理:以基点为中心通过周围8方向辐射的方式不断的添加新格子(以基点作为父节点)到开放区
  //      (完成这个工作后这个辐射基点要加到封闭区)
  // 在辐射的过程中,同时检测早先被加入开放区的格子的G值是不是最小的(从辐射基点到该格子的G值)
  // 如果这个基点到该格子的G值更小,则变换这个格子的父节点为基点并重新计算F值
  //,然后在开放区中选出最小F值的格子,再继续以选定的点往外辐射,直至辐射到目的点
  function AStarSearchMinF(fatherNode:pAStarPathNode):pAStarPathNode;
  var
    x,y,x1,y1:integer;
    p,p2:pAStarPathNode;
    pX,pY:integer;
    g,h,f:word;
  begin
    pX:=fatherNode.x;
    pY:=fatherNode.y;
    AStarInitNearG(pX,pY);//周围的G值如何,路障,原点排除

    for y:=1 downto -1 do  //Y的3行
      begin
        for x:=1 downto -1 do //X的3列
          begin
             x1:=x+pX;    //尝试周围的坐标
             y1:=y+pY;
             g:=nearGBuf[y+1,x+1]; //有可能被预先清0了(路障在中间时)
             if g=0 then continue ;//跳过没有G值的
             p:=AStarOpenArea.headNode;
             while true do  //搜索此点是否在开发区内
               begin
                 if p=nil then break;
                 if (p.x=x1) and (p.y=y1) then break; //在开放区
                 p:=p.next;
               end;

             if p<>nil then //在开放区
               begin
                  g:=p.G+g;  //起点经由选中点到此点的G值
                  if g                    begin
                       p.father:=fatherNode;  //更新它的父节点
                       p.G:=g;
                       p.F:=p.G+p.H;//重新计算F值
                    end;
                  continue;//后面不用再搜索封闭区了
               end;
             p:=AStarCloseArea.headNode;
             while true do  //搜索封闭区
               begin
                  if p=nil then break;  //没有在封闭区
                  if (p.x=x1) and (p.y=y1) then break; //在封闭区
                  p:=p.next;
               end;
             if p=nil then  //这是个新点,设定父节点
               begin
                  new(p);
                  p.prev:=AStarOpenArea.endNode ; //接到最后的开放节点上
                  AStarOpenArea.endNode:=p;
                  p.next:=nil;
                  p.x:=x1;
                  p.y:=y1;
                  p.G:=fatherNode.G+g;
                  p.H:=AStarGetHValue(x1,y1);
                  p.F:=p.G+p.H;
                  p.father:=fatherNode; //父节点,保存是谁把它加入开放区的
                  AStarInsertByF(AStarOpenArea,p);//插入开发区中
               end;
          end; // next x
      end; //next y;

    //把此节点从开放区抽出来,做好开放区节点的重新连接
    if fatherNode.prev=nil then  //当前点是开放区的第一个
      begin
         p:=fatherNode.next;//下一个接的准备变成头
         p.prev:=nil;      //头属性
         AStarOpenArea.headNode:=p;//更新头
      end
    else
      begin
        p:=fatherNode.next;    //下个节点
        p.prev:=fatherNode.prev; //连到这个点的上个节点上
        fatherNode.prev.next:=p;
      end;

    //准备加到封闭区的末尾
    p:=AStarCloseArea.endNode;  //封闭区最末的点
    if p=nil then AStarCloseArea.headNode:=fatherNode; //如果没有,那这个也是头也是未
    AStarCloseArea.endNode:=fatherNode; //是未位
    fatherNode.prev:=nil;
    fatherNode.next:=nil;
    if p<>nil then //有其他的末位
      begin
       p.next:=fatherNode; //连到尾巴上
       fatherNode.prev:=p;
      end;
     
    AStarOpenArea.headNode.prev:=nil; //最小的F已经在首个,设置成首属性
    result:=AStarOpenArea.headNode;//返回F值最小的
  end;

end.

调用方法: AStarFindPath
地图数据为字节型的数组,大小为mapW x mapH,调用时传入该数组的地址(pByte) 


推荐阅读
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • ASP.NET中数据邦定效率问题的一点看法转载(自由的天空)
    在做Asp.NET开发的时候经常用到DataList、Repeater等,用这些控件的时候经常用到数据邦定,很多程序员都是按照MS提供的方法 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • java代码利用aspose,java初学者代码
    本文目录一览:1、aspose.cellsjava合并excel ... [详细]
  • 动态分页实现
    Code分页存储过程CREATEprocedurePagersqlstrnvarchar(4000),--查询字符串currentpageint,--第N页pagesizeint- ... [详细]
  • 提交后Activity4新 ... [详细]
  • DropDownList分层显示!
    publicstaticvoidBindDropFatherItem(DropDownListDropDownList){DropDownList.Items.Clear();st ... [详细]
  • SQL   AS关键字
    AS关键字1.as是别名关键字,换句话说就是重新给sql某个字段取个别名的关键字,但as本身并不改变sql的字段的名称,只是在使用的时候有 ... [详细]
  • NPOI创建Excel并导出数据列表DataSetdsExReportds;数据源创建NPOI对象HSSFWorkbookbooknewHSSFWorkbook();MemoryS ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
author-avatar
菠萝97
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有