基于ncurse库的贪吃蛇小游戏

目录

ncurse库的基本使用                                         

ncurse获取上下左右s键值  

地图规划

使用结构体数据显示贪吃蛇身

显示贪吃蛇多个节点

贪吃蛇身子使用链表动态添加

贪吃蛇移动

获取移动的方向和刷新界面

使用绝对值解决走位不合理

贪吃蛇食物

 撞墙死亡和撞自己死亡


ncurse库的基本使用                                         

为啥要用ncurse库?

      对于贪吃蛇我们需要用到方向键控制方向;在按下方向键就需要立马响应的话,需要无缓冲输入。而nucrse库中的函数getch()函数可以满足需求。

ncurse库的基本使用

#include <curses.h>

int main ()
{
    initscr();//ncurse界面初始化函数
    printw("this is a ncurses window\n");//在ncurses模式下的printf
    getch();//等待用户输入,如果没有这句话,程序就退出了
    endwin();//程序退出,该函数来恢复shell终端的显示,如果没有这句话,终端会出现乱>码




    return 0;
}

ncurse获取上下左右键值  

ncurse库安装的指令:sudo apt-get install libncurses5-dev(编译程序时要链接该库 -lcurses)

 1.路径切换到 /usr/include/ncursesw/找到curses.h头文件,vi进入编辑该头文件。

2.输入/KEY_LEFT就可以查找到

#include <curses.h>




int main ()
{
    initscr();//ncurse界面初始化函数
    keypad(stdscr,1);//在stdscr中接受键盘的功能键

    int dir;

    while(1)
    {
            printw("plese input dir:\n");
            dir = getch();

            switch(dir)
            {
                    case KEY_UP:

                            printw("up\n");
                            break;
                    case KEY_DOWN:

                            printw("down\n");
                            break;
                    case KEY_LEFT:

                            printw("left\n");
                            break;
                    case KEY_RIGHT:

                            printw("right\n");
                            break;

            }



}









    getch();//等待用户输入,如果没有这句话,程序就退出了
    endwin();//程序退出,该函数来恢复shell终端的显示,如果没有这句话,终端会出现乱码




    return 0;
}

地图规划

地图大小:20×20

地图竖直方向上的边界"|"

地图水平方向上的边界"--"

贪吃蛇的身子"[ ]"

贪吃蛇的食物"##"

使用for嵌套循环

地图的实现:

地图是一个20乘20的方格通过for嵌套循环打印,外层循环控制行,当循环到第一行或者第二十行时,打印20列 "--"符号;在第二行至第十九行打印时,如果是第一列或者是第二十列,则打印"|";否则打印"  "符号。(打印完每一行后都需要打印一个换行符

void initpicture()
{

      int row;
      int column;

      for(row = 0;row <= 20; row++)
      {
             if(row == 0 || row == 20)
             {
                     for(column = 0;column < 20;column++)
                     {
                          printw("--");
                     }
                     printw("\n");
             }
             else
             {
                     for(column = 0;column <= 20;column++)
                     {
                           if(column == 0 || column == 20)
                           {
                                printw("|");
                           }
                          else
                           {
                                printw("  ");
                           }
                     }

                     printw("\n");
             }

      }



}

使用结构体数据显示贪吃蛇身

贪吃蛇身子节点:

1. 行坐标

2. 列坐标

3. 下一个节点的位置(地址/指针)

struct Snake

{

           int row; //行坐标

           int column;   //列坐标

           struct Snake *next;

}

2.在地图打印空格之前判断蛇节点的row和column是否等于当前的row和column  , 如果是 就 打印"[ ]"作为蛇身的节点。

假设蛇身的节点是:行坐标2,列坐标2

if(row == x.row && column == x.lie)

{

            printw("[ ]");

}

显示贪吃蛇多个节点

1.先手动静态创建几个节点,并且使用链表将节点串起来,在打印空格之前判断是不方便的,不能把所有的节点都一一进行判断。

可以封装一个hasSnakeBody( )函数进行判断,然后在这个函数中进行蛇身链表的遍历判断。如果找到相同的节点,函数返回1;否则函数返回0。

int hasSnakeBody(int i,int j)
{
    Node *p = &node1;

    while(p)
   {
       if(p->row == i && p->column == j)
       {
             return 1;
       }
       p = p->next;
   }



    return 0;

}

贪吃蛇身子使用链表动态添加

    蛇身的链表创建是静态创建的,如果要增加节点也是非常不方便,需要手动添加。现在使用链表动态添加,定义两个全局变量;一个是头节点head,一个是尾节点tail;刚刚开始时head等于tail。使用尾插法创建新的节点。

void initSnake()
{
    head = (Node*)malloc(sizeof(Node));
    head->row = 2;
    head->column = 2;
 //   head->next = NULL;

    tail=head;
    addNode();
    addNode();
    addNode();

}

void addNode()
{
    Node *new = (Node*)malloc(sizeof(Node));
    new->next = NULL;
    new->row = tail->row+1;
    new->column = tail->column;

    tail->next = new;
    tail = new;



}

贪吃蛇移动

 对蛇身链表进行改动  删除一个头节点,增加一个尾节点。

然后再次打印地图就会形成移动的效果

void deleteNode()
{

    Node *p = head;
    head = head->next;

    free(p);


}

void moveSnake()
{

    addNode();
    deleteNode();


}

要在打印地图函数里面加上光标定位函数move(0,0),这样打印就会显示在同一位置。

获取移动的方向和刷新界面

     需要创建两个线程,一个线程刷新界面;另一个线程获取方向键,而方向键的改变可以影响addNode()函数新增节点的里面的数据的改变,这样就能形成移动的效果。

刷新界面线程:

void* refreshPic()
{
   while(1)
   {

           moveSnake();
           initpicture();
           usleep(100000);
           refresh();

   }

}

获取方向线程:

void* changeDir()
{
    for(;;)
    {
         dir = getch();

    }


}

修改addNode( )函数,根据方向键的值来判断新增节点的值

void addNode()
{
    Node *new = (Node*)malloc(sizeof(Node));
    new->next = NULL;
    switch(dir)
    {
          case KEY_RIGHT:
                          new->row = tail->row;
                          new->column = tail->column+1;
                          break;
          case KEY_LEFT:
                          new->row = tail->row;
                          new->column = tail->column-1;
                          break;
          case KEY_DOWN:
                          new->row = tail->row+1;
                          new->column = tail->column;
                          break;
          case KEY_UP:
                          new->row = tail->row-1;
                          new->column = tail->column;
                          break;
    }

    tail->next = new;
    tail = new;



}

使用绝对值解决走位不合理

定义4个方向宏:

 将键盘获取的键值转换成相应的宏,通过判断上一次方向的绝对值与这一次方向绝对值是否相等;如果不相等,就改变方向的值。

void turn(int direction)
{
     if(abs(dir) != abs(direction))
     {
           dir = direction;
     }

}

之前是直接通过键值判断的,所以addNode( )和changdir( )函数都需要修改一下:

void addNode()
{
    Node *new = (Node*)malloc(sizeof(Node));
    new->next = NULL;
    switch(dir)
    {
          case right:
                          new->row = tail->row;
                          new->column = tail->column+1;
                          break;
          case left:
                          new->row = tail->row;
                          new->column = tail->column-1;
                          break;
          case down:
                          new->row = tail->row+1;
                          new->column = tail->column;
                          break;
          case up:
                          new->row = tail->row-1;
                          new->column = tail->column;
                          break;
    }

    tail->next = new;
    tail = new;



}
void* changeDir()
{
    int key;
    for(;;)
    {
        key = getch();
        switch(key)
        {
             case KEY_RIGHT:
                            turn(right);
                            break;
             case KEY_LEFT:
                            turn(left);
                            break;
             case KEY_DOWN:
                            turn(down);
                            break;
             case KEY_UP:
                            turn(up);
                            break;

        }


    }


}

贪吃蛇食物

食物同样使用结构体来存储,打印地图时判断行和列是否和食物的数据相等,如果相等则显示食物为“##”。

void initFood()
{
     food.row = rand()%20;
     food.column = rand()%20;

}

判断尾节点是数据是否跟食物的数据是否重合,如果重合,重新生成一个食物;如果不重合,就删除一个头节点。

 撞墙挂掉和撞自己挂掉

    挂掉就相当于在打印地图之前,删除原来的链表数据;然后重新调用下initSnake( )函数重新初始化链表。

void deleteSnake()
{
     Node *p;
     while(head)
     {
         p = head;
         head = head->next;
         free(p);
     }


     return 0;
}

撞墙就判断尾节点否和墙重合:

void hitWall()
{
    if(tail->row == 0 || tail->row == 20 || tail->column == 0 || tail->column == 20)
    {
           deleteSnake();
           initSnake();
    }
}

撞自己就判断尾节点是否和链表本身进行重合:

void hitSelf()
{
    Node *p = head;

    while(p->next != NULL)
    {
          if(tail->row == p->row && tail->column == p->column)
          {
                  deleteSnake();
                  initSnake();

          }
          p = p->next;
    }
}

全部代码

#include <curses.h>
#include <stdlib.h>
#include<pthread.h>
#include<unistd.h>
#define right 1
#define left -1
#define down  4
#define up   -4

typedef struct node{
    int row;
    int column;
    struct node *next;

}Node;

void initcurses();
void initpicture();
int hasSnakeBody(int i,int j);
void initSnake();
void addNode();
void deleteNode();
void moveSnake();
void* refreshPic();
void* changeDir();
void turn(int direction);
void initFood();
void deleteSnake();
void hitWall();
void hitSelf();

Node *head;
Node *tail;
Node food;
int dir;



int main ()
{
    int dir;
    pthread_t t1;
    pthread_t t2;

    initcurses();
    initSnake();
    initpicture(); 
      
    pthread_create(&t1,NULL,refreshPic,NULL);
    pthread_create(&t2,NULL,changeDir,NULL);



    while(1);
    getch();//等待用户输入,如果没有这句话,程序就退出了
    endwin();//程序退出,该函数来恢复shell终端的显示,如果没有这句话,终端会出现乱码


    return 0;
}

void initcurses()
{
    initscr();//ncurse界面初始化函数
    keypad(stdscr,1);//在stdscr中接受键盘的功能键
}

void initpicture()
{

      int row;
      int column;
      move(0,0);

      for(row = 0;row <= 20; row++)
      {
             if(row == 0 || row == 20)
             {
                     for(column = 0;column < 20;column++)
                     {
                          printw("--");
                     }
                     printw("\n");
             }
             else
             {
                     for(column = 0;column <= 20;column++)
                     {
                           if(column == 0 || column == 20)
                           {
 				printw("|");
                           }
                          else if(hasSnakeBody(row,column))
                           {
 				printw("[]");
                           }
                          else if(food.row == row && food.column == column)
                           {
				printw("##");
                           }
          		  else
                           {
				printw("  ");
                           }
                     }

                     printw("\n");
             }
             
      }

      printw("by lizhiwen\n");

} 


int hasSnakeBody(int i,int j)
{
    Node *p = head;
   
    while(p)
   {
       if(p->row == i && p->column == j)
       {
             return 1;
       }
       p = p->next;
   }    

   

    return 0;
 
}



void initSnake()
{
    dir = right; 
    initFood();

    head = (Node*)malloc(sizeof(Node));
    head->row = 2;
    head->column = 2;
 //   head->next = NULL;
 
    tail=head;
    addNode();
    addNode();
    addNode();

}

void addNode()
{
    Node *new = (Node*)malloc(sizeof(Node));
    new->next = NULL;
    switch(dir)
    {
          case right: 
                          new->row = tail->row;
       			  new->column = tail->column+1;
			  break;
          case left:
                          new->row = tail->row;
                          new->column = tail->column-1;
                          break;
          case down:
			  new->row = tail->row+1;
                          new->column = tail->column;
                          break;
          case up:
                          new->row = tail->row-1;
                          new->column = tail->column;
                          break;
    }    

    tail->next = new;
    tail = new;



}


void deleteNode()
{

    Node *p = head;
    head = head->next;
    
    free(p);


}

void moveSnake()
{

    addNode();
    if(food.row == tail->row && food.column == tail->column)
    {
        initFood();
    }
    else
    {
        deleteNode();
    }

    hitWall();
    hitSelf();


}



void* refreshPic()
{
   while(1)
   {

	   moveSnake();
	   initpicture();    
	   usleep(90000);
	   refresh();

   }

}


void* changeDir()
{
    int key;
    for(;;)
    {
        key = getch();
        switch(key)
        {
             case KEY_RIGHT:
                            turn(right);                            
                            break;
             case KEY_LEFT:
                            turn(left);                            
                            break;
             case KEY_DOWN:
                            turn(down);                            
                            break;
             case KEY_UP:
                            turn(up);                            
                            break;

        }


    }


}




void turn(int direction)
{
     if(abs(dir) != abs(direction))
     {
           dir = direction;
     } 

}


void initFood()
{
     food.row = rand()%20;
     food.column = rand()%20;     

}

void deleteSnake()
{
     Node *p;
     while(head)
     {
         p = head;
         head = head->next;
         free(p);    
     }


}

void hitWall()
{
    if(tail->row == 0 || tail->row == 20 || tail->column == 0 || tail->column == 20)
    {
           deleteSnake();
           initSnake();
    }
}


void hitSelf()
{
    Node *p = head;

    while(p->next != NULL)
    {
          if(tail->row == p->row && tail->column == p->column)
          {
		  deleteSnake();
		  initSnake();

          } 
          p = p->next; 
    } 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值