“智能”贪吃蛇

本次项目的任务是让蛇有一定的智能,能通过算法具有 “感知 - 决策 - 行动” 的能力。近一步,你可以做出华丽的字符界面,实现 人控蛇 与 智能蛇 抢食大战。

一、实验目的
1.了解 算法 与 “智能” 的关系
2.通过算法赋予蛇智能
3.了解 Linux IO 设计的控制
二、实验环境
Linux Only. 你可以选择 Unbutu、Centos 等发行版。
建议编辑环境 Vim 或 Vscode, 编译 gcc

1.算法与“智能”的关系:
也许通过能够自动纠错或者不断优化自身的算法来编写程序能够实现一定程度上近似思考的“智能”
2.通过算法赋予蛇智能:
step1:首先运行案例——实现sin函数随时间的动态图像:
参考代码出处:
http://www.cnblogs.com/zengjfgit/p/4373564.html

#include <stdio.h>
 #include <math.h>
 #include <unistd.h>

 /**
  * 定义圆周率的值
  */
 #define PI  3.14
 /**
  * 本Demo中假设sin曲线周期为20,幅值也是20,幅值分正负幅值,
  * 所以后面的很多地方有SIN_AMPLITUDE*2,也就是Y轴方向上的值.
  */
 #define SIN_AMPLITUDE  20      
 /**
  * 定义每次刷新图形时间间隔为100ms
  */
 #define DELAY_TIME  100000  
 /**
  * 定义圆的一周角度为360度
  */
 #define TRIANGLE  360.0   
 /**
  * 输出的时候,数字行放在哪一行,也就是输出图形中的这行数字:
  *   0123456789012345678901234567890123456789
  * 本Demo中把上面这行数字放在界面的第3行
  */
 #define Y_NUMBER_BEGIN_LINE  3
 /**
  * 在本Demo中,图形就在上面数字行的下一行,也就是输出图形中如下面的内容:
  *     0000    --------------------@--------------------> Y
  *     0001                        |-----*             
  *     0002                        |----------*        
  *     0003                        |---------------*   
  *     0004                        |------------------*
  *     0005                        |------------------*
  *     0006                        |------------------*
  *     0007                        |---------------*   
  *     0008                        |----------*        
  *     0009                        |-----*             
  *     0010                        *                   
  *     0011                  *-----|                   
  *     0012             *----------|                   
  *     0013        *---------------|                   
  *     0014     *------------------|                   
  *     0015     *------------------|                   
  *     0016     *------------------|                   
  *     0017        *---------------|                   
  *     0018             *----------|                   
  *     0019                  *-----|                   
  *     0020                        V X  
  */
 #define SIN_GRAPH_BEGIN_LINE (Y_NUMBER_BEGIN_LINE+1)

 int main(int argc, char* argv[]){

     /**
      * 局部变量说明:
      *     1. i                 : 主要用于循环计算;
      *     2. lineNumber        : 用于保存行号;
      *     3. offsetCenter      : 用于保存sin曲线上的点的相对于中心轴的偏移;
      *     4. nextInitAngle     : 保存下一屏要输出图形的初始角度制角度(如30度);
      *     5. currentInitAngle  : 当前一屏要输出的图形的初始角度制角度(如30度);
      *     6. currentInitradian : 当前一屏要输出的图形的初始弧度制弧度(如PI/6)
      *                            根据currentInitAngle换算而来,因为sin函数需要
      *                            角度制进行求值;
      *
      */
     int    i                   = 0;    
     int    lineNumber          = 0;    
     int    offsetCenter        = 0;    
     int    nextInitAngle       = 0;
     double currentInitAngle    = 0;    
     double currentInitradian   = 0;    

     //软件开始运行,清一次屏,保证屏幕上没有无关内容
     printf("\033[2J");

     //输出标题,因为这个软件名字叫: SinDemo
     printf("\033[1;1H                       | SinDemo |\t");

     /**
      * 这里主要是完成那一行重复的0-9,SIN_AMPLITUDE*2是因为sin曲线的
      * 最高点和最低点是2倍的幅值
      */
     printf("\033[%d;1H\t", Y_NUMBER_BEGIN_LINE);
     for (i = 0; i < SIN_AMPLITUDE*2; i++) 
         printf("%d", i%10);
     printf("\n");

     /**
      * while循环主要完成内容:
      *     1. 每次循环对局部变量重新初始化;
      *     2. 将下一屏图形的初始角度赋值给当前的图形初始角;              
      *     3. 将下一屏图形的初始角度加上间隔角度(TRIANGLE/SIN_AMPLITUDE),
      *        TRIANGLE/SIN_AMPLITUDE在本Demo中是360/20=18度,就相当于X轴 
      *        每格代表18度 
      *     2. 调整光标到固定的位置;
      *     3. 重新绘制整屏图形;
      */
     while(1){

         //重新初始化局部变量,因为每一屏图形都像一个新的开始
         i                  = 0;        
         offsetCenter       = 0;       
         lineNumber         = 0;      
         currentInitradian  = 0;     

         //从nextInitAngle中获取当前的初始化角度
         currentInitAngle   =  nextInitAngle;

         //为下一次循环提供下一次的初始化角度
         nextInitAngle     += TRIANGLE/SIN_AMPLITUDE;  

         //将光标移动到开始绘图的位置去
         printf("\033[%d;1H", SIN_GRAPH_BEGIN_LINE);

         /**
          * 根据不同的情况绘制图形, 每一次循环,就是绘制了图形中的一行
          */
         while(1){
             //判断是不是最后一行,lineNumber起始行是从0开始
             if(lineNumber == SIN_AMPLITUDE){
                 //打印最后一行前面的数字行号
                 printf("\033[%d;1H%04d\t", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber);
                 for (i = 0; i < SIN_AMPLITUDE*2; i++)
                     /**
                      * 判断是否到达中间位置,因为中间位置要放V的箭头,同时在旁边输出一个X,
                      * 代表这是X轴方向.
                      */
                     i == SIN_AMPLITUDE ? printf("V X") : printf(" ");    
                 break;
             }


             /**
              * 对currentInitAngle角度进行修整,比如370度和10度是对应相同的sin值
              * 其实这一步可以不用,但是这里保留了,后面是将currentInitAngle角度制的值
              * 换算成对应的弧度制的值,便于sin求值.
              */
             currentInitAngle = ((int)currentInitAngle)%((int)TRIANGLE);
             currentInitradian = currentInitAngle/(TRIANGLE/2)*PI;    

             /**
              * 算出当前次currentInitradian对应的sin值,并乘以幅值SIN_AMPLITUDE,获取sin曲线
              * 在Y轴上相对于中心轴的偏移offsetCenter,offsetCenter可能是正值,也可能是负值,
              * 因为中心轴在中间.
              */
             offsetCenter = (int)(sin(currentInitradian)*SIN_AMPLITUDE);                

             /**
              * 在正确的地方输出正确的行号   :)
              */
             printf("\033[%d;1H%04d", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber);

             //用一个制表符,给出行号与图形的空间距离
             printf("\t");

             /**
              * 第一行,和其他的行不一样,有区别,输出结果如下:
              * 0000    ------------@-------+--------------------> Y
              */
             if(lineNumber == 0){
                 for (i = 0; i < SIN_AMPLITUDE*2; i++){
                     /**
                      * 判断当前输出的字符位置是否是X,Y轴交叉的位置,如果是就输出'+',
                      * 不是就输出'-'
                      */
                     i == SIN_AMPLITUDE ? printf("+") : printf("-");
                     /**
                      * 判断当前输出的字符位置是否是sin曲线上的点对应的位置,
                      * 如果是就输出'@'
                      */
                     if(i == offsetCenter+SIN_AMPLITUDE)
                         printf("@");
                 }
                 //代表这个方向是Y轴
                 printf("-> Y\n");
             } else { 
                 for (i = 0; i < SIN_AMPLITUDE*2; i++){
                     //判断当前输出的字符位置是否是sin曲线上的点对应的位置,如果是就输出'*'
                     if(i == (offsetCenter+SIN_AMPLITUDE)){
                         printf("*");
                     //判断当前输出的字符位置是否是X轴上对应的位置,如果是就输出'|'
                     }else if(i == SIN_AMPLITUDE){
                         printf("|");
                     }else{
                         /**
                          * 这里主要是要处理一行里面除了画'*'、'|'、之外的'-'、' '
                          * 其中的SIN_AMPLITUDE到SIN_AMPLITUDE+offsetCenter正好就是需要输出'-'的地方
                          * 其他的地方输出' '
                          */
                         (((i > SIN_AMPLITUDE) && (i < SIN_AMPLITUDE+offsetCenter)) || \
                             ((i < SIN_AMPLITUDE) && (i > SIN_AMPLITUDE+offsetCenter))) \
                                 ? printf("-") : printf(" ");
                     }
                     //行尾,输出换行符
                     if(i == (SIN_AMPLITUDE*2-1)) 
                         printf("\n");
                 }
             }

             /**
              * 一行输出完成,为下一行输出作准备,下一行比上一行在角度上多加TRIANGLE/SIN_AMPLITUDE,
              * 在本Demo中相当于360/20=18,也就是加18度.
              */
             currentInitAngle += TRIANGLE/SIN_AMPLITUDE;      

             //行号加1
             lineNumber++;                                    
         }
         /**
          * 一屏图像输出完毕,最后输出一个换行符,并且延时一段时间再开始绘制下一屏图形
          */
         printf("\n");
         usleep(DELAY_TIME);
     }

     return 0;
 }

