不用依靠A*,使用C++撰写新的寻路算法

一,说在前面的话

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

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


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


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


二,讲解部分

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

[cpp]  view plain copy
  1. #include   
  2. #include   
  3. #include   

为几个冗长的类型重命名,用来使后来的代码更明了。

[cpp]  view plain copy
  1. typedef     unsigned int                uint;  
  2. typedef     std::vector<int>          CRow;  
  3. //相当于把CLabyrinth定义成一个整型的二维数组  
  4. typedef     std::vector         CLabyrinth;  
定义一个类类型表示二维数组中的位置:

[cpp]  view plain copy
  1. class CPoint  
  2. {  
  3.   
  4. public:  
  5.   
  6.     int col;            //列  
  7.     int row;            //行  
  8.   
  9. public:  
  10.   
  11.     //构造函数,接受行和列的初始化  
  12.     CPoint(int c = 0, int r = 0)  
  13.         : col(c)  
  14.         , row(r)  
  15.     {  
  16.         return;  
  17.     }  
  18.   
  19.     //赋值操作  
  20.     CPoint& operator=(const CPoint& pt)  
  21.     {  
  22.         col = pt.col;  
  23.         row = pt.row;  
  24.         return *this;  
  25.     }  
  26.   
  27.     //比较操作  
  28.     bool operator==(const CPoint& pt)  
  29.     {  
  30.         return col == pt.col && row == pt.row;  
  31.     }  
  32.   
  33.     //判断该位置是否合法  
  34.     bool allRight()  
  35.     {  
  36.         return col >= 0 && row >= 0;  
  37.     }  
  38.   
  39. };  
  40.   
  41. typedef     std::vector           CRoute;  

然后到了核心类类型CLabyrinthAI

