一、基本要求
C编程基础以及Linux基本操作
C编程基础:包括但不限于数据结构链表基础,C变量,流程控制,函数,指针,结构体等
Linux基本操作:包括但不限于 如何编写代码,如何编译代码,如何运行程序,如何创建文件夹
二、为什么需要ncurse
通俗易懂的来讲就是,C语言基础自带的库不存在单按方向键就能直接控制蛇的移动,必须按下方向键和回车键才可以响应,ncurse封装的一些库不需要按下回车,直接按下方向键就可以响应
三、安装ncurse
编译linux内核需ncurses, Ubuntu命令:
sudo apt-get install libncurses5-dev
四、ncurse的用法
- 包含头文件:curses.h
- 编译时应加上链接语句-lcurses,如:gcc temp.c -o temp -lcurses
- 重要的函数: 函数 功能
程序中所调用的函数
initscr() 初始化curses库和ttty(在开始curses编程之前,必须使用initscr()这个函数来开启curses模式)
endwin() 关闭curses并重置tty(结束curses编程时,最后调用的一个函数)
move(y,x) 将游标移动至 x,y 的位置
printw(format,str) curses中的 printf() ,以一定的格式输出至萤幕
getch() 从键盘读取一个字元。(注意!传回的是整数值)
refresh() 使屏幕按照你的意图显示。比较工作屏幕和真实屏幕的差异,然后refresh通过终端驱动送出那些能使真实屏幕于工作屏幕一致的字符和控制码。(工作屏幕就像磁盘缓存,curses中的大部分的函数都只对它进行修改)
keypad()函数 这个函数允许使用功能键,例如:F1、F2、方向键等功能键。几乎所有的交互式程序都需要使用功能键,因为绝大多数用户界面主要用方向键进行操作。使用keypad(stdscr,TURE)就为“标准屏幕”(stdscr)激活了功能键。
五、实现贪吃蛇代码
#include<stdio.h>
#include<curses.h>
#include<stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct JieGouT
{
int hang;
int lie;
struct JieGouT *next;
};
struct JieGouT *head = NULL;
struct JieGouT *tail = NULL;
int key;
int dir;
struct JieGouT sfood;
void initJieM()
{
initscr();//打开curses模式 进入你的终端
//完成initscr后,输入模式为预处理模式,(1)所有处理是基于行的,就是说,只有按下回车,输入数据才被传给程序;(2)键盘特殊字符启用,按下合适组合键会产生信号
//keypad(WINDOW *, bool);
keypad(stdscr,1);//指定窗口 激活功能键 上下左右 F1 F2等
//Stdscr数据结构对应的是“标准屏幕”,它的工作方式与stdout相似,是curses程序中的默认输出窗口
//Stdscr:指curses窗口,它与显示器的物理屏幕的尺寸完全一样。//标准屏幕
noecho();//关闭回显 让键盘输入字符 不显示在终端上
}
void initFood(){//确定食物的坐标在地图内
int x=rand()%20;//在0-20之间随机出现一个数
int y=rand()%20;
sfood.hang = x;
sfood.lie =y;
}
int bianli(int hang,int lie){//确定蛇的坐标
struct JieGouT *p;
p = head;
while(p !=NULL){
if(p->hang == hang && p->lie == lie){
return 1;
}
p = p->next;
}
return 0;
}
int food(int hang,int lie){//确定食物的坐标
if(sfood.hang == hang && sfood.lie == lie){
return 1;
}
return 0;
}
void map()//打印地图
{
int hang;
int lie;
move(0,0);
for(hang=0;hang<20;hang++){
if(hang==0){
for(lie=0;lie<20;lie++){
printw("--");//在ncurse模式下的printf
}
printw("\n");
}
if(hang>=0 || hang<=19){
for(lie=0;lie<=20;lie++){
if(lie==0 || lie==20){
printw("|");
}
else if(bianli(hang,lie)){
printw("[]");
}
else if(food(hang,lie)){
printw("##");
}
else{
printw(" ");
}
}
printw("\n");
}
if(hang==19){
for(lie=0;lie<20;lie++){
printw("--");
}
printw("\n");
}
}
}
void addNode()//用链表的方式设计蛇的身子增长
{
struct JieGouT *new = (struct JieGouT *)malloc(sizeof(struct JieGouT));
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连接在一起
tail=new;//增加一节长度
}
void initSnake()//初始化蛇
{
struct JieGouT *p;
dir=RIGHT;
while(head != NULL){ //判断是否是第二次游戏
p=head;
head=head->next;
free(p);
/*防止内存泄漏,当第一场游戏结束,第二场游戏开始时,遍历链表,释放内存*/
}
initFood();
head = (struct JieGouT *)malloc(sizeof(struct JieGouT));
head->hang=1;
head->lie=1;
head->next=NULL;
tail=head;
addNode();
addNode();
addNode();
}
void deleNode()//用链表的方式设计蛇的身子减少
{
struct JieGouT *p;
p=head;
head=head->next;
free(p);//释放内存
}
int ifSnakeDie()//蛇碰到边界和自己的身体死亡
{
struct JieGouT *p=NULL;
p=head;
if(tail->hang < 0/*地图是从等于0开始的所以最上面一行要满足小于0才能死掉*/ | tail->lie == 0 | tail->hang == 20 | 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(food(tail->hang,tail->lie)){
initFood();
}else{
deleNode();
}
if(ifSnakeDie()){
initSnake();
}
}
void *refresh1(){
while(1){
moveSnake();
map();
refresh();//每次在屏幕绘制之后,需要调用refresh()刷新屏幕
usleep(100000);//蛇移动的速度
}
}
void *refresh2(){
while(1){
moveSnake();
map();
refresh();
usleep(80000);
}
}
void turn(int dir1){
if(abs(dir) != abs(dir1)){/*就是蛇在右移的过程中不能左移只能上下,反之一样,在上移的时候不能下移只能左右,反之一样*/
//判断此时蛇移动的方向值的绝对值是否等于你按下键的值的绝对值
dir = dir1;
}
}
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()
{
int data1;
pthread_t t1;
pthread_t t2;
pthread_t t3;
printf("plese choose model:\n 1.difficult\n 2.easy\n");
scanf("%d",&data1);
initJieM();
initSnake();
if(data1 == 1){
pthread_create(&t3,NULL,refresh2,NULL);
}
pthread_create(&t1,NULL,refresh1,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();//等待用户输入,如果没有这句话程序就会退出,看不到运行的结果
endwin();//程序退出,调用该函数来恢复shell终端的显示,如果没有这句话,shell终端乱码
return 0;
}