C语言——贪吃蛇小游戏

目录

一、ncurse

1.1 为什么需要用ncurse:

1.2 ncurse的输入输出:

1.2.1 如何使用ncurse:

1.2.2 编译ncurse的程序:

1.2.3 测试输入一个按键ncurse的响应速度:

1.3 ncurse上下左右键获取:

1.3.1 如何查看宏定义的.h文件:

1.3.2 ncurse上下左右键获取:

二、地图规划

2.1 地图规划算法显示第一行:

2.2 实现贪吃蛇完整地图:

2.3 优化贪吃蛇地图:

三、显示贪吃蛇身子

3.1 显示贪吃蛇身子的一个节点:

3.2 显示贪吃蛇完整身子:

3.3 显示贪吃蛇完整身子改进:

四、贪吃蛇移动

4.1 按下▶贪吃蛇向右移动:

4.2 贪吃蛇撞墙重新开始: 

4.3 贪吃蛇脱缰自由向右行走

五、Linux线程引入

5.1 贪吃蛇方向移动和刷新界面一起实现面临的问题:

5.2 线程的基本用法:

5.3 线程demo案例:

5.4 使用线程解决贪吃蛇方向移动和刷新界面一起实现面临的问题:

六、贪吃蛇跑起来

6.1 实现贪吃蛇四方向的风骚走位:

6.2 用绝对值方式来解决不合理的走位:

6.3 贪吃蛇吃饭了(食物的位置是随机的):

七、项目代码


  • 项目运行环境:Linux,基于Ncurse图形库的C语言小游戏

  • 项目的目的和意义:起到承上启下的作用,对于前面学的C语言的基础和数据结构链表做一个比较好的巩固,对于后面的Linux系统编程的开发做铺垫

  • 项目基础要求:C语言基础、Linux基本操作

    /*项目步骤*/
    (1)选择ncurses库的原因
    在进行贪吃蛇游戏时,贪吃蛇的行进方向需要你按下上下左右键进行操控,如果使用C语言自带的函数,例如:scanf或者getchar之类的,需要你按下回车键,程序才能进行响应,而这显然是十分不方便的,但是ncurses库就很好的解决了这个问题。ncurses库自带的函数getch就能实现迅速便捷的贪吃蛇方向响应。(2)ncurses库的基本入门
    对于该项目而言,ncurses库我们不需要进行过于深入的学习,只需要知道一些基本的函数使用即可。下列程序中的函数,就是一个基于ncurses库的基本代码框架。
    #include <curses.h>int main()
    {initscr();//ncurse界面的初始化函数printw("this is a curses window\n");//在ncurse模式下的printfgetch();//等待用户的输入,如果没有这句话,程序就退出了,看不到运行的结果,也就是无法看到上面那句话endwin();//程序退出,恢复shell终端的显示,如果没有这句话,shell终端字乱码,坏掉return 0;
    }(3)贪吃蛇地图的整体规划
    整个贪吃蛇地图的大小将它设置成一个20*20的近似正方形,使用"|"来表示左右边框,使用"--"来表示上下边框。(4)实现贪吃蛇第一个节点的显示(5)显示贪吃蛇的完整身子
    注意,在这里,我们设置两个全局变量,struct Snake *head和struct Snake *tail,一个指向贪吃蛇的头,一个指向贪吃蛇的尾。在将第一个节点打印完后,将尾指向头,即:head = tail。每一次节点的添加,我们调用一个单独的函数去执行,并其使用尾插法实现。(6)实现贪吃蛇的右移
    贪吃蛇的移动,整体来说就是链表节点的删除与添加。我们首先实现贪吃蛇的右移,每当按键按下时,贪吃蛇右移一格,即左侧的头结点删除head = head->next,右侧再次添加一个新的节点。新节点的坐标应该是行不变,列加一。注意:不要忘记清楚垃圾节点。(7)实现贪吃蛇的撞墙死亡
    将贪吃蛇的尾节点坐标进行判断,判断其是否达到边界坐标。满足条件时,将贪吃蛇重新初始化。注意:不要忘记清楚垃圾节点。(8)实现贪吃蛇的自由行走
    在这里,我们发现了一个问题,地图需要实时刷新进行贪吃蛇位置的变更,这是一个while(1)的死循环,而获取键值也是一个实时读取的操作,因此也是一个while(1)死循环,代码执行逻辑上出现了问题,所以我们引入了线程的概念。(9)了解什么是线程(10)用线程解决上述问题,实现贪吃蛇的分骚走位
    开辟两个线程,一个用来执行地图刷新操作,一个用来获取键值。pthread_create(&t1,NULL,refreshScreen,NULL);pthread_create(&t2,NULL,changeDir,NULL);(11)解决贪吃蛇的不合理走位
    在这里,我们使用绝对值法来解决问题,abs函数的作用便是取绝对值,我们将上下左右键,两两对应,宏定义为1,-1,2,-2之类的数就能成功解决问题。(12)实现贪吃蛇食物的打印(13)实现食物的随机出现
    取随机数,C语言有一个自带的函数可以解决这个问题,rand()函数可以实现随机取数,我们只要再对它进行取余操作,便可以防止食物出现在地图以外的位置。(14)实现贪吃蛇咬到自己结束游戏,重新开始的操作
    当贪吃蛇的尾节点与自身除尾巴节点以外的其他节点进行比较后,若行列数相同,则初始化整个贪吃蛇,注意:不要忘记垃圾节点的清除(我们可以在每次贪吃蛇初始化之前进行这个操作)。

    一、ncurse

    1.1 为什么需要用ncurse:

  • 因为的按键响应牛逼哄哄

  • 1.2 ncurse的输入输出:

  • ncurse用的最多的地方是在Linux内核编译之前的内核配置

  • 1.2.1 如何使用ncurse:

