智能蛇算法

这里写图片描述

这张gif是我从网上找到的,尽管参考了很多资料以及看了很多dalao的帖子,但是我现在的能力的确是没有办法作出这个智能蛇的,不过大概的算法思路还是有的。

在我看来,作出智能蛇的一个关键点在于怎么让蛇知道并让蛇头找到食物的位置。 主要运用 【哈密顿回路】原理:由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。在图论中是指含有哈密顿回路的图,闭合的哈密顿路径称作哈密顿回路(Hamiltonian cycle),含有图中所有顶点的路径称作哈密顿路径。
这里写图片描述

身子短还短的时候比较好说,不会自己撞自己,但是随着吃的食物逐渐增多,自身的长度也会慢慢增加,从而就会蛇头无脑去追捕食物从而撞上自己“game over!”
那么现在就要区分这些情况了,我们看一下这个伪代码

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

1、能吃食物,吃完食物后还能找到尾巴,这时候就直接去吃食物。
    这里写图片描述
    2、蛇不能吃食物,或者吃食物后找不到尾巴(找不到尾巴还去吃是不允许的,这回导致吃完后无路可走),这时候就跟着蛇尾巴走,走到能去吃食物为止(为什么能走到下面解释)。
    这里写图片描述
    3、在状态2的前提下,尾巴都找不到,这时候只能随便走一步了(Wander)
    这里写图片描述
  以上,也是我检索到的AI算法的核心部分,但是,在实践中,我发现很多细节这些文章都没有说清楚,实践起来是不允许有一点差错的,所以,结合自己的实践,对这几种情况做一下更详细的分析:
    1、能吃食物,并且吃完之后能找到尾巴。
 这种情况只在蛇身较短的时候出现,而且这里吃完之后能找到尾巴,指的是沿最短路径去吃。或许,走最短路吃完食物找不到尾巴了,而走一条较长的路径能找到,但还是认为后一种不属于状态一,否则,编写代码将变得十分复杂。但是有时候,可能不止一个方向到食物的路径最短,这时候可以用类似蒙特卡洛法随机选择方向,可以提高吃完食物后找到尾巴的可能性,实践证明也确实如此。
    2、追着尾巴走,也是又讲究的,首先,要保证走完这一步后还能继续找到尾巴,这是基本前提,其次,如果两个方向都能找到尾巴,应该选择离食物较远的方向(注意不是离食物较近或者离尾巴较近),应为只有这样,才能保证留出了足够空间给蛇去吃食物。
这里写图片描述

所以现在伪代码是有了,如下是这个伪代码的实现:

#include "Prepare.h"

void setPosition(int x, int y) {

    COORD pos;

    HANDLE hOutput;

    pos.X = x;

    pos.Y = y;

    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

    SetConsoleCursorPosition(hOutput, pos);

}



void hideCursor() {

    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };

    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);

}



void checkSnake() {

    int x = Snake.front().x;

    int y = Snake.front().y;

    list<SnakeNode>::iterator it = Snake.begin();

    for (it++; it != Snake.end(); it++) {

        if (x == (*it).x && y == (*it).y) {

            setPosition(2, 20);printf("check ERROR 1");

            getchar();

        }

        if ((*it).x > WIDTH || (*it).x < 2 || (*it).y > HEIGHT || (*it).y < 1) {

            setPosition(2, 20);printf("check ERROR 2");

            getchar();

        }

    }

}



void isHeadequalTail() {

    SnakeNode Head = Snake.front();

    SnakeNode Tail = Snake.back();

    if (Head.x == Tail.x && Head.y == Tail.y) {

        setPosition(10, 10);

        printf("isHeadequalTail ERROR");

        for (;true;) {

            getchar();

        }

    }

}



