#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;
}
里面有详细的注释,可以每天看一看,然后自己打出来。
下面为程序运行的一个小视频
贪吃蛇大功告成