接着在Ubuntu中输入如下命令运行

$ gcc sin-demo.c -osin.out -lm
$ ./sin.out

结果如下:
这里写图片描述
step2:实现 kbhit()
参考代码来源:(http://bbs.chinaunix.net/thread-935410-1-1.html

#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;
}

接着在Ubuntu中输入如下命令运行

$ gcc tty.c -otty.out
$ ./tty.out

不含贪吃蛇的程序结果如下:
这里写图片描述
实际上只需要将贪吃蛇代码套入然后就会发现TTY使我们不用按回车键,也就是直接在终端玩贪吃蛇

三.编写智能算法

编写人工智能程序,使得 snake 每秒自动走一步。

1、程序要求

1.请修改初始化字符矩阵,或者通过文件读入地图。地图中有一些你设定的障碍物(墙)
2.请实现如下算法决定蛇行走的方向

决定蛇行走的方向函数的伪代码
    // 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]
    }

2、智能蛇的程序框架

    输出字符矩阵
    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!!! 

编写出智能蛇的结果如下:
这里写图片描述
不过比较关键的一点是遇到相同的多种情况需要取rand()不然有可能会死循环(就看到一只蛇转一个圈)
还有请不要在意那些测试数值
如果在程序中加入

system("cls");

就可以清屏的不过这样也不好看测试数据所以大家将就看吧
代码:

