贪吃蛇的升级——智能蛇

智能蛇

在上一篇博客当中,已经粗略完成一个小小的“贪吃蛇”的程序框架,接下来我们来看一下如何实现更高级的玩法–不需要控制,让蛇自己去吃!

实验环境的准备

其实在上一篇博客中的使用颜色选择时已经在Ubuntu系统下使用了VT-100控制码。
这个实验我选择在Ubuntu下的Code::Blocks完成

非阻塞地检测键盘输入的方法

在之前的游戏中,为了输入命令后不需要回车,并且使输入的命令不显示
Windows下使用的是conio.h中的getch()函数
Ubuntu下使用的是

        system("stty -icanon");
        system("stty -echo");
        input=getchar();
        system("stty icanon");
        system("stty echo");

而今天我们在Ubuntu下使用更高级的kbhit()函数来解决这个问题

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

#define SNAKE_MAX_LENGTH 20
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
#define BLOCKS '#'

char map[12][12]={"************",
                "*XXXXH     *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "************"};

int snakeX[SNAKE_MAX_LENGTH]={1,2,3,4,5};
int snakeY[SNAKE_MAX_LENGTH]={1,1,1,1,1};
int snakeLength=5;
int tempX=0;
int tempY=0;
int color=0;
int score=0;
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;
}

void color_choose(void)
{
    printf("Please choose color (Input 1~5):\n");
    printf("\033[30m(1)Black  \033[31m(2)Red  \033[32m(3)Green  \033[33m(4)Yellow  \033[34m(5)Blue\033[0m\n");
    scanf("%d",&color);
    system("clear");
}

void set_blocks(void)
{
    int number=0;
    int x = 0;
    int y = 0;
    int turn=0;
    srand((unsigned)time(NULL));
    printf("How many blocks do you want to set:(range from 0 to 5)\n");
    scanf("%d",&number);
    while(turn<number)
    {
        x=rand()%10+1;
        y=rand()%10+1;
        if(map[x][y]==BLANK_CELL)
        {
            map[x][y]=BLOCKS;
            turn++;
        }
    }
}
void snakeMove(int dx,int dy)
{
    int turn=0;
    tempX=snakeX[snakeLength-1];
    tempY=snakeY[snakeLength-1];
    int tempX1=0;
    int tempY1=0;
    if(map[snakeY[snakeLength-1]+dy][snakeX[snakeLength-1]+dx]==BLOCKS)//当蛇头面前是blocks时无法前进
    {
        return;
    }
    snakeY[snakeLength-1]+=dy;
    snakeX[snakeLength-1]+=dx;
    for(turn=snakeLength-2;turn>=0;turn--)
    {
        tempX1=snakeX[turn];
        tempY1=snakeY[turn];
        snakeX[turn]=tempX;
        snakeY[turn]=tempY;
        tempX=tempX1;
        tempY=tempY1;
    }
}

void put_money(void)
{
    int x = 0;
    int y = 0;
    srand((unsigned)time(NULL));
    while(map[x][y]!=BLANK_CELL)
    {
        x=rand()%10+1;
        y=rand()%10+1;
    }
    map[x][y]=SNAKE_FOOD;
}

void eat_food(void)
{
    int turn = 0;
    if(map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]==SNAKE_FOOD)
    {
        snakeLength++;
        score+=100;
        if(snakeLength>20)
        {
            return;
        }
        for(turn=snakeLength-1;turn>0;turn--)
        {
            snakeX[turn]=snakeX[turn-1];
            snakeY[turn]=snakeY[turn-1];
        }
        snakeX[turn]=tempX;
        snakeY[turn]=tempY;
        put_money();
    }
}
void output(void)
{
    int turnx=0;
    int turny=0;
    for(turnx=1;turnx<11;turnx++)
    {
        for(turny=1;turny<11;turny++)
        {
            if(map[turnx][turny]!=SNAKE_FOOD&&map[turnx][turny]!=BLOCKS)
            {
                map[turnx][turny]=BLANK_CELL;
            }
        }
    }
    for(turnx=0,turny=0;turnx<snakeLength;turnx++,turny++)
    {
        if(map[snakeY[turny]][snakeX[turnx]]!=SNAKE_FOOD)
        {
            map[snakeY[turny]][snakeX[turnx]]=SNAKE_BODY;
        }
    }
    map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_HEAD;
    system("clear");
    for(turnx=0;turnx<12;turnx++)
    {
        for(turny=0;turny<12;turny++)
        {
            printf("\033[?25l \033[%dm %c",29+color,map[turnx][turny]);
        }
        printf("\n");
    }
    printf("Score:%d\n",score);
}

