不再依赖A*,利用C++编写全新寻路算法

不再依赖A*,利用C++编写全新寻路算法

分类C/C++ 1841人阅读 评论(8) 收藏 举报

目录(?)[+]

一,说在前面的话

大概在半年前,看见一到信息竞赛题:在任意方格阵中设置障碍物,确定起始点后,求这两点之间路径。当时觉得蛮有意思的,但是没有时间去做,今天花了两个小时来实现它。据说有一个更高级的寻路算法叫做a*,那我就把我的算法叫做W*。

这个算法主要用于解迷宫和实现战棋游戏(SLG)的寻路。


首先讲一讲我的算法的思路:
我们先确定起始点,然后从起点出发,按一定顺序判断这个位置上下左右是否有可走的位置,如果发现有可走的位置,则递归进入该位置的判断。在递归的同时记录所走的路线。当发现某个位置无路可走,则删除路线的最后一个位置并返回上级位置进行判断。如此反复尝试最终找到路线。


说了这么多,就来讲解一下代码吧。


二,讲解部分

包含头文件(全部都是stl中的):

  1. #include<map>
  2. #include<vector>
  3. #include<iostream>

为几个冗长的类型重命名,用来使后来的代码更明了。
copy
    typedefunsignedintuint;
  1. typedefstd::vector<int>CRow;
  2. //相当于把CLabyrinth定义成一个整型的二维数组
  3. typedefstd::vector<CRow>CLabyrinth;