1.2.2 编译ncurse的程序:

1.2.3 测试输入一个按键ncurse的响应速度:

1 #include <curses.h>2 3 int main()4 {5         char c;6 7         initscr();8         c = getch();9         printw("you Input :%c\n",c);10         getch();11         endwin();12         return 0;13 }           
  • 使用ncurse的好处是:按下一个按键不需要按下回车,直接就可以输出c的值,和我们C语言的其他输入函数好用

1.3 ncurse上下左右键获取:

1.3.1 如何查看宏定义的.h文件:

vi /usr/include/curses.h    //查看宏定义.h文件的指令
:q                            //退出查看

1.3.2 ncurse上下左右键获取:

 1 #include <curses.h>2 3 int main()4 {5         int key;6 7         initscr();8         keypad(stdscr,1);	//这个函数允许使用功能键,例如:F1、F2、方向键等功能键。几乎所有的交互式程序都需要使用功能			键,因为绝大多数用户界面主要用方向键进行操作。使用keypad(stdscr,TURE)就为“标准屏幕”(stdscr)激活了功能键。9 10         while(1){11                 key = getch();12                 switch(key){13                         case KEY_DOWN:14                                 printw("DOWN\n");15                                 break;16                         case KEY_UP:17                                 printw("up\n");18                                 break;19                         case KEY_LEFT:20                                 printw("LEFT\n");21                                 break;22                         case KEY_RIGHT:23                                 printw("RIGHT\n");24                                 break;25                 }26 27 28         }29         endwin();30         return 0;31 }

  • 我们按下上下左右▲▼◀▶之后,可以获取到上下左右的打印信息

二、地图规划

2.1 地图规划算法显示第一行:

#include <curses.h>void initNcurse()
{initscr();keypad(stdscr,1);
}void gamPic()
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else{printw("  ");}}}}
}int main()
{initNcurse();	//初始化NcursegamPic();		//地图规划显示第一行getch();endwin();return 0;
}

2.2 实现贪吃蛇完整地图:

