这是我们讲的传说中的一项措施A×算法。事实上,类上传之前似小件,下面我们分析一下它去

毕竟,在游戏程序,我们从移动一个点到另一个点。和得到的轨迹的最短距离,类别似这样的算法以及几个。运营效率几乎是相同的,最根本无法跟踪


首先,从一点移动到还有一点。最快就是直接走过去了,就像小男生爱上小女生。最好的办法就是直接走到她面前说:我爱你

只是理想状态。差点儿是没有的,弯路那是必定的经过,有曲线。事实上更美……


那么弯路该怎么走呢,是不是先去背景看下毛主席,再去三亚晒个太阳,再回来告诉她外面的世界好美,不。不,不……

再怎么弯的曲线,越接近直接。越好,所以步骤是:


1.向周围4个或8个方向,全部不是边界的点,记录这些点到目标的距离。从開始到这个点的距离。上一个到这里的点

2.依据两个距离和最小为标准,依次“递归”全部的点直到目标点

3.反向得到路径,就是近期的曲线路径


问:会不会到达同一个点,存在不同的距离

答:是,可是依据近期距离标准递推下去,更远距离的将被丢弃


问:递归为什么加引號

答:用队列实现堆栈,也算是递归,可是不会耗尽线程堆栈


为了,方便在推箱子中检測能否打到某个点,我改动下代码实现不记录上一个点,加速运行

只是后来就没有使用这个功能。主要代码例如以下:

alpha.cpp

// ****************************************************************************************************
// 文件: alpha.cpp
// 凝视:
// A*寻路算法模块, 推断两点之间是否通行, 并得到路径和各节点的权值
// 扩展不保存路径提速, 不使用Dijkstra, 參见http://www.2cto.com/kf/201104/87378.html
// ****************************************************************************************************
//#include <stdlib.h>
#include <malloc.h>

#define __LATTICE_DLL_INC_ // DLL内部编译
#include "api.h"

// 创建一个场景, 并初始化
V32API PSCENE __stdcall AlphaInit(long dwSizeX, long dwSizeY, long dwMaxCost, UINT dwFlags)
{
PSCENE ps = (PSCENE)malloc(sizeof(SCENE));
if(ps == NULL)
{
return NULL;
}
V32Clear32(ps, 0, sizeof(SCENE)); // memset, 自己主动计算DWORD数量

ps->NodeCount = dwSizeX * dwSizeY; // 节点数量
ps->Nodes = (PNODE)malloc(ps->NodeCount * sizeof(NODE));
if(ps->Nodes == NULL)
{
AlphaExit(ps, 0xFFFFFFFF);
return NULL;
}

ps->Matrix = (PBYTE)malloc(ps->NodeCount * sizeof(BYTE)); // 场景矩阵
if(ps->Matrix == NULL)
{
AlphaExit(ps, 0xFFFFFFFF);
return NULL;
}

ps->Steps = (PSTEP)malloc(ps->NodeCount * sizeof(STEP));
if(ps->Steps == NULL)
{
AlphaExit(ps, 0xFFFFFFFF);
return NULL;
}

if(dwMaxCost <= 0)
{
dwMaxCost = dwSizeX * dwSizeY; // 相邻格子逐个走遍
}
ps->MaxCost = dwMaxCost * CDC_PROP_NEAR; // 最大消耗(距离值)
ps->SizeY = dwSizeY;
ps->SizeX = dwSizeX;
ps->Flags = dwFlags;
return ps;
}

// 编辑场景数据
V32API int __stdcall AlphaEdit(PSCENE pScene, long dwPosX, long dwPosY, void *lpValue)
{
PBYTE p;
if(dwPosX < 0 && dwPosY < 0)
{
// 读取
p = pScene->Scene;
if(p == NULL) p = pScene->Matrix;
*(PBYTE)lpValue = p[dwPosY * pScene->SizeX + dwPosX];
return 2;
}
if(dwPosX < 0 || dwPosY < 0)
{
// 使用外部数据指针
pScene->Scene = (PBYTE)lpValue;
return 3;
}
// 设置字节数值
if(dwPosX >= pScene->SizeX || dwPosY >= pScene->SizeY)
{
//memcpy(pScene->Matrix, lpValue, pScene->NodeCount);
return 0;
}
p = pScene->Scene;
if(p == NULL) p = pScene->Matrix;
if(lpValue == NULL)
{
return p[dwPosY * pScene->SizeX + dwPosX]; // 读取
}else
{
p[dwPosY * pScene->SizeX + dwPosX] = *(PBYTE)lpValue; // 写入
}
return 1;
}