void createFood() {

    int x, y;

    bool flag = true;

    while (flag) {

        x = rand() % (WIDTH / 2);

        x = 2 * (x + 1);

        y = rand() % HEIGHT + 1;

        flag = false;

        for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

            if (x == (*it).x && y == (*it).y) {

                flag = true;

                break;

            }

        }

    }

    Food.x = x;

    Food.y = y;

    setPosition(Food.x, Food.y);

    printf("◆");

}



void printSnake() {

    SnakeNode Head = Snake.front();



    if (Head.x == Food.x && Head.y == Food.y) {

        createFood();

        setPosition(WIDTH / 2 + 2, 0);

        printf("%3d", Snake.size());

    }

    else {

        setPosition(theTail.x, theTail.y);

        printf("  ");

    }

    setPosition(Head.x, Head.y);

    printf("■");

}



bool isOpposite(int a, int b) {

    if (0 == a && 1 == b) {

        return true;

    }

    else if (1 == a && 0 == b) {

        return true;

    }

    else if (2 == a && 3 == b) {

        return true;

    }

    else if (3 == a && 2 == b) {

        return true;

    }

    else {

        return false;

    }



}



void createInterface() {

    system("mode con cols=44 lines=24");                                    //设置控制台

    hideCursor();                                                           //

    for (int i = 0; i <= WIDTH; i += 2) {

        setPosition(i, 0);

        printf("●");

        setPosition(i, HEIGHT + ROWUNIT);

        printf("●");

    }

    for (int i = 0; i < HEIGHT + 2; i++) {

        setPosition(0, i);

        printf("●");

        setPosition(WIDTH + COLUMNUNIT, i);

        printf("●");//

    }

    setPosition(WIDTH / 2 - 2, 21);

    printf("AI Snake");

    SnakeNode temp;

    for (int i = 0; i < 4; i++) {

        temp.x = 2 + i * 2;

        temp.y = 1;

        setPosition(temp.x, temp.y);

        printf("■");

        Snake.push_front(temp);

    }

    SnakeDirection = 2;

    setPosition(WIDTH / 2 - 2, 0);

    printf("Score:");

    setPosition(WIDTH / 2 + 2, 0);

    printf("%3d", Snake.size());

    createFood();

}



void getFoodDistance() {





    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            bfsDistance[x][y] = INF;                                                        //初始化

        }

    }

    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        if ((*it).x < 2 || (*it).x > WIDTH || (*it).y < 1 || (*it).y >HEIGHT) {             //调试使用,异常情况

            setPosition(44, 20);printf("110:%d--d%", (*it).x, (*it).y);

            getchar();

        }

        bfsDistance[(*it).x][(*it).y] = SNAKEBODY;                                          //蛇身用常量SNAKEBODY标记

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        bfsDistance[x][0] = bfsDistance[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        bfsDistance[0][y] = bfsDistance[WIDTH + COLUMNUNIT][y] = WALL;

    }                                                                                       //围墙用常量WALL标记

    bfsDistance[Food.x][Food.y] = 0;                                                        //食物位置记为 0



    /************************************************************************/

    /*依据bfsDistance通过BFS搜索每个节点,计算与食物的距离,蛇身与墙视为无穷大   */

    /************************************************************************/

    queue<SnakeNode> Point;

    Point.push(Food);

    SnakeNode temp, adj;

    while (!Point.empty()) {

        temp = Point.front();                                               

        Point.pop();                                                

        for (int i = 0; i < 4; i++) {

            adj.x = temp.x + Direction[i][0];

            adj.y = temp.y + Direction[i][1];

            if (INF == bfsDistance[adj.x][adj.y]) {                                         //INF说明是尚未遍历的位置

                bfsDistance[adj.x][adj.y] = bfsDistance[temp.x][temp.y] + 1;

                Point.push(adj);

            }

        }

    }

    /************************************************************************/

    /* 注:函数运行结束应该求出每个位置与食物的距离                               */

    /************************************************************************/

}



