字符游戏-智能蛇
注:此次智能蛇是基于上一篇博客的字符蛇进行展开的
http://blog.csdn.net/chimo_hs/article/details/78906848
啥是智能蛇,就是会判断自己下一步要如何走的蛇,通过算法来计算出下一步该往哪走,这里就需要程序周期性得读取判断出的下一步走的方向所代表的字符,并输入到上一篇博客的贪吃蛇程序中。
这里我们暂且将食物数量设置为1,就如经典贪吃蛇那样。
接下来就是判断要如何走了,而影响蛇行走方向的要素无非就是蛇头与各个对象的关系,与蛇身的关系:下一步不能碰到蛇身自己撞死自己,下一步不能碰到墙,下一步要在以上条件之下尽可能往离食物更近的方向走。
由于之前设计的贪吃蛇就有判断下一步会不会自杀的函数块judgeMove(),可以很方便得运用到智能蛇的移动判断中。下面可以拟出判断函数judgeNextMove(),返回的是下一步的方向。
char judgeNextMove(){
int w = 1,a = 1,s = 1,d = 1;//1表示可以执行的状态,w,a,s,d的值分别表示向上,左,下,右移动的可行性,1为可行,0为不可行
//先判断是否会撞墙或自杀,再考虑最短距离的问题
if(judgeMove(UP)== 0) w = 0;
if(judgeMove(LEFT)== 0) a = 0;
if(judgeMove(DOWN)== 0) s = 0;
if(judgeMove(RIGHT)== 0) d = 0;
//判断哪个最好
char DO ;
double minDistant = 10000;
printf("%d %d ",snakeX[snakeLength-1],snakeY[snakeLength-1]);
//对于UP这一方向来说
if(w == 1){
double distantUP = (foodX[foodNumber-1]-snakeX[snakeLength-1])*(foodX[foodNumber-1]-snakeX[snakeLength-1]) + (foodY[foodNumber-1]-(snakeY[snakeLength-1]-1))*(foodY[foodNumber-1]-(snakeY[snakeLength-1]-1));
minDistant = distantUP;
DO = 'W';
printf("%lf ",distantUP);
}
//对于DOWN这一方向来说
if(s == 1){
double distantDOWN = (foodX[foodNumber-1]-snakeX[snakeLength-1])*(foodX[foodNumber-1]-snakeX[snakeLength-1]) + (foodY[foodNumber-1]-(snakeY[snakeLength-1]+1))*(foodY[foodNumber-1]-(snakeY[snakeLength-1]+1));
if(distantDOWN<=minDistant){
minDistant = distantDOWN;
DO = 'S';
printf("%lf ",distantDOWN);
}
}
//对于LEFT这一方向来说
if(a == 1){
double distantLEFT = (foodX[foodNumber-1]-(snakeX[snakeLength-1]-1))*(foodX[foodNumber-1]-(snakeX[snakeLength-1]-1)) + (foodY[foodNumber-1]-snakeY[snakeLength-1])*(foodY[foodNumber-1]-snakeY[snakeLength-1]);
if(distantLEFT<=minDistant){
minDistant = distantLEFT;
DO = 'A';
printf("%lf ",distantLEFT);
}
}
//对于RIGHT这一方向来说
if(d == 1){
double distantRIGHT = (foodX[foodNumber-1]-(snakeX[snakeLength-1]+1))*(foodX[foodNumber-1]-(snakeX[snakeLength-1]+1)) + (foodY[foodNumber-1]-snakeY[snakeLength-1])*(foodY[foodNumber-1]-snakeY[snakeLength-1]);
if(distantRIGHT<=minDistant){
minDistant = distantRIGHT;
DO = 'D';
printf("%lf \n",distantRIGHT);
}
};
printf("w:%d a:%d s:%d d:%d \n ",w,a,s,d);
printf("%d %d\n",foodX[foodNumber-1],foodY[foodNumber-1]);
return DO;
}
接下来就是把主函数里边的readInput换成judgeNextMove就可以了。
这样直接运行程序就可以看到一条疾如风的智障蛇。
若要让蛇一秒一步慢慢地走向死亡就可以通过使用老师提供Linux下非阻塞地检测键盘输入的代码来完成
#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;
}
并将我们的snake代码放入其中main函数中提示的位置即可。
通过这种算法可以初步解决一些问题,但是蛇会把自己绕入死胡同里自杀。我们可以发现一种玩贪吃蛇的极端保守的方法:让蛇沿着固定的“S”型路线循环走过界面,这样可以百分百通关。再结合上边的思路,可以形成新的思路:
若食物满足其坐标符合在蛇的每一个部位的同一方向(如左上方);则执行上述的移动法则,当不符合时,蛇头就沿着会让蛇死亡的物体(墙,蛇身)行走(即不考虑食物位置,仅仅选择一个不会让他死亡的方向走并按着这一方向直到将要碰到让蛇死亡的物体),直到出现满足食物满足其坐标符合在蛇的每一个部位的同一方向这一条件时,再进行上述移动法则。
这样蛇便可以很好的存活。