1、全部代码(代码相应功能详见注释)
#include#include #include #include #define UP 1 #define DOWN -1 #define LEFT 2 #define RIGHT -2 struct Snake { int i; int j; struct Snake *next; }; struct Snake *head = NULL; struct Snake *tail = NULL; int key; int dir; struct Snake food; void initFood() { int x; int y; x = rand()%20; //rand()可随机取大于等于0的自然数,注意取余操作可使得x、y均满足(0<=x,y<=20)将食物“#”限制在游戏边界以内 y = rand()%20; food.i = x; if (y != 0){ //当food的列等于0时,测试代码时发现界面没有打印“#” food.j = y; } } int eatFood(int i, int j) { if (food.i == i && food.j == j){ return 1; } return 0; } void addSnakeNode() { struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake)); switch(dir){ case UP: new->i = tail->i - 1; new->j = tail->j; break; case DOWN: new->i = tail->i + 1; new->j = tail->j; break; case LEFT: new->i = tail->i; new->j = tail->j - 1; break; case RIGHT: new->i = tail->i; new->j = tail->j + 1; break; } new->next = NULL; tail->next = new; tail = new; } void deleteSnakeNode() { struct Snake *p = head; head = head->next; free(p); } void initSnakeNode() { struct Snake *p = head; while(head != NULL){ //这里不能直接free(head)因为后面还会再此使用head结构体指针 p = head; head = head->next; free(p); } dir = RIGHT; initFood(); head = (struct Snake *)malloc(sizeof(struct Snake)); head->i = 2; head->j = 2; head->next = NULL; tail = head; addSnakeNode(); addSnakeNode(); addSnakeNode(); } void initNcurse() { initscr(); //ncurses界面初始化函数 keypad(stdscr, 1); //ncurses库自带的keypad函数,允许用户终端的键盘,允许getch()函数获取功能键 noecho(); //不回显用户输入的内容 } int printSnakeNode(int i, int j) { struct Snake *p = head; while(p != NULL){ if (p->i == i && p->j == j){ return 1; } p = p->next; } return 0; } void gamePic() //设计游戏边界 { int i; int j; move(0, 0); //move( int begin_x, int begin_y)//移动光标,基于标准屏幕stdscr(将移动后的界面覆盖掉先前的界面) for(i = 0; i < 20; i++){ if (i == 0){ for(j = 0; j < 20; j++){ printw("--"); } printw("n"); } if (i >= 0 && i <= 19){ for(j = 0; j <= 20; j++){ if (j == 0 || j == 20){ printw("|"); } else if (printSnakeNode(i, j) == 1){ printw("[]"); } else if (eatFood(i, j) == 1){ printw("##"); } else { printw(" "); } } printw("n"); } if (i == 19){ for(j = 0; j < 20; j++){ printw("--"); } printw("n"); } } printw("Designed by: kk key value: %dn", key); printw("food.hang = %d, food.lie = %dn", food.i, food.j); } int snakeDie()//两种情况:1、撞向边界死亡;2、自己撞自己死亡 { struct Snake *p = head; if (tail->i < 0 || tail->j == 0 || tail->i == 20 || tail->j == 20){ //撞向游戏边界死亡 return 1; } while(p->next != NULL){ //当p走到的尾巴tail时,以下条件语句恒成立则会出现一直初始化snake,故应该是p->next!=NULL,避免尾巴与尾巴自身比较 if (p->i == tail->i && p->j == tail->j){ return 1; //撞向自己死亡 } p = p->next; } return 0; } void moveSnakeNode() { addSnakeNode(); if (eatFood(tail->i, tail->j) == 1){ initFood(); //当吃到食物“#”初始化“#”,即蛇只增节点不删除节点 } else { deleteSnakeNode(); //其余情况就是蛇增加尾巴节点删除头节点 } if (snakeDie() == 1){ initSnakeNode(); } } void *refreshSnakeView() //刷新界面线程 { while(1){ moveSnakeNode(); gamePic(); refresh(); //每次在屏幕绘制之后,需要调用refresh()刷新屏幕 usleep(100000); } } void turnByAbs(int direction) //以绝对值改变来控制是否转向 { if (abs(dir) != abs(direction)){ dir = direction; } } void *changeDir() //转向线程 { while(1){ key = getch(); switch(key){ case KEY_UP: turnByAbs(UP); break; case KEY_DOWN: turnByAbs(DOWN); break; case KEY_LEFT: turnByAbs(LEFT); break; case KEY_RIGHT: turnByAbs(RIGHT); break; } } } int main(int argc, char *argv[]) { pthread_t t1; pthread_t t2; initNcurse(); initSnakeNode(); gamePic(); //创建两个线程同时运行 pthread_create(&t1, NULL, refreshSnakeView, NULL); pthread_create(&t2, NULL, changeDir, NULL); while(1);//等待输入,避免主程序提前结束 getch(); //接收键盘上的输入一个字符, 相当于getchar() endwin(); //ncurses自带库函数,避免终端界面错乱,结束ncurse屏幕绘画 return 0; }
2、代码测试
这样就可以实现贪吃蛇小游戏了(当然在调试代码过程中会出现各式各样的问题,不要放弃,把基础打好,一定可以找到问题所在的地方并解决好它)



