上大学学习编程的初始目的就是冲着游戏来的~不过在刚学习C语言的时候,完全无法利用所学知识弄出一个可玩的游戏╮(╯_╰)╭,学了1年后仍然对最简单的贪吃蛇没有思路(当然不是说没有打代码的思路,而是没有办法弄出动态的东西,而且当时往算法那走了~),直到如今大二,利用寒假的一点时间入门Linux的时候,发现了curses库函数!当我看明白它的作用时,拍案而起,TM这玩意不就是我梦寐以求的可实现界面的东东么!当我学了一点点就觉悟到,用一个move()和printw()函数就完全可以把贪吃蛇这种级别的写出来。。。
既然要写就要写自己喜欢玩的~一条蛇太单调没有可玩价值,弄两条刚好有点挑战性!
游戏规则:
初始时两条蛇会在中间,一条蛇头是‘@‘,食物是‘$’;另一条蛇头是‘+‘,食物是‘*‘,蛇身都是‘#‘。WASD控制‘+‘蛇方向,方向键控制‘@‘蛇方向,每条蛇只能吃自己的食物。撞墙,碰到自己或另一条蛇的身体,吃到另一条蛇的食物都会结束游戏。
运行环境:Linux操作系统,请确认已安装curses库,没有的请在终端输入sudo apt-get libncurses5-dev 安装(移植性不好~可惜了)
编译指令:例如保存为snake.c 就在终端键入:g++ -o snake snake.c -lcurses
执行指令:在终端键入:./snake
思路:
怎样弄出一条蛇呢?我觉得用双链表还是比较方便的,每个结点记录的是当前蛇身的坐标跟前驱后继指针。这样在蛇移动时在头结点前添加一个结点,用move()移动到蛇头坐标,打印蛇头;接着如果没有吃到食物的话,把尾结点删掉,用move()移动到蛇尾,打印空值。之后~完了(高中语文都是及格低分飘过,再加上懒,只能写这么一点。。。)
感慨:
唉~想归想,实际动起手来还是有点麻烦的。主要是键盘响应上的问题,由于是刚刚接触curses库,学得还是很浅的。还是那句话,语文太差,连问题都不知道如何形容。。。反正就是按下任一方向键不松手,一段时间后松开,然后再按别的方向,它会先朝之前的方向移动,直到移动完后在去别的方向。移动一步的时间调得越慢,这种现象越明显。意思就是我不懂怎样在移动一步的时间内把多余的输入getch()忽略掉。还有一个蛋疼的就是让我在无数次尝试中发现了一个客观性的东西:C语言不支持函数的引用参数。我个蛋!害我无数次在终端中输入gcc开头的可爱句子找问题,结果可想而知。本来想加上计分的,后来发现,两条蛇的话,不知道怎样计才公平,唉~还需要一点点数学。。。最后想说一点就是:在编程的很多时候,我们并不是缺少能力,而是缺少工具。(像这个例子:我懂点C语言,但我不懂curses库,我就做不出东西),所以不敢说精通,但至少要熟练运用一门语言,之后再去找合适的工具,编程就不是什么难事情。写到这里,突然提醒了我,也提醒了大家,学编程,基础与实践很重要,反正切不可急!相信别人能做的,自己也可以!
PS:这只是刚写完版本的,bug众多~我觉得在继续学习curses的过程中为程序添加生机,这样才不那么枯燥。虽然curses库是基于文本的图形编程,但一样可以把游戏做得很好看。为QT打下基础先。。。
最后一点,CSND博客里的字符显示是不是出现了点问题。。。
转载请注明出处:http://blog.csdn.net/u013351484?viewmode=contents,谢谢!
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <curses.h> #include <time.h> int dir_y[] = {-1, 1, 0, 0}; int dir_x[] = {0, 0, -1, 1}; int vis[100][100]; int scord; typedef struct body { int y, x; struct body *pre, *sub; } Body; typedef struct { Body *head, *tail; int DIRECTION; int HEAD_X; int HEAD_Y; int FOOD_X; int FOOD_Y; char food; char H; } Snake; //随机函数 int Get_Rand_Number(int k) { static int first_time = 0; if(!first_time){ first_time = 1; srand((unsigned int)(time(NULL))); } return rand()%k + 1; } void Add(Snake &snake) { Body *s; s = (Body *)malloc(sizeof(Body)); s->y = snake.HEAD_Y; s->x = snake.HEAD_X; s->pre = NULL; s->sub = snake.head; snake.head->pre = s; snake.head = s; } void Free(Body *head) { if(NULL == head) return; Free(head->sub); free(head); } int Judge_Kill(int y, int x) { if(y == LINES - 1 || x == COLS - 1 || y == 0 || x == 0) return 1; if(1 == vis[y][x]) return 1; return 0; } void Initialization(Snake &snake, int y, int x) { snake.DIRECTION = 0; int &FOOD_Y = snake.FOOD_Y; int &FOOD_X = snake.FOOD_X; snake.HEAD_Y = y; snake.HEAD_X = x; while(1) { FOOD_Y = Get_Rand_Number(LINES - 3); FOOD_X = Get_Rand_Number(COLS - 3); if(!vis[FOOD_Y][FOOD_X]){ vis[FOOD_Y][FOOD_X] = 2; break; } } move(FOOD_Y, FOOD_X); printw("%c", snake.food); snake.head = (Body *)malloc(sizeof(Body)); snake.head->y = y; snake.head->x = x; snake.head->pre = snake.head->sub = NULL; snake.tail = snake.head; move(y, x); printw("%c", snake.H); refresh(); } void New_Food(Snake &snake) { scord++; int &FOOD_Y = snake.FOOD_Y; int &FOOD_X = snake.FOOD_X; while(1){ FOOD_Y = Get_Rand_Number(LINES - 3); FOOD_X = Get_Rand_Number(COLS - 3); if(!vis[FOOD_Y][FOOD_X]){ vis[FOOD_Y][FOOD_X] = 2; break; } } move(FOOD_Y, FOOD_X); printw("%c", snake.food); } void Reverse_Direction(Snake &snake) { int &HEAD_Y = snake.HEAD_Y; int &HEAD_X = snake.HEAD_X; Body *&head = snake.head; HEAD_Y += dir_y[snake.DIRECTION]; HEAD_X += dir_x[snake.DIRECTION]; //在蛇长不为1时,下一个移动跟蛇运动方向相反 if(head->sub && HEAD_Y == head->sub->y && HEAD_X == head->sub->x){ if(HEAD_Y == head->y){ if(HEAD_X > head->x) HEAD_X = head->x - 1; else HEAD_X = head->x + 1; } else{ if(HEAD_Y > head->y) HEAD_Y = head->y - 1; else HEAD_Y = head->y + 1; } } } int Move(Snake &snake, int fy, int fx) { int &FOOD_Y = snake.FOOD_Y; int &FOOD_X = snake.FOOD_X; int &HEAD_Y = snake.HEAD_Y; int &HEAD_X = snake.HEAD_X; Body *&head = snake.head; Body *&tail = snake.tail; //输出蛇头 move(HEAD_Y, HEAD_X); printw("%c", snake.H); vis[HEAD_Y][HEAD_X] = 1; //如果有蛇身,输出之 if(head->sub){ move(head->sub->y, head->sub->x); printw("#"); } if(HEAD_Y == FOOD_Y && HEAD_X == FOOD_X) New_Food(snake);//吃到食物 else if(HEAD_Y == fy && HEAD_X == fx) return 1;//吃到另一条蛇的食物 else{//什么都没有吃到 move(tail->y, tail->x); printw(" "); vis[tail->y][tail->x] = 0; free(tail); if(tail->pre){ tail = tail->pre; tail->sub = NULL; } } return 0; } int main() { initscr(); curs_set(0);//隐藏物理指针 noecho();//不回显输入 box(stdscr, '|', '-');//画框 Snake snake1, snake2; snake1.food = '$'; snake2.food = '*'; snake1.H = '@'; snake2.H = '+'; //两条蛇的初始化位置(小蛇放中间比较舒服) int init1_y = LINES / 2; int init1_x = COLS / 2 - 1; int init2_y = LINES / 2; int init2_x = COLS / 2 + 1; vis[init1_y][init1_x] = vis[init2_y][init2_x] = 1; //初始化两条蛇位置跟食物位置 Initialization(snake1, init1_y, init1_x); Initialization(snake2, init2_y, init2_x); keypad(stdscr, true);//开启键盘功能键 int key = 0; key = getch(); while(1) { if(KEY_UP == key) snake1.DIRECTION = 0; else if(KEY_DOWN == key) snake1.DIRECTION = 1; else if(KEY_LEFT == key) snake1.DIRECTION = 2; else if(KEY_RIGHT == key) snake1.DIRECTION = 3; else if('w' == key) snake2.DIRECTION = 0; else if('s'== key) snake2.DIRECTION = 1; else if('a' == key) snake2.DIRECTION = 2; else if('d' == key) snake2.DIRECTION = 3; //获取向前移动的坐标及判断是否跟蛇运动方向相反(一条长度不为1的运动着的蛇 //是有一个方向是不能走的) Reverse_Direction(snake1); Reverse_Direction(snake2); //判断游戏是否结束 if(Judge_Kill(snake1.HEAD_Y, snake1.HEAD_X) || Judge_Kill(snake2.HEAD_Y, snake2.HEAD_X)){ //释放内存,很重要 Free(snake1.head); Free(snake2.head); break; } //往各自的链表添加蛇身 Add(snake1); Add(snake2); //移动蛇及判断是否吃到另一条蛇的食物 int over1 = 0, over2 = 0; over1 = Move(snake1, snake2.FOOD_Y, snake2.FOOD_X); over2 = Move(snake2, snake1.FOOD_Y, snake1.FOOD_X); if(over1 || over2) break; refresh(); usleep(150*1000);//停留150毫秒 if(ERR != halfdelay(1)) key = getch();//在100毫秒内等待输入,如无输入,往下执行 } sleep(3); endwin(); exit(0); }