#include <curses.h>void initNcurse()
{initscr();keypad(stdscr,1);
}void gamPic()
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else{printw("  ");}}printw("\n");}if(hang>0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}int main()
{initNcurse();	//初始化NcursegamPic();		//实现贪吃蛇地图getch();endwin();return 0;
}

2.3 优化贪吃蛇地图:

#include <curses.h>void initNcurse()
{initscr();keypad(stdscr,1);
}void gamPic()
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){						//第0行打“--”for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {			//第0行-19行的第0列和第20列打“|”for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else{printw("  ");}}printw("\n");}if(hang == 19){						//第19行打“--”for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");		//作者}}
}int main()
{initNcurse();gamPic();getch();endwin();return 0;
}
//实现的贪吃蛇地图和上面一样,只不过是优化了一下代码

三、显示贪吃蛇身子

3.1 显示贪吃蛇身子的一个节点:

#include <curses.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake node1 = {2,2,NULL};void initNcurse()
{initscr();keypad(stdscr,1);
}void gamPic()
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(node1.hang == hang && node1.lie == lie){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}int main()
{initNcurse();gamPic();getch();endwin();return 0;
}

3.2 显示贪吃蛇完整身子:

#include <curses.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake node1 = {2,2,NULL};
struct Snake node2 = {2,3,NULL};
struct Snake node3 = {2,4,NULL};
struct Snake node4 = {2,5,NULL};void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = &node1;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}int main()
{initNcurse();node1.next = &node2;node2.next = &node3;node3.next = &node4;gamPic();getch();endwin();return 0;
}

3.3 显示贪吃蛇完整身子改进:

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;	//指向链表头
struct Snake *tail = NULL;	//指向链表尾void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()				//地图规划
{int hang;int lie;for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));	//创建新节点if(new == NULL){printw("malloc error\n");}new->hang = tail->hang;		//新节点的行等于链表尾的行new->lie  = tail->lie+1;	//新节点的行等于链表尾的列+1new->next = NULL;tail->next = new;			//从链表尾部插入新节点tail = new;					//新节点当作尾部
}void initSnake()
{head = (struct Snake *)malloc(sizeof(struct Snake));	//创建链表头if(head == NULL){printw("malloc error\n");}head->hang = 2;			head->lie = 2;head->next = NULL;tail = head;		//第一个节点链表头和链表尾是一样的addNode();			//调用一次代表增加一个节点addNode();addNode();
}int main()
{initNcurse();initSnake();gamPic();getch();endwin();return 0;
}

四、贪吃蛇移动

4.1 按下▶贪吃蛇向右移动:

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;tail->next = new;tail = new;
}void initSnake()
{head = (struct Snake *)malloc(sizeof(struct Snake));if(head == NULL){printw("malloc error\n");}head->hang = 2;head->lie = 2;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();		//增加一个节点deletNode();	//删除头节点
}int main()
{int con;initNcurse();initSnake();gamPic();while(1){con = getch();			//con获取键值if(con == KEY_RIGHT){	//如果是右键moveSnake();		//向右移动gamPic();			//必须刷新一下界面,否则看不到🐍移动}		}getch();endwin();return 0;}

4.2 贪吃蛇撞墙重新开始: 

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));if(new == NULL){printw("malloc error\n");}new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;while(head != NULL){	判断蛇是否为空,清理内存 p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));if(head == NULL){printw("malloc error\n");}head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();//判断蛇的尾巴碰到上下左右的四个边框后就重新开始if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}int main()
{int con;initNcurse();initSnake();gamPic();while(1){con = getch();if(con == KEY_RIGHT){moveSnake();gamPic();}		}getch();endwin();return 0;}