int gameover(void)
{
    int turn = 0;
    if(map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]==WALL_CELL)
    {
        printf("Game Over!!!");
        return 0;
    }
    if(snakeLength>SNAKE_MAX_LENGTH)
    {
        printf("Game Over!!!");
        return 0;
    }
    for(turn=snakeLength-2;turn>=0;turn--)
    {
        if(snakeX[turn]==snakeX[snakeLength-1]&&snakeY[turn]==snakeY[snakeLength-1])
        {
            printf("Game Over!!!");
            return 0;
        }
    }
    return 1;
}
//将你的 snake 代码放在这里

int main()
{
        char input;
        //设置终端进入非缓冲状态
        int tty_set_flag;
        tty_set_flag = tty_set();
        color_choose();
        set_blocks();
        put_money();
        output();
        //将你的 snake 代码放在这里
        printf("pressed `q` to quit!\n");
        while(1) {

                if( kbhit() )
                {
                        input = getchar();
                        switch(input)
                        {
                            case'A':snakeMove(-1,0);break;
                            case'D':snakeMove(1,0);break;
                            case'W':snakeMove(0,-1);break;
                            case'S':snakeMove(0,1);break;
                            case'q':return 0;
                            default:break;
                        }
                }
                else
                {
                    continue;
                }
                eat_food();
                if(gameover()!=0)
                {
                    output();
                    continue;
                }
                else
                {
                    break;
                }
        }
        //恢复终端设置
        if(tty_set_flag == 0)
                tty_reset();
        return 0;
}

智能蛇

好了,接下来就开始正式开始我们的智能蛇的编写
首先根据
决定蛇行走的方向函数的伪代码

     // Hx,Hy: 头的位置
    // Fx,Fy:食物的位置
    function whereGoNext(Hx,Hy,Fx,Fy) {
    // 用数组movable[3]={“a”,”d”,”w”,”s”} 记录可走的方向
    // 用数组distance[3]={0,0,0,0} 记录离食物的距离
    // 分别计算蛇头周边四个位置到食物的距离。H头的位置,F食物位置
    //     例如:假设输入”a” 则distance[0] = |Fx – (Hx-1)| + |Fy – Hy|
    //           如果 Hx-1,Hy 位置不是Blank,则 distance[0] = 9999
    // 选择distance中存最小距离的下标p,注意最小距离不能是9999
    // 返回 movable[p]

这是我写的Autorun函数

void Autorun(void)
{
    int add=0;
    int turn=0;
    int nowx=snakeX[snakeLength-1];
    int nowy=snakeY[snakeLength-1];
    if(map[nowy-1][nowx]==WALL_CELL||map[nowy-1][nowx]==BLOCKS||map[nowy-1][nowx]==SNAKE_BODY)
    {
        distance[0]=9999;
    }
    else
    {
        distance[0]=abs(nowy-1-Moneyx)+abs(nowx-Moneyy);
    }
    if(map[nowy][nowx-1]==WALL_CELL||map[nowy][nowx-1]==BLOCKS||map[nowy][nowx-1]==SNAKE_BODY)
    {
        distance[1]=9999;
    }
    else
    {
        distance[1]=abs(nowy-Moneyx)+abs(nowx-Moneyy-1);
    }
    if(map[nowy+1][nowx]==WALL_CELL||map[nowy+1][nowx]==BLOCKS||map[nowy+1][nowx]==SNAKE_BODY)
    {
        distance[2]=9999;
    }
    else
    {
        distance[2]=abs(nowy-Moneyx+1)+abs(nowx-Moneyy);
    }
    if(map[nowy][nowx+1]==WALL_CELL||map[nowy][nowx+1]==BLOCKS||map[nowy][nowx+1]==SNAKE_BODY)
    {
        distance[3]=9999;
    }
    else
    {
        distance[3]=abs(nowy-Moneyx)+abs(nowx-Moneyy+1);
    }
    for(turn=0;turn<3;turn++)
    {
        if(distance[turn]>distance[turn+1])
        {
            add=0;
        }
        else
        {
            distance[turn+1]=distance[turn];
            add++;
        }
    }
    if(distance[3]==9999)
    {
        return;
    }
    switch((3-add))
    {
        case 0:snakeMove(0,-1);break;
        case 1:snakeMove(-1,0);break;
        case 2:snakeMove(0,1);break;
        case 3:snakeMove(1,0);break;
        default:break;
    }
}

但是由于算法太过简陋、粗暴,经常自己走向死局,所以以后还会慢慢改进。
这是本次“智能蛇”的完整代码

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

#define SNAKE_MAX_LENGTH 100
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
#define BLOCKS '#'

char map[12][12]={"************",
                "*XXXXH     *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "*          *",
                "************"};

int snakeX[SNAKE_MAX_LENGTH]={1,2,3,4,5};
int snakeY[SNAKE_MAX_LENGTH]={1,1,1,1,1};
int snakeLength=5;
int tempX=0;
int tempY=0;
int color=0;
int score=0;
int Moneyx=0;
int Moneyy=0;
int distance[4]={0};
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;
}