[cpp]  view plain copy
  1. {  
  2.   
  3. protected:  
  4.   
  5.     //装有迷宫数据的二维数组  
  6.     CLabyrinth      m_xLabyrinth;  
  7.     //起点位置  
  8.     CPoint          m_ptBeginning;  
  9.     //终点位置  
  10.     CPoint          m_ptEnding;  
  11.     //记录路线的数组  
  12.     CRoute          m_vRoute;  
  13.   
  14. public:  
  15.   
  16.     //枚举表示起点、终点的值  
  17.     enum{Beginning = -1, Ending = -2};  
  18.     //枚举表示障碍物与可走区的值  
  19.     enum{CanntGo = 0, CanGo = 1};  
  20.     //枚举是否找到终点  
  21.     enum{FoundEnding = 0, NotFoundEnding = 1};  
  22.   
  23. protected:  
  24.   
  25.     //判断某个位置是否已在路线数组中,用于别走重复的路  
  26.     bool isRepeat(const CPoint& pt)  
  27.     {  
  28.         bool bRes = false;  
  29.         CRoute::iterator it = m_vRoute.begin();  
  30.         for(; it != m_vRoute.end(); it++){  
  31.             CPoint pt0 = *it;  
  32.             if(pt0 == pt){  
  33.                 bRes = true;  
  34.                 break;  
  35.             }  
  36.         }  
  37.         return bRes;  
  38.     }  
  39.   
  40.     //将某一位置加入路线数组  
  41.     void advance(const CPoint& ptTo)  
  42.     {  
  43.         m_vRoute.push_back(ptTo);  
  44.     }  
  45.   
  46.     //将路线数组最后一个位置弹出  
  47.     void back()  
  48.     {  
  49.         m_vRoute.pop_back();  
  50.     }  
  51.   
  52.     //判断某一位置是否是起点  
  53.     bool isBeginning(const CPoint& pt)  
  54.     {  
  55.         return m_ptBeginning == pt;  
  56.     }  
  57.   
  58.     //判断某一位置是否是终点  
  59.     bool isEnding(const CPoint& pt)  
  60.     {  
  61.         return m_ptEnding == pt;  
  62.     }  
  63.   
  64. /*-----------------核心算法------------------------*/  
  65.     //判断某一位置是否可以向上移动  
  66.     CPoint canUp(const CPoint& ptCurrent)   //接受当前位置  
  67.     {  
  68.         CPoint ptRes = CPoint(-1, -1);  
  69.         int col = ptCurrent.col;  
  70.         int row = ptCurrent.row;  
  71.         if(row > 0){  
  72.             CPoint ptNext = CPoint(col, row - 1);   //上移后位置  
  73.             //检查上移后位置是否已经走过,以免寻路过程中绕圈子进入死循环  
  74.             if(!isRepeat(ptNext)){  
  75.                 //获得迷宫二维数组中上移后位置的求是杂志属性(起点、终点、可走、障碍)  
  76.                 int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];  
  77.                 //如果上移后位置为可走或到达终点,则设定返回值为上移后的位置  
  78.                 if(nAttr == CanGo || nAttr == Ending){  
  79.                     ptRes = ptNext;  
  80.                 }  
  81.             }  
  82.         }  
  83.         return ptRes;   //如果上移后位置不可走则返回非法的位置  
  84.     }  
  85.   
  86.     //以下判断某一位置可否移动的原理大致与上相同,就不多说了  
  87.   
  88.     //判断某一位置是否可以向下移动  
  89.     CPoint canDown(const CPoint& ptCurrent)  
  90.     {  
  91.         CPoint ptRes = CPoint(-1, -1);  
  92.         int col = ptCurrent.col;  
  93.         int row = ptCurrent.row;  
  94.         if(row < m_xLabyrinth.size() - 1){  
  95.             CPoint ptNext = CPoint(col, row + 1);  
  96.             if(!isRepeat(ptNext)){  
  97.                 int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];  
  98.                 if(nAttr == CanGo || nAttr == Ending){  
  99.                     ptRes = ptNext;  
  100.                 }  
  101.             }  
  102.         }  
  103.         return ptRes;  
  104.     }  
  105.   
  106.     //判断某一位置是否可以向左移动  
  107.     CPoint canLeft(const CPoint& ptCurrent)  
  108.     {  
  109.         CPoint ptRes = CPoint(-1, -1);  
  110.         int col = ptCurrent.col;  
  111.         int row = ptCurrent.row;  
  112.         if(col > 0){  
  113.             CPoint ptNext = CPoint(col - 1, row);  
  114.             if(!isRepeat(ptNext)){  
  115.                 int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];  
  116.                 if(nAttr == CanGo || nAttr == Ending){  
  117.                     ptRes = ptNext;  
  118.                 }  
  119.             }  
  120.         }  
  121.         return ptRes;  
  122.     }  
  123.   
  124.     //判断某一位置是否可以向右移动  
  125.     CPoint canRight(const CPoint& ptCurrent)  
  126.     {  
  127.         CPoint ptRes = CPoint(-1, -1);  
  128.         int col = ptCurrent.col;  
  129.         int row = ptCurrent.row;  
  130.         if(col < m_xLabyrinth[0].size() - 1){  
  131.             CPoint ptNext = CPoint(col + 1, row);  
  132.             if(!isRepeat(ptNext)){  
  133.                 int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];  
  134.                 if(nAttr == CanGo || nAttr == Ending){  
  135.                     ptRes = ptNext;  
  136.                 }  
  137.             }  
  138.         }  
  139.         return ptRes;  
  140.     }  
  141.   
  142. /* 
  143. *判断某一位置是否可以向四周移动,如果判断到某一位置可以移动,则递归进入该位置判断。 
  144. *如果该位置没有任何位置可移动,则返会上级位置并且调用back函数。如果走到终点, 
  145. *则立刻返回枚举值FoundEnding,上级位置检查到返回值为FoundEnding,也直接返回。 
  146. */  
  147.     int findRoute(const CPoint& ptCurrent)  
  148.     {  
  149.         int nRes = NotFoundEnding;      //默认返回值为没有找到终点  
  150.         CPoint ptNext = CPoint(-1, -1);  
  151.   
  152.         advance(ptCurrent);         //将当前位置加入路线数组  
  153.   
  154.         //判断当前位置是否是终点,如果是终点则不进行下面的判断,将返回值设置为找到终点  
  155.         if(isEnding(ptCurrent)){  
  156.             nRes = FoundEnding;  
  157.         }else{                  //按上左下右的顺序判断有无可走路径  
  158.             //尝试向上  
  159.             ptNext = canUp(ptCurrent);  //获取向上走后的位置  
  160.             //判断向上走后的位置是否是合法位置,若不合法,则表明上走到了迷宫的边缘,或者上面没有可走路径  
  161.             if(ptNext.allRight()){  
  162.                 //上述判断成功,则将向上移动后的位置传入给自己,进行递归。当该函数退出,查看返回值是否为找到终点。若找到终点则立刻返回FoundEnding  
  163.                 if(findRoute(ptNext) == FoundEnding){  
  164.                     nRes = FoundEnding;  
  165.                     return nRes;  
  166.                 }  
  167.             }  
  168. //下列尝试四周位置是否可走的代码与上述大体相同,就不多说了  
  169.             //尝试向左  
  170.             ptNext = canLeft(ptCurrent);  
  171.             if(ptNext.allRight()){  
  172.                 if(findRoute(ptNext) == FoundEnding){  
  173.                     nRes = FoundEnding;  
  174.                     return nRes;  
  175.                 }  
  176.             }  
  177.             //尝试向下  
  178.             ptNext = canDown(ptCurrent);  
  179.             if(ptNext.allRight()){  
  180.                 if(findRoute(ptNext) == FoundEnding){  
  181.                     nRes = FoundEnding;  
  182.                     return nRes;  
  183.                 }  
  184.             }  
  185.             //尝试向右  
  186.             ptNext = canRight(ptCurrent);  
  187.             if(ptNext.allRight()){  
  188.                 if(findRoute(ptNext) == FoundEnding){  
  189.                     nRes = FoundEnding;  
  190.                     return nRes;  
  191.                 }  
  192.             }  
  193.         }  
  194.   
  195.         //检测是否到达终点,若没有到达终点,则立刻从路线表中删除该位置  
  196.         if(nRes != FoundEnding){  
  197.             back();  
  198.         }  
  199.   
  200.         return nRes;  
  201.     }  
  202. /*-----------------核心算法------------------------*/  
  203.   
  204. public:  
  205.   
  206.     //构造函数  
  207.     CLabyrinthAI()  
  208.     {  
  209.         return;  
  210.     }  
  211.   
  212.     //带有初始化迷宫数组构造函数  
  213.     CLabyrinthAI(const CLabyrinth& vLabyrinth)  
  214.     {  
  215.         m_xLabyrinth = vLabyrinth;  
  216.         getBeginning();  
  217.         getEnding();  
  218.     }  
  219.   
  220.     //初始化迷宫数组  
  221.     void setLabyrinth(const CLabyrinth& vLabyrinth)  
  222.     {  
  223.         m_xLabyrinth = vLabyrinth;  
  224.     }  
  225.   
  226.     //查找起点  
  227.     void getBeginning()  
  228.     {  
  229.         uint nRow = 0;  
  230.         for(; nRow < m_xLabyrinth.size(); nRow++){  
  231.             CRow xRow = m_xLabyrinth[nRow];  
  232.             uint nCol = 0;  
  233.             for(; nCol < xRow.size(); nCol++){  
  234.                 int n = xRow[nCol];  
  235.                 if(n == Beginning){  
  236.                     m_ptBeginning = CPoint(nCol, nRow);  
  237.                     break;  
  238.                 }  
  239.             }  
  240.         }  
  241.     }  
  242.   
  243.     //查找终点  
  244.     void getEnding()  
  245.     {  
  246.         uint nRow = 0;  
  247.         for(; nRow < m_xLabyrinth.size(); nRow++){  
  248.             CRow xRow = m_xLabyrinth[nRow];  
  249.             uint nCol = 0;  
  250.             for(; nCol < xRow.size(); nCol++){  
  251.                 int n = xRow[nCol];  
  252.                 if(n == Ending){  
  253.                     m_ptEnding = CPoint(nCol, nRow);  
  254.                     break;  
  255.                 }  
  256.             }  
  257.         }  
  258.     }  
  259.   
  260.     //调用核心算法函数,输出获得的路线  
  261.     void AI()  
  262.     {  
  263.         findRoute(m_ptBeginning);  
  264.         if(!m_vRoute.empty()){  
  265.             CRoute::iterator it = m_vRoute.begin();  
  266.             for(; it != m_vRoute.end(); it++){  
  267.                 CPoint pt = *it;  
  268.                 std::cout << "(" << pt.row << ", " << pt.col << ")";  
  269.                 if(it != m_vRoute.end() - 1){  
  270.                     std::cout << "->";  
  271.                 }else{  
  272.                     std::cout << std::endl;  
  273.                 }  
  274.             }  
  275.         }else{  
  276.             //如果没有找到路线到达终点  
  277.             std::cout << "Sorry cannot file any ways to get ending." << std::endl;  
  278.         }  
  279.     }  
  280.   
  281. };  
