通过俄罗斯方块浅谈游戏中的AI(六)A*算法

本文深入探讨了A*算法在俄罗斯方块游戏中的应用,详细阐述了如何利用该算法优化游戏路径搜索过程,包括路径点的结构定义、算法执行流程以及在游戏中的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开场

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(序)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值