📄 astarz.cpp
字号:
/*===============================================================
通用最短路径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;
typedef short (*ATMoveAble)(ADWORD,ADWORD);
typedef short (*ATEstimate)(ADWORD,ADWORD);
class AstarWay
{
protected:
struct TAstarNode
{
ADWORD Pos; // 该点的坐标(16,16)的模式保存(y,x)
long ActualCost; // 保存从起点到该节点的实际开销
long EstimateCost; // 保存此点的估价开销
long SumCost; // 上面两者之和
long Index; // 从源点开始的递增序号
TAstarNode *Father; // 此点的父节点
TAstarNode *Prev; // 在Open或者Next中的上一个节点
TAstarNode *Next; // 在Open或者Next链表中的下一个节点
long Modified; // 该节点是否被修改过,记录而备清除1空,2 Open,4 Close
};
TAstarNode **Node; // 对应地图中每个节点
TAstarNode *Open; // 保存没有处理的按估计值排序的节点
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;
short (*MoveAble)(ADWORD PosTo,ADWORD PosSrc); // 检查地图是否可以移动函数指针
short (*Estimate)(ADWORD PosAt,ADWORD PosTag); // 取得估计值的函数指针
char AddOpenHeap(TAstarNode *node); // 将节点排序加入Open队列
char PopOpenHeap(TAstarNode **node); // 从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,ATMoveAble FunMove,ATEstimate FunEsmt);
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 SetEstimate(ATEstimate Fun);
void SetMapCheck(ATMoveAble Fun);
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 AstarWay::AddOpenHeap(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 AstarWay::PopOpenHeap(TAstarNode **node)
{
TAstarNode *BestChoice=Open;
if (node) *node = BestChoice;
if (!Open) return -1;
Open=Open->Next;
if (Open) Open->Prev=NULL;
if (BestChoice->Modified&4) return -2; // 已经在Close中了
BestChoice->Prev=NULL;
BestChoice->Modified&=5; // 清除Open标志
BestChoice->Modified|=4; // 设置Close标志
OpenCount--;
CloseCount++;
return 0;
}
// 还原修改过的所有节点
void AstarWay::ClearModified()
{
ADWORD i;
for (i=0;i<ModifyPoint;i++) {
Modify[i]->Modified=0;
Modify[i]->Index=0;
Modify[i]->ActualCost=COST_MAX;
}
ModifyPoint=0;
OpenCount=0;
CloseCount=0;
Open=NULL;
//Close=NULL;
}
// 尝试下一步移动到 x,y 可行否
char AstarWay::TryTile(short x,short y,TAstarNode *Father,char FromDir)
{
static int DirCost[9] = { 0, 10, 14, 10, 14, 10, 14, 10, 14 };
register TAstarNode *node;
short ActualCost;
if (!MoveAble(tile_pos(x,y),Father->Pos)) return 1; // 如果地图无法通过则退出
node=&Node[y][x];
ActualCost=Father->ActualCost+DirCost[FromDir]; // 实际值计算
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--;
} else if (node->Modified&4) { // 在Close表中就清除
node->Modified=1;
CloseCount--;
} else { // 不在Open也不在Clsoe
if (!(node->Modified&1)) Modify[ModifyPoint++]=node; // 记录这个修改过的点以还原
node->EstimateCost=Estimate(tile_pos(x,y),tile_pos(TargetX,TargetY));
}
node->Modified=1;
node->Father=Father;
node->ActualCost=ActualCost;
node->SumCost=ActualCost+node->EstimateCost;
node->Index=Father->Index+1;
AddOpenHeap(node); // 将节点加入Open队列
return 0;
}
// 路径寻找主函数
int AstarWay::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=Estimate(tile_pos(startx,starty),tile_pos(endx,endy));
root->SumCost=root->EstimateCost;
root->Index=0;
root->Father=NULL;
root->Modified=1;
Modify[ModifyPoint++]=root;
AddOpenHeap(root);
MinPos=tile_pos(startx,starty);
MinJudge=root->EstimateCost;
TargetX=endx;
TargetY=endy;
for (ok=0;ok==0;)
{
ok=PopOpenHeap(&root); // 取出Open队列估价值最小的节点放入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,1);
if (DirMask&2) j&=TryTile(x+1,y-1,root,2);
if (DirMask&4) j&=TryTile(x+1,y,root,3);
if (DirMask&8) j&=TryTile(x+1,y+1,root,4);
if (DirMask&16) j&=TryTile(x,y+1,root,5);
if (DirMask&32) j&=TryTile(x-1,y+1,root,6);
if (DirMask&64) j&=TryTile(x-1,y,root,7);
if (DirMask&128) j&=TryTile(x-1,y-1,root,8);
if (j) root->Modified &= 3, CloseCount--; // 如果不是通路则从Close中取出
}
if (OpenCount>=OpenLimited||CloseCount>=CloseLimited) ok=2;
}
if (ok<0) return -2;
return 0;
}
int AstarWay::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++)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -