智能贪吃蛇-学习记录

上次写了个简单的字符贪吃蛇,这次让电脑自己跑贪吃蛇,也就是简单的智能蛇。
上次的蛇还有一些问题,比如输入方向键后要输入回车才会运动,老师提供了一种在linux上的程序,可以不用输回车就能读入键盘输入,代码如下:

#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
#include <unistd.h>

static struct termios ori_attr, cur_attr;

static __inline 
int tty_reset(void)
{
        if (tcsetattr(STDIN_FILENO, TCSANOW, &ori_attr) != 0)
                return -1;

        return 0;
}


static __inline
int tty_set(void)
{

        if ( tcgetattr(STDIN_FILENO, &ori_attr) )
                return -1;

        memcpy(&cur_attr, &ori_attr, sizeof(cur_attr) );
        cur_attr.c_lflag &= ~ICANON;
//        cur_attr.c_lflag |= ECHO;
        cur_attr.c_lflag &= ~ECHO;
        cur_attr.c_cc[VMIN] = 1;
        cur_attr.c_cc[VTIME] = 0;

        if (tcsetattr(STDIN_FILENO, TCSANOW, &cur_attr) != 0)
                return -1;

        return 0;
}

static __inline
int kbhit(void) 
{

        fd_set rfds;
        struct timeval tv;
        int retval;

        /* Watch stdin (fd 0) to see when it has input. */
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        /* Wait up to five seconds. */
        tv.tv_sec  = 0;
        tv.tv_usec = 0;

        retval = select(1, &rfds, NULL, NULL, &tv);
        /* Don't rely on the value of tv now! */

        if (retval == -1) {
                perror("select()");
                return 0;
        } else if (retval)
                return 1;
        /* FD_ISSET(0, &rfds) will be true. */
        else
                return 0;
        return 0;
}

//将你的 snake 代码放在这里

int main()
{
        //设置终端进入非缓冲状态
        int tty_set_flag;
        tty_set_flag = tty_set();

        //将你的 snake 代码放在这里
        printf("pressed `q` to quit!\n");
        while(1) {

                if( kbhit() ) {
                        const int key = getchar();
                        printf("%c pressed\n", key);
                        if(key == 'q')
                                break;
                } else {
                       ;// fprintf(stderr, "<no key detected>\n");
                }
        }

        //恢复终端设置
        if(tty_set_flag == 0) 
                tty_reset();
        return 0;
}

代码太复杂(反正我看不懂)不过好在只要把贪吃蛇代码加到指定位置就可以了。本人试了一下确实可以,不过随机产生食物还要在最前面加上引用time.h和stdlib.h的库文件。

这里写图片描述

接下来就是智能蛇的算法了,老师提供的算法为走曼哈顿距离:

这里写图片描述

分别计算蛇头到食物的距离,算出最小距离,最小距离决定了头往哪走,即返回下一个操作。具体代码如下:

char WhereGoNext(int Hx, int Hy, int Fx, int Fy)
{
    char moveable[4] = { 'a','d','w','s' };
    int distance[4] = { 0 };
    distance[0] = abs(Fy - (Hy - 1)) + abs(Fx - Hx);
    if (map[Hx][Hy - 1] != BLANK_CELL&&map[Hx][Hy - 1] != SNAKE_FOOD)
        distance[0] = 9999;

    distance[1] = abs(Fy - (Hy + 1)) + abs(Fx - Hx);
    if (map[Hx][Hy + 1] != BLANK_CELL&&map[Hx][Hy + 1] != SNAKE_FOOD)
        distance[1] = 9999;

    distance[2] = abs(Fy - Hy) + abs(Fx - (Hx - 1));
    if (map[Hx - 1][Hy] != BLANK_CELL&&map[Hx - 1][Hy] != SNAKE_FOOD)
        distance[2] = 9999;

    distance[3] = abs(Fy - Hy) + abs(Fx - (Hx + 1));
    if (map[Hx + 1][Hy] != BLANK_CELL&&map[Hx + 1][Hy]!=SNAKE_FOOD)
        distance[3] = 9999;

    int min=0;
    for (int i = 0; i <= 3; i++)
    {
        if (distance[i] < distance[min] && distance[i] != 9999)
            min = i;
    }
    return moveable[min];
}

用这个代码替换原来贪吃蛇的scanf就可以实现自动寻路。但是计算机运算的速度非常快,可能你还没看清楚蛇就已经死了。这时候要加入一个操作,让生成下一步的方向时停一下。这要引入windows.h里面的Sleep(注意,S是大写)Sleep(i)可以让进行下一个操作之前暂停i的时间,单位为毫秒,所以我让蛇每0.1.秒运动一次。做出来的效果如图:

这里写图片描述

可见,蛇已经可以自己跑了,但是缺点很显然:容易死。因为这种算法不会考虑走完后下一步该怎么走,按最短路线的话非常容易把自己困死。我在网上找了一下进阶的算法,主要的思想就是蛇头要追着蛇尾走,而且走的不是最近路线,而是最远路线。蛇必须判断走完这次后是否还能找到尾巴的路线才去吃,否则就要往别的方向走一格,而这个走法是走最远的距离。
算法伪代码:

 1 if 可以吃食物
 2         if 虚拟蛇沿规则最短路吃食物后能找到尾巴
 3                 真实蛇移动一步
 4                 重新判断
 5         else if 虚拟蛇沿不规则最短路去吃食物能找到尾巴
 6                 真实蛇移动一步
 7                 重新判断
 8 else if 可知到达自己的尾巴并且移动一步已让可以到达自己尾巴
 9         选择离食物最远的位置移动
10         重新判断
11 else
12         向最深的路径移动一步

据说这样可以让蛇铺满整个地图(不过我还不会搞)不过自己做出来了第一个类似人工智能的作品还是很高兴的(可把我厉害坏了,插会腰.jpg)另外感谢让我搞死无数次的小蛇:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/ZhangyunqingGC/article/details/78905640