bool isReachable() {

    SnakeNode temp;

    SnakeNode Head = Snake.front();

    for (int i = 0; i < 4; i++) {

        if (isOpposite(i, SnakeDirection)) {                                    //相反方向排除

            continue;

        }

        temp.x = Head.x + Direction[i][0];

        temp.y = Head.y + Direction[i][1];

        if (bfsDistance[temp.x][temp.y] < INF) {                                //检查是否有可达到的方向

            return true;

        }

    }

    return false;

}



bool isVirtualTailReachable_Regular() {

    int tempDirection = -1, virtualDirection = SnakeDirection;

    /************************************************************************/

    /* 为了判断吃了食物后能否找到蛇尾,建立一条虚拟蛇前去吃食物,并判断           */

    /************************************************************************/

    VirtualSnake.clear();                                                           //初始化

    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        VirtualSnake.push_back(*(it));

    }                                                                               //复制蛇身

    SnakeNode MoveHead = Snake.front();                                             //获取蛇头

    SnakeNode adj;                                                                  //保存临时使用的邻居变量

    SnakeNode Result;                                                               //保存最终选择的邻居位置

    while (MoveHead.x != Food.x || MoveHead.y != Food.y) {                          //如果蛇头不在食物位置

        /************************************************************************/

        /* 寻找蛇头附近距离食物最近的位置                                          */

        /************************************************************************/

        int MinDistance = INF;

        for (int i = 0; i < 4; i++) {

            if (isOpposite(i, virtualDirection)) {

                continue;

            }

            adj.x = MoveHead.x + Direction[i][0];

            adj.y = MoveHead.y + Direction[i][1];

            if (bfsDistance[adj.x][adj.y] < MinDistance) {

                MinDistance = bfsDistance[adj.x][adj.y];

                Result.x = adj.x;

                Result.y = adj.y;

                tempDirection = i;

            }

        }

        if (MinDistance >= INF) {                                               //异常情况

            setPosition(46, 10);

            printf("isVirtualTailReachable_Regular ERROR");

            getchar();

            exit(-101);                                                         //设置一个错误代码

        }

        VirtualSnake.push_front(Result);

        if (Result.x != Food.x || Result.y != Food.y) {                         //如果没吃到食物

            VirtualSnake.pop_back();                                                //虚拟蛇移动

        }

        virtualDirection = tempDirection;                                       //更新虚拟蛇的方向

        if (MoveHead.x == Snake.front().x && MoveHead.y == Snake.front().y) {   //第一步记录下来,以备使用

            NextStep.x = Result.x;

            NextStep.y = Result.y;

            NextDirection = virtualDirection;

        }                                                                       //逻辑正确的话这个语句块只执行一次

        MoveHead = VirtualSnake.front();                                        //更新蛇头位置

    }

    //VirtualSnake.pop_back();注意蛇尾不用减去,虚拟蛇吃蛇舞后蛇厂家一

    /************************************************************************/

    /* 运行到此,虚拟蛇应该吃了食物                                            */

    /************************************************************************/





    /************************************************************************/

    /* 这里检查一下虚拟蛇的移动是否正确,如果正确至少蛇长度+1                   */

    /************************************************************************/

    if (VirtualSnake.size() != Snake.size()+1) {

        setPosition(46, 11);printf("VirtualSnake != Snake, enter to exit");

        getchar();getchar();

        exit(-1);

    }



    /************************************************************************/

    /* 通过虚拟蛇计算虚拟蛇头是否能到达虚拟蛇尾                                 */

    /*计算方法同样是 BFS,蛇头可达位置与蛇尾可达即能到达                           */

    /*首先初始化计算矩阵 bfsJudgeVirtualTail                                 */

    /************************************************************************/

    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            bfsJudgeVirtualTail[x][y] = INF;

        }

    }

    for (list<SnakeNode>::iterator it = VirtualSnake.begin(); it != VirtualSnake.end(); it++) {

        bfsJudgeVirtualTail[(*it).x][(*it).y] = SNAKEBODY;

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        bfsJudgeVirtualTail[x][0] = bfsJudgeVirtualTail[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        bfsJudgeVirtualTail[0][y] = bfsJudgeVirtualTail[WIDTH + COLUMNUNIT][y] = WALL;

    }

    SnakeNode VirtualSnakeTail = VirtualSnake.back();

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = 0;                    //蛇尾是出发点,标记为0

    /************************************************************************/

    /* 利用队列 BFS 求出与虚拟蛇尾的曼哈顿距离                                 */

    /************************************************************************/

    queue<SnakeNode> Point;

    Point.push(VirtualSnakeTail);

    SnakeNode temp;

    while (!Point.empty()) {

        temp = Point.front();                                               //取队首

        Point.pop();

        for (int i = 0; i < 4; i++) {

            adj.x = temp.x + Direction[i][0];

            adj.y = temp.y + Direction[i][1];

            if (INF == bfsJudgeVirtualTail[adj.x][adj.y]) {                         //未搜索点

                bfsJudgeVirtualTail[adj.x][adj.y] = bfsJudgeVirtualTail[temp.x][temp.y] + 1;

                Point.push(adj);

            }

        }

    }

    /************************************************************************/

    /*运行到此处应该正确输出虚拟蛇尾到每个位置的距离                               */

    /************************************************************************/

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = SNAKEBODY;//遗忘此句会出现蛇头吃掉蛇尾的可能



    /************************************************************************/

    /*现在判断虚拟蛇头是否可达                                                */

    /************************************************************************/

    int TailDistance = INF;

    SnakeNode VirtualSnakeHead = VirtualSnake.front();

    for (int i = 0; i < 4; i++) {

        adj.x = VirtualSnakeHead.x + Direction[i][0];

        adj.y = VirtualSnakeHead.y + Direction[i][1];

        if (bfsJudgeVirtualTail[adj.x][adj.y] < INF) {

            return true;

        }

    }

    return false;

}