代码都加上了注释,大家可以慢慢看。
如果上述过程把你搅晕了,那就用图来为你解答吧。

不用依靠A*,使用C++撰写新的寻路算法_第1张图片

然后来到main函数

[cpp]  view plain copy
  1. //用VC 6.0貌似不需要给main传参数,那我就偷一下懒  
  2. int main()  
  3. {  
  4.     //定义迷宫数组,定义成C风格的二维数组方便查看  
  5.     int vLabyrinthArray[][4] = {  
  6.         {1,0,-1,1}  
  7.         , {1,0,0,1}  
  8.         , {0,0,1,1}  
  9.         , {0,1,1,0}  
  10.         , {0,1,1,1}  
  11.         , {-2,1,0,0}  
  12.     };  
  13.   
  14.     //以下代码为将C风格的二维数组导入成C++风格的二维数组  
  15.     int nRowNum = sizeof(vLabyrinthArray) / sizeof(vLabyrinthArray[0]);  
  16.     int nColNum = sizeof(vLabyrinthArray[0]) / sizeof(int);  
  17.   
  18.     CLabyrinth vLabyrinth;  
  19.     for(int row = 0; row < nRowNum; row++){  
  20.         CRow xRow;  
  21.         for(int col = 0; col < nColNum; col++){  
  22.             int n = vLabyrinthArray[row][col];  
  23.             xRow.push_back(n);  
  24.         }  
  25.         vLabyrinth.push_back(xRow);  
  26.     }  
  27.   
  28.     //实例化CLabyrinthAI  
  29.     CLabyrinthAI xAI(vLabyrinth);  
  30.     //打出路线  
  31.     xAI.AI();  
  32.   
  33.     //使程序暂停,方便查看数据  
  34.     system("Pause");  
  35.   
  36.     return 0;  
  37. }  

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

