您的位置:首页 > 编程语言 > Java开发

Java写一条吃满屏幕的贪吃蛇(A*自动寻路算法和一些简单的策略) 二

2018-03-02 01:24 330 查看
上一篇文章介绍了一条贪吃蛇的简单实现,这一篇介绍贪吃蛇的自动寻路算法 ,说是自动寻路其实也就是一种遍历地图的算法,如果玩过游戏肯定知道游戏里面一般会有自动寻路功能,贪吃蛇的自动寻路算法也就是实现这个功能。下面慢慢介绍这种算法吧。
    最开始,我没有在网上找资料的时候,我对于设计寻路算法的看法是遍历整个地图,将障碍物做上标记,然后找一条能够到达目的地的线路就可以了。所以最开始我用了特别暴力的方式来寻找路径--广度优先算法。简单的来说,所谓地图就是一个二维数组,行和列分别表示x,y轴,我们知道当我们新建一个对象的时候,系统会在堆内存开辟空间,并且赋予初始值,如果我们使用一个整形二维数组,那么当开辟空间的时候,数组每一个元素都会被默认初始值为0,那我们就可以用0表示地图某个位置没有障碍物,用1表示某个地方存在障碍物,在贪吃蛇游戏里面障碍物一般都是蛇的身体或者墙壁,我们很容易知道障碍物的位置,只要遍历蛇的每一个节点然后在地图上将相应节点位置标记为1就好了,这样子非障碍物的地方就是0,我们遍历地图,找到一条没有障碍物而且可以到达目的地的线路,就是自动寻路算法的目的。
    所谓广度优先算法,就是一种好听的名字,原理简单粗暴,不像一个真正的算法(鄙人个人看法,不严谨)。广度优先按照字面理解就是在起点处先找到所有的可以行走路径,然后再进行下一步‘与之相对的深度优先,就是在起点处先找到一条路径(一般有顺序的,比如说上左下右这样的顺序),走到黑,如果到达目的地就不找了,如果没有那么找下一条路径继续走到黑。

  贪吃蛇的寻路算法,如果是广度优先算法,那么我们只需要获得地图数组之后遍历蛇头部位置的上下左右,只要不是障碍物我们就记录下来,用堆栈或者简单点用一个ArrayList 1集合记录下来,我们记录的是蛇头上下左右 点在数组中的位置,因为这样比较方便我们继续寻找他们的相邻点;当我们记录下起点的相邻点后,就需要在负责记录的集合尾巴取出一个数据,我们遍历这一点的临近点,用新的ArrayList 2集合记录下来,我们要将ArrayList 1中的所有点都取出来找到相邻坐标,这是深度遍历应该做的——先找到当前所有能到达的点,在找下一层,就像水的涟漪,一层一层从起点扩散,直到和目标点相遇。下面看一下这种思路的算法实现,前面只贴功能代码,后面贴完整代码,可能还要分一章来写。
    
public void run(Food fd) {
int[][] map;
map = new int[this.len][this.width];

for (Node node : snake) {
if (node.getX() >= 0 && node.getY() >= 0)
map[node.getX()][node.getY()] = 1;
}
int findx = this.x;
int findy = this.y;
Road father = new Road(findx, findy, null);
ArrayList<Road> list = new ArrayList<Road>();
ArrayList<Road> list1 = new ArrayList<Road>();
list.add(father);
ArrayList<Road> dad = null, boy = null;
boolean flag = false;
int step = 0;
while (!(findx == fd.getX() && findy == fd.getY())) {

dad = dad == list ? list1 : list;
// System.out.println(dad.size()+"this.length"+this.length);
if (dad.size() == 0 && step < this.length / 2) {
System.out.println("无路可走,Enter退出");
Scanner sc = new Scanner(System.in);
sc.nextLine();
sc.close();
System.exit(0);
} else if (dad.size() == 0 && step > this.length / 2) {
System.out.println(step);
break;
}
boy = boy == list1 ? list : list1;
for (int i = 0; i < dad.size(); i++) {
Play.looop++;
findx = dad.get(i).getX();
findy = dad.get(i).getY();
father = dad.get(i);
step++;
// System.out.println(step+":
// "+findx+","+findy+"----"+fd.getX()+","+fd.getY());
if (findx == fd.getX() && findy == fd.getY()) {
flag = true;
break;
}
if (findx - this.pace >= 0 && map[findx - this.pace][findy] != 1) {
map[findx - this.pace][findy] = 1;
boy.add(new Road(findx - this.pace, findy, father));
}
if (findy - this.pace >= 0 && map[findx][findy - this.pace] != 1) {
map[findx][findy - this.pace] = 1;
boy.add(new Road(findx, findy - this.pace, father));
}
if (findx + this.pace < len && map[findx + this.pace][findy] != 1) {
map[findx + this.pace][findy] = 1;
boy.add(new Road(findx + this.pace, findy, father));
}

if (findy + this.pace < width && map[findx][findy + this.pace] != 1) {
map[findx][findy + this.pace] = 1;
boy.add(new Road(findx, findy + this.pace, father));
}

}
dad.clear();
if (flag) {
break;
}
}

// System.out.println(father);

int getx = this.x, gety = this.y;
while (father != null) {
if (getx != father.getX() || gety != father.getY()) {
this.x = father.getX();
this.y = father.getY();
}
// System.out.println(father.getX()+","+father.getY());
father = father.getFather();
}
moveNode(this.x, this.y);
}

代码里相关的参数解释可以看第一篇文章里的注释,这个方法是重写上一个方法的,实现的是广度优先寻路算法 ps:本人小白,行文可能不够严谨,
留了一些坑,欢迎读者帮我指出,日后我水平高了也会回来填坑的。
当贪吃蛇学会广度优先算法之后,就有了初步的智力,你告诉他食物的位置,他就可以自己跑过去找吃的了,不管怎么说,如果读者老爷是比我还要小白的小白,那当你看到小蛇自动吃食物的时候内心一定会很激动,但是你会发现一些问题比如说小蛇跑的时候卡顿严重,控制台输出的话更明显,再者这条蛇还是会死,并不能吃到很多的食物。
    关于第一个问题,卡顿严重,主要是广度优先算法造成的,广度优先算法寻找的效率很低,因为它是盲目的寻找,没有一个方向,所以会浪费很多计算量,严重的时候为了找一个点要遍历整个地图,而且因为贪吃蛇的身体也是不断跟着蛇头变化的,我们不是某一个时刻找到路径就按照这个路径去吃食物,我们应该每走一步找一次路径才能保证蛇的安全,为此我们可以改进算法,让算法变得更加的智能,比如说我们选择路径的时候可以考虑一下找一个距离目标最近的点进行遍历,如果每次遍历找到都是最近的点,那么最终结果虽然不是最优解但是也会比较贴近。所以下一章会介绍这样的聪明算法——A*算法。

关于第二个问题,能吃到但无法吃全屏估计会在第四篇文章中介绍,这是一个策略的问题,当贪吃蛇会吃食物了,我们还希望它吃多点甚至吃满屏幕,那么就要遵循一定的规则,只有满足规则才去吃食物。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java