4.3 贪吃蛇脱缰自由向右行走

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));if(new == NULL){printw("malloc error\n");}new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;while(head != NULL){p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));if(head == NULL){printw("malloc error\n");}head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}int main()
{int con;initNcurse();initSnake();gamPic();while(1){	//之前受方向键控制,现在自由行走moveSnake();	gamPic();	refresh();			//刷新界面usleep(100000);		//延时100ms}getch();endwin();return 0;}

五、Linux线程引入

5.1 贪吃蛇方向移动和刷新界面一起实现面临的问题:

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!\n");}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));if(new == NULL){printw("malloc error\n");}new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;while(head != NULL){p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));if(head == NULL){printw("malloc error\n");}head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}int main()
{int key;initNcurse();initSnake();gamPic();while(1){moveSnake();gamPic();refresh();usleep(100000);}while(1){key = getch();switch(key){case KEY_DOWN:printw("DOWN\n");break;	case KEY_UP:printw("UP\n");break;case KEY_LEFT:printw("LEFT\n");break;case KEY_RIGHT:printw("RIGHT\n");break;}}	getch();endwin();return 0;}
  • 在上面的程序中main函数中有两个while(1)循环,这样就会出现问题,程序运行的现象是:获取按键值的这个while循环根本不会执行,那该如何解决?于是引入“Linux线程”!

  • 在贪吃蛇运动过程中,我们需要改变蛇的移动方向,这是就需要不停扫描键盘输入的值来判断方向,同时还需要不停的刷新界面,为了多个while循环并存这里需要引入linux线程。

5.2 线程的基本用法:

#include <pthread.h>  // 头文件pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;
如:pthread_t t1;  //多线程定义pthread_create(&t1,NULL,refreshInterface,NULL); 
参数1:传出参数,保存系统为我们分配好的线程ID
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装。使用多线程的函数必须返回指针型,如void *refreshInterface()注:gcc xxx.c -lcurses -lpthead  //编译需要连接pthead库

5.3 线程demo案例:

/*在这个程序当中只有func1一个函数会被执行,func2函数根本不会执行想要解决这个问题就需要引入Linux的线程
*/
#include <stdio.h>void pfunc1()
{while(1){printf("this is a pfunc1\n");sleep(1);} 
}void pfunc2()
{while(1){printf("this is a pfunc2\n");sleep(1);} 
}int main()
{pfunc1();pfunc2();return 0;
}
/*引入Linux线程修改代码,func1和func2两个函数都可以执行
*/
#include <stdio.h>
#include <pthread.h>	//线程头文件void* func1()
{while(1){printf("this is a func1\n");sleep(1);}}void* func2()
{while(1){printf("this is a func2\n");sleep(1);}
}int main()
{pthread_t th1;	//定义一个th1线程pthread_t th2;	//定义一个th2线程pthread_create(&th1, NULL, func1, NULL);pthread_create(&th2, NULL, func2, NULL);while(1);return 0;
}

5.4 使用线程解决贪吃蛇方向移动和刷新界面一起实现面临的问题:

#include <curses.h>
#include <stdlib.h>struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;int key;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!key = %d\n",key);}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;while(head != NULL){p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}void* refreshJieMian()
{while(1){moveSnake();gamPic();refresh();usleep(100000);}
}void* changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:printw("DOWN\n");break;	case KEY_UP:printw("UP\n");break;case KEY_LEFT:printw("LEFT\n");break;case KEY_RIGHT:printw("RIGHT\n");break;}}	
}int main()
{initNcurse();initSnake();//注意:线程创建要放在初始化后面,不然就会导致程序段错误(答疑老师解决)pthread_t t1;pthread_t t2;pthread_create(&t1, NULL, refreshJieMian, NULL);pthread_create(&t2, NULL, changeDir, NULL);gamPic();while(1);getch();endwin();return 0;}

  • 蛇在向右移动的同时也可以按方向键,这就是引入线程之后的牛逼之处!

六、贪吃蛇跑起来

6.1 实现贪吃蛇四方向的风骚走位:

#include <curses.h>
#include <stdlib.h>
#include <pthread.h>#define UP    1
#define DOWN  2
#define LEFT  3
#define RIGHT 4struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;int key;
int dir;void initNcurse()
{initscr();keypad(stdscr,1);
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");:}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!key = %d\n",key);}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;switch(dir){case UP:	new->hang = tail->hang-1;new->lie  = tail->lie;break;case DOWN:	new->hang = tail->hang+1;new->lie  = tail->lie;break;case LEFT:	new->hang = tail->hang;new->lie  = tail->lie-1;break;case RIGHT:	new->hang = tail->hang;new->lie  = tail->lie+1;break;}tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;dir = RIGHT;while(head != NULL){p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}void* refreshJieMian()
{while(1){moveSnake();gamPic();refresh();usleep(100000);}
}void* changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:dir = DOWN;break;	case KEY_UP:dir = UP;break;case KEY_LEFT:dir = LEFT;break;case KEY_RIGHT:dir = RIGHT;break;}}	
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();gamPic();pthread_create(&t1, NULL, refreshJieMian, NULL);pthread_create(&t2, NULL, changeDir, NULL);while(1);getch();endwin();return 0;}

