码迷,mamicode.com
首页 > 编程语言 > 详细

致佳音: 推箱子游戏自动求解算法设计(二)

时间:2015-03-14 23:15:01      阅读:128      评论:0      收藏:0      [点我收藏+]

标签:

这一个小节我们说一说传说中的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++特效,比如智能指针等,详见有关头文件


致佳音: 推箱子游戏自动求解算法设计(二)

标签:

原文地址:http://blog.csdn.net/prsniper/article/details/44264071

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!