#define DELAY_TIME  100000

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

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

//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);

char map[12][12]=
    {"************",
    "*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 foodX;
int foodY;
int snakeLength = 5;
int num[4]={0,0,0,0};
void output(void){//输出矩阵
    int i,j;
    for(i=0;i<12;i++){
        for(j=0;j<12;j++){
        printf("%c ",map[i][j]);
        }   printf("\n");
    }
}

int judRand(int random1,int random2){
    if (map[random1][random2]!=BLANK_CELL) return 1;
    else return 0;
}

void put_money(void){//放置新的SNAKE_FOOD
        int randomX=rand()%10+1,randomY=rand()%10+1;
        while (judRand(randomY,randomX)){
            randomX=rand()%10+1;randomY=rand()%10+1;
        }
        map[randomY][randomX]=SNAKE_FOOD;
        foodX=randomX;
        foodY=randomY;
}

void gameover(void) {
    printf("%s\n","Game Over!!!" );
}

void snakeMove(int x,int y){//描述snake的运动模式
    int i;
    if (map[snakeY[snakeLength-1]+y][snakeX[snakeLength-1]+x] != SNAKE_FOOD){
        if ((snakeY[snakeLength-1]+y) != snakeY[snakeLength-2] || (snakeX[snakeLength-1]+x) != snakeX[snakeLength-2] ){
            map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=BLANK_CELL;
            for(i=0;i<=snakeLength-2;i++){
                if (i == 0)
                map[snakeY[i]][snakeX[i]]=BLANK_CELL;
                snakeX[i]=snakeX[i+1];
                snakeY[i]=snakeY[i+1];
                map[snakeY[i]][snakeX[i]]=SNAKE_BODY;
            }
            snakeX[snakeLength-1] += x;
            snakeY[snakeLength-1] += y;
            map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_HEAD;
        }
    }else{
        map[snakeY[snakeLength-1]+y][snakeX[snakeLength-1]+x] = SNAKE_HEAD;
        snakeY[snakeLength]=snakeY[snakeLength-1]+y;
        snakeX[snakeLength]=snakeX[snakeLength-1]+x;
        map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_BODY;
        snakeLength++;
        put_money();
    }
}

int abs(int a){
  int b = (-1)*a;
    if(a>0) return a;
    else return b;
}

char whereGoNext(int Hx,int Hy,int Fx,int Fy) {
    int i,p=0,count=0,temp=9999;
    // 用数组movable[3]={“a”,”d”,”w”,”s”} 记录可走的方向
    int movable[4]={'A','D','W','S'};
    // 用数组distance[3]={0,0,0,0} 记录离食物的距离
    int distance[4]={0,0,0,0};
    // 分别计算蛇头周边四个位置到食物的距离。H头的位置,F食物位置
    //     例如:假设输入”a” 则distance[0] = |Fx – (Hx-1)| + |Fy – Hy|
    //           如果 Hx-1,Hy 位置不是Blank,则 distance[0] = 9999
        if(map[Hx][Hy-1]==BLANK_CELL||map[Hx][Hy-1]==SNAKE_FOOD)
        distance[0]=abs(Fx-Hx)+abs(Fy-(Hy-1));
        else
        distance[0]=9999;
        if(map[Hx][Hy+1]==BLANK_CELL||map[Hx][Hy+1]==SNAKE_FOOD)
        distance[1]=abs(Fx-Hx)+abs(Fy-(Hy+1));
        else
        distance[1]=9999;
        if(map[Hx-1][Hy]==BLANK_CELL||map[Hx-1][Hy]==SNAKE_FOOD)
        distance[2]=abs(Fx-(Hx-1))+abs(Fy-Hy);
        else
        distance[2]=9999;
        if(map[Hx+1][Hy]==BLANK_CELL||map[Hx+1][Hy]==SNAKE_FOOD)
        distance[3]=abs(Fx-(Hx+1))+abs(Fy-Hy);
        else
        distance[3]=9999;

    //TEST
    for(i=0;i<=3;i++){
      printf("%d\n",distance [i]);
    }

    // 选择distance中存最小距离的下标p,注意最小距离不能是9999
    for(i=0;i<=3;i++){
      if(distance[i]<temp)  temp=distance[i];
    }

    printf("%d\n",temp);

    for(i=0;i<=3;i++){
      if(distance[i]==temp)  {
      num[i]=1;
      count++;
      }
    }

    int k=rand()%count+1;

    for(i=0;i<=3;i++){
      if(num[i])  k--;
      if(k==0)
      {p=i;break;}
    }
    //TEST
    printf("\n");
    for(i=0;i<=3;i++){
      printf("%d\n",distance [i]);
    }
    // 返回 movable[p]
    printf("%d\n",p);
    memset(num,0,sizeof(num));
    return movable[p];
}

int main(){

    //软件开始运行,清一次屏,保证屏幕上没有无关内容
    printf("\033[2J");
    //输出标题: GreedySnake
    printf("\033[1;1H                       | GreedySnake |\n");

    //输出字符矩阵
      put_money();
    output();

    //智能追食程序
while ((map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]!=SNAKE_BODY)&&(map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]!=WALL_CELL)&&(snakeX[snakeLength-1]<11)&&(snakeX[snakeLength-1]>0)&&(snakeY[snakeLength-1]<11)&&(snakeY[snakeLength-1]>0)){
      char ch;
    ch = whereGoNext(snakeY[snakeLength-1],snakeX[snakeLength-1],foodY,foodX);
        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();
    printf("\n");
    /**
     * 延时一段时间再开始绘制下一屏图形
     */
     usleep(DELAY_TIME);
    }

    //输出Game Over!!!
    gameover();
    getchar(); 
    return 0;
}