6.2 用绝对值方式来解决不合理的走位:

#include <curses.h>
#include <stdlib.h>#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;int key;
int dir;void initNcurse()
{initscr();keypad(stdscr,1);noecho();
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao!key = %d\n",key);}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;switch(dir){case UP:	new->hang = tail->hang-1;new->lie  = tail->lie;break;case DOWN:	new->hang = tail->hang+1;new->lie  = tail->lie;break;case LEFT:	new->hang = tail->hang;new->lie  = tail->lie-1;break;case RIGHT:	new->hang = tail->hang;new->lie  = tail->lie+1;break;}tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;dir = RIGHT;while(head != NULL){p = head;head = head->next;free(p);}head = (struct Snake *)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();deletNode();if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}void refreshJieMian()
{while(1){moveSnake();gamPic();refresh();usleep(100000);}
}void turn(int direction)			通过绝对值判断相反方向不触发
{if(abs(dir) != abs(direction)){dir = direction;}
}void changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;	case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}	
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();gamPic();pthread_create(&t1, NULL, refreshJieMian, NULL);pthread_create(&t2, NULL, changeDir, NULL);while(1);getch();endwin();return 0;}

6.3 贪吃蛇吃饭了(食物的位置是随机的):

#include <curses.h>
#include <stdlib.h>
#include <pthread.h>#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;int key;
int dir;struct Snake food;void initFood()
{int x = rand()%20;int y = rand()%20;food.hang = x;food.lie  = y;
}void initNcurse()
{initscr();keypad(stdscr,1);noecho();
}int hasSnakeNode(int i, int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}int hasFood(int i, int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}void gamPic()
{int hang;int lie;move(0,0);for(hang=0; hang<20; hang++){if(hang == 0){for(lie=0; lie<20; lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19) {for(lie=0; lie<=20; lie++){if(lie == 0 || lie == 20){printw("|");}else if(hasSnakeNode(hang,lie)){printw("[]");}else if(hasFood(hang,lie)){printw("##");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0; lie<20; lie++){printw("--");}printw("\n");printw("By ShiYaHao! food.hang = %d,food.lie = %d\n",food.hang,food.lie);}}
}void addNode()
{struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie  = tail->lie+1;new->next = NULL;switch(dir){case UP:	new->hang = tail->hang-1;new->lie  = tail->lie;break;case DOWN:	new->hang = tail->hang+1;new->lie  = tail->lie;break;case LEFT:	new->hang = tail->hang;new->lie  = tail->lie-1;break;case RIGHT:	new->hang = tail->hang;new->lie  = tail->lie+1;break;}tail->next = new;tail = new;
}void initSnake()
{struct Snake *p;dir = RIGHT;while(head != NULL){p = head;head = head->next;free(p);}initFood();head = (struct Snake *)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();addNode();
}void deletNode()
{struct Snake *p;p = head;head = head->next;free(p);
}void moveSnake()
{addNode();if(hasFood(tail->hang,tail->lie)){initFood();}else{	deletNode();}if(tail->hang < 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){initSnake();}
}void* refreshJieMian()
{while(1){moveSnake();gamPic();refresh();usleep(100000);}
}void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}void* changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;	case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}	
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();gamPic();pthread_create(&t1, NULL, refreshJieMian, NULL);pthread_create(&t2, NULL, changeDir, NULL);while(1);getch();endwin();return 0;}