bool isVirtualTailReachable_Random() {

    int tempDirection = -1, virtualDirection = SnakeDirection;

    /************************************************************************/

    /* 为了判断吃了食物后能否找到蛇尾,建立一条虚拟蛇前去吃食物,并判断           */

    /************************************************************************/

    VirtualSnake.clear();                                                           //初始化

    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        VirtualSnake.push_back(*(it));

    }                                                                               //复制蛇身

    SnakeNode MoveHead = Snake.front();                                             //获取蛇头

    SnakeNode adj;                                                                  //保存临时使用的邻居变量

    SnakeNode Result;                                                               //保存最终选择的邻居位置

    while (MoveHead.x != Food.x || MoveHead.y != Food.y) {                          //如果蛇头不在食物位置

        /************************************************************************/

        /* 寻找蛇头附近距离食物最近的位置                                          */

        /************************************************************************/

        int MinDistance = INF;

        int random_i = rand() % 4;

        for (int t = 0; t < 4; random_i = (random_i + 1) % 4,t++) {

            if (isOpposite(random_i, virtualDirection)) {

                continue;

            }

            adj.x = MoveHead.x + Direction[random_i][0];

            adj.y = MoveHead.y + Direction[random_i][1];

            if (bfsDistance[adj.x][adj.y] < MinDistance) {

                MinDistance = bfsDistance[adj.x][adj.y];

                Result.x = adj.x;

                Result.y = adj.y;

                tempDirection = random_i;

            }

        }

        if (MinDistance >= INF) {                                               //异常情况

            setPosition(46, 10);

            printf("isVirtualTailReachable_Regular ERROR");

            getchar();

            exit(-1);

        }

        VirtualSnake.push_front(Result);

        if (Result.x != Food.x || Result.y != Food.y) {                         //如果没吃到食物

            VirtualSnake.pop_back();                                                //虚拟蛇移动

        }

        virtualDirection = tempDirection;                                       //更新虚拟蛇的方向

        if (MoveHead.x == Snake.front().x && MoveHead.y == Snake.front().y) {   //第一步记录下来,以备使用

            NextStep.x = Result.x;

            NextStep.y = Result.y;

            NextDirection = virtualDirection;

        }                                                                       //逻辑正确的话这个语句块只执行一次

        MoveHead = VirtualSnake.front();                                        //更新蛇头位置

    }

    /************************************************************************/

    /* 运行到此,虚拟蛇应该吃了食物                                            */

    /************************************************************************/



    /************************************************************************/

    /* 这里检查一下虚拟蛇的移动是否正确,如果正确至少蛇长度+1                    */

    /************************************************************************/

    if (VirtualSnake.size() != Snake.size()+1) {

        setPosition(46, 11);printf("VirtualSnake != Snake, enter to exit");

        getchar();getchar();

        exit(-1);

    }



    /************************************************************************/

    /* 通过虚拟蛇计算虚拟蛇头是否能到达虚拟蛇尾                                 */

    /*计算方法同样是 BFS,蛇头可达位置与蛇尾可达即能到达                           */

    /*首先初始化计算矩阵 bfsJudgeVirtualTail                                 */

    /************************************************************************/

    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            bfsJudgeVirtualTail[x][y] = INF;

        }

    }

    for (list<SnakeNode>::iterator it = VirtualSnake.begin(); it != VirtualSnake.end(); it++) {

        bfsJudgeVirtualTail[(*it).x][(*it).y] = SNAKEBODY;

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        bfsJudgeVirtualTail[x][0] = bfsJudgeVirtualTail[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        bfsJudgeVirtualTail[0][y] = bfsJudgeVirtualTail[WIDTH + COLUMNUNIT][y] = WALL;

    }

    SnakeNode VirtualSnakeTail = VirtualSnake.back();

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = 0;                    //蛇尾是出发点,标记为0

    /************************************************************************/

    /* 利用队列 BFS 求出与虚拟蛇尾的曼哈顿距离                                 */

    /************************************************************************/

    queue<SnakeNode> Point;

    Point.push(VirtualSnakeTail);

    SnakeNode temp;

    while (!Point.empty()) {

        temp = Point.front();                                               //取队首

        Point.pop();

        for (int i = 0; i < 4; i++) {

            adj.x = temp.x + Direction[i][0];

            adj.y = temp.y + Direction[i][1];

            if (INF == bfsJudgeVirtualTail[adj.x][adj.y]) {                         //未搜索点

                bfsJudgeVirtualTail[adj.x][adj.y] = bfsJudgeVirtualTail[temp.x][temp.y] + 1;

                Point.push(adj);

            }

        }

    }

    /************************************************************************/

    /*运行到此处应该正确输出虚拟蛇尾到每个位置的距离                             */

    /************************************************************************/

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = SNAKEBODY;            //遗忘此句会出现蛇头吃掉蛇尾的可能



    /************************************************************************/

    /*现在判断虚拟蛇头是否可达                                                */

    /************************************************************************/

    int TailDistance = INF;

    SnakeNode VirtualSnakeHead = VirtualSnake.front();

    for (int i = 0; i < 4; i++) {

        adj.x = VirtualSnakeHead.x + Direction[i][0];

        adj.y = VirtualSnakeHead.y + Direction[i][1];

        if (bfsJudgeVirtualTail[adj.x][adj.y] < INF) {

            return true;

        }

    }

    return false;



}



