上一篇:qt c++实现的ai贪吃蛇吃满屏幕,超详细!(一)基本组件
不同的人写的界面不同,我就不讲ui界面了,直接讲ai蛇的具体实现。
实现绘制和更新等游戏功能的类是drawWidget,由于这个类里面的东西太多太杂,我只把和ai有关的部分放上来,如果你想了解更多,可以看我的完整代码,放到了GitHub上:github项目:snake ai
一、ai思路
在有障碍的方格图中搜索两点之间的一条路径,有bfs算法和a*算法。我用的是bfs算法,后面会讲bfs的实现。那么有了这个bfs就好办了。
蛇怎么样才能吃满屏幕?在很多人看来这是一个很复杂的问题,由于每走一步,全局的形势都在变化。但是如果把蛇的行动置于一个策略下来看,这个问题就变得很简单了。我们这样来思考,由于蛇头永远在占领空格,而蛇尾永远在留出空格,如果此时蛇头能找到一条路径到蛇尾,那么说明此时蛇是安全的,因为蛇沿着这个路径走,肯定是不会死的,否则我们就称蛇是不安全的。
下面是我的策略:
此时真蛇正要走一步。如果真蛇(为了和后面的虚拟蛇区别,叫它“真蛇”)能找到吃食物的路径,就派出一条“虚拟蛇”(虚拟蛇最开始和真蛇的位置一样)去吃食物。如果虚拟蛇吃完食物后还是安全的(虚拟蛇的头能找到虚拟蛇的尾),那么就派真蛇去吃食物,否则,真蛇就跟着自己的尾巴走(如果能找到自己的尾巴)。如果真蛇找不到自己的尾巴,那么真蛇只好wander随机走一步了。
如果真蛇找不到吃食物的路径,真蛇就跟着自己的尾巴走(如果能找到自己的尾巴)。如果真蛇找不到自己的尾巴,那么真蛇wander随机走一步。
那么可以写出伪代码:
if(canFindBfsPath(realSnake.head,food)){
snake virtualSnake=realSnake;
eatFood(virtualSnake);//派虚拟蛇去吃食物
if(canFindBfsPath(virtualSnake.head,virtualSnake.tail)){
eatFood(realSnake);//派真蛇去吃食物
}else if(canFindBfsPath(realSnake.head,realSnake.tail)){
followSnakeTail(realSnake);//跟蛇尾
}else wander(realSnake);
}else if(canFindBfsPath(realSnake.head,realSnake.tail)){
*
followSnakeTail(realSnake);
}else wander(realSnake);
经过我研究,这个头跟尾(跟着自己尾巴走)的函数应该找头尾的最远路径,这样就能保证当蛇是不安全的时候,蛇的头尾之间留出最多的空格,这样当蛇吃完食物后不会蛇头撞到蛇尾,同时由于头尾之间留出很多空格,所以总能找到头尾路径,也就是说蛇总是“安全”的,wander函数极少被调用。
怎么找到头尾的最远路径?用dfs?也许可以,但我有更好的方法。由于dfs是递归实现,找最远路径当地图很大时时间复杂度很高,并且效果也不理想。我的思想是:蛇头附近有3个方向的空格可以走,找一个空格,这个空格生成的到蛇尾的bfs路径在这3个空格中(如果存在到蛇尾的bfs路径)最长。这是一步,如果是蛇连续走,产生的效果就跟走最远路径一样了,并且bfs的时间复杂度远比dfs低。
二、bfs的实现
我把ai部分代码贴了出来便于理解。下面是drawWidget类中与ai有关的成员和函数。
//"drawWidget.h"
class drawWidget : public QWidget
{
Q_OBJECT
public:
...//其他函数在此处省略
...