linux下通过C语言实现贪吃蛇

如何在linux下通过C语言实现贪吃蛇

上图是我们最后的成果,可以通过上下左右键控制蛇的移动,并且蛇吃到食物后会变长,然后食物会随机刷新,如果撞墙和撞到蛇身都会重置游戏.

1 ncurse:

为什么开始要提到这个库嘞,我们想,游戏打开后我们需要键入上下左右键来改变方向,我们立马就会想到stdio库里面的getchar(),scanf(),gets(),但是我们知道这3个函数键入值后需要按下回车键才能继续运行程序,那么可想而知,如果我们在游戏中,按下一个方向键,还需要按个回车键才能改变方向,那么肯定达不到需求;

以下就是引用ncurse库的函数

#include <curses.h>

int main()

{

       initscr();//ncurse界面初始化函数

       printw("This is a curses window.\n");//ncurse模式下的print

       getch();//等待用户输入,如果没这句话程序就退出了,看不到运行结果,即看不到上面那句话,同时用户输入值后会直接响应,不需要再按回车键了;

       endwin();//程序退出,调用改函数来恢复shell终端显示,如果没这句话,shell终端字乱码,坏掉

       return 0;

 }

2 ncurse上下左右键获取

实现我们需要知到键盘中上下左右键对应的值,这里可以进入curses.h来查看,输入vi /usr/include/curses.h 即可进入
#define  KEY_DOWN == 0402;//8进制

#define  KEY_UP    == 0403;
#define  KEY_LEFT  == 0404;
#define  KEY_RIGHT == 0405;

现在知道键盘中上下左右键对应的值了,但是上下左右键属于快捷键,需要使用函数keypad,因为函数keypad设置了可以中stdscr中接受键盘的功能键(快捷键)

代码如下

#include <curses.h>

int main()

{

       int key;

       initscr();//ncurse界面初始化函数

       keypad(stdscr,1);//从标准的stdscr中接收功能键,1表示接收

       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;

              }

       }

       endwin();//程序退出

       return 0;

 }

3 地图的规划

大小20x20
竖直方向上的边界用 “|”
水平边界用 “--”
第0行和第19行既有横边界也有竖边界,1-18行只有竖边界

如下是封装,的贪吃蛇图像函数,也可以说是图像扫描函数,图像构建函数

void gamePic()//图像封装,图像扫描

{

       int hang;

       int lie;

       for(hang=0;hang<20;hang++)//一共20行 0-19

       {

              if(hang==0)//先判断是不是第0行

              {

                     for(lie=0;lie<20;lie++)

                            printw("--");

                     printw("\n");  //记得换行

              }

              if(hang>=0||hang<=19)//第0行的|也要打

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   printw("  ");

                     }

                     printw("\n");//记得换行

              }

              if(hang==19)//判断是不是第19行,第19行的--也要打

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");//记得换行

              }

       }

}

4 贪吃蛇的身体及移动

贪吃蛇身子节点(1行坐标,2列坐标,3下一个节点的位置)

A蛇身初步显示

#include<curses.h>

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake node1={2,2,NULL};

struct Snake node1={2,3,NULL};

struct Snake node1={2,4,NULL};

struct Snake node1={2,5,NULL};

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=&node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

}

void gamePic()//图像封装,图像扫描

{

       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(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");

              }

       }

}

int main()

{

       initNcurses();// Ncurses初始化

       node1.next=&node2;

       node2.next=&node3;

       node3.next=&node4;

       gamePic();//图像封装,图像扫描

       getch();

       endwin();

       return 0;

}结果如下

这样子虽然可以显示蛇身,但是需要节点一个一个手动创建和链接,好像有点麻烦,我们想一想一条贪吃蛇,好像是不是就是头(head)和尾(tail)和身子组成,显然我们把头和尾定义一下,身子就好操作了,

下面我们用链表动态添加蛇的身子

#include<curses.h>

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head;//头

struct Snake *tail;//尾

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=head;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

}

void gamePic()//图像封装,图像扫描

{

       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(printwSnakeNode(hang,lie))//是蛇身就打印[]

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\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));

       head->hang=2;

       head->lie =2;

       head->next=NULL;

       tail=head;//第一个蛇的节点,头也是尾

       addNode();//增加节点

       addNode();

       addNode();

       addNode();

}

int main()

{

       initNcurses();// Ncurses初始化

       initSnake();//初始化蛇身

       gamePic();//图像封装,图像扫描

       getch();

       endwin();

       return 0;

}