void eatFoodMove() {

    Snake.push_front(NextStep);                                                 //删除尾巴的操作在打印时进行

    SnakeDirection = NextDirection;                                             //注意要更新方向

    theTail = Snake.back();

    if (NextStep.x != Food.x || NextStep.y != Food.y) {

        Snake.pop_back();                                                       //没吃到食物蛇尾就删除

    }

}



bool eatFood() {

    getFoodDistance();

    if (isReachable()) {

        if (isVirtualTailReachable_Regular() || isVirtualTailReachable_Random()) {//注:这里用了短路运算

            eatFoodMove();

            return true;

        } else {

            return false;

        }

    }

    return false;                                                                  //如果不可达

}



bool isSafe(SnakeNode NewHead) {

    VirtualSnake.clear();

    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        VirtualSnake.push_back(*(it));

    }                                                                           //复制真实蛇

    VirtualSnake.push_front(NewHead);

    VirtualSnake.pop_back();



    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {                              //广度优先求出每个位置与虚拟蛇尾的距离

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            bfsJudgeVirtualTail[x][y] = INF;

        }

    }

    for (list<SnakeNode>::iterator it = VirtualSnake.begin(); it != VirtualSnake.end(); it++) {

        bfsJudgeVirtualTail[(*it).x][(*it).y] = SNAKEBODY;

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        bfsJudgeVirtualTail[x][0] = bfsJudgeVirtualTail[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        bfsJudgeVirtualTail[0][y] = bfsJudgeVirtualTail[WIDTH + COLUMNUNIT][y] = WALL;

    }

    SnakeNode VirtualSnakeTail = VirtualSnake.back();

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = 0;

    queue<SnakeNode> Point;

    Point.push(VirtualSnakeTail);                                               //利用队列 BFS 求出与虚拟蛇尾的曼哈顿距离

    SnakeNode temp, adj;

    while (!Point.empty()) {

        temp = Point.front();                                                   //取队首

        Point.pop();

        for (int i = 0; i < 4; i++) {

            adj.x = temp.x + Direction[i][0];

            adj.y = temp.y + Direction[i][1];

            if (INF == bfsJudgeVirtualTail[adj.x][adj.y]) {                     //未搜索点

                bfsJudgeVirtualTail[adj.x][adj.y] = bfsJudgeVirtualTail[temp.x][temp.y] + 1;

                Point.push(adj);

            }

        }

    }

    bfsJudgeVirtualTail[VirtualSnakeTail.x][VirtualSnakeTail.y] = SNAKEBODY;    //易忘



    SnakeNode VirtualSnakeHead = VirtualSnake.front();

    if (VirtualSnakeHead.x != NewHead.x || VirtualSnakeHead.y != NewHead.y) {

        setPosition(46, 16);printf("VirtualSnakeHead != NewHead");

        getchar();

        exit(-105);                                                             //异常情况,调试使用

    }

    for (int i = 0; i < 4; i++) {

        adj.x = VirtualSnakeHead.x + Direction[i][0];

        adj.y = VirtualSnakeHead.y + Direction[i][1];

        if (bfsJudgeVirtualTail[adj.x][adj.y] < INF) {

            return true;

        }

    }

    return false;

}