3.了解 Linux IO 设计的控制
关于Linux IO 设计的控制详细可以参考以下网址:

1.http://www.jianshu.com/p/486b0965c296
2.http://blog.chinaunix.net/uid-22556372-id-3486502.html
3.http://blog.csdn.net/weixin_35921178/article/details/52733764

这里写图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Snake-AI,这是一个用 C/C 语言编写的贪吃蛇游戏的人工智能。AI 的目的是让蛇尽可能的吃更多的食物,直到吃满整个地图。想参与这个项目,请查看todos。Demo使用方法编译与运行:$ make $ make run为了解详细使用方法, 请查看主函数main()算法介绍函数Snake.decideNext(): 计算蛇S1的下一个移动方向D计算从蛇S1的头部到达食物的最短路径P1。派一条与蛇S1完全一样的虚拟蛇S2沿路径P1吃掉食物。计算从蛇S2的头部到其尾部的最长路径P2。如果路径P2存在,将移动方向D设置为路径P1的第一个方向,否则进行步骤4。计算从蛇S1的头部到达其尾部的最长路径P3。如果P3存在,将移动方向D设置为路径P3的第一个方向,否则进行步骤5。将移动方向D设置为离食物最远的方向。函数Map.findMinPath(): 计算两个位置间的最短路径算法建立在BFS的基础上。为了使路径尽可能直,每次遍历邻接点时,在当前移动方向上的位置会被优先遍历。效果展示:(绿色区域为搜索算法扫描到的区域,红色区域为最后计算出的最短路径,每个位置上的数字表示了从起始位置开始到该位置的最短距离)函数Map.findMaxPath(): 计算两个位置间的最长路径算法建立在DFS与贪心算法的基础上。每次遍历邻接点时,离目标位置最远(使用曼哈顿距离估计)的位置将会被优先遍历到。另外,为了使路径尽可能直,如果两个位置到目标位置的距离相等,在当前移动方向上的位置将被优先遍历到。这个问题是一个NP完全问题,此算法得出的结果路径只是一个近似最长路径。效果展示:(绿色区域为搜索算法扫描到的区域,红色区域为最后计算出的最长路径,每个位置上的数字表示了从该位置开始到目标位置的估计距离) 标签:Snake
好的,我可以用Java语言编写一个简单的贪吃蛇游戏示例,以下是代码: ``` java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class AIGreedySnake extends JPanel implements ActionListener { private final int WIDTH = 600; private final int HEIGHT = 500; private final int BOX_SIZE = 20; private final int TOTAL_BOXES = (WIDTH * HEIGHT) / (BOX_SIZE * BOX_SIZE); private int[] snakeX = new int[TOTAL_BOXES]; private int[] snakeY = new int[TOTAL_BOXES]; private int snakeLength = 3; private int foodX; private int foodY; private boolean isRunning = false; private Timer timer; private AIGreedySnake() { setPreferredSize(new Dimension(WIDTH, HEIGHT)); setBackground(Color.black); setFocusable(true); setLayout(null); addKeyListener(new TAdapter()); initGame(); } private void initGame() { snakeLength = 3; snakeX[0] = WIDTH / 2; snakeY[0] = HEIGHT / 2; snakeX[1] = snakeX[0] + BOX_SIZE; snakeY[1] = snakeY[0]; snakeX[2] = snakeX[1] + BOX_SIZE; snakeY[2] = snakeY[1]; generateFood(); isRunning = true; timer = new Timer(140, this); timer.start(); } private void generateFood() { int random = (int) (Math.random() * (TOTAL_BOXES - snakeLength)); int foodIndex = 0; for (int i = 0; i < TOTAL_BOXES; i++) { boolean isSnakeSegment = false; for (int j = 0; j < snakeLength; j++) { if (i == snakeIndex(j)) { isSnakeSegment = true; } } if (!isSnakeSegment) { if (foodIndex == random) { foodX = (i % (WIDTH / BOX_SIZE)) * BOX_SIZE; foodY = (i / (WIDTH / BOX_SIZE)) * BOX_SIZE; break; } foodIndex++; } } } public void actionPerformed(ActionEvent e) { if (isRunning) { moveSnake(); checkCollisions(); repaint(); } } private void moveSnake() { for (int i = snakeLength; i > 0; i--) { snakeX[i] = snakeX[i - 1]; snakeY[i] = snakeY[i - 1]; } snakeX[0] += BOX_SIZE; } private void checkCollisions() { if (snakeX[0] == foodX && snakeY[0] == foodY) { snakeLength++; generateFood(); } for (int i = snakeLength; i > 0; i--) { if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { isRunning = false; } } if (snakeX[0] >= WIDTH || snakeY[0] >= HEIGHT || snakeX[0] < 0 || snakeY[0] < 0) { isRunning = false; } } private int snakeIndex(int i) { return snakeY[i] / BOX_SIZE * (WIDTH / BOX_SIZE) + snakeX[i] / BOX_SIZE; } public void paintComponent(Graphics g) { super.paintComponent(g); drawSnake(g); drawFood(g); } private void drawSnake(Graphics g) { g.setColor(Color.green); for (int i = 0; i < snakeLength; i++) { g.fillRect(snakeX[i], snakeY[i], BOX_SIZE, BOX_SIZE); } } private void drawFood(Graphics g) { g.setColor(Color.red); g.fillRect(foodX, foodY, BOX_SIZE, BOX_SIZE); } private class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if ((key == KeyEvent.VK_LEFT || key == KeyEvent.VK_A) && snakeX[1] != snakeX[0] - BOX_SIZE) { snakeX[0] -= BOX_SIZE; } else if ((key == KeyEvent.VK_RIGHT || key == KeyEvent.VK_D) && snakeX[1] != snakeX[0] + BOX_SIZE) { snakeX[0] += BOX_SIZE; } else if ((key == KeyEvent.VK_UP || key == KeyEvent.VK_W) && snakeY[1] != snakeY[0] - BOX_SIZE) { snakeY[0] -= BOX_SIZE; } else if ((key == KeyEvent.VK_DOWN || key == KeyEvent.VK_S) && snakeY[1] != snakeY[0] + BOX_SIZE) { snakeY[0] += BOX_SIZE; } checkCollisions(); } } public static void main(String[] args) { JFrame frame = new JFrame("AI Greedy Snake"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.getContentPane().add(new AIGreedySnake(), BorderLayout.CENTER); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } ``` 这里的AI部分可以通过编写一个自动操作的方法实现,不过需要注意的是,在这个游戏中,贪吃蛇的方向可能会发生变化,因此需要修改自动操作的策略。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值