运行结果同上

B 贪吃蛇向右移动

我们初始化的蛇是这样子的(head)[][][](tail),向右移动一次不就是把头变成中间的蛇身,然后向右添加一个节点变成新的蛇尾吗,然后把之前的头释放掉,ok开始操作;

#include<curses.h>

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head;

struct Snake *tail;

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

}

void gamePic()//图像封装,图像扫描

{

       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(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\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));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

addNode();

addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//右移

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

}

int main()

{

       int con;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       while(1)

              {

                     con=getch();//获得键入

                     if(con==KEY_RIGHT)//如果是右方向键

                            moveSnake();//向右移动一个单位

              }

       getch();

       endwin();

       return 0;

}

上述代码的运行结果是,按下右方向键,图像没有任何变化,这里其实可以想到,链表已经是发生变化的了,但是图像没有刷新,即没有重新扫描图像,没有重新构建图像,所有我们稍微修改主函数;

新的主函数如下

int main()

{

       int con;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       while(1)

              {

                     con=getch();//获得键入

                     if(con==KEY_RIGHT)//如果是右方向键

                            moveSnake();//向右移动一个单位

                            gamePic()//重新扫描图像,来得到新的图像

              }

       getch();

       endwin();

       return 0;

}

运行结果如下

这两张別看错了,他们是在一个实验结果里面,一个在上面,一个在下面,右移一位的新图像并没有覆盖原图像,而是在原图像下面重新构建了一个图像所以他们是在一个实验结果里面,这里有出现问题了,我们需要的肯定是覆盖原图像啊,不然肯定无法顺利运行的;这里其实就是Ncurse只带的一个光标,我们移动后的新图像会在光标所在位置开始构建,所以会在原图像正下方开始构建新图像了,所以我们只需要在每次扫描图像,构建图像的时候,把光标移动到起始位置就行了,即修改扫描图像函数

新的扫描函数图像如下

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");

              }

       }

}

修改后的代码运行结果就正常了

C 贪吃蛇撞墙重置

我们仍然根据先前的代码来构造,我们先前是向右移动,这里我们肯定就是先撞右墙,而撞墙蛇肯定是要动的我们就可以在moveSnake()中加入一个判断,看蛇是否撞墙了,开始操作,新的函数代码如下

#include<curses.h>

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head;

struct Snake *tail;

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\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()

