最近实现了一下A*算法,感觉蛮好的,尤其是修改地图然后看电脑正确寻路后的那种成就感,有点像小时候蹲在地上,看着一堆蚂蚁搬家,然后故意在他们的路上设置障碍物,然后看蚂蚁不停的探索,然后重新找到新的路线的感觉,真是很有意思。
好!把代码记录在此,便于以后参考。
1#include <iostream>
2#include <string>
3#include <vector>
4#include <list>
5#include <map>
6#include <set>
7
8using namespace std;
9
10/**//************************************************************************/
11/**//* A*算法实现,测试A*算法地图
1240X10的地图,B代表起始点,E代表终点;
13空格代表能通过;|代表是墙,不能通过;
14xx0123456789012345678901234567890123456789xx
15xx————————————————————xx
160| |0
171| |1
182| | |2
193| | |3
204| | E |4
215| B | |5
226| | |6
237| |7
248| |8
259| |9
26xx————————————————————xx
27xx0123456789012345678901234567890123456789xx
28/************************************************************************/
29const static int X = 40;
30const static int Y = 10;
31//地图上的情况
32enum E_Map
33{
34 E_River=-2, //有河流,无法通过,在地图上用 ~ 标出
35 E_Wall=-1, //有墙,无法通过,在地图上用 | 标出
36 E_Road = 0, //没有障碍物,能最快的顺利通行,代价为0,在地图上用空格标出
37 E_Sand=1, //是沙地,能通过,但是相对难一些,代价为1,在地图上用 * 标出
38};
39
40struct Point
41{
42 int x;
43 int y;
44 Point(int i=0,int j=0):x(i),y(j){}
45 bool operator==(const Point & r)
46 {
47 return (x==r.x)&&(y==r.y);
48 }
49};
50bool operator==(const Point& l,const Point & r)
51{
52 return (l.x==r.x)&&(l.y==r.y);
53}
54bool operator<(const Point& l,const Point & r)
55{
56 if(l.y<r.y) return true;
57 else if(l.y>r.y) return false;
58 else return (l.x < r.x);
59}
60
61//const static int GameMap[Y][X] = {
62// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
63// 0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
64// 0,0,0,-2,-2,-2,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
65// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-2,0,0,0,0,0,0,
66// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,0,0,0,0,0,
67// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,0,0,
68// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
69// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
70// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
71// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
72//};
73
74//const static int GameMap[Y][X] = {
75// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
76// 0,0,-2,-2,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
77// 0,0,0,-2,-2,-2,0,0,-1,0,-1,0,0,0,0,0,0,-1,-1,0,0,0,-1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
78// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,-1,0,0,0,0,0,-1,-1,0,0,0,0,0,-2,-2,-2,0,0,0,0,0,0,
79// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-2,-2,0,0,0,0,0,
80// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,-2,0,0,0,0,0,
81// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,
83// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
84// 0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
85//};
86
87const static int GameMap[Y][X] = {
88 0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
89 0,0,-2,-2,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
90 0,0,0,-2,-2,-2,0,-1,-1,0,0,0,0,0,0,0,0,-1,-1,0,0,0,-1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
91 0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,-1,-1,0,0,0,0,0,-2,-2,-2,0,0,0,0,0,0,
92 0,0,0,0,0,-1,0,0,-1,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-2,-2,0,0,0,0,0,
93 0,0,0,0,0,-1,0,0,-1,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,-2,0,0,0,0,0,
94 0,0,0,0,0,-1,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
95 0,0,0,0,0,-1,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,
96 0,0,0,0,0,0,-1,0,0,-1,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
97 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
98};
99
100//打印地图
101void PrintMap(const Point &B,const Point &E,const vector<Point>& path=vector<Point>());
102void PrintMap(const Point &B,const Point &E,const vector<Point>& path)
103{
104 int LastMap[Y][X] = {0};
105 for (int y=0;y<Y;++y)
106 {
107 for (int x=0;x<X;++x)
108 {
109 LastMap[y][x] = GameMap[y][x];
110 }
111 }
112 //路径
113 vector<Point>::const_iterator itr = path.begin();
114 for (;itr != path.end();++itr)
115 {
116 LastMap[(*itr).y][(*itr).x] = '&';
117 }
118 //起始和目标
119 LastMap[B.y][B.x] = 'B';
120 LastMap[E.y][E.x] = 'E';
121
122 cout<<"A*寻路路径为:"<<endl;
123 cout<<"xx0123456789012345678901234567890123456789xx"<<endl;
124 cout<<"xx————————————————————xx"<<endl;
125 for (int y=0;y<Y;++y)
126 {
127 cout<<y<<"[";
128 for (int x=0;x<X;++x)
129 {
130 if (LastMap[y][x] == E_Road)
131 {
132 cout<<" ";
133 }
134 else if (LastMap[y][x] == E_Wall)
135 {
136 cout<<"|";
137 }
138 else if (LastMap[y][x] == E_River)
139 {
140 cout<<"~";
141 }
142 else if (LastMap[y][x] == E_Sand)
143 {
144 cout<<"*";
145 }
146 else if (LastMap[y][x] == 'B')
147 {
148 cout<<"B";
149 }
150 else if (LastMap[y][x] == 'E')
151 {
152 cout<<"E";
153 }
154 else if (LastMap[y][x] == '&')
155 {
156 cout<<"&";
157 }
158 }
159 cout<<"]"<<y<<endl;
160 }
161 cout<<"xx————————————————————xx"<<endl;
162 cout<<"xx0123456789012345678901234567890123456789xx"<<endl;
163}
164
165//计算当前位置与终点位置的Hn
166double Hn(const Point & E,const Point&p)
167{
168 return abs(E.y-p.y) + abs(E.x-p.x);
169}
170
171//计算相邻两个位置(即包括对象线在内的周围8个坐标)的相对Gn
172double Gg(const Point & p1,const Point& p2)
173{
174 double d = GameMap[p2.y][p2.x];
175 return ((p1.x-p2.x)!=0&&(p1.y-p2.y)!=0)?(1.5+d):(1.0+d);
176}
177
178//探测位置p的下一步(p.x + addx,p.y + addy)的Gn,Hn
179void testNext(const Point & E,const Point&p,const set<Point>& closeTbl,
180 map<Point,double> &mapGn,map<Point,double> &mapGnTemp,
181 multimap<double,Point> &HnPoint,int addx,int addy)
182{
183 int x = p.x + addx;
184 int y = p.y + addy;
185 if (x>=0 && y>=0 && x<X && y<Y && GameMap[y][x]>=0)
186 {
187 Point t = Point(x,y);
188 if (closeTbl.find(t) != closeTbl.end())
189 {
190 return;
191 }
192 //得到对应本次遍历的Gn
193 double dgn = mapGn[p] + Gg(p,t);
194 mapGnTemp[t] = dgn;
195
196 map<Point,double>::iterator itr = mapGn.find(t);
197 if (itr == mapGn.end() || itr->second > dgn)
198 {
199 mapGn[t] = dgn;
200 }
201 HnPoint.insert(make_pair(Hn(E,t),t));
202 }
203}
204
205bool APath(const Point & B,const Point & E,vector<Point>&path)
206{
207 //A*算法:Fn = Gn + Hn
208 path.clear();
209 vector<Point> openTbl;
210 openTbl.push_back(B);
211 set<Point> closeTbl;
212 closeTbl.insert(B);
213 map<Point,double> mapGn;
214 mapGn[B] = 0;
215 while(!openTbl.empty())
216 {
217 Point p = openTbl.back();
218 openTbl.pop_back();
219 if (p == E)
220 {
221 path.push_back(E);
222 break;
223 }
224 //multimap<double,Point> FnPoint;
225 multimap<double,Point> HnPoint; //当前位置周围所有可选择的位置到终点的Hn
226 map<Point,double> mapGnTemp; //当前位置周围所有可选择的位置到起点的Gn
227
228 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,-1,-1); //左上位置
229 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,-1,0); //左边位置
230 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,-1,1); //左下位置
231 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,0,-1); //上面位置
232 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,0,1); //下面位置
233 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,1,-1); //右上位置
234 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,1,0); //右边位置
235 testNext(E,p,closeTbl,mapGn,mapGnTemp,HnPoint,1,1); //右下位置
236 if (HnPoint.empty())
237 {
238 //无路可走了,只能一步一步回退。。。
239
240 //PrintMap(B,E,path);
241 //标记该点已经被探测,使得下次不再被选择
242 closeTbl.insert(p);
243 //回退一步,重新探测之前走过的一个点
244 if (path.empty())
245 {
246 return false;
247 }
248 p = path.back();
249 path.pop_back();
250 closeTbl.erase(p);
251 openTbl.push_back(p);
252 continue;
253 }
254
255 //HnPoint的第一维就是从p点开始到达终点(Hn)最高效的周围坐标点pt
256 multimap<double,Point>::iterator itr = HnPoint.begin();
257 double hn = itr->first;
258 Point pt = itr->second;
259
260 map<Point,double>::iterator itrFind = mapGn.find(pt);
261 while (itrFind != mapGn.end() && (itrFind->second) < mapGnTemp[pt])
262 {
263 //如果这个不是最优,则优先试探其他的相同Hn的路径
264 ++itr;
265 if (itr != HnPoint.end() )
266 {
267 if (hn < itr->first)
268 {
269 break;
270 }
271 pt = itr->second;
272 itrFind = mapGn.find(pt);
273 }
274 else
275 break;
276 }
277 //判断pt是否被探测过
278 if (itrFind != mapGn.end() && (itrFind->second) < mapGnTemp[pt])
279 {
280 /**//**************************************************************************************
281 pt已经被探测过,并且之前的Gn更小,说明可以用更小的代价到达pt。
282 所以说明我们之前选择的p点不是最佳选择,首先应该标记p无效,然后回退到之前的坐标重新选择!
283 ****************************************************************************************/
284 //PrintMap(B,E,path);
285 //标记该点已经被探测,使得下次不再被选择
286 closeTbl.insert(p);
287 //回退一步,重新探测之前走过的一个点
288 p = path.back();
289 path.pop_back();
290 closeTbl.erase(p);
291 openTbl.push_back(p);
292 continue;
293 }
294
295 //pt没有被探测过,或者是最优选项,所以将p加入路径,然后下一步探测pt
296 openTbl.push_back(pt);
297
298 closeTbl.insert(p);
299 path.push_back(p);
300 }
301 return !path.empty();
302}
303
304int main(int argc, char * argv[])
305{
306 const Point B(4,5),E(36,4);
307 vector<Point> path;
308 bool bFind = APath(B,E,path);
309
310 PrintMap(B,E,path);
311 if (!bFind)
312 {
313 cout<<"++++++找不到可达路径!+++++++++"<<endl;
314 }
315
316 return 0;
317}
最上面注释里面画的地图是为了简单演示地图最终的显示效果,其实代码二维数组给出的地图有趣多了。