七、项目代码

#include <curses.h>
#include <stdlib.h>
#include <pthread.h>#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2struct Snake{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int key;
int dir;void addNode();                   /*从尾部插入新节点*///void initNcurses();               /*ncurses库的初始化函数*/
//void gameMap();                   /*贪吃蛇地图的初始化*/
//int printSnakeNode(int i,int j);  /*在地图上打印贪吃蛇的节点*/
//void initSnake();                 /*初始化贪吃蛇*///void deletNode();                 /*删除头结点*/
//void moveSnake();                 /*实现贪吃蛇的移动*/
//void *refreshScreen();            /*线程实现图像刷新*/
//void *changeDir();                /*线程实现贪吃蛇方向的改变*/
//void turn(int direction);         /*防止出现不合理走位*/
//void creatFood();                 /*随机出现食物*/
//int hasFood(int i,int j);         /*打印食物*/
//int ifSnakeDie();                 /*判断贪吃蛇是否死亡*//*随机出现食物*/
void creatFood()
{int x = rand()%20;int y = rand()%19+1;food.hang = x;food.lie = y;
}int hasFood(int i,int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}/*ncurses库的初始化函数*/
void initNcurses()
{initscr();//ncurse界面的初始化函数keypad(stdscr,1);//使用keypad函数,才可以使用键盘功能键noecho();//防止打印无关键值
}/*贪吃蛇地图的初始化*/
void gameMap()
{int hang;int lie;move(0,0);//把光标的位置移到头,实现地图刷新时的覆盖for(hang=0;hang<20;hang++){if(hang == 0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0 && hang<=19){for(lie=0;lie<=20;lie++){if(lie == 0 || lie == 20){printw("|");}else if(printSnakeNode(hang,lie)){printw("[]");}else if(hasFood(hang,lie)){printw("##");}else{printw("  ");}}printw("\n");}if(hang == 19){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}printw("By ShiYaHao!,food.hang = %d,food.lie = %d\n",food.hang,food.lie);
}/*在地图上打印贪吃蛇的节点*/
int printSnakeNode(int i,int j)
{struct Snake *p = head;while(p != NULL){if(p->hang == i && p->lie == j){return 1;}p = p->next;}return 0;
}/*初始化贪吃蛇*/
void initSnake()
{	struct Snake *p = NULL;if(head != NULL){   //当贪吃蛇死亡后,把多余节点释放p = head;head = head->next;	free(p);}creatFood();dir = RIGHT;head = (struct Snake *)malloc(sizeof(struct Snake));head->hang = 1;head->lie = 1;head->next = NULL;tail = head;addNode();addNode();addNode();
}/*从尾部插入新节点*/
void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));switch(dir){case UP:new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN:new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT:new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT:new->hang = tail->hang;new->lie = tail->lie+1;break;}new->next = NULL;tail->next = new;tail = new;}/*删除头结点*/
void deletNode()
{struct Snake *p = head;head = head->next;free(p);	
}/*判断贪吃蛇是否死亡*/
int ifSnakeDie()
{struct Snake *p;p = head;if(tail->hang < 0 || tail->hang == 20 || tail->lie == 0 || tail->lie == 20){return 1;}while(p->next != NULL){if(p->hang == tail->hang && p->lie == tail->lie){return 1;}p = p->next;}return 0;}/*实现贪吃蛇的移动*/
void moveSnake()
{addNode();if(hasFood(tail->hang,tail->lie)){creatFood();}else{deletNode();}if(ifSnakeDie()){initSnake();}}/*线程实现图像刷新*/
void *refreshScreen()
{usleep(100000);while(1){moveSnake();gameMap();//刷新地图	refresh();//界面刷新函数usleep(100000);}
}
/*防止不合理走位*/
void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}/*线程实现贪吃蛇方向的改变*/
void *changeDir()
{while(1){key = getch();switch(key){case KEY_UP:turn(UP);break;case KEY_DOWN:turn(DOWN);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}int main()
{pthread_t t1;pthread_t t2;initNcurses();initSnake();gameMap();pthread_create(&t1,NULL,refreshScreen,NULL);pthread_create(&t2,NULL,changeDir,NULL);while(1);getch();//等待用户的输入,如果没有这句话,程序就退出了,看不到运行的结果,也就是无法看到上面那句话endwin();//程序退出,恢复shell终端的显示,如果没有这句话,shell终端字乱码,坏掉return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/136790.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【IntelliJ IDEA】cmd和idea Terminal查看java版本不一致

问题描述 原来win10电脑上安装的是jdk8的版本&#xff0c;因某些原因&#xff0c;现在想换成jdk7的版本&#xff0c;修改环境变量后&#xff0c;在cmd中执行 [java -version]命令&#xff0c;显示的是7的版本。 但在idea的Terminal中执行&#xff0c;确实显示8的版本。 原因分…

基于深度强化学习的四旋翼无人机航线跟随

源自&#xff1a;指挥与控制学报 作者&#xff1a;杨志鹏 李波 甘志刚 梁诗阳 摘 要 针对无人机在空中执行航线跟随任务时无法对未知环境作出合理应对措施等问题, 提出了一种基于深度强化学习的四 旋翼无人机航线跟随方法. 通过无人机受力分析、欧拉角变换建立四旋翼无人…

2023.9.18 网络层 IP 协议详解

目录 IP协议 IPv4 32位 源IP 地址 / 32位 目的IP 地址 IP 地址管理 特殊 IP 路由选择 IP协议 IPv4 32位 源IP 地址 / 32位 目的IP 地址 基本知识&#xff1a; 在 IP 报头中一般表示为 32位 二进制整数日常生活中的 IP 一般将 32位 二进制整数&#xff0c;也就是 4字节 的二…

2023/9/19 -- C++/QT

作业 1> 登录框实现注册功能&#xff0c;将注册的结果放入文件中&#xff08;君子作业&#xff09; 2> 完成文本编辑器的保存工作 void Widget::on_saveBtn_clicked() {QString fileName QFileDialog::getSaveFileName(this,"另存为","./","…

036:vue导出页面生成pdf文件

第036个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

元数据管理平台Datahub0.10.5版本安装部署与导入各种元数据手册

官网文档连接 DataHub Quickstart Guide | DataHub (datahubproject.io) 本文所选择的Python的版本为3.8.16&#xff0c;Docker版本为20.10.0&#xff0c;Datahub为0.10.5版本 python必须为3.7以上的版本。0.10.5不支持以下的版本 如果要使用web上的 添加数据源 直接调用的…

中国提出FastSAM:在RTX3090上提升了ViT-H E(32×32) 50倍速度

文章目录 1. Abstract2. 背景介绍3. 框架详情 (Methodology)3.1 Overview3.2 All-instance Segmentation3.3 Prompt-guided Selection4. Experiments4.1 Run-time Efficiency Evaluation4.2 Zero-Shot Edge Detection4.2.1 BSDS5004.2.2 Sobel filtering4.2.3 NMS4.3 Zero-Shot…

Redis 列表操作实战(全)

目录 LINDEX 获取指定下标元素 LSET 指定下标添加元素 LPUSH 将元素插入列表头 LPUSHX RPUSH 将元素插入列表尾 RPUSHX LINSERT 将元素插入列表某位置之前 LLEN 列表长度 LPOP 取列表头元素 RPOP 取列表尾元素 BLPOP 阻塞式取列表头元素 BRPOP 阻塞式取列表尾元素…

视频监控系统/视频汇聚平台EasyCVR平台页面展示优化

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

企望制造ERP存在远程命令执行漏洞 附POC

文章目录 企望制造ERP存在远程命令执行漏洞 附POC1. 企望制造ERP简介2.漏洞描述3.影响版本4.fofa查询语句5.漏洞复现6.POC&EXP7.整改意见8.往期回顾 企望制造ERP存在远程命令执行漏洞 附POC 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播…

C# OpenCvSharp 图片模糊检测(拉普拉斯算子)

效果 项目 代码 using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows.Forms.VisualStyl…

python+django学习资料在线分享系统vue

本站是一个B/S模式系统&#xff0c;采用vue框架作为开发技术&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得校园资料分享平台管理工作系统化、规范化。技术栈 后端&#xff1a;pyth…

零基础学前端(四)1. 重点讲解 CSS:盒子模型、样式选择器

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…

RK3568平台开发系列讲解(驱动篇)RK3568 I2C总线介绍

🚀返回专栏总目录 文章目录 一、I2C 简介1.1、起始位1.2、停止位1.3、数据传输1.4、应答信号1.5、I2C 写时序1.6、I2C 读时序1.7、I2C 多字节读写时序二、RK3568 I2C 总线介绍沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解RK3568 I2C总线特性。 一、…

使用 K 均值聚类进行颜色分割

介绍 颜色分割是计算机视觉中使用的一种技术,用于根据颜色识别和区分图像中的不同对象或区域。聚类算法可以自动将相似的颜色分组在一起,而不需要为每种颜色指定阈值。当处理具有大范围颜色的图像时,或者当事先不知道确切的阈值时,这非常有用。 在本教程中,我们将探讨如何…

Zipping

Zipping 信息收集端口扫描目录扫描webbanner信息收集 漏洞利用空字节绕过---->失败sqlI-preg_match bypass反弹shell 稳定维持 提权-共享库漏洞 参考&#xff1a;https://rouvin.gitbook.io/ibreakstuff/writeups/htb-season-2/zipping#sudo-privileges-greater-than-stock-…

【ComfyUI】RuntimeError: CUDA error: operation not supported

文章目录 前言解决办法方式一&#xff1a;黑名单策略方式二&#xff1a;启动时添加--disable-cuda-malloc 前言 最近好不容易&#xff0c;安装好ComfyUI后&#xff0c;启动也OK&#xff0c;点击生成图片时&#xff0c;报错了 got prompt model_type EPS adm 0 making attenti…

Postman应用——Collection、Folder和Request

文章目录 Collection新建CollectionCollection重命名保存Request到Collection在Collection下创建Request删除Collection Folder新建FolderFolder重命名保存Request到Folder在Folder下创建Request在Folder下创建Folder删除Folder Request创建临时RequestRequest重命名删除Reques…

【AAAI2023】Spatial-Spectral Transformer for Hyperspectral Image Denoising

论文&#xff1a;https://readpaper.com/paper/4694783227240398849 代码&#xff1a;https://github.com/MyuLi/SST 1、总体介绍 高光谱图像&#xff08;HSI&#xff09;去噪是后续HSI应用的关键预处理过程&#xff0c;但是基于CNN的方法需要在计算效率与非局部特征建模能力之…

如何快速走出网站沙盒期(关于优化百度SEO提升排名)

网站沙盒期是指新建立的网站在百度搜索引擎中无法获得好的排名&#xff0c;甚至被完全忽略的现象。这个现象往往发生在新建立的网站上&#xff0c;因为百度需要时间来评估网站的质量和内容。蘑菇号www.mooogu.cn 为了快速走出网站沙盒期&#xff0c;需要优化百度SEO。以下是5个…