void followTailMove() {



    Snake.push_front(NextStep);//删除尾巴的操作在打印时进行

    SnakeDirection = NextDirection;//注意要更新方向

    theTail = Snake.back();

    if (NextStep.x != Food.x || NextStep.y != Food.y) {

        Snake.pop_back();

    }

}



int FoodDistance(SnakeNode adj) {

    if (bfsDistance[adj.x][adj.y] < INF) {                                      //如何可达就用这个距离

        return bfsDistance[adj.x][adj.y];                               

    } else {

        return abs(adj.x - Food.x) + abs(adj.y - Food.y);                       //不可达就用曼哈顿距离

    }

}



bool followTail() {

    /************************************************************************/

    /* 如果不能去吃食物,则执行方案二:远离食物,但是必须保证移动后能找到蛇      */

    /************************************************************************/

    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            bfsJudgeRealTail[x][y] = INF;

        }

    }

    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        bfsJudgeRealTail[(*it).x][(*it).y] = SNAKEBODY;

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        bfsJudgeRealTail[x][0] = bfsJudgeRealTail[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        bfsJudgeRealTail[0][y] = bfsJudgeRealTail[WIDTH + COLUMNUNIT][y] = WALL;

    }



    SnakeNode RealSnakeTail = Snake.back();

    bfsJudgeRealTail[RealSnakeTail.x][RealSnakeTail.y] = 0;

    //利用队列 BFS 求出与蛇尾的曼哈顿距离

    queue<SnakeNode> Point;

    Point.push(RealSnakeTail);

    SnakeNode temp, adj;

    while (!Point.empty()) {

        temp = Point.front();                                               //取队首

        Point.pop();

        for (int i = 0; i < 4; i++) {

            adj.x = temp.x + Direction[i][0];

            adj.y = temp.y + Direction[i][1];

            if (INF == bfsJudgeRealTail[adj.x][adj.y]) {                    //未搜索点

                bfsJudgeRealTail[adj.x][adj.y] = bfsJudgeRealTail[temp.x][temp.y] + 1;

                Point.push(adj);

            }

        }

    }

    //运行到这里应该计算出了蛇尾到所有位置的距离,下面判定蛇头是否可以到达蛇尾

    //尤其需要注意的是,蛇尾位置要重置为 SNAKEBODY,否则蛇头会与蛇尾重合

    bfsJudgeRealTail[RealSnakeTail.x][RealSnakeTail.y] = SNAKEBODY;

    /************************************************************************/

    /* 先判断蛇尾是否可达                                                        */

    /************************************************************************/

    SnakeNode tempAdj;

    SnakeNode tempHead = Snake.front();

    bool tempReachable = false;

    for (int i = 0; i < 4; i++) {

        tempAdj.x = tempHead.x + Direction[i][0];

        tempAdj.y = tempHead.y + Direction[i][1];

        if (bfsJudgeRealTail[tempAdj.x][tempAdj.y] < INF) {

            tempReachable = true;

            break;

        }

    }

    /************************************************************************/

    /*如果可以则移动一步,应满足移动之后仍能找到蛇尾                            */

    /*移动与食物尽可能远离,以腾出空间等待食物可以到达                          */

    /************************************************************************/

    int SafeDirection[4];

    int count = 0;

    SnakeNode RealHead = Snake.front();

    for (int i = 0; i < 4; i++) {                                           //找出可以移动的方向

        if (isOpposite(i, SnakeDirection)) {

            continue;

        }

        adj.x = RealHead.x + Direction[i][0];

        adj.y = RealHead.y + Direction[i][1];

        if (bfsJudgeRealTail[adj.x][adj.y] < INF) {

            if (isSafe(adj)) {

                SafeDirection[count] = i;

                count++;

            }

        }

    }



    int MaxDistance = -1;

    SnakeNode Result;

    if (0 == count) {                                                       //如果没有安全的方向

        return false;

    }

    else {

        for (int k = 0; k < count; k++) {                                   //找出最远的方向

            adj.x = RealHead.x + Direction[SafeDirection[k]][0];

            adj.y = RealHead.y + Direction[SafeDirection[k]][1];

            int tempDistance = FoodDistance(adj);

            if (tempDistance > MaxDistance) {

                MaxDistance = tempDistance;

                Result.x = adj.x;

                Result.y = adj.y;

                NextDirection = SafeDirection[k];

            }

        }

        NextStep.x = Result.x;

        NextStep.y = Result.y;

        followTailMove();

        checkSnake();                                                       //检查

        isHeadequalTail();

        return true;

    }

    return false;

}



