Linux系统下用ncurses实现贪吃蛇游戏

运用ncurses里的getc()函数接收输入的方向键
运用Linux里的双线程实现同时执行多个循环

#include<curses.h>//调用curses库
#include<stdlib.h>
#include<pthread.h>//由于需要多线程,固需要调用此库来实现多线程
#define up    1		//定义方向键,并赋值用来实现蛇身不能在向右移动的过程突然向左移动之类场景得出现
#define down  -1
#define right 2
#define left  -2

void initncurses(){
	 initscr();
      	 keypad(stdscr,1);
	 noecho();//调用curses时一些必不可少的初始化函数

}//对需要用到curses里的东西做初始化,即调用此函数必不可少的语句
struct snake
{
	int hang;
	int lie;
	struct snake *next;
};//定义结构体构建蛇身节点
int key;//定义key 用来存放用户输入的方向键的数码
int dir;//定义dir变量 表示方向
struct snake *head=NULL;
struct snake *tail=NULL;
struct snake food;
void initfood()//在地图随机位置出现食物函数
{
	 int x=rand()%20;
	 int y=rand()%20;//运用rand()函数,%求余运算可将结果的范围限制在0到n之内,这里的n=20
	food.hang=x;
	food.lie=y;
}
int havesnakenode(int i,int j)//在遍历地图时判断蛇身节点在地图的位置
{
	struct snake *p;
	p=head;
	while(p!=NULL){
		if(p->hang==i&&p->lie==j){
			return 1;//如果在地图的i行,j列出现蛇身节点,则返回1供函数被调用时做判断
		}
		p=p->next;
	}
	return 0;

}

int havefood(int i,int j)//判断食物是否被蛇吃掉,这里传的参数是蛇身的尾节点对应的行与列
{
	if((food.hang==i)&&(food.lie==j)){
		return 1;
	}else{
	return 0;
	}

}
void gamemap()//打印地图,即蛇和食物
{	
	int hang;
	int lie;
	move(0,0);//curses函数里的内置函数,让每一次打印地图时,不是重新再打印一张而是在视觉上显示的同一张地图上的运动
	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((havesnakenode(hang,lie))){
					printw("[]");//扫描地图的行列时若发现此行列位置有蛇身节点,就打印[]当做蛇身
				}
				else if(havefood(hang,lie)){
					printw("##");	//扫描地图的行列时,若发现此位置有食物节点,打印##当做食物
				}
				else{
					printw("  ");//若既没有蛇身也没有食物打印空格
				}

			}
			printw("\n");
		}
		if(hang==19){
			for(lie=0;lie<20;lie++){
					printw("--");//在地图低端(即最后一行打印--)
				}
			printw("\n");
			printw("By zhang ");
		}
		
	}
	
}
void addnode()//在尾部添加一个蛇身节点
{
	struct snake *new=(struct snake*)malloc(sizeof(struct snake));//用malloc函数定义一个指针变量new
	new->next=NULL;
	switch(dir){//判断方向dir
		case up:
			new->hang=tail->hang-1;
		        new->lie=tail->lie;//按上键时,相当于新节点的行-1,列不变
			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继续定义为指向节点尾部的指针
	tail=new;

}
void deletenode()//在头部删除一个节点
{	struct snake *p;
	p=head;
	head=head->next;将头节点指向头节点的下一个,即删除了原来的头节点
	free(p);//将原来存放头节点的内存free掉来优化内存
}
void initsnake()//蛇的初始化函数,即蛇首次出现的位置和长度,食物首次出现的位置
{	initfood();//调用此函数初始化一个食物
	dir=right;//这里定义刚开始蛇向右移动
	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;//初始化位置为(1,1)
	tail=head;//因为只建立了一个蛇身节点,头尾相等
	addnode();
	addnode();//调用此函数在原来的基础上再往尾部添加两个节点,即初始化后蛇身长度为3
}
int ifdie()//判断蛇是否死亡
{
	struct snake *p;
	p=head;
	if(tail->hang<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(havefood(tail->hang,tail->lie)){//如果蛇的尾部节点的位置等于食物的位置时代表蛇吃掉了食物
		initfood();//那么重新初始化食物
	}else{
		deletenode();//没吃掉食物,就实现食物就删掉头节点来实现蛇的正常移动
	}//如果吃掉食物 那么就不要删掉头节点了表示蛇身的增长
	if(ifdie()){
			initsnake();
		}//如果蛇死亡,则初始化蛇身
}
void * refreshmap()//刷新地图
{
    while(1){
      	 movesnake();//不断移动
         gamemap();//不断显示地图
         refresh();//ncureses机制,如果没有这条语句出现乱码
         usleep(100000);//可以通过刷新时间来控制蛇的速度

 	}
}
void turn(int direction)//转弯函数 为了避免出现突然拐弯的情况 ,传递的参数是(up|down|left|right)
{
	if(abs(dir)!=abs(direction)){
		
		dir = direction;//因为前面定义的up down left right 不能让蛇突然转弯
	}

}
void *getdirection()//通过ncurses里的getch()函数捕获用户输入
{
	while(1){
		key=getch();
             	switch(key){

                        case 0402://如果用户按的是向下 那么将down传到turn()函数里,并通过turn函数判断dic是否要等于down,一下caes语句类似这种效果
				turn(down);
                                printw("DOWN\n");break;
                        case 0403:
				turn(up);
                                printw("UP\n");break;
                        case 0404:
				turn(left);
                                printw("LEFT\n");break;
                        case 0405:
				turn(right);
                                printw("RIGHT\n");break;
                }
        }

}
int  main()
{	int con;
	pthread_t th1;//定义线程 th1
	pthread_t th2;//定义线程 th2
	initncurses();//初始化ncurses
	initsnake();//初始化蛇
        gamemap();//初始显示地图
	pthread_create(&th1,NULL,refreshmap,NULL);//通过多线程执行refreshmap()函数不断刷新游戏
	pthread_create(&th2,NULL,getdirection,NULL);//通过多线程执行getdirection()函数不断捕获用户输入
	while(1);//主函数不断循环
	getch();
	endwin();
	

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值