{

       struct Snake *temp;

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

addNode();

addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//右移

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

int main()

{

       int con;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       while(1)

              {

                     con=getch();//获得键入

                     if(con==KEY_RIGHT)//如果是右方向键

                            moveSnake();//向右移动一个单位

                            gamePic()//重新扫描图像,来得到新的图像

              }

       getch();

       endwin();

       return 0;

}

运行结果如下两图

按下一次右移键后,覆盖后的新图如下

撞墙也成功重置

D贪吃蛇的自动游走

我们想一想,先前我们只需要按一下向右键既可以向右移动一个单位,我们不妨把while循环中的getcch();输出,那么程序就不用等我们键入了,会一直向右移动,所有我们只需要稍微修改主函数就行了;

新的主函数如下

int main()

{

       int con;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       while(1)

              {

                            moveSnake();//向右移动一个单位

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

       getch();

       endwin();

       return 0;

}

修改后的运行结果就是贪吃蛇自动向右移动,然后重置,如此循环,但很显然,我们最后还是需要键入方向键,同时贪吃蛇也在自动移动,这样一想,贪吃蛇自动移动是一个while循环,而游戏也要让我们可以随时输入一个方向键来改变方向,那不也是一个循环吗,两个循环,这好像就有点烧脑了,最开始我们就写了ncurse上下左右键获取,这里想当然的话我们把这个函数加入主函数试一下,

新的主函数如下

int main()

{

       int con;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       while(1)

              {

                            moveSnake();//向右移动一个单位

                            gamePic()//重新扫描图像,来得到新的图像

                            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;

}

观察上述函数,很显然,程序会一直继续第一个while循环,第二个while循环根本没用;

我们可以再写一个函数,让这个问题简单化一点

新函数如下

#include<stdio.h>

void func1()

{

       while(1)

       {

              printf("this is func1\n");

              sleep(1);

       }

}

void func2()

{

       while(1)

       {

              printf("this is func2\n");

              sleep(1);

       }

}

int main()

{

       func1();

       func2();

       return 0;

}

上述程序运行结果是每隔一秒不断打印this is func1,很显然,程序在func1函数里面就出来了;

所以这里我们就要引入linux线程的概念了如下图

通过上述图片,大致了解,我们需要通过某个新函数来创建一个新的进程,让这个新进程来运行第二个while循环,且两个while循环互不影响;

我们找到的有关代码如下

#include <stdio.h>

#include <pthread.h>

void* thread( void *arg )//线程需要运行的函数thread

{

    printf( "This is a thread and arg = %d.\n", *(int*)arg);

    *(int*)arg = 0;

    return arg;

}

int main( int argc, char *argv[] )

{

    pthread_t th;//创建一个线程

    int ret;

    int arg = 10;

    int *thread_ret = NULL;

    ret = pthread_create( &th, NULL, thread, &arg );//线程的地址,NULL暂时不用,thread就是线程需要运行的函数,&arg暂时也不用

    if( ret != 0 ){

        printf( "Create thread error!\n");

        return -1;

    }

    printf( "This is the main process.\n" );

    pthread_join( th, (void**)&thread_ret );

    printf( "thread_ret = %d.\n", *thread_ret );

    return 0;

}

现在我们来通过观察线程代码来修改之前的func函数代码;

新的func代码如下

#include<stdio.h>

#include<pthread.h>

void* func1()

{

       while(1)

       {

              printf("this is func1\n");

              sleep(1);

       }

}

void* func2()

{

       while(1)

       {

              printf("this is func2\n");

              sleep(1);

       }

}

int main()

{

       pthread_t th1;//定义一个线程提示符

       pthread_t th2;//

       pthread_create( &th1, NULL, func1, NULL );//给线程提示符的地址,func1就是线程需要执行的函数,剩下两个暂时不管func1函数是void*型

       pthread_create( &th2, NULL, func2, NULL );

       while(1);//主线程,防止程序结束

       return 0;

}

运行结果就是this is func1和this is func2随机循环输出,现在我们有基本概念去处理贪吃蛇的自动移动和随时等待键入的问题了

E 贪吃蛇的四个方向的自动游走

刚才我们运用了线程成功处理两个while循环,而贪吃蛇中的两个while循环不就是自动移动后的新界面吗(刷新界面)和随时等待用户键入方向键吗;

故新代码如下

#include<curses.h>

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head;

struct Snake *tail;

int key;//全局变量,键入值

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");

                     printw(“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 *temp;

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

addNode();

addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//右移

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

void refreshScreen()//循环1 刷新界面

{

       while(1)

              {

                            moveSnake();//向右移动一个单位

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

}

void changeDir()//循环2 随时等待用户输入方向键

{

       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()

{

       pthread_t t1;//定义进程提示符

pthread_t t2;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       pthread_create=(&t1,NULL,refreshScreen,NULL);

pthread_create=(&t2,NULL,changeDir ,NULL);

       while(1);

       getch();

       endwin();

       return 0;

}

运行结果如下

 

 

上图是截取的4张图片,很显然不仅键入的方向键有效,贪吃蛇也在自动运动,现在就需要继续修改代码来实现,键入方向键后贪吃蛇实际的方向改变

修改代码如下

#include<curses.h>

#define UP    1;//宏定义上下左右

#define DOWN  2;

#define LEFT  3;

#define RIGHT 4;

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head=NULL;

struct Snake *tail=NULL;

int key;//全局变量,键入值

int dir;//全局变量,方向

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

noecho();

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");

                     printw("key:%d\n",key);//判断键入方向键是否有效

              }

       }

}

void addNode()

{

       struct Snake *new=(struct Snake*)malloc(sizeof(struct Snake));

       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 *temp;

       dir=RIGHT;//初始方向向右

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

       addNode();

       addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//移动蛇

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

void refreshScreen()//循环1 刷新界面

{

       while(1)

              {

                            moveSnake();//移动蛇

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

}

void changeDir()//循环2 随时等待用户输入方向键

{

       while(1)

       {

              key = getch();//等待键入

              switch(key)//键入的方向值,来改变dir的值,以改变蛇的自动运动方向

              {

                     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;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       pthread_create=(&t1,NULL,refreshScreen,NULL);//刷新图像,即移动蛇

       pthread_create=(&t2,NULL,changeDir ,NULL);//随时等待用户输入,即改变dir的值

       while(1);

       getch();

       endwin();

       return 0;

}

运行结果是,蛇在自动移动的同时键入方向键也可以正确的改变蛇的方向,但是有一个缺点,贪吃蛇可以直接从向左变成向右或者直接从向上变成向下;这不就像头尾互换了吗,显然不是我们要的结果,这里我们就直接引入一个绝对值方法来解决,abs(a),返回a的绝对值

修改后的代码如下

#include<curses.h>

#define UP     1;

#define DOWN  -1;

#define LEFT   2;

#define RIGHT -2;

struct Snake//定义蛇身节点

{

       int hang;

       int lie;

       struct Snake* next;

};

struct Snake *head=NULL;

struct Snake *tail=NULL;

int key;//全局变量,键入值

int dir;

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

       noecho();//删除图像上的无关信息

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(hang,lie))

                                          printw("[]");

                                   else

                                          printw("  ");

                     }

                     printw("\n");

              }

              if(hang==19)

              {

                     for(lie=0;lie<=20;lie++)

                            printw("--");

                     printw("\n");

                     printw("key:%d\n",key);//判断键入方向键是否有效

              }

       }

}

void addNode()

{

       struct Snake *new=(struct Snake*)malloc(sizeof(struct Snake));

       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 *temp;

       dir=RIGHT;

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

       addNode();

       addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//移动蛇

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

void refreshScreen()//循环1 刷新界面

{

       while(1)

              {

                            moveSnake();//移动蛇

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

}

void turn(int direction)//绝对值判断方向是否有效

{

       if(abs(dir)!=abs(direction))//只有水平和竖直方向可以互相影响来改变贪吃蛇的方向

       {

              dir=direction;

       }

}

void changeDir()//循环2 随时等待用户输入方向键

{

       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;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       pthread_create=(&t1,NULL,refreshScreen,NULL);

       pthread_create=(&t2,NULL,changeDir ,NULL);

       while(1);

       getch();

       endwin();

       return 0;

}

上述代码运行结果,实现蛇的自由运动,也防止了直接左变右,上变下等操作;

5 贪吃蛇吃食物

同样食物也可以用结构体来表示,同样也像贪吃蛇一样.食物初始化,

修改代码如下

#include<curses.h>

#define UP     1;

#define DOWN  -1;

#define LEFT   2;

#define RIGHT -2;

struct 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()//食物初始化

{

       static int x=4;//静态变量,每次重新运行这个函数,x和y的值不会重新定义

       static int y=5;

       food.hang=x;

       food.lie=y;

       x+=1;//自加

       y+=1;

}

int hasFood(int i,int j)//判断是不是食物

{

              if(food.hang==i&& food.lie==j)

                     return 1;//是食物

       return 0;//不是食物

}

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

       noecho();//删除图像上的无关信息

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(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("key:%d\n",key);//判断键入方向键是否有效

              }

       }

}

void addNode()

{

       struct Snake *new=(struct Snake*)malloc(sizeof(struct Snake));

       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 *temp;

       dir=RIGHT;

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       initFood();//贪吃蛇初始化的时候把食物也初始化了

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

       addNode();

       addNode();

}

void deleteNode()//删除节点

{

       struct Snake *p;

       p=head;

       head=head->next;//新的头结点

       free(p);//释放原来的头节点

}

void moveSnake()//移动蛇

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

void refreshScreen()//循环1 刷新界面

{

       while(1)

              {

                            moveSnake();//移动蛇

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

}

void turn(int direction)//绝对值判断方向是否有效

{

       if(abs(dir)!=abs(direction))//只有水平和竖直方向可以互相影响来改变贪吃蛇的方向

       {

              dir=direction;

       }

}

void changeDir()//循环2 随时等待用户输入方向键

{

       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;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       pthread_create=(&t1,NULL,refreshScreen,NULL);

       pthread_create=(&t2,NULL,changeDir ,NULL);

       while(1);

       getch();

       endwin();

       return 0;

}

运行结果,成功显示食物,但是无法吃掉食物,这里就很容易了,每一次肯定是尾巴碰着食物,所以在moveSnake函数里面判断如果吃到食物就不用删除节点了

void moveSnake()//移动蛇

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

if(hasFood(tail->hang,tail->lie))

       initFood();

else

       deleteNode();//构建一个删除节点函数

       if(tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

运行结果,吃食物后,蛇会变长,同时按照行加一,列加一规律性刷新食物,但是我们肯定不想这样子,因为食物会刷出界限,同时这样刷新就没有可玩性了,所以我们直接引入随机刷新函数rand(),返回一个随机值,所以我们只需要稍微修改一下initFood()函数即可;

新的initFood()函数如下

void initFood()//食物初始化

{

       int x=rand()%20;//随机值对20取余,因为图像界面行和列都是0-19

       int y=rand()%20;

       food.hang=x;

       food.lie=y;

}

修改后运行程序,吃掉食物后,食物会随机刷新,基本成功,但是发现贪吃蛇无法走到第0行,需要稍加修改,让尾部的行等于0的时候不让贪吃蛇重置;

修改后的moveSnake()函数如下

void moveSnake()//移动蛇

{

       addNode();//这个函数我们定义的时候刚好就是右加一个节点

if(hasFood(tail->hang,tail->lie))

       initFood();

else

       deleteNode();//构建一个删除节点函数

       if(tail->hang<0||tail->lie==0||tail->hang==20||tail->lie==20)//判断是否撞墙

              initSnake();//贪吃蛇初始化

}

修改后运行代码即可走到第0行;

最后一步,如果贪吃蛇咬到自己,则直接重置;

最后代码如下

#include<curses.h>

#define UP     1;

#define DOWN  -1;

#define LEFT   2;

#define RIGHT -2;

struct 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()//食物初始化

{

       static int x=4;//静态变量,每次重新运行这个函数,x和y的值不会重新定义

       static int y=5;

       food.hang=x;

       food.lie=y;

       x+=1;//自加

       y+=1;

}

int hasFood(int i,int j)//判断是不是食物

{

              if(food.hang==i&& food.lie==j)

                     return 1;//是食物

       return 0;//不是食物

}

int printwSnakeNode(int i,int j)//判断是不是蛇身

{

       struct Snake *p;

       p=node1;

       while(p!=NULL)//蛇的每个节点都要判断

       {

              if(p->hang==i&&p->lie==j)

                     return 1;//是蛇身

              p=p->next;

       }

       return 0;//不是蛇身

}

int initNcurses()//Ncurses初始化

{

       initscr();

       keypad(stdscr,1);

       noecho();//删除图像上的无关信息

}

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)

              {

                     for(lie=0;lie<=20;lie++)

                     {

                            if(lie==0||lie==20)

                                   printw("|");

                            else

                                   if(printwSnakeNode(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("key:%d\n",key);//判断键入方向键是否有效

              }

       }

}

void addNode()

{

       struct Snake *new=(struct Snake*)malloc(sizeof(struct Snake));

       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 *temp;

       dir=RIGHT;

       while(head)

       {

              temp=head;

              head=temp->next;

              free(temp);

       }//目的是为了释放原来的贪吃蛇

       initFood();//贪吃蛇初始化的时候把食物也初始化了

       head=(struct Snake*)malloc(sizeof(struct Snake));

       head->hang=3;

       head->lie =3;

       head->next=NULL;

       tail=head;

       addNode();

       addNode();

       addNode();

       addNode();

}

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)//注意用p->next判断,因为tail不用和tail自己比较

       {

              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));//判断是否吃了食物

              initFood;

       else

              deleteNode();//构建一个删除节点函数

       if(ifSnakeDie())//判断是否死亡

              initSnake();//贪吃蛇初始化

}

void refreshScreen()//循环1 刷新界面

{

       while(1)

              {

                            moveSnake();//移动蛇

                            gamePic()//重新扫描图像,来得到新的图像

                            refresh();//刷新图像的函数

                            usleep(100000);//延时微妙单位

              }

}

void turn(int direction)//绝对值判断方向是否有效

{

       if(abs(dir)!=abs(direction))//只有水平和竖直方向可以互相影响来改变贪吃蛇的方向

       {

              dir=direction;

       }

}

void changeDir()//循环2 随时等待用户输入方向键

{

       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;

       initNcurses();// Ncurses初始化

       initSnake();//创建贪吃蛇

       gamePic();//图像封装,图像扫描

       pthread_create=(&t1,NULL,refreshScreen,NULL);

       pthread_create=(&t2,NULL,changeDir ,NULL);

       while(1);

       getch();

       endwin();

       return 0;

}

以上就是全部贪吃蛇项目了,用了Ncurse库,编译时就得加-lcurses,用了pthread库,编译时就得加-lpthread,仅作参考,如有错误,欢迎指出

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值