void DFS(int x, int y)

{

    if (WALL == dfsDistance[x][y]) {

        return;

    }

    dfsDistance[x][y] = WALL;                                           //这一句要加深理解

    if (currentDepth > deepest) {

        deepest = currentDepth;

    }

    currentDepth++;

    SnakeNode Temp;

    for (int i = 0; i < 4; i++) {

        Temp.x = x + Direction[i][0];

        Temp.y = y + Direction[i][1];

        if (Temp.x < 2 || Temp.x > WIDTH || Temp.y < 1 || Temp.y > HEIGHT) {

            continue;

        }

        if (WALL == dfsDistance[Temp.x][Temp.y]) {

            continue;

        }

        DFS(Temp.x, Temp.y);

    }

    currentDepth--;

}



int getDepth(SnakeNode temp) {

    for (int x = 2; x <= WIDTH; x += COLUMNUNIT) {                      //初始化;为了不至于递归导致始终横跨,所以边沿不计入递归

        for (int y = 1; y <= HEIGHT; y += ROWUNIT) {

            dfsDistance[x][y] = 0;

        }

    }

    //这里留个边界

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        dfsDistance[x][0] = dfsDistance[x][HEIGHT + ROWUNIT] = WALL;

    }

    for (int y = 1; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        dfsDistance[0][y] = dfsDistance[WIDTH + COLUMNUNIT][y] = WALL;

    }



    for (list<SnakeNode>::iterator it = Snake.begin(); it != Snake.end(); it++) {

        if ((*it).x < 2 || (*it).x> WIDTH || (*it).y > HEIGHT || (*it).y < 1) {     //调试使用

            setPosition(44, 4);printf("iterator Error\t%d\t%d", (*it).x, (*it).y);

            getchar();

            exit(-1);

        }

        dfsDistance[(*it).x][(*it).y] = WALL;

    }



    currentDepth = 0, deepest = -1;                                         //初始化

    DFS(temp.x, temp.y);                                                    //递归深度优先遍历

    return deepest;

}



