开场:
A*这个经典的最短路径搜索算法--很多人都用过它快速计算迷宫出路,也有人用来进行2d游戏的路径搜索。总之在搜索路径时大都第首先想到它。
备注:我之前对A*算法的执行顺序上有认识上的误区,我是广度优先查找的,实际A*是类似深度优先方式查找的,在一定程度上,降低了遍历的次数,现在对本文进行进一步纠正.
简述A*最短路径算法的方法:
目标:从当前位置A到目标位置B找到一条最短的行走路径。
方法:
结构_路径点(MapPoint)
{
当前位置坐标,
到达当前位置的最小距离数 ,
到达目标的理论最小距离数 ,
理论到达最小值 = 到达当前位置的最小距离数 + 到达目标的理论最小距离数 ,
谁到达这个MapPoint使距离数最小
}
初始化过程:
建立两个集合:
1.所有开启的路径点集合(待选路径点)简称OpenList;
2.所有关闭的路径点集合(已经走过的或者不应该再走到的路径点)简称CloseList;
这两个集合不应该有重复的元素,初始化过程,只有OpenList中存在A点这个点,而CloseList为空
执行过程:
查找OpenList中"理论到达最小值"最小的点,把其从OpenList移动到CloseList中,并把其下一步可达的路径集合
添加到OpenList中,一直到找到B点为止.
查找所有一步可达路径的过程时,如果发现某点存在于OpenList或CloseList中时,判断其新到达值,如果小于原值,
则更新(更新其"到达此处的上一个MapPoint"和其"到达当前位置的最小距离数"),大于则放弃
当找到B后,从它的上一个点开始一个一个找回去,就找到了整个路线.
总结:A*算法实际是个深度优先和策略结合的算法
课本上教的是广度优先,从出发点开始遍历,如果到达某点有两条路,则屏蔽掉长的那条,一直到找到最终节点。
进入正题:A*在俄罗斯方块游戏中的应用。
根据算法本身的特点,我们建立一个结构体MapPoint:
结构体中有个变量horizontalTimes是记录连续横向移动的次数,如果横向次数太多,势必造成自动下降,这样就不能达到指定拐点位置了,
所以最好的办法是减少连续横向移动次数,因此这里记录它就是为了给出惩罚算法.
2.A*算法:内部哈希映射函数
此函数的目的,更快速的判断一个对象是否存在,计算的内容包含,其位置/方块样式/
比如(3,0)坐标上的一个横向的长条 会表示为getHashValueOfAPoint(坐标(3,0),横向长条)
private int getHashValueOfAPoint(XYPair position, SetOfTetris theTetris)
{
return (theTetris.GetHashCode() << 4 | position.X) << 5 | position.Y;
}
3.A*算法:主体 第一部分,计算直到找到目标
Dictionary<int, MapPoint> OpenPoint = new Dictionary<int, MapPoint>();
Dictionary<int, MapPoint> ClosePoint = new Dictionary<int, MapPoint>();
// Dictionary <int,int> allIndexs = new Dictionary<int,int>();//作为所有已经添加元素的索引,如果重复了则不能添加;value表达到达此的最小距离
OpenPoint.Add(getHashValueOfAPoint(theDroppingPosition, theDroppingTetris),
new MapPoint(theDroppingPosition, theDroppingTetris, AimPosition, theAimTetris, 0, 0,null));
int theAimHash = getHashValueOfAPoint(AimPosition, theAimTetris);
bool finish = false;
#region toFindThePath
while (!finish && OpenPoint.Count>0)
{
finish = true;
int theOperKey=0;
MapPoint theMostSmallestPoint = null;
//找到FullCost最小的点;
foreach (int theTempKey in OpenPoint.Keys)
{
if (theMostSmallestPoint == null || theMostSmallestPoint.FullCost >= OpenPoint[theTempKey].FullCost)
{
theOperKey = theTempKey;
theMostSmallestPoint = OpenPoint[theTempKey];
}
}
if (theMostSmallestPoint == null || theOperKey==0) return false;
OpenPoint.Remove(theOperKey);
ClosePoint.Add(theOperKey,theMostSmallestPoint);
//判断其可能的下一步,并分别添加到OpenPoint中;
//下移一次10分;横向移动,按照越高,横向连续次数越多扣分越多,5*高度*横移次数 + 10 ;变形一次15
//横向这样处理是避免竖条一类的长时间横向移动,造成卡住的现象!
//下移
XYPair newPosition = theMostSmallestPoint.Position.Add(new XYPair(0, 1));
int theHash ;
int theValue;
if (insidePositionIsPermit(nowSituation,newPosition, theMostSmallestPoint.TheTetris))
{
//得到目标位置的哈希值
theHash = getHashValueOfAPoint(newPosition, theMostSmallestPoint.TheTetris);
//得到到达它这一步所花费的实际路径值
theValue = theMostSmallestPoint.NowCost + 10;
//如果在两个集合中没有找到这个点,才继续进行
if (!OpenPoint.ContainsKey(theHash) && !ClosePoint.ContainsKey(theHash))
{
finish = false;
//为open列表添加节点
OpenPoint.Add(theHash, new MapPoint(newPosition, theMostSmallestPoint.TheTetris, AimPosition, theAimTetris, theValue, 0, theMostSmallestPoint));
}
//当在Open集合中找到,则判断它是否有必要更新给Open列表
else if (OpenPoint.ContainsKey(theHash) && OpenPoint[theHash].NowCost > theValue)
{
OpenPoint[theHash].NowCost = theValue;
OpenPoint[theHash].TheComePoint = theMostSmallestPoint;
}
//当在close集合中找到,则判断它是否有必要更新给Close列表
else if (ClosePoint.ContainsKey(theHash) && ClosePoint[theHash].NowCost > theValue)
{
ClosePoint[theHash].NowCost = theValue;
ClosePoint[theHash].TheComePoint = theMostSmallestPoint;
}
//如果找到了目标节点,则退出
if (theHash == theAimHash) break;
}
//右移
...略
//左移
...略
//左变形
...略
//右变形
...略
}
#endregion
4.A*算法:主体 从终点往回找各个路径,形成路线
if (!OpenPoint.ContainsKey(theAimHash)) return false;//说明没有找到
#region 找到路点
//从路点开始往回找,一直其周边最小的,正常应该在close里面找,不再回到敞开式里的了
MapPoint theAimPoint = OpenPoint[theAimHash];
WayPoint endWay = new WayPoint(theAimPoint.Position, theAimPoint.TheTetris);
theRoad.Insert(0, endWay);
while (theAimPoint!= null && theAimPoint.TheComePoint!=null)
{
//当时往上走的时候,不切换路点,否则根据变化切换
if (theAimPoint.TheTetris != theAimPoint.TheComePoint.TheTetris || theAimPoint.Position.X != theAimPoint.TheComePoint.Position.X)
{
WayPoint oneTempWay = new WayPoint(theAimPoint.Position, theAimPoint.TheTetris);
theRoad.Insert(0, oneTempWay);
}
theAimPoint = theAimPoint.TheComePoint;
}
#endregion
return true;
源代码思想上就是这样,但是实际代码可能会根据版本不同有一定调整,希望看到最新代码就下载下面的连接。
或者返回开场那个连接,那里总是最新的通过俄罗斯方块浅谈游戏中的AI(序)