贪吃蛇的升级——智能蛇
来源:互联网 发布:mac启动windows 编辑:程序博客网 时间:2024/06/13 10:13
智能蛇
在上一篇博客当中,已经粗略完成一个小小的“贪吃蛇”的程序框架,接下来我们来看一下如何实现更高级的玩法–不需要控制,让蛇自己去吃!
实验环境的准备
其实在上一篇博客中的使用颜色选择时已经在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 __inlineint tty_reset(void){ if (tcsetattr(STDIN_FILENO, TCSANOW, &ori_attr) != 0) return -1; return 0;}static __inlineint 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 __inlineint 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 __inlineint tty_reset(void){ if (tcsetattr(STDIN_FILENO, TCSANOW, &ori_attr) != 0) return -1; return 0;}static __inlineint 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 __inlineint 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
虽然编写贪吃蛇和智能蛇花费了不少的时间,但是那种专心于当中的感觉真的很好,对程序设计、对软导课的兴趣也在不断增加!!!!
阅读全文