void snakeWander() {

    SnakeNode NextHead;                                                     //为了让蛇在困境中尽可能坚持,采用 DFS 

    SnakeNode temp;

    SnakeNode CurrentHead = Snake.front();



    for (int x = 4; x <= WIDTH - COLUMNUNIT; x += COLUMNUNIT) {             //辅助矩阵,标记不可移动到的点

        for (int y = 2; y <= HEIGHT - ROWUNIT; y += ROWUNIT) {

            Mark[x][y] = INF;

        }

    }                                                                       //蛇以及墙的位置再特殊标记

    for (list<SnakeNode>::iterator it = VirtualSnake.begin(); it != VirtualSnake.end(); it++) {

        Mark[(*it).x][(*it).y] = SNAKEBODY;

    }

    for (int x = 0; x <= WIDTH + COLUMNUNIT; x += COLUMNUNIT) {

        Mark[x][0] = Mark[x][HEIGHT + ROWUNIT] = WALL;

        Mark[x][1] = Mark[x][HEIGHT] = WALL;

    }

    for (int y = 0; y <= HEIGHT + ROWUNIT; y += ROWUNIT) {

        Mark[0][y] = Mark[WIDTH + COLUMNUNIT][y] = WALL;

        Mark[2][y] = Mark[WIDTH][y] = WALL;

    }



    int MaxDepth = -100;

    int tempDirection = -1;

    for (int i = 0; i < 4; i++) {

        if (isOpposite(i, SnakeDirection)){

            continue;

        }

        temp.x = CurrentHead.x + Direction[i][0];

        temp.y = CurrentHead.y + Direction[i][1];

        if (INF == Mark[temp.x][temp.y]) {                                  //判断是否符合要求

            int depth = getDepth(temp);

            if (depth > MaxDepth) {

                MaxDepth = depth;

                tempDirection = i;

                NextHead.x = temp.x;

                NextHead.y = temp.y;

            }

        }

    }

    if (-1 == tempDirection) {

        getchar();

        exit(-3);

    } else {

        SnakeDirection = tempDirection;

        Snake.push_front(NextHead);

        theTail = Snake.back();

        if (NextStep.x != Food.x || NextStep.y != Food.y) {

            Snake.pop_back();

        }

    }

}



void snakeMove() {

    if (!eatFood()) {                                                       //满足状态1:去吃食物

        if (!followTail()) {                                                //满足状态2:追尾巴

            snakeWander();                                                  //随机选择

        }

    }

}

所以这就完了 谢谢阅读。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值