贪吃蛇出笼,请注意安全——可实现游走,吃食物,撞墙和咬自己身子死亡,新增计分功能

#include<curses.h>
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#define UP 1
#define DOWN -1
#define LEFT 3
#define RIGHT -3
void initNcurse()//用Ncurse的初始函数
{
    
    
	initscr();//Ncurse的界面初始化函数
	keypad(stdscr,1);

	noecho();	
}
struct snake
{
    
    
	int hang;
	int lie;
	struct snake *next;
};

struct snake *head=NULL;
struct snake *tail=NULL;

int key;
int dir;
int fenshu;
struct snake food;//全局变量

void initfood()//随机出现食物
{
    
    
	int x=rand()%20;
	int y=rand()%20;
	if(x>=1 && x<= 19 && y<=19 && y>=1)//因为食物位置是个随机数,可能超过边界,所以对食物位置进行限定
	{
    
    
		
	food.hang=x;
	food.lie=y;
	}
	else
	{
    
    
		initfood();
	}
}
int hasNode(int i,int j)
{
    
    
	struct snake* p;
	p=head;

	while(p!=NULL)
	{
    
    
		if(p->hang==i && p->lie==j)//若身子结点=刷新时的结点,则返回1,显示身子
		{
    
    
			return 1;
		}
		p=p->next;

	}
	return 0;



}
int hasfood(int i,int j)
{
    
    
	if(food.hang==i && food.lie==j)//若食物结点=刷新时的结点,则返回1,显示食物
	{
    
    
		return 1;
	}
	return 0;
}
void gamePic()//刷新游戏页面
{
    
    
	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)//0到19行中
		{
    
    
			for(lie=0;lie<=20;lie++)
			{
    
    
				if(lie==0 ||lie==20)//左右两边边缘画"|"
				{
    
    
					printw("|");
				}
				else if(hasNode(hang,lie)==1)//如果刷新到蛇身子节点,输出蛇身子符号
				{
    
    
					printw("[]");
				}
				else if(hasfood(hang ,lie)==1)//如果刷新到食物节点,输出蛇食物符号
				{
    
    
					printw("##");
				}
				else
				{
    
    
					printw("  ");//中间不显示任何符号
				}
			}
			printw("\n");
			
		}
		if(hang==19)//0到19一共20行
		{
    
    
			for(lie=0;lie<20;lie++)//最后输出下面边界
			{
    
    
				printw("--");
			}
			printw("\n");
			printw("food.hang=%d,food.lie=%d\n",food.hang,food.lie);//输出
			printw("Designed by QYY  KEY=%d\n",key);
		}
	}
}
void addn()
{
    
    
	struct snake *new=(struct snake *)malloc(sizeof(struct snake));//定义一个新增的结点new
		
	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=NULL;//定义一个指针
	dir =RIGHT;	//设定一开始蛇的direction为向右
	while(head!=NULL)//第一次肯定为空,不执行这个,第二次就执行,相当于第二次执行的时候头变少一个
	{
    
    
		p=head;
		head=head->next;
		free(p);
	}
	initfood();
	head=(struct snake*)malloc(sizeof(struct snake));//定链表义头指针

		head->hang=2;
        head->lie=1;
        head->next=NULL;//定义链表头的坐标为(2,1)

        tail=head;

        addn();
        addn();
        addn();//刚开始蛇的身子有三个节点
}
void deletenode()
{
    
    
	struct snake* p;
	p=head;
	head=head->next;//头指向下一个就删除了开始的头节点
	free(p);
}

int ifsnakedie()
{
    
    
	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 movsnake()
{
    
    

	addn();//新增一个节点然后删除一个节点就形成了蛇的移动。
	if(hasfood(tail->hang,tail->lie))//如果蛇吃到了食物,就重新增添一个食物
	{
    
    
		initfood();
		fenshu+=5;
	}
	else
	{
    
    
	deletenode();//如果没有吃到食物,就删除最后的节点
	}
	if(ifsnakedie())//如果蛇死了
	{
    
    
		initsnake();//重新开始
		printw("YOUR SCORE:%d\n",fenshu);//打印游戏结束时的分数
		fenshu=0;
	}
}

void* refreshJieMian()
{
    
    
	while(1)
        {
    
    
                movsnake();
                gamePic();
                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();
	gamePic();

	pthread_create(&t1,NULL,refreshJieMian,NULL);//刷新界面
	pthread_create(&t2,NULL,changeDir,NULL);//改变方向//两个线程开辟这两个函数的通道,同时运行
	while(1);
	getch();//等待用户输入,如果没有的话,程序就自动退出了,看不到结果
	endwin();//程序退出
	return 0;
}

里面有详细的注释,可以每天看一看,然后自己打出来。
下面为程序运行的一个小视频

贪吃蛇大功告成

猜你喜欢

转载自blog.csdn.net/qq_43482790/article/details/115002891