void color_choose(void)
{
    printf("Please choose color (Input 1~5):\n");
    printf("\033[30m(1)Black  \033[31m(2)Red  \033[32m(3)Green  \033[33m(4)Yellow  \033[34m(5)Blue\033[0m\n");
    scanf("%d",&color);
    system("clear");
}

void set_blocks(void)
{
    int number=0;
    int x = 0;
    int y = 0;
    int turn=0;
    srand((unsigned)time(NULL));
    printf("How many blocks do you want to set:(range from 0 to 15)\n");
    scanf("%d",&number);
    while(turn<number)
    {
        x=rand()%10+1;
        y=rand()%10+1;
        if(map[x][y]==BLANK_CELL)
        {
            map[x][y]=BLOCKS;
            turn++;
        }
    }
}
void snakeMove(int dx,int dy)
{
    int turn=0;
    tempX=snakeX[snakeLength-1];
    tempY=snakeY[snakeLength-1];
    int tempX1=0;
    int tempY1=0;
    if(map[snakeY[snakeLength-1]+dy][snakeX[snakeLength-1]+dx]==BLOCKS)//当蛇头面前是blocks时无法前进
    {
        return;
    }
    snakeY[snakeLength-1]+=dy;
    snakeX[snakeLength-1]+=dx;
    for(turn=snakeLength-2;turn>=0;turn--)
    {
        tempX1=snakeX[turn];
        tempY1=snakeY[turn];
        snakeX[turn]=tempX;
        snakeY[turn]=tempY;
        tempX=tempX1;
        tempY=tempY1;
    }
}

void put_money(void)
{
    Moneyx = 0;
    Moneyy = 0;
    srand((unsigned)time(NULL));
    while(map[Moneyx][Moneyy]!=BLANK_CELL)
    {
        Moneyx=rand()%10+1;
        Moneyy=rand()%10+1;
    }
    map[Moneyx][Moneyy]=SNAKE_FOOD;
}

void eat_food(void)
{
    int turn = 0;
    if(map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]==SNAKE_FOOD)
    {
        snakeLength++;
        score+=100;
        if(snakeLength>20)
        {
            return;
        }
        for(turn=snakeLength-1;turn>0;turn--)
        {
            snakeX[turn]=snakeX[turn-1];
            snakeY[turn]=snakeY[turn-1];
        }
        snakeX[turn]=tempX;
        snakeY[turn]=tempY;
        put_money();
    }
}
void output(void)
{
    int turnx=0;
    int turny=0;
    for(turnx=1;turnx<11;turnx++)
    {
        for(turny=1;turny<11;turny++)
        {
            if(map[turnx][turny]!=SNAKE_FOOD&&map[turnx][turny]!=BLOCKS)
            {
                map[turnx][turny]=BLANK_CELL;
            }
        }
    }
    for(turnx=0,turny=0;turnx<snakeLength;turnx++,turny++)
    {
        if(map[snakeY[turny]][snakeX[turnx]]!=SNAKE_FOOD)
        {
            map[snakeY[turny]][snakeX[turnx]]=SNAKE_BODY;
        }
    }
    map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_HEAD;
    system("clear");
    for(turnx=0;turnx<12;turnx++)
    {
        for(turny=0;turny<12;turny++)
        {
            printf("\033[?25l \033[%dm %c",29+color,map[turnx][turny]);
        }
        printf("\n");
    }
    printf("Score:%d\n",score);
}

void Autorun(void)
{
    int add=0;
    int turn=0;
    int nowx=snakeX[snakeLength-1];
    int nowy=snakeY[snakeLength-1];
    if(map[nowy-1][nowx]==WALL_CELL||map[nowy-1][nowx]==BLOCKS||map[nowy-1][nowx]==SNAKE_BODY)
    {
        distance[0]=9999;
    }
    else
    {
        distance[0]=abs(nowy-1-Moneyx)+abs(nowx-Moneyy);
    }
    if(map[nowy][nowx-1]==WALL_CELL||map[nowy][nowx-1]==BLOCKS||map[nowy][nowx-1]==SNAKE_BODY)
    {
        distance[1]=9999;
    }
    else
    {
        distance[1]=abs(nowy-Moneyx)+abs(nowx-Moneyy-1);
    }
    if(map[nowy+1][nowx]==WALL_CELL||map[nowy+1][nowx]==BLOCKS||map[nowy+1][nowx]==SNAKE_BODY)
    {
        distance[2]=9999;
    }
    else
    {
        distance[2]=abs(nowy-Moneyx+1)+abs(nowx-Moneyy);
    }
    if(map[nowy][nowx+1]==WALL_CELL||map[nowy][nowx+1]==BLOCKS||map[nowy][nowx+1]==SNAKE_BODY)
    {
        distance[3]=9999;
    }
    else
    {
        distance[3]=abs(nowy-Moneyx)+abs(nowx-Moneyy+1);
    }
    for(turn=0;turn<3;turn++)
    {
        if(distance[turn]>distance[turn+1])
        {
            add=0;
        }
        else
        {
            distance[turn+1]=distance[turn];
            add++;
        }
    }
    if(distance[3]==9999)
    {
        return;
    }
    switch((3-add))
    {
        case 0:snakeMove(0,-1);break;
        case 1:snakeMove(-1,0);break;
        case 2:snakeMove(0,1);break;
        case 3:snakeMove(1,0);break;
        default:break;
    }
}

int gameover(void)
{
    int turn = 0;
    if(map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]==WALL_CELL)
    {
        printf("Game Over!!!");
        return 0;
    }
    if(distance[3]==9999)
    {
        printf("Game Over!!!");
        return 0;
    }
    for(turn=snakeLength-2;turn>=0;turn--)
    {
        if(snakeX[turn]==snakeX[snakeLength-1]&&snakeY[turn]==snakeY[snakeLength-1])
        {
            printf("Game Over!!!");
            return 0;
        }
    }
    return 1;
}
//将你的 snake 代码放在这里

int main()
{
        char input;
        unsigned int timE=1000000;
        //设置终端进入非缓冲状态
        int tty_set_flag;
        tty_set_flag = tty_set();
        color_choose();
        set_blocks();
        put_money();
        output();
        //将你的 snake 代码放在这里
        printf("pressed `Q` to quit!\n");
        printf("pressed `F` to accelerate!\n");
        printf("pressed `L` to decelerate!\n");
        while(1) {

                if( kbhit() )
                {
                        input = getchar();
                        switch(input)
                        {
                            case'A':snakeMove(-1,0);break;
                            case'D':snakeMove(1,0);break;
                            case'W':snakeMove(0,-1);break;
                            case'S':snakeMove(0,1);break;
                            case'F':timE-=200000;break;
                            case'L':timE+=200000;break;
                            case'Q':return 0;
                            default:break;
                        }
                }
                else
                {
                    Autorun();
                    usleep(timE);
                }
                eat_food();
                if(gameover()!=0)
                {
                    output();
                    continue;
                }
                else
                {
                    break;
                }
        }
        //恢复终端设置
        if(tty_set_flag == 0)
                tty_reset();
        return 0;
}

End

虽然编写贪吃蛇和智能蛇花费了不少的时间,但是那种专心于当中的感觉真的很好,对程序设计、对软导课的兴趣也在不断增加!!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值