inline long AlphaCalc(PSTAR pStar1, PSTAR pStar2)
{
// 直接使用坐标差和取代三角函数开方
if(pStar1->X > pStar2->X)
{
if(pStar1->Y > pStar2->Y)
return (pStar1->X - pStar2->X) + (pStar1->Y - pStar2->Y);
else
return (pStar1->X - pStar2->X) + (pStar2->Y - pStar1->Y);
}else
{
if(pStar1->Y > pStar2->Y)
return (pStar2->X - pStar1->X) + (pStar1->Y - pStar2->Y);
else
return (pStar2->X - pStar1->X) + (pStar2->Y - pStar1->Y);
}
//return dX + dY;
}

inline void AlphaNode(PSCENE pScene, long dwIndex, long dwPrevId, long dwCost, long dwLast, long dwPosX, long dwPosY)
{
// 開始和循环各调用一次。dwIndex在调用之后自加。初始为0
if(dwIndex >= pScene->NodeCount)
return;
//pScene->Nodes[dwIndex].Star = *pStar; // 是否会创建暂时结构体??
pScene->Nodes[dwIndex].Star.X = dwPosX;
pScene->Nodes[dwIndex].Star.Y = dwPosY;
pScene->Nodes[dwIndex].Prev = dwPrevId;
if(dwPrevId != -1)
pScene->Nodes[dwIndex].Step = pScene->Nodes[dwPrevId].Step + 1;
else
pScene->Nodes[dwIndex].Step = 1;
pScene->Nodes[dwIndex].Cost = dwCost;
pScene->Nodes[dwIndex].Last = dwLast * 10; // 每次dwLen又一次计算得到格子数
pScene->Nodes[dwIndex].Flags = SNF_PROP_READY;
return;
}

inline BYTE fnAlphaUnit(PSCENE pScene, PSTAR ps)
{
BYTE *pUnit = pScene->Scene;
if(pUnit == NULL) pUnit = pScene->Matrix;
return pUnit[ps->Y * pScene->SizeX + ps->X];
}

inline PSTEP fnAlphaStep(PSCENE pScene, PSTAR ps)
{
return &pScene->Steps[ps->Y * pScene->SizeX + ps->X];
}

