以前大学时候用MFC写过贪吃蛇,现在完全忘了,哈哈。自从搞跨平台以来对linux比较喜欢,不断学习和练习c语言。本次借助curses库控制终端显示画布实现贪吃蛇。
1.实现主要通过Snake结构体,记录每个节点的x和y坐标。每个节点可以指向下一个节点。这样基于蛇头head节点就可以遍历整条蛇。
2.蛇的移动通过在当前方向前面加一个节点,如果加的位置是食物那么就不用删尾节点。否则删除最后尾节点,就实现了所有节点向前移一位。
3.定义两个线程,一个用了固定节拍,每个节拍按当前方向移动一位蛇,然后执行画布绘图。另一个线程监听用户按键,把上下左右处理成方向变量值,供节拍线程在方向加节点。
编译命令
[root@zlzlinux zhanglianzhu]#
[root@zlzlinux zhanglianzhu]#
[root@zlzlinux zhanglianzhu]#
[root@zlzlinux zhanglianzhu]# cd /zlz/snake
[root@zlzlinux snake]# ll
总用量 32
-rwxr-xr-x 1 root root 22688 7月 19 22:31 snake
-rwxrwxrwx 1 root root 8171 7月 19 22:30 snake.c
[root@zlzlinux snake]# gcc snake.c -o snake -lcurses -lpthread
snake.c: 在函数‘main’中:
snake.c:130:30: 警告:传递‘pthread_create’的第 3 个参数时在不兼容的指针类型间转换 [-Wincompatible-pointer-types]
pthread_create(&tick, NULL, TickDo, NULL);
^~~~~~
In file included from snake.c:7:
/usr/include/pthread.h:236:15: 附注:需要类型‘void * (*)(void *)’,但实参的类型为‘void (*)()’
void *(*__start_routine) (void *),
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
snake.c:132:33: 警告:传递‘pthread_create’的第 3 个参数时在不兼容的指针类型间转换 [-Wincompatible-pointer-types]
pthread_create(&control, NULL, ControlDirection, NULL);
^~~~~~~~~~~~~~~~
In file included from snake.c:7:
/usr/include/pthread.h:236:15: 附注:需要类型‘void * (*)(void *)’,但实参的类型为‘void (*)()’
void *(*__start_routine) (void *),
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
[root@zlzlinux snake]# ./snake
[root@zlzlinux snake]#
//编译命令 //gcc snake.c -o snake -lcurses -lpthread #include#include #include #include #include //按键方向 #define UP 1 #define DOWN -1 #define LEFT 2 #define RIGHT -2 //最大x和y #define MaxX 70 #define MaxY 30 //统计刷新次数 int refreshNum = 0; //一个蛇节点的结构体 struct Snake { int x; int y; struct Snake* next; }; //头节点,通过头遍历整个蛇链条 struct Snake* head = NULL; int key; int dir; //食物节点 struct Snake food; //随机产生食物 void MakeFood(); /// /// 判断当前x和y是否有蛇节点 /// /// x /// y ///头返回1,中间返回2,尾返回3 int HasNode(int x, int y); ////// 判断当前x和y是否有食物 /// /// x /// y ///int HasFood(int x, int y); /// /// 绘制画布 /// void Draw(); ////// 在当前方位前面添加一个节点 /// void AddNode(); ////// 初始化蛇 /// void InitSnake(); ////// 删除节点 /// void DeleteNode(); ////// 判断是蛇否死亡 /// ///int SnakeDie(); /// /// 移动蛇 /// void MoveSnake(); ////// 执行节拍 /// void TickDo(); ////// 按键转换 /// /// 方向 void TransKey(int direct); ////// 控制方向 /// void ControlDirection(); ////// 执行入口 /// /// /// ///int main(int count, char* args[]) { //定义节拍线程,按节拍移动蛇和重绘 pthread_t tick; //定义按键控制线程,处理按键 pthread_t control; //初始化curses initscr(); keypad(stdscr, 1); noecho(); //初始化蛇对象 InitSnake(); //绘图 Draw(); //开启节拍线程 pthread_create(&tick, NULL, TickDo, NULL); //开启控制线程 pthread_create(&control, NULL, ControlDirection, NULL); //阻塞 while (1); getch(); endwin(); return 0; } //随机产生食物 void MakeFood() { //随机坐标 int x = rand() % (MaxX - 2); int y = rand() % (MaxY - 2); food.x = x + 1; food.y = y + 1; food.next = NULL; } /// /// 判断当前x和y是否有蛇节点 /// /// x /// y ///头返回1,中间返回2,尾返回3 int HasNode(int x, int y) { //从蛇头开始寻找 struct Snake* p = head; //如果是蛇头返回1 if (head->x == x && head->y == y) { return 1; } //遍历一直找到蛇尾 while (p != NULL) { if (p->x == x && p->y == y) { //是蛇尾返回3 if (p->next == NULL) { return 3; } //否则是蛇身 return 2; } p = p->next; } //其他就不是蛇节点 return 0; } ////// 判断当前x和y是否有食物 /// /// x /// y ///int HasFood(int x, int y) { //是食物坐标就返回1 if (food.x == x && food.y == y) { return 1; } return 0; } /// /// 绘制画布 /// void Draw() { int row; int col; //移动光标到00开始绘制 move(0, 0); //绘制边框和蛇 for (row = 0; row <= MaxY; row++) { //顶部先 if (row == 0) { //画顶部横线 for (col = 0; col < MaxX; col++) { printw("-"); } //右上角输出刷新次数 printw("%d", refreshNum); refreshNum++; //换行 printw("n"); } //中间的蛇 if (row > 0 && row < MaxY) { for (col = 0; col <= MaxX; col++) { //两边的边框 if (col == 0 || col == MaxX) { printw("|"); } //蛇头节点 else if (HasNode(col, row) == 1) { printw("#"); } //蛇身节点 else if (HasNode(col, row) == 2) { printw("O"); } //蛇尾节点 else if (HasNode(col, row) == 3) { printw("o"); } //食物 else if (HasFood(col, row)) { printw("*"); } //空白 else { printw(" "); } } printw("n"); } //底部线 if (row == MaxY) { for (col = 0; col < MaxX; col++) { printw("-"); } printw("n"); printw("write by zlz walk in lossing on centos !n"); } } } ////// 在当前方位前面添加一个节点 /// void AddNode() { struct Snake* new = (struct Snake*)malloc(sizeof(struct Snake)); switch (dir) { //在上添加 case UP: new->x = head->x; new->y = head->y - 1; break; //在下添加 case DOWN: new->x = head->x; new->y = head->y + 1; break; //在左添加 case LEFT: new->x = head->x - 1; new->y = head->y; break; //在由添加 case RIGHT: new->x = head->x + 1; new->y = head->y; break; } //新节点指向蛇头 new->next = head; //头节点等于新节点 head = new; } ////// 初始化蛇 /// void InitSnake() { struct Snake* p; //初始化方向 dir = DOWN; //如果头不为空,释放之前的蛇链,从头一个个释放 while (head != NULL) { p = head; head = head->next; free(p); } //初始化食物 MakeFood(); //申请一个新的头对象 head = (struct Snake*)malloc(sizeof(struct Snake)); //从上中间开始往下走 head->x = MaxX / 2; head->y = 3; head->next = NULL; //默认的节点 AddNode(); AddNode(); AddNode(); } ////// 删除节点 /// void DeleteNode() { struct Snake* p; //记录最后一个节点的前一个节点(倒数第二个) struct Snake* pPre = NULL; p = head; //一直找到next为空的节点,即最后节点 while (p->next != NULL) { pPre = p; p = p->next; } //这时候把倒数第二个节点下一个指向空 if (pPre != NULL) { pPre->next = NULL; } //释放最后的节点 free(p); } ////// 判断是蛇否死亡 /// ///int SnakeDie() { struct Snake* p = head->next; //头碰到边界就死了 if ((head->x < 1) || (head->x > MaxX - 1) || (head->y < 1) || (head->y > MaxY - 1)) { return 1; } //检查每个非头节点是否和头节点重合,重合就死了 while (p->next != NULL) { //头和身体重叠也死亡 if (p->x == head->x && p->y == head->y) { return 1; } p = p->next; } //否则就没死 return 0; } /// /// 移动蛇 /// void MoveSnake() { //先到前进方向加一个节点 AddNode(); //有食物就初始化食物(食物被吃) if (HasFood(head->x, head->y)) { MakeFood(); } //如果加完节点的头不是食物,就删蛇最后一个节点(蛇前进一步) else { DeleteNode(); } //死亡了重新开始 if (SnakeDie()) { InitSnake(); } } ////// 执行节拍 /// void TickDo() { //死循环移动蛇和刷新画布 while (1) { //移动蛇异常 MoveSnake(); //刷新画布 Draw(); //休眠 usleep(150000); //算下面板 refresh(); //休眠 usleep(150000); } } ////// 按键转换 /// /// 方向 void TransKey(int direct) { if (abs(dir) != abs(direct)) { dir = direct; } } ////// 控制方向 /// void ControlDirection() { //死循环监测按键 while (1) { key = getch(); switch (key) { case KEY_DOWN: TransKey(DOWN); break; case KEY_UP: TransKey(UP); break; case KEY_LEFT: TransKey(LEFT); break; case KEY_RIGHT: TransKey(RIGHT); break; } } }