定义一个类类型表示二维数组中的位置:
copy
    classCPoint
  1. {
  2. public:
  3. intcol;//列
  4. introw;//行
  5. public:
  6. //构造函数,接受行和列的初始化
  7. CPoint(intc=0,intr=0)
  8. :col(c)
  9. ,row(r)
  10. {
  11. return;
  12. }
  13. //赋值操作
  14. CPoint&operator=(constCPoint&pt)
  15. col=pt.col;
  16. row=pt.row;
  17. return*this;
  18. //比较操作
  19. booloperator==(returncol==pt.col&&row==pt.row;
  20. //判断该位置是否合法
  21. boolallRight()
  22. returncol>=0&&row>=0;
  23. };
  24. typedefstd::vector<CPoint>CRoute;

然后到了核心类类型CLabyrinthAI
copy
    {
  1. protected:
  2. //装有迷宫数据的二维数组
  3. CLabyrinthm_xLabyrinth;
  4. //起点位置
  5. CPointm_ptBeginning;
  6. //终点位置
  7. CPointm_ptEnding;
  8. //记录路线的数组
  9. CRoutem_vRoute;
  10. //枚举表示起点、终点的值
  11. enum{Beginning=-1,Ending=-2};
  12. //枚举表示障碍物与可走区的值
  13. enum{CanntGo=0,CanGo=1};
  14. //枚举是否找到终点
  15. enum{FoundEnding=0,NotFoundEnding=1};
  16. //判断某个位置是否已在路线数组中,用于别走重复的路
  17. boolisRepeat(boolbRes=false;
  18. CRoute::iteratorit=m_vRoute.begin();
  19. for(;it!=m_vRoute.end();it++){
  20. CPointpt0=*it;
  21. if(pt0==pt){
  22. bRes=true;
  23. break;
  24. }
  25. returnbRes;
  26. //将某一位置加入路线数组
  27. voidadvance(constCPoint&ptTo)
  28. m_vRoute.push_back(ptTo);
  29. //将路线数组最后一个位置弹出
  30. voidback()
  31. m_vRoute.pop_back();
  32. //判断某一位置是否是起点
  33. boolisBeginning(constCPoint&pt)
  34. returnm_ptBeginning==pt;
  35. //判断某一位置是否是终点
  36. boolisEnding(returnm_ptEnding==pt;
  37. /*-----------------核心算法------------------------*/
  38. //判断某一位置是否可以向上移动
  39. CPointcanUp(constCPoint&ptCurrent)//接受当前位置
  40. CPointptRes=CPoint(-1,-1);
  41. intcol=ptCurrent.col;
  42. introw=ptCurrent.row;
  43. if(row>0){
  44. CPointptNext=CPoint(col,row-1);//上移后位置
  45. //检查上移后位置是否已经走过,以免寻路过程中绕圈子进入死循环
  46. if(!isRepeat(ptNext)){
  47. //获得迷宫二维数组中上移后位置的属性(起点、终点、可走、障碍)
  48. intnAttr=m_xLabyrinth[ptNext.row][ptNext.col];
  49. //如果上移后位置为可走或到达终点,则设定返回值为上移后的位置
  50. if(nAttr==CanGo||nAttr==Ending){
  51. ptRes=ptNext;
  52. returnptRes;//如果上移后位置不可走则返回非法的位置
  53. //以下判断某一位置可否移动的原理大致与上相同,就不多说了
  54. //判断某一位置是否可以向下移动
  55. CPointcanDown(constCPoint&ptCurrent)
  56. CPointptRes=CPoint(-1,-1);
  57. intcol=ptCurrent.col;
  58. introw=ptCurrent.row;
  59. if(row<m_xLabyrinth.size()-1){
  60. CPointptNext=CPoint(col,row+1);
  61. intnAttr=m_xLabyrinth[ptNext.row][ptNext.col];
  62. returnptRes;
  63. //判断某一位置是否可以向左移动
  64. CPointcanLeft(if(col>0){
  65. CPointptNext=CPoint(col-1,row);
  66. //判断某一位置是否可以向右移动
  67. CPointcanRight(if(col<m_xLabyrinth[0].size()-1){
  68. CPointptNext=CPoint(col+1,0); background-color:inherit">/*
  69. *判断某一位置是否可以向四周移动,如果判断到某一位置可以移动,则递归进入该位置判断。
  70. *如果该位置没有任何位置可移动,则返会上级位置并且调用back函数。如果走到终点,
  71. *则立刻返回枚举值FoundEnding,上级位置检查到返回值为FoundEnding,也直接返回。
  72. */
  73. intfindRoute(intnRes=NotFoundEnding;//认返回值为没有找到终点
  74. CPointptNext=CPoint(-1,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> advance(ptCurrent);//将当前位置加入路线数组
  75. //判断当前位置是否是终点,如果是终点则不进行下面的判断,将返回值设置为找到终点
  76. if(isEnding(ptCurrent)){
  77. nRes=FoundEnding;
  78. }else{//按上左下右的顺序判断有无可走路径
  79. //尝试向上
  80. ptNext=canUp(ptCurrent);//获取向上走后的位置
  81. //判断向上走后的位置是否是合法位置,若不合法,则表明上走到了迷宫的边缘,或者上面没有可走路径
  82. if(ptNext.allRight()){
  83. //上述判断成功,则将向上移动后的位置传入给自己,进行递归。当该函数退出,查看返回值是否为找到终点。若找到终点则立刻返回FoundEnding
  84. if(findRoute(ptNext)==FoundEnding){
  85. returnnRes;
  86. //下列尝试四周位置是否可走的代码与上述大体相同,就不多说了
  87. //尝试向左
  88. ptNext=canLeft(ptCurrent);
  89. if(findRoute(ptNext)==FoundEnding){
  90. nRes=FoundEnding;
  91. returnnRes;
  92. //尝试向下
  93. ptNext=canDown(ptCurrent);
  94. //尝试向右
  95. ptNext=canRight(ptCurrent);
  96. //检测是否到达终点,若没有到达终点,则立刻从路线表中删除该位置
  97. if(nRes!=FoundEnding){
  98. back();
  99. //构造函数
  100. CLabyrinthAI()
  101. return;
  102. //带有初始化迷宫数组构造函数
  103. CLabyrinthAI(constCLabyrinth&vLabyrinth)
  104. m_xLabyrinth=vLabyrinth;
  105. getBeginning();
  106. getEnding();
  107. //初始化迷宫数组
  108. voidsetLabyrinth(//查找起点
  109. voidgetBeginning()
  110. uintnRow=0;
  111. for(;nRow<m_xLabyrinth.size();nRow++){
  112. CRowxRow=m_xLabyrinth[nRow];
  113. uintnCol=0;
  114. for(;nCol<xRow.size();nCoL++){
  115. intn=xRow[nCol];
  116. if(n==Beginning){
  117. m_ptBeginning=CPoint(nCol,nRow);
  118. break;
  119. //查找终点
  120. voidgetEnding()
  121. uintnRow=0;
  122. for(;nRow<m_xLabyrinth.size();nRow++){
  123. CRowxRow=m_xLabyrinth[nRow];
  124. uintnCol=0;
  125. for(;nCol<xRow.size();nCoL++){
  126. intn=xRow[nCol];
  127. if(n==Ending){
  128. m_ptEnding=CPoint(nCol,nRow);
  129. //调用核心算法函数输出获得的路线
  130. voidAI()
  131. findRoute(m_ptBeginning);
  132. if(!m_vRoute.empty()){
  133. CPointpt=*it;
  134. std::cout<<"("<<pt.row<<","<<pt.col<<")";
  135. if(it!=m_vRoute.end()-1){
  136. std::cout<<"->";
  137. else{
  138. std::cout<<std::endl;
  139. //如果没有找到路线到达终点
  140. std::cout<<"Sorrycannotfileanywaystogetending."<<std::endl;
  141. };
代码加上了注释,大家可以慢慢看。
如果上述过程把你搅晕了,那就用图来为你解答吧。

然后来到main函数

copy
    //用VC6.0貌似不需要给main传参数,那我就偷一下懒
  1. intmain()
  2. //定义迷宫数组,定义成C风格的二维数组方便查看
  3. intvLabyrinthArray[][4]={
  4. {1,-1,1}
  5. ,{1,1}
  6. };
  7. //以下代码为将C风格的二维数组导入成C++风格的二维数组
  8. intnRowNum=sizeof(vLabyrinthArray)/sizeof(vLabyrinthArray[0]);
  9. intnColNum=sizeof(vLabyrinthArray[0])/sizeof(int);
  10. CLabyrinthvLabyrinth;
  11. for(introw=0;row<nRowNum;row++){
  12. CRowxRow;
  13. intcol=0;col<nColNum;coL++){
  14. intn=vLabyrinthArray[row][col];
  15. xRow.push_back(n);
  16. vLabyrinth.push_back(xRow);
  17. //实例化CLabyrinthAI
  18. CLabyrinthAIxAI(vLabyrinth);
  19. //打出路线
  20. xAI.AI();
  21. //使程序暂停,方便查看数据
  22. system("Pause");
  23. return0;
  24. }

以上代码同样加了注释,相信了解C++的同学都能看懂。

运行截图:


(Dos的,有点丑……)

三,Javascript版

顺便我也把C++版的移植到了Javascript上,代码如下:

[javascript] copy
    functionCLabyrinthAI(){
  1. vars= s.m_xLabyrinth=newArray(newArray());
  2. s.m_ptBeginning={};
  3. s.m_ptEnding={};
  4. s.m_vRoute=newArray();
  5. s.Beginning=-1;
  6. s.Ending=-2;
  7. s.CannotGo=0;
  8. s.CanGo=1;
  9. s.FoundEnding=0;
  10. s.NotFoundEnding=1;
  11. CLabyrinthAI.prototype.initAI=function(){
  12. this;
  13. s.getBeginning();
  14. s.getEnding();
  15. CLabyrinthAI.prototype.isRepeat=function(pt){
  16. varbRes=false;
  17. for(varn=0;n<s.m_vRoute.length;n++){
  18. varpt0=s.m_vRoute[n];
  19. if(pt0.col==pt.col&&pt0.row==pt.row){
  20. CLabyrinthAI.prototype.advance=function(ptTo){
  21. this.m_vRoute.push(ptTo);
  22. CLabyrinthAI.prototype.back=this.m_vRoute.splice(this.m_vRoute.length-1,1);
  23. CLabyrinthAI.prototype.isBeginning=if(this.m_ptBeginning.col==pt.col&&this.m_ptBeginning.row==pt.row){
  24. return }else{
  25. CLabyrinthAI.prototype.isEnding=function(pt){
  26. this.m_ptEnding.col==pt.col&&this.m_ptEnding.row==pt.row){
  27. true;
  28. CLabyrinthAI.prototype.canUp=function(ptCurrent){
  29. varptRes={col:-1,row:-1};
  30. varcol=ptCurrent.col;
  31. varrow=ptCurrent.row;
  32. if(row>0){
  33. varptNext={col:col,row:row-1};
  34. if(!s.isRepeat(ptNext)){
  35. varnAttr=s.m_xLabyrinth[ptNext.row][ptNext.col];
  36. if(nAttr==s.CanGo||nAttr==s.Ending){
  37. CLabyrinthAI.prototype.canDown=if(row<s.m_xLabyrinth.length-1){
  38. CLabyrinthAI.prototype.canLeft=varptNext={col:col-1,row:row};
  39. CLabyrinthAI.prototype.canRight=if(col<s.m_xLabyrinth[0].length-1){
  40. varptNext={col:col+1,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CLabyrinthAI.prototype.allRight=function(p){
  41. if(p.col>=0&&p.row>=0){
  42. CLabyrinthAI.prototype.findRoute=function(ptCurrent){
  43. varnRes=s.NotFoundEnding;
  44. varptNext={col:-1,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> s.advance(ptCurrent);
  45. if(s.isEnding(ptCurrent)){
  46. nRes=s.FoundEnding;
  47. ptNext=s.canUp(ptCurrent);
  48. if(s.allRight(ptNext)){
  49. if(s.findRoute(ptNext)==s.FoundEnding){
  50. nRes=s.FoundEnding;
  51. ptNext=s.canLeft(ptCurrent);
  52. ptNext=s.canDown(ptCurrent);
  53. ptNext=s.canRight(ptCurrent);
  54. if(nRes!=s.FoundEnding){
  55. s.back();
  56. CLabyrinthAI.prototype.getBeginning=varnRow=0;nRow<s.m_xLabyrinth.length;nRow++){
  57. varxRow=s.m_xLabyrinth[nRow];
  58. varnCol=0;nCol<xRow.length;nCoL++){
  59. varn=xRow[nCol];
  60. if(n==s.Beginning){
  61. s.m_ptBeginning={col:nCol,row:nRow};
  62. CLabyrinthAI.prototype.getEnding=function(){
  63. varnRow=0;nRow<s.m_xLabyrinth.length;nRow++){
  64. varxRow=s.m_xLabyrinth[nRow];
  65. varnCol=0;nCol<xRow.length;nCoL++){
  66. varn=xRow[nCol];
  67. if(n==s.Ending){
  68. s.m_ptEnding={col:nCol,row:nRow};
  69. CLabyrinthAI.prototype.AI=function(data){
  70. s.m_xLabyrinth=data;
  71. s.initAI();
  72. s.findRoute(s.m_ptBeginning);
  73. returns.m_vRoute;
  74. };
设计原理和C++版差不多,只是没有CPoint类而已。

虽然这套算法是研究出来了,但是还不能判断是否为最近路线,因此有待更新。不过以现在的算法,开发一个SLG应该不是问题了。

※感谢我的哥哥与我一起讨论其中的原理。

代码下载:

http://files.cnblogs.com/yorhom/findRoute.rar

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图:提供一种方法顺序访问一个聚合对象中的每个元素,而又不想暴露该对象的内部表示。应用:STL标准库迭代器实现、Java集合类型迭代器等模式结构:心得:迭代器模式的目的是在不获知集合对象内部细节的同时能对集合元素进行遍历操作
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(BlockingIO):即传统的IO模型。(2)同步非阻塞IO(Non-blockingIO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的N
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定义一系列算法,把他们封装起来,并且使他们可以相互替换,使算法可以独立于使用它的客户而变化。应用:排序的比较方法、封装针对类的不同的算法、消除条件判断、寄存器分配算法等。模式结构:心得:对对象(Context)的处理操作可
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下定义作用于这些元素的新操作。应用:作用于编译器语法树的语义分析算法。模式结构:心得:访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方
命令模式(Command)命令模式(Command)[Action/Transaction]意图:将一个请求封装为一个对象,从而可用不同的请求对客户参数化。对请求排队或记录请求日志,以及支持可撤消的操作。应用:用户操作日志、撤销恢复操作。模式结构:心得:命令对象的抽象接口(Command)提供的两个
生成器模式(Builder)生成器模式(Builder)意图:将一个对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 应用:编译器词法分析器指导生成抽象语法树、构造迷宫等。模式结构:心得:和工厂模式不同的是,Builder模式需要详细的指导产品的生产。指导者(Director)使用C
设计模式学习心得《设计模式:可复用面向对象软件的基础》一书以更贴近读者思维的角度描述了GOF的23个设计模式。按照书中介绍的每个设计模式的内容,结合网上搜集的资料,我将对设计模式的学习心得总结出来。网络上关于设计模式的资料和文章汗牛充栋,有些文章对设计模式介绍生动形象。但是我相信“一千个读者,一千个
工厂方法模式(Factory Method)工厂方法模式(Factory Method)[Virtual Constructor]意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实力化延迟到子类。应用:多文档应用管理不同类型的文档。模式结构:心得:面对同一继承体系(Produc
单例模式(Singleton)单例模式(Singleton)意图:保证一个类只有一个实例,并提供一个访问它的全局访问点。应用:Session或者控件的唯一示例等。模式结构:心得:单例模式应该是设计模式中最简单的结构了,它的目的很简单,就是保证自身的实例只有一份。实现这种目的的方式有很多,在Java中
装饰者模式(Decorator)装饰者模式(Decorator)[Wrapper]意图:动态的给一个对象添加一些额外的职责,就增加功能来说,比生成子类更为灵活。应用:给GUI组件添加功能等。模式结构:心得:装饰器(Decorator)和被装饰的对象(ConcreteComponent)拥有统一的接口
抽象工厂模式(Abstract Factory)抽象工厂模式(Abstract Factory)[Kit]意图:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。应用:用户界面工具包。模式结构:心得:工厂方法把生产产品的方式封装起来了,但是一个工厂只能生产一类对象,当一个工厂需要生
桥接模式(Bridge)桥接模式(Bridge)[Handle/Body]意图:将抽象部分与它的实现部分分离,使他们都可以独立的变化。应用:不同系统平台的Windows界面。模式结构:心得:用户所见类体系结构(Window派生)提供了一系列用户的高层操作的接口,但是这些接口的实现是基于具体的底层实现
适配器模式(Adapter)适配器模式(Adapter)[Wrapper]意图:将类的一个接口转换成用户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。应用:将图形类接口适配到用户界面组件类中。模式结构:心得:适配器模式一般应用在具有相似接口可复用的条件下。目标接口(Targ
组合模式(Composition)组合模式(Composition)意图:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。应用:组合图形、文件目录、GUI容器等。模式结构:心得: 用户(Client)通过抽象类(Component)提供的公用接口统一
原型模式(Prototype)原型模式(Prototype)意图:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象。应用:Java/C#中的Clonable和IClonable接口等。模式结构:心得:原型模式本质上就是对象的拷贝,使用对象拷贝代替对象创建的原因有很多。比如对象的初始化构
什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。