// 寻路指定场景
V32API int __stdcall AlphaStar(PSCENE pScene, PSTAR lpStart, PSTAR lpTarget, long *pdwStep)
{
PSTEP pStep;

long lCurCost, nMaxCost; // 暂存消耗,最大列表
long i, j; // temp & looping var
STAR tps, cps; // test pos, current pos

long dwValue;
long dwIndex = 0;
long dwNode = 0; // 当前Node数
long dwLoop;
long dwLen; // 与终点距离

// check for memory address accessable
if(lpStart == NULL || lpTarget == NULL)
{
return 0; // 始末坐标无效
}

dwLen = AlphaCalc(lpStart, lpTarget); // dwLen = ΔX + ΔY
if(dwLen == 0)
{
return -1; // 始末坐标同样
}
// zero step memory(cell prop list)
V32Clear32(pScene->Steps, 0, pScene->NodeCount * sizeof(STEP));
// 加入第一个点
dwNode = 0;
AlphaNode(pScene, dwNode, -1, 0, dwLen, lpStart->X, lpStart->Y);
dwNode++;

// enter loop - check around cells
while(1)
{
nMaxCost = pScene->MaxCost; // 不可能比这个大
dwIndex = -1;
for(dwLoop = 0; dwLoop < dwNode; dwLoop++)
{
if(pScene->Nodes[dwLoop].Flags != SNF_PROP_ERROR)
{
//找未关闭中最小路程和的点
lCurCost = pScene->Nodes[dwLoop].Cost + pScene->Nodes[dwLoop].Last;
if(lCurCost < nMaxCost)
{
nMaxCost = lCurCost; // 调整最大距离
dwIndex = dwLoop; // 保存节点序号
//break; // 全部节点都要计算
}
}
}
if(dwIndex == -1)
{
return -2; // there is no path exist!
}
cps.X = pScene->Nodes[dwIndex].Star.X;
cps.Y = pScene->Nodes[dwIndex].Star.Y;
if((cps.X == lpTarget->X)&&(cps.Y == lpTarget->Y))
{
break; // 当前点已是终点, 跳出while循环
}
//sprintf(szText, "select best cell:[%d,%d] for check:", cps.X, cps.Y);
for(i = -1; i <= 1; i++)
{
for(j = -1; j <= 1; j++)
{
//if(i == 0 && j == 0) continue; // 同意走对角线,仅仅要两个不同一时候为零(即不是自身)
if(i == 0 && j == 0) continue;
if(i != 0 && j != 0) continue; // 禁止走对角线,必有且仅仅有一个为零{[(i & j) == 0]&&[(i | j) != 0]}
tps.X = cps.X + i;
tps.Y = cps.Y + j;
if(tps.X < 0) continue; // 左边越界
if(tps.X >= pScene->SizeX) continue; // 右边越界
if(tps.Y < 0) continue; // 顶边越界
if(tps.Y >= pScene->SizeY) continue; // 底边越界
// 该点坐标在矩阵范围内
if(fnAlphaUnit(pScene, &tps) & (BYTE)pScene->Flags)
{
continue; // 消除特殊位以后仍然不可通行(独立程序: 仅同意空格或目标)
}
pStep = fnAlphaStep(pScene, &tps);
switch(pStep->Flags){
case SSF_PROP_UNKNOW: // it'v not been check
dwValue = pScene->Nodes[dwIndex].Cost;
//if(i * j != 0)
// dwValue += CDC_PROP_AWAY; // 横纵坐标都是非零, 对角
//else
dwValue += CDC_PROP_NEAR; // 不论什么一个坐标为零, 相邻
dwLen = AlphaCalc(&tps, lpTarget); //dwLen = ΔX + ΔY
AlphaNode(pScene, dwNode, dwIndex, dwValue, dwLen, tps.X, tps.Y);
pStep->Flags = SSF_PROP_OPENED; // open it
pStep->Index = dwNode++;
// sprintf(szText, "add cell:[%d,%d] for check:", tps.X, tps.Y);
break;
case SSF_PROP_OPENED: // this cell is valid
dwValue = pStep->Index;
dwLen = pScene->Nodes[dwIndex].Cost;
dwLen += CDC_PROP_NEAR; // 仅仅能相邻
if(dwLen < pScene->Nodes[dwValue].Cost)
{
pScene->Nodes[dwValue].Cost = dwLen;
pScene->Nodes[dwValue].Prev = dwIndex;
pScene->Nodes[dwValue].Step = pScene->Nodes[dwIndex].Step + 1; //change level
}
//sprintf(szText, "change pID for cell:[%d,%d] to %d.", tps.X, tps.Y, nID);
break;
default:
//end if(lpOGrid[tps.X][tps.Y].State..
break;
}
//end if((lpCell..
//end if((tps.X..
//end if(((i..
} //next j
} //next i
// it will continue if any path and not at target
pStep = fnAlphaStep(pScene, &cps);
pScene->Nodes[dwIndex].Flags = SNF_PROP_ERROR;
pStep->Flags = SSF_PROP_CLOSED; // close it
//sprintf(szText, "close cell:[%d,%d] ok.", cps.X, cps.Y);
}
// 函数malloc()和realloc()时间消耗大,用空间换取时间
dwValue = pScene->Nodes[dwIndex].Cost + pScene->Nodes[dwIndex].Last;
if(pdwStep == NULL)
{
// 不须要结果详情
return dwValue; // return cost to get here
}
//sprintf(szText, "best path found in cost %d.", dwValue);
pScene->StarCount = pScene->Nodes[dwIndex].Step;
SafeFree(pScene->Stars); // release old path
pScene->Stars = (PSTAR)malloc(pScene->StarCount * sizeof(STAR));
if(pScene->Stars == NULL)
{
return -3; // Out of memory
}
// ...
dwLoop = pScene->StarCount;
*pdwStep = dwLoop; // set the steps
while(dwLoop > 0) // it can also base on dwIndex
{
dwLoop--;
//pScene->m_pStarPath[dwLoop].X = pScene->Nodes[dwIndex].Star.X;
//pScene->m_pStarPath[dwLoop].Y = pScene->Nodes[dwIndex].Star.Y;
pScene->Stars[dwLoop] = pScene->Nodes[dwIndex].Star;
dwIndex = pScene->Nodes[dwIndex].Prev; // parent id
//sprintf(szText, "the %d cell:[%d,%d] added to path.", i, lpRoad[i].X, lpRoad[i].Y);
}
return dwValue; // return cost to get here
}

V32API int __stdcall AlphaData(PSCENE pScene, UINT dwPropId, long dwIndex)
{
VAR v;
v.pValue = (void*)pScene;
v.dwValue += dwPropId;
if(dwIndex < 0)
{
return (int)*v.plValue; // 直接定位成员
}
v.dwValue = *v.pdwValue;
v.dwValue += dwIndex;
return (int)*v.plValue;
}

// 销毁一个场景, 释放资源
V32API int __stdcall AlphaExit(PSCENE pScene, UINT dwFlags)
{
if(pScene)
{
SafeFree(pScene->Stars);
pScene->StarCount = 0;
SafeFree(pScene->Steps);
pScene->Scene = NULL; // 外部数据指针
SafeFree(pScene->Matrix);
SafeFree(pScene->Nodes);
pScene->NodeCount = 0;
free(pScene);
//pScene = NULL;
}
return 1;
}


当中V32API是一个宏定义。在v32/api.h中因此依赖V32.dll

这是我的一个基础类库的基类,包含经常使用的C语言函数和C++封装类,另一些宏定义

函数大多是内联汇编的裸函数。宏定义通常是高速释放内存用的,如

#define SafeDelete(p)   if(p){delete p; p = NULL;}


C++仅仅是封装C函数为类,同一时候扩展了C++特效。例如,智能指针,请参阅相关的头