/*=============================================================== 通用最短路径A*算法优化求解 Common shortest path finding in A* 作者 林伟 于2001年12月19日 Written by Wei Lin On Dec.19 2001 电子科技大学通讯工程学院20013080班 Uestc Dept.1 - 20013080 */ #ifndef _ASTAR_H_ #define _ASTAR_H_ // 从上面#ifndef开始到#endif结束是头文件内容 typedef unsigned long ADWORD; class AstarPathFind { protected: struct TAstarNode { ADWORD Pos; // 该点的坐标(16,16)的模式保存(y,x) short ActualCost; // 保存从起点到该节点的实际开销 short EstimateCost; // 保存此点的估价开销 short SumCost; // 上面两者之和 TAstarNode *Father; // 此点的父节点 TAstarNode *Prev; // 在Open或者Next中的上一个节点 TAstarNode *Next; // 在Open或者Next链表中的下一个节点 char Modified; // 该节点是否被修改过,记录而备清除1空,2 Open,4 Close short DirWalked; // 有字节点的方向(未用) short DirFrom; // 从哪个方向来的(未用) }; TAstarNode **Node; // 对应地图中每个节点 TAstarNode *Open; // 保存没有处理的按估计值排序的节点 TAstarNode *Close; // 保存处理过的节点 TAstarNode **Modify; // 保存修改过的节点 int height; // 地图的高度 int width; // 地图的宽度 ADWORD MaxSize; // 最大面积即height*width ADWORD ModifyPoint; // Modify数组的指针 ADWORD OpenCount; // Open队列中的节点数目 ADWORD CloseCount; // Close队列里面的节点数目 ADWORD OpenLimited; // Open队列最多节点数目 ADWORD CloseLimited; // Close队列最多节点数目 short DirMask; // 要搜索方向的标志,0-7位为从上开始顺时针七个方向 ADWORD MinPos; // 终点或最接近终点的节点 short TargetX; // 终点坐标 short TargetY; char (*MoveAble)(short x,short y); // 检查地图是否可以移动函数指针 short (*JudgeCost)(short,short,short,short); // 取得估计值的函数指针 char AddToOpenQueue(TAstarNode *node); // 将节点排序加入Open队列 char GetFromOpenQueue(); // 从Open队列取出最小的并放入Close队列 char PopCloseStack(); // 取出Close队列中的节点 void ClearModified(); // 清除所有搜索记录 char TryTile(short x,short y,TAstarNode *Father,char FromDir); public: int Create(int map_w,int map_h,char (*MoveCheck)(short x,short y),short (*JudgeFun)(short x,short y,short dx,short dy)); int Release(); virtual int FindPath(short startx,short starty,short endx,short endy); int GetPosPath(short *PosXY,int maxpos); int GetDirPath(char *Dirs,int maxpos); void SetJudgeFun(short (*JudgeFun)(short,short,short,short)); void SetMapCheck(char (*MapCheck)(short,short)); void SetOpenLimited(long OpenLimited); void SetCloseLimited(long CloseLimited); void SetDirMask(long DirMask); }; #endif #include <stdio.h> #define COST_MAX 30000 #define tile_pos(x,y) (((ADWORD)y<<16)|x) #define tile_x(pos) (pos&0xffff) #define tile_y(pos) (pos>>16) // 待处理节点入队列, 依靠对目的地估价距离插入排序 char AstarPathFind::AddToOpenQueue(TAstarNode *node) { ADWORD i; short f=node->SumCost; register TAstarNode *p,*b; node->Modified|=2; // 记录Open标志 for (b=NULL,p=Open,i=0;p&&i<=OpenCount;b=p,p=p->Next,i++) if (f<=p->SumCost) break; if (i>OpenCount) return -2; node->Next=p; node->Prev=b; if (b) b->Next=node; else Open=node; if (p) p->Prev=node; OpenCount++; return 0; } // 将离目的地估计最近的方案出队列 char AstarPathFind::GetFromOpenQueue() { TAstarNode *BestChoice=Open; if (!Open) return -1; Open=Open->Next; if (Open) Open->Prev=NULL; if (BestChoice->Modified&4) return -2; // 已经在Close中了 BestChoice->Next=Close; BestChoice->Prev=NULL; BestChoice->Modified&=5; // 清除Open标志 BestChoice->Modified|=4; // 设置Close标志 if (Close) Close->Prev=BestChoice; Close=BestChoice; OpenCount--; CloseCount++; return 0; } // 释放栈顶节点 char AstarPathFind::PopCloseStack() { if (Close) { Close->Modified&=3; // 清除Close标志 Close=Close->Next; if (Close) Close->Prev=NULL; CloseCount--; return 0; } return -1; } // 还原修改过的所有节点 void AstarPathFind::ClearModified() { ADWORD i; for (i=0;i<ModifyPoint;i++) { Modify[i]->Modified=0; Modify[i]->ActualCost=COST_MAX; } ModifyPoint=0; OpenCount=0; CloseCount=0; Open=NULL; Close=NULL; } // 尝试下一步移动到 x,y 可行否 char AstarPathFind::TryTile(short x,short y,TAstarNode *Father,char FromDir) { register TAstarNode *node; short ActualCost; if (!MoveAble(x,y)) return 1; // 如果地图无法通过则退出 node=&Node[y][x]; ActualCost=Father->ActualCost+1; // 实际值计算 if (ActualCost>=node->ActualCost) return 1; if (node->Modified&2) // 在Open表中就清除 { if (node->Next) node->Next->Prev=node->Prev; if (node->Prev) node->Prev->Next=node->Next; else Open=node->Next; OpenCount--; node->Modified=1; node->Father=Father; node->ActualCost=ActualCost; node->SumCost=ActualCost+node->EstimateCost; AddToOpenQueue(node); } else if (node->Modified&4) { // 在Close表中就清除 if (node->Next) node->Next->Prev=node->Prev; if (node->Prev) node->Prev->Next=node->Next; else Close=node->Next; CloseCount--; node->Modified=1; node->Father=Father; node->ActualCost=ActualCost; node->SumCost=ActualCost+node->EstimateCost; AddToOpenQueue(node); } else { if (!(node->Modified&1)) Modify[ModifyPoint++]=node; // 记录这个修改过的点以还原 node->Modified=1; node->Father=Father; node->DirFrom=FromDir; node->ActualCost=ActualCost; node->EstimateCost=JudgeCost(x,y,TargetX,TargetY); node->SumCost=ActualCost+node->EstimateCost; AddToOpenQueue(node); // 将节点加入Open队列 } return 0; } // 路径寻找主函数 int AstarPathFind::FindPath(short startx,short starty,short endx,short endy) { TAstarNode *root; int j,ok; short x,y; ADWORD max=0; short MinJudge; if (!Node||!Modify) return -1; ClearModified(); root=&Node[starty][startx]; root->ActualCost=0; root->EstimateCost=JudgeCost(startx,starty,endx,endy); root->SumCost=root->EstimateCost; root->Father=NULL; root->Modified=1; Modify[ModifyPoint++]=root; AddToOpenQueue(root); MinPos=tile_pos(startx,starty); MinJudge=root->EstimateCost; TargetX=endx; TargetY=endy; for (ok=0;ok==0;) { ok=GetFromOpenQueue(); // 取出Open队列估价值最小的节点放入Close中 root=Close; // 得到刚才取出的节点 if (ok||root==NULL) { ok=-1; break; } x=(short)tile_x(root->Pos); y=(short)tile_y(root->Pos); if (root->EstimateCost<MinJudge) // 找到一个估计离终点最近的点 MinJudge=root->EstimateCost, MinPos=root->Pos; if (CloseCount>max) max=CloseCount; if (x==endx&&y==endy) MinPos=root->Pos,ok=1; // 如果走到终点了 else { j=1; if (DirMask&1) j&=TryTile(x,y-1,root,0); if (DirMask&2) j&=TryTile(x+1,y-1,root,0); if (DirMask&4) j&=TryTile(x+1,y,root,0); if (DirMask&8) j&=TryTile(x+1,y+1,root,0); if (DirMask&16) j&=TryTile(x,y+1,root,0); if (DirMask&32) j&=TryTile(x-1,y+1,root,0); if (DirMask&64) j&=TryTile(x-1,y,root,0); if (DirMask&128) j&=TryTile(x-1,y-1,root,0); if (j) if (PopCloseStack()) { ok=-2; break; } // 如果不是通路则从Close中取出 } if (OpenCount>=OpenLimited||CloseCount>=CloseLimited) ok=2; } if (ok<0) return -2; return 0; } int AstarPathFind::GetPosPath(short *PosXY,int maxpos) { short x,y; ADWORD j; TAstarNode *p,*s; int i; if (maxpos>1) maxpos--; for (p=&Node[tile_y(MinPos)][tile_x(MinPos)],s=p,j=0;p&&j<MaxSize;p=p->Father,j++) { x=(short)tile_x(p->Pos); y=(short)tile_y(p->Pos); i=(p->ActualCost<maxpos)?p->ActualCost:maxpos; PosXY[i<<1]=x; PosXY[(i<<1)+1]=y; } i=(s->ActualCost+1<maxpos)?(s->ActualCost+1):maxpos; PosXY[i*2]=-1; PosXY[i*2+1]=-1; return 0; } int AstarPathFind::GetDirPath(char *PosDir,int maxpos) { static char inc2r[10]={ 7,0,1,6,8,2,5,4,3,0 }; short i,x,y,nx,ny; ADWORD j; TAstarNode *p,*s; x=(short)tile_x(MinPos); y=(short)tile_y(MinPos); if (maxpos>1) maxpos--; for (p=&Node[y][x],s=p,j=0;p&&j<MaxSize;p=p->Father,j++) { nx=(short)tile_x(p->Pos); ny=(short)tile_y(p->Pos); i=(p->ActualCost<maxpos)?(p->ActualCost):maxpos; PosDir[i]=inc2r[(y-ny+1)*3+x-nx+1]; x=nx; y=ny; } i=(s->ActualCost+1<maxpos)?(s->ActualCost+1):maxpos; PosDir[i]=8; return 0; } int AstarPathFind::Create(int mapw,int maph,char (*MoveCheck)(short,short),short (*JudgeFun)(short x,short y,short,short)) { int i,j; height=maph; width=mapw; MaxSize=maph; MaxSize*=mapw; Modify=new TAstarNode*[MaxSize*2]; if (!Modify) return -1; Node=new TAstarNode*[maph]; if (!Node) { delete Modify; Modify=NULL; return -1; } for (i=0;i<maph;i++) Node[i]=new TAstarNode[mapw]; for (i=0,j=1;i<maph;i++) if (!Node[i]) j=0; if (!j) { for (i=0;i<maph;i++) if (Node[i]) delete Node[i]; delete Node; delete Modify; Node=NULL; Modify=NULL; return -2; } for (j=0;j<maph;j++) for (i=0;i<mapw;i++) { Node[j][i].Pos=tile_pos(i,j); Node[j][i].Modified=0; Node[j][i].ActualCost=COST_MAX; } ModifyPoint=0; SetMapCheck(MoveCheck); SetJudgeFun(JudgeCost=JudgeFun); SetDirMask(0x55); SetOpenLimited(200); SetCloseLimited(MaxSize/20); return 0; } int AstarPathFind::Release() { int j; if (Node) for (j=0;j<height;j++) if (Node[j]) delete Node[j]; if (Node) delete Node; if (Modify) delete Modify; Node=NULL; Modify=NULL; return 0; } void AstarPathFind::SetJudgeFun(short (*JudgeFun)(short,short,short,short)) { JudgeCost=JudgeFun; } void AstarPathFind::SetMapCheck(char (*MapCheck)(short,short)) { MoveAble=MapCheck; } void AstarPathFind::SetOpenLimited(long OL) { OpenLimited=OL; } void AstarPathFind::SetCloseLimited(long CL) { CloseLimited=CL; } void AstarPathFind::SetDirMask(long DM) { DirMask=(short)DM; } ///////////////////////////////////////////////////////////////////// #include <conio.h> #include <stdlib.h> #include <ctype.h> char map[100][100]; int maph,mapw; static short incx[8]={ 0, 1, 1, 1, 0, -1, -1, -1 }; static short incy[8]={ -1, -1, 0, 1, 1, 1, 0, -1 }; AstarPathFind PathFind; int startx,starty,endx,endy; short Judge(short x,short y,short endx,short endy) { short dx=abs(x-endx); short dy=abs(y-endy); return (dx+dy)<<3; } char Possible(short x,short y) { if (x<0||y<0||x>=mapw||y>=maph) return 0; return (map[y][x]==' ')?1:0; } int readmap() { int i,j; FILE *f; if ((f=fopen("map.dat","r"))==NULL) return -1; fscanf(f,"%d,%d\n",&mapw,&maph); for (j=0;j<maph;j++) fgets(map[j],mapw+2,f); fclose(f); for (j=0;j<maph;j++) for (i=0;i<mapw;i++) { if (map[j][i]=='s'||map[j][i]=='S') startx=i,starty=j,map[j][i]=' '; if (map[j][i]=='e'||map[j][i]=='E') endx=i,endy=j,map[j][i]=' '; } return 0; } #include <time.h> #define TIMES 512 int main(void) { int i,j; int x,y; long t; double f; if (readmap()) { printf("no map file\n"); return -1; } short PosXY[500]; char PosR[500]; for (j=0;j<maph;j++) { for (i=0;i<mapw;i++) putchar(map[j][i]); putchar('\n'); } i=PathFind.Create(mapw,maph,Possible,Judge); if (i) { printf("创建失败\n"); return -1; } PathFind.SetCloseLimited(79*24L); PathFind.SetDirMask(0xff); PathFind.FindPath(startx,starty,endx,endy); PathFind.GetPosPath(PosXY,250); PathFind.GetDirPath(PosR,500); getch(); for (i=0;PosXY[i*2]>=0;i++) { x=PosXY[i*2]; y=PosXY[i*2+1]; map[y][x]='.'; } for (j=0;j<maph;j++) { for (i=0;i<mapw;i++) { putchar(map[j][i]); if (map[j][i]=='.') map[j][i]=' '; } putchar('\n'); } printf("Any key to Testing speed, finding 512 paths...\n"); getch(); for (t=clock();t==clock();); for (i=0,t=clock();i<TIMES;i++) { PathFind.FindPath(startx,starty,endx,endy); PathFind.GetPosPath(PosXY,250); } t=clock()-t; f=TIMES*CLK_TCK; f/=t; PathFind.Release(); printf("Time to find %d paths is %ld TCK use! freq=%f \n",TIMES,t,f); getch(); return 0; } /*=============================================================================== 原始算法 98.088421次每秒 算法记录一 152.760656次每妙 算法记录二 157.938983次每妙 算法记录三 179.200000次每秒 用VC优化 190.334572次每妙 地图文件:map.dat 79,24 ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o o ooooooo o o o o o oooooooooooooo o o s o o o oooooooooooooooooo o o o oooooooooooooooooooooooooo oooooooo o o oooooo o oooo o o o o o o ooo ooo o o oooo oooo o o o oooooooooooooooooooooooooooooooooooooooooooooo o oooooooooooooooooooooooo o o o o o ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo o o o ooooooooooo o o ooooooo o o o o o o o o ooooooooooo oooooooooo o o o o e ooo o o o ooooo o o o o o oooooooooooooooooo oooooooo o o o o ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ---------------------------------------------------------------------------------*/