字符游戏-智能蛇
一、VT 100 终端尺度
这里按照老师的课件要求,体验一下VT 100 输入输出功能以及清屏操纵,代码直接复制课件中代码,这里就不再放一次了,直接给出运行效果:
- gcc sin-demo.c -osin.out -lm ./sin.out
复制代码
二、kbhit()
我们照旧按照课件要求,体验一检测tty输入的程序,将我们之前的snake的代码放入对应位置,运行后查察效果:
- #include #include #include #include #include #include #include #include #include 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 __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;}//将你的 snake 代码放在这里#define SNAKE_MAX_LENGTH 20#define SNAKE_HEAD 'H'#define SNAKE_BODY 'X'#define BLANK_CELL ' '#define WALL_CELL '*'#define SNAKE_FOOD '$'// -1体现向上(dy)或者向左(dy) +1体现向下(dy)或者向右(dx)void snakeMove(int, int);//随机投放食物 void put_money(void);//输出二维数组图形 void output(void);//游戏竣事 void gameover(void);//吃食物int eat_money(int dx, int dy);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 moneyX, moneyY;int snakeLength = 5;int continuegame = 1;int main(){ put_money(); // 首先投放食物 output(); //设置终端进入非缓冲状态 int tty_set_flag; tty_set_flag = tty_set(); int key; while(continuegame == 1) { if( kbhit() ) { key = getchar(); if(key == 'w' || key == 'W'){ snakeMove(0, -1); } if(key == 's' || key == 'S'){ snakeMove(0, 1); } if(key == 'a' || key == 'A'){ snakeMove(-1, 0); } if(key == 'd' || key == 'D'){ snakeMove(1, 0); } system("clear");//对于 VT100 终端, printf("\033[2J"); 也可以实现清屏。 output(); gameover(); } } //规复终端设置 if(tty_set_flag == 0) tty_reset(); return 0;}void snakeMove(int dx, int dy) { int i; //判断是否吃到食物 if (eat_money(dx, dy)); else { map[snakeY[0]][snakeX[0]] = BLANK_CELL; // 如果不是,那么以前的最后一节变成空缺 map[snakeY[snakeLength - 1]][snakeX[snakeLength - 1]] = SNAKE_BODY; // 以前的头变成身子 for (i = 0; i < snakeLength - 1; i++) { // 以前所有的坐标都向前移动 snakeX[i] = snakeX[i + 1]; snakeY[i] = snakeY[i + 1]; } snakeX[snakeLength - 1] += dx; snakeY[snakeLength - 1] += dy; map[snakeY[snakeLength - 1]][snakeX[snakeLength - 1]] = SNAKE_HEAD; // 新的头 }}void put_money(void) { int i; srand(time(NULL)); moneyX = rand() % 9 + 1; srand(time(NULL)); moneyY = rand() % 9 + 1; for (i = 0; i < snakeLength; i++) { while (moneyX == snakeX[i] && moneyY == snakeY[i]) { srand(time(NULL)); moneyX = rand() % 9 + 1; srand(time(NULL)); moneyY = rand() % 9 + 1; i = -1; } } map[moneyY][moneyX] = '$';}void output(void) { int i, j; for (i = 0; i < 12; ++i) { for (j = 0; j < 12; ++j) { printf("%c", map[i][j]); } putchar('\n'); }}void gameover(void) { if (snakeX[snakeLength - 1] == 0 || snakeX[snakeLength - 1] == 10 || snakeY[snakeLength - 1] == 0 || snakeY[snakeLength - 1] == 10) { continuegame = 0; printf("撞到墙了!") ; } int i; for (i = 0; i < snakeLength - 1; i++) { // 以前所有的坐标都向前移动 if (snakeX[i] == snakeX[snakeLength - 1] && snakeY[i] == snakeY[snakeLength - 1]) { printf("咬到自己了!") ; continuegame = 0; } } if (SNAKE_MAX_LENGTH == snakeLength) { continuegame = 0; printf("congratulations!\twin!\n"); }}int eat_money(int dx, int dy) { if (snakeX[snakeLength - 1] + dx == moneyX && snakeY[snakeLength - 1] + dy == moneyY) { snakeLength++; snakeX[snakeLength - 1] = snakeX[snakeLength - 2] + dx; snakeY[snakeLength - 1] = snakeY[snakeLength - 2] + dy; map[snakeY[0]][snakeX[0]] = SNAKE_BODY; map[snakeY[snakeLength - 2]][snakeX[snakeLength - 2]] = SNAKE_BODY; map[snakeY[snakeLength - 1]][snakeX[snakeLength - 1]] = SNAKE_HEAD; put_money(); return 1; } else return 0;}
复制代码
- gcc kihib.c -oihib.out -lm./kihib.out
复制代码
- 可以观察看我们的程序变成了检测键盘输入,而不会在终端上出现我们输入的wasd等操纵了,体验到了tty输入的优势
三、实现智能蛇
所谓的智能蛇,也就是我们要用程序来为我们的snake选择下一步行走的道路,而取代我们的键盘输入,所以整体逻辑和我们原本的snack是相同的,我们要做的是增加一个选择函数,来取代我们的键盘输入来为snake选择门路
- //自动寻找char wheregonext(int hx, int hy, int fx, int fy);
复制代码
- 之后我们要在检测键盘输入的位置,将字符读取更换为使用我们的函数
- while (continuegame == 1) { // 每次循环都要判断是否游戏已经竣事 ch = wheregonext(snakeX[snakeLength - 1], snakeY[snakeLength - 1], moneyX, moneyY); printf("\033[2J"); switch (ch) {
复制代码
实现代码如下:
[code]//用数组distance[4]={0,0,0,0} 纪录离食物的间隔char wheregonext(int hx, int hy, int fx, int fy) {// Hx,Hy: 头的位置 // Fx,Fy:食物的位置 char p = 0; int min = 9999; int distance[4]; distance[0] = abs(fx - (hx - 1)) + abs(fy - hy); distance[1] = abs(fx - (hx + 1)) + abs(fy - hy); distance[2] = abs(fx - hx) + abs(fy - (hy - 1)); distance[3] = abs(fx - hx) + abs(fy - (hy + 1)); // 分别盘算蛇头周边四个位置到食物的间隔。H头的位置,F食物位置 if (distance[0] |