运行截图:

不用依靠A*,使用C++撰写新的寻路算法_第2张图片

(Dos的,有点丑……)

三,Javascript版

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

[javascript]  view plain copy
  1. function CLabyrinthAI(){  
  2.     var s = this;  
  3.     s.m_xLabyrinth = new Array(new Array());  
  4.     s.m_ptBeginning = {};  
  5.     s.m_ptEnding = {};  
  6.     s.m_vRoute = new Array();  
  7.     s.Beginning = -1;  
  8.     s.Ending = -2;  
  9.     s.CannotGo = 0;  
  10.     s.CanGo = 1;  
  11.     s.FoundEnding = 0;  
  12.     s.NotFoundEnding = 1;  
  13. }  
  14. CLabyrinthAI.prototype.initAI = function(){  
  15.     var s = this;  
  16.     s.getBeginning();  
  17.     s.getEnding();  
  18. }  
  19. CLabyrinthAI.prototype.isRepeat = function(pt){  
  20.     var s = this;  
  21.     var bRes = false;  
  22.     for(var n = 0; n < s.m_vRoute.length; n++){  
  23.         var pt0 = s.m_vRoute[n];  
  24.         if(pt0.col == pt.col && pt0.row == pt.row){  
  25.             bRes = true;  
  26.             break;  
  27.         }  
  28.     }  
  29.     return bRes;  
  30. };  
  31. CLabyrinthAI.prototype.advance = function(ptTo){  
  32.     this.m_vRoute.push(ptTo);  
  33. };  
  34. CLabyrinthAI.prototype.back = function(){  
  35.     this.m_vRoute.splice(this.m_vRoute.length-1,1);  
  36. };  
  37. CLabyrinthAI.prototype.isBeginning = function(pt){  
  38.     if(this.m_ptBeginning.col == pt.col && this.m_ptBeginning.row == pt.row){  
  39.         return true;  
  40.     }else{  
  41.         return false;  
  42.     }  
  43. };  
  44. CLabyrinthAI.prototype.isEnding = function(pt){  
  45.     if(this.m_ptEnding.col == pt.col && this.m_ptEnding.row == pt.row){  
  46.         return true;  
  47.     }else{  
  48.         return false;  
  49.     }  
  50. };  
  51. CLabyrinthAI.prototype.canUp = function(ptCurrent){  
  52.     var s = this;  
  53.     var ptRes = {col:-1,row:-1};  
  54.     var col = ptCurrent.col;  
  55.     var row = ptCurrent.row;  
  56.     if(row > 0){  
  57.         var ptNext = {col:col,row:row - 1};  
  58.         if(!s.isRepeat(ptNext)){  
  59.             var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];  
  60.             if(nAttr == s.CanGo || nAttr == s.Ending){  
  61.                 ptRes = ptNext;  
  62.             }  
  63.         }  
  64.     }  
  65.     return ptRes;  
  66. };  
  67. CLabyrinthAI.prototype.canDown = function(ptCurrent){  
  68.     var s = this;  
  69.     var ptRes = {col:-1,row:-1};  
  70.     var col = ptCurrent.col;  
  71.     var row = ptCurrent.row;  
  72.     if(row < s.m_xLabyrinth.length - 1){  
  73.         var ptNext = {col:col,row:row + 1};  
  74.         if(!s.isRepeat(ptNext)){  
  75.             var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];  
  76.             if(nAttr == s.CanGo || nAttr == s.Ending){  
  77.                 ptRes = ptNext;  
  78.             }  
  79.         }  
  80.     }  
  81.     return ptRes;  
  82. };  
  83. CLabyrinthAI.prototype.canLeft = function(ptCurrent){  
  84.     var s = this;  
  85.     var ptRes = {col:-1,row:-1};  
  86.     var col = ptCurrent.col;  
  87.     var row = ptCurrent.row;  
  88.     if(col > 0){  
  89.         var ptNext = {col:col-1,row:row};  
  90.         if(!s.isRepeat(ptNext)){  
  91.             var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];  
  92.             if(nAttr == s.CanGo || nAttr == s.Ending){  
  93.                 ptRes = ptNext;  
  94.             }  
  95.         }  
  96.     }  
  97.     return ptRes;  
  98. };  
  99. CLabyrinthAI.prototype.canRight = function(ptCurrent){  
  100.     var s = this;  
  101.     var ptRes = {col:-1,row:-1};  
  102.     var col = ptCurrent.col;  
  103.     var row = ptCurrent.row;  
  104.     if(col < s.m_xLabyrinth[0].length - 1){  
  105.         var ptNext = {col:col+1,row:row};  
  106.         if(!s.isRepeat(ptNext)){  
  107.             var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];  
  108.             if(nAttr == s.CanGo || nAttr == s.Ending){  
  109.                 ptRes = ptNext;  
  110.             }  
  111.         }  
  112.     }  
  113.     return ptRes;  
  114. };  
  115. CLabyrinthAI.prototype.allRight = function(p){  
  116.     if(p.col >= 0 && p.row >= 0){  
  117.         return true;  
  118.     }else{  
  119.         return false;  
  120.     }  
  121. };  
  122. CLabyrinthAI.prototype.findRoute = function(ptCurrent){  
  123.     var s = this;  
  124.     var nRes = s.NotFoundEnding;  
  125.     var ptNext = {col:-1,row:-1};  
  126.   
  127.     s.advance(ptCurrent);  
  128.       
  129.     if(s.isEnding(ptCurrent)){  
  130.         nRes = s.FoundEnding;  
  131.     }else{  
  132.         ptNext = s.canUp(ptCurrent);  
  133.         if(s.allRight(ptNext)){  
  134.             if(s.findRoute(ptNext) == s.FoundEnding){  
  135.                 nRes = s.FoundEnding;  
  136.                 return nRes;  
  137.             }  
  138.         }  
  139.           
  140.         ptNext = s.canLeft(ptCurrent);  
  141.         if(s.allRight(ptNext)){  
  142.             if(s.findRoute(ptNext) == s.FoundEnding){  
  143.                 nRes = s.FoundEnding;  
  144.                 return nRes;  
  145.             }  
  146.         }  
  147.           
  148.         ptNext = s.canDown(ptCurrent);  
  149.         if(s.allRight(ptNext)){  
  150.             if(s.findRoute(ptNext) == s.FoundEnding){  
  151.                 nRes = s.FoundEnding;  
  152.                 return nRes;  
  153.             }  
  154.         }  
  155.           
  156.         ptNext = s.canRight(ptCurrent);  
  157.         if(s.allRight(ptNext)){  
  158.             if(s.findRoute(ptNext) == s.FoundEnding){  
  159.                 nRes = s.FoundEnding;  
  160.                 return nRes;  
  161.             }  
  162.         }  
  163.     }  
  164.     if(nRes != s.FoundEnding){  
  165.         s.back();  
  166.     }  
  167.       
  168.     return nRes;  
  169. };  
  170. CLabyrinthAI.prototype.getBeginning = function(){  
  171.     var s = this;  
  172.     for(var nRow = 0; nRow < s.m_xLabyrinth.length; nRow++){  
  173.         var xRow = s.m_xLabyrinth[nRow];  
  174.         for(var nCol = 0; nCol < xRow.length; nCol++){  
  175.             var n = xRow[nCol];  
  176.             if(n == s.Beginning){  
  177.                 s.m_ptBeginning = {col:nCol,row:nRow};  
  178.                 break;  
  179.             }  
  180.         }  
  181.     }  
  182. };  
  183. CLabyrinthAI.prototype.getEnding = function(){  
  184.     var s = this;  
  185.     for(var nRow = 0; nRow < s.m_xLabyrinth.length; nRow++){  
  186.         var xRow = s.m_xLabyrinth[nRow];  
  187.         for(var nCol = 0; nCol < xRow.length; nCol++){  
  188.             var n = xRow[nCol];  
  189.             if(n == s.Ending){  
  190.                 s.m_ptEnding = {col:nCol,row:nRow};  
  191.                 break;  
  192.             }  
  193.         }  
  194.     }  
  195. };  
  196. CLabyrinthAI.prototype.AI = function(data){  
  197.     var s = this;  
  198.     s.m_xLabyrinth = data;  
  199.     s.initAI();  
  200.     s.findRoute(s.m_ptBeginning);  
  201.     return s.m_vRoute;  
  202. };  
设计原理和C++版差不多,只是没有CPoint类而已。

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

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

源代码下载:

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


谢谢大家阅读本文,支持就是最大的鼓励。

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

欢迎大家转载我的文章。

转载请注明:转自Yorhom's Game Box

http://blog.csdn.net/yorhomwang

你可能感兴趣的:(不用依靠A*,使用C++撰写新的寻路算法)