字符游戏-智能蛇

继上周写完贪吃蛇后,我们很快又有了新的作业,写一个智能蛇,虽然这条蛇可能有点智障。。

这里先写一下上一次的总结。对于上次的贪吃蛇,虽然成功写了出来,但是感觉写的时候有点磕磕绊绊,我反思了一下,也学习了一下别人的代码,有几点感悟。
1.要有图层的概念,background,snake都是可以分离的,糅在一起,虽然代码可能短,但增加了思维复杂度,当然也不能分得太多,money就可以揉在background里。
2.对于子程序的功能要有很清楚的了解。
3.全局变量的含义的维护。

下面才是这次的主角——智能蛇

这是老师给的智能蛇的程序框架

输出字符矩阵
    WHILE not 游戏结束 DO
        wait(time)
        ch=whereGoNext(Hx,Hy,Fx,Fy)
        CASE ch DO
        ‘A’:左前进一步,break 
        ‘D’:右前进一步,break    
        ‘W’:上前进一步,break    
        ‘S’:下前进一步,break    
        END CASE
        输出字符矩阵
    END WHILE
    输出 Game Over!!! 

很容易看出,智能蛇的智能体现在whereGoNext(Hx,Hy,Fx,Fy)这个函数上,下面是老师给出的这个函数的伪代码。

    // 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]
    }

因为蛇是沿着线走的,所以这里采用的是曼哈顿距离,找出曼哈顿距离最小,便是我们的最短路,有一种贪心的思想,虽然很快,但是走到后面,还是会被蛇身困住或走进死胡同里,而这两个问题便是贪吃蛇AI最棘手的问题。

当然,下面先来实现这个简单的智能方法。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define SNAKE_MAX_LENGTH 100
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
#define INF 0x3f3f3f3f

//snake stepping: dy = -1(up),1(down); dx = -1(left),1(right),0(no move)
void snakeMove(int,int);
//put a food randomized on a blank cell
void put_money(void);
//out cells of the grid
void output(void);
//outs when gameover
void gameover(void);
//find next
char whereGoNext(int ,int ,int ,int );

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

// define vars for snake , notice name of vars in C
int snakeX[SNAKE_MAX_LENGTH]={1,2,3,4,5};
int snakeY[SNAKE_MAX_LENGTH]={1,1,1,1,1};
int snakelength=5;
//记录分数
int score=0;
//判断游戏是否结束
int ok=1;
int money_x,money_y;
int step;

int main(){
    srand(time(NULL));
    put_money();
    output();
    step=-1;
    while(ok){
        step++;
        char ch=whereGoNext(snakeX[snakelength-1],snakeY[snakelength-1],money_x,money_y);
        if (ch!='A'&ch!='D'&ch!='W'&ch!='S') continue;
        switch (ch){
            case 'A':
                snakeMove(-1,0);
                break;
            case 'D':
                snakeMove(1,0);
                break;
            case 'W':
                snakeMove(0,-1);
                break;
            case 'S':
                snakeMove(0,1);
                break;
        }
        output();
    }
    gameover();
    return 0;
}

void snakeMove(int dx,int dy){
    int x=snakeX[snakelength-1]+dx;
    int y=snakeY[snakelength-1]+dy;
    snakeX[snakelength]=x;
    snakeY[snakelength]=y;

    //如果碰到边界
    if (map[y][x]=='*') {
        ok=0;
        return ;
    }   
    //如果碰到蛇身
    else if (map[y][x]=='X'){
        ok=0;
        return ;
    }
    //如果吃到钱,不用处理蛇尾,处理蛇头,长度+1,放钱,分数+1
    else if (map[y][x]=='$') {
        snakelength++;
        //蛇头处理
        map[snakeY[snakelength-2]][snakeX[snakelength-2]]='X';
        map[y][x]='H';//蛇头处理


        score++;
        put_money();

    }
    //如果吃不到钱,处理蛇头,蛇尾位置变空格,身体移一位
    else if (map[y][x]==' ') {
        map[snakeY[0]][snakeX[0]]=' ';
        for(int i=0;i<snakelength;i++){
            snakeX[i]=snakeX[i+1];
            snakeY[i]=snakeY[i+1];
        }
        //蛇头处理
        map[snakeY[snakelength-2]][snakeX[snakelength-2]]='X';
        map[y][x]='H';      
    }


}

void put_money(void){
    //找到合法位置
    do{
        money_x=rand()%10+1;
        money_y=rand()%10+1;
    } while (map[money_y][money_x]!=' ');
    map[money_y][money_x]='$';
}

void output(void){
    system("cls");
    printf("score:%d\n",score);
    for(int i=0;i<12;i++){
        for(int j=0;j<12;j++){
            printf("%c",map[i][j]);
        }
        printf("\n");
    }
}

void gameover(void){
    printf("Game Over!!!");
}

char whereGoNext(int Hx,int Hy,int Fx,int Fy){
    char moveable[4]={'A','D','W','S'};
    int distance[4]={INF,INF,INF,INF};
    int dx[4]={-1,1,0,0},
        dy[4]={0,0,-1,1};
    for(int i=0;i<4;i++){
        int X=Hx+dx[i];
        int Y=Hy+dy[i];
        if (map[Y][X]==' '||map[Y][X]=='$'){
            distance[i]=abs(Fx-(Hx+dx[i]))+abs(Fy-(Hy+dy[i]));
        }
    }
    int k=-1,tmp=INF;
    for(int i=0;i<4;i++){
        if (distance[i]<tmp){
            k=i;
            tmp=distance[i];
        }
    }
    if (k!=-1) return moveable[k];
    else {
        ok=0;
        return moveable[0];
    }
}

这里写图片描述

上面是通过计算曼哈顿距离来寻路的gif,不出所料,蛇被自己蛇身困住。

我将在下面一篇博客,介绍一些可能更加智能或者更加有效的寻路算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值