(Linux C)贪吃蛇项目

主要思路

一、封装函数定义结构体
①创建贪吃蛇结构点(行坐标,列左边,下一结点位置)
②封装初始化函数initNcurse( )
③封装判断蛇身位置函数 hasSnakeNode(int x,inty)
二、构建地图及判断蛇身显示
①第0行打印 (–)
②第1至19行列为0处打印 (|),此处插入函数hasSnakeNode(i,j)判断蛇身是否在地图内,
若在地图内则打印蛇身[],否打印" ";
③第20行打印(|)
三、动态创建蛇身链表
①封装添加蛇身结点函数addNode()和初始化蛇身函数initSnake();
四、移动贪吃蛇
①右移(删除头结点,增尾结点).
②判断是否撞墙,若撞墙则调用初始化地图.
注意:应移动光标位置,并且定于局部指针便于释放上一地图的空间.
③上下左右移动贪吃蛇(使用Linux线程概念)
移动蛇应该注意统一直线的移动问题,即蛇尾不能方向.
五、贪吃蛇吃食物
①定义食物
②初始化食物函数initFood();
③判断是否吃到食物hasFood()
六、优化贪吃蛇死亡方式

1.了解ncurse

①需要加头文件: #include <curses.h>
②项目编译时需要手动链接 eg: gcc 后需要接-lcurses
③进入手册 vi /usr/include/curses.h,查找 /要查找的东西
④keypad函数:设置了可以在stdscr中接收键盘的功能键(快捷键)
keypad(stdscr``,1);

initscr();                //ncurse界面的初始化函数.
printw("hellow world\n"); //在ncurse模式下的printf.
getch();                  //等待用户输入,如果没有这句话,程序就退出了,看不到运行的结果.也就是看不到printw()的结果.
endwin();                 //程序退出,调用改函数来恢复shell终端的显示,若没有这句话,shell终端字乱码。

2.ncurse上下左右键的获取.

#include<curses.h>
int main()
{
    int key;
    initscr();
    keypad(stdscr,1);
    while(1)
         {
           key=getch();
           switch(key)
                 {
                   case KEY_UP:
                                printw("UP\n");
                                break;
                   case KEY_DOWN:
                                printw("DOWN\n");
                                break;
                   case KEY_LEFT:
                                printw("LEFT\n");
                                break;
                   case KEY_RIGHT:
                                printf("RIGHT\n");
                                break;
                 }
         }
         endwin();
         return 0;
}

3、贪吃蛇地图搭建

构建20×20的贪吃蛇地图(分3部分进行打印输出)
①打印第1行
在这里插入图片描述

 int i,j;
 move(0,0) //移动光标至0,0位置
 for(i=0;i<20;i++)
       {
        if(i==0)
          {
             for(j=0;j<20;j++)
               {
                 printw("--");      //此时打印--
               }
               printw("\n");

             for(j=0;j<=20;j++)
               {
                 if(j==0||j==20)
                   {
                     printw("|");  //打印|
                   }
                 else
                   {
                    printw("  ");  //因为--和|所占空间大小不同,此处应该用2个空格;
                   }
              }
              printw("\n");
          }
       }

②打印2-19行

在这里插入图片描述

 if(i>0 && i<=19) 
            {
              for(j=0;j<=20;j++)
                {
                 if(j==0 || j==20)
                  {
                   printw("|");
                  }
                 else
                  {
                   printw("  ");
                  }
                }
                printw("\n");
            }

③打印第19行的- -部分
在这里插入图片描述

if(i==19)
           {
             for(j=0;j<20;j++)
               {
                 printw("--");
               }
                printw("\n");
                printw("By PPPPPPPKD");
           }

创建意义:创建空间以便下一步写入蛇身和果实扫描.

4、贪吃蛇身体显示

(1)
①单个结点的显示
设置贪吃蛇结构体(行坐标,列左边,下一个节点地址);

struct Snake
{
     int i; //行
     int j; //列
     struct Snake *next;
}

定义蛇的一个结点

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

地图中插入结点位置(修改0-19行代码)

 if(i>0 && i<=19) 
            {
              for(j=0;j<=20;j++)
                {
                 if(j==0 || j==20)
                  {
                   printw("|");
                  }
                 else if(node1.i==i && node1.j==j)         //判断是否插入;若坐标相同,则打印蛇身[],否则打印空格.
                        {
                         printw("[]");
                        }
                 else
                  {
                   printw("  ");
                  }
                }
                printw("\n");
            }

效果如图:
在这里插入图片描述
②蛇身整体显示(动态链表添加蛇的结点)

(1)封装函数判断是否存在蛇身

struct Snake node1={2,2,NULL};     //创建蛇身结点.

int hasSnakeNode(int x,int y)       //封装函数
{ 
    struct Snake *p;                //创建指向结构体的指针p
    p=&node1;                       //指针指向node1的地址
    while(p!=NULL)                  //指向地址不为空
    {
      if(p->i==x &&p->j==y)         //判断当前左边是否在地图内
        {
          return 1;
         }
        p=p->next;                  //遍历指针
    }
    return 0;
}

else if(node1.i==i && node1.j==j)
改为else if(hasSnakeNode(i,j))

(2)动态创建蛇身链表
定义全局变量

struct Snake *head=NULL;  //头结点
struct Snkae *tail=NULL;  //尾结点

②封装添加蛇身结点函数addNode()

void addNode()
{
   struct Snake *new=(struct Snake *)malloc(sizeof(struct Snake));
   new->i=tail->i;
   new->j=tail->j+1;
   new->next=NULL;
   tail->next=new;
   tail=new;
   
}

②封装初始化蛇身函数initSnake()

void initSnake()
{
     struct Snake *p;        //定义p得作用是方便释放空间
     p=head;
     while(head!=NULL)       //每次撞墙后初始化地图都需要释放原本开辟的空间.
         {
           p=head;
           head=head->next;
           free(head);
         }
         
   head=(struct Snake*)malloc(sizeof(struct Snake));
 //定义首个位置
   head->i=2;
   head->j=2;
   head->next=NULL
  
   tail=head;  //尾结点指向头节点
   addNode(); //调用函数
}

五、控制贪吃蛇移动
①右移
原理:删除头结点,增添尾结点.
a.增加结点函数

void addNode()
{
  struct new *new=(struct Snake*)malloc(sizeof(struct Snake));
  new->i == tail->i;
  new->j == tail->j+1;   //增添尾结点
  new->next=NULL;
  tail->next=new;   //改变原本结点位置
  tail=new;
}

②删除结点函数

void deleteNode()
{
  struct Snake *p;
  p=head;           //定义p指向head的目的:方便释放删除的结点
  head=head->next;  //删除头节点
  free(p);
}

③移动指令函数并判断是否撞墙

int moveSnake()
{
  addNode();
  deleteNode();
  if(tail->i==0 || tail->j==0 ||tail->i==20 || tail->j==20)
    {
      initSnake(); //撞墙后调用初始化条件,输出原本地图
    }
}

④控制右移
(1)按键KEY_LEFT一次右移一次

int control;
while(1)
     {
      control=getch();
      if(control==KEY_RIGHT)
        {
          moveSnake();
          gamePic();   //注意:此处得重新扫面地图才能显示
        }
     }

(2)自动向右移动

void refreshInterface()
{
     while(1)
     {
     moveSnake();
     gamePic();
     refresh();    //刷新界面
     usleep(100000);     //移动速度
     }
} 

⑤上下左右移动贪吃蛇

a.问题:刷新界面和判断方向需要同时进行.
解决方法:创建Linux线程

线程函数介绍

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>
 
void *fun(void *arg)
{
	printf("I'm thread, Thread ID = %lu\n", pthread_self());
	return NULL;
}
 
int main(void)
{
	pthread_t tid;
	pthread_create(&tid, NULL, fun, NULL);
	
	sleep(1);      // 在多线程环境中,父线程终止,全部子线程被迫终止
	printf("I am main, my pid = %d\n", getpid());
 
	return 0;

封装函数refreshInterface()用来更新页面和changeDirection()函数用来判断方向。
创建线程

pthread_t t1;
pthread_t t2;
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDirection,NULL);

b.实现贪吃蛇四个方向的移动
实现方法:修改addNode( )和changeDirection( )函数和turn()函数.

//定义全局变量
#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2
int dir

turn()函数
作用:使蛇尾不能移动方向

void turn(int direction)
{
         if( abs(dir)!= abs(dirction))    //即水平方向不能都可移动蛇
           {
             dir=direction;
           }
}

addNode( )函数

switch(dir)          //判断方向增加结点.
          {
             case UP:
                    new->i=tail->i-1;
                    new->j=tail->j;
                    break;
             case DOWN:
                    new->i=tail->i+1;
                    new->j=tail->j;
                    break;
             case LEFT:
                    new->i=tail->i;
                    new->j=tail->j-1;
                    break;
             case RIGHT:
                    new->i=tail->i;
                    new->j=tail->j+1;
                    break;
          }


changeDirection( )函数

 while(1)
          {                     
           key=getch();            //接受判断改变方向
           switch(key)
                 {
                  case KEY_DOWN:
                          dir=turn(DOWN);
                          break;
                  case KEY_UP:
                          dir=turn(UP);
                          break;
                  case KEY_LEFT:
                          dir=turn(LEFT);
                          break;
                  case KEY_RIGHT:
                          dir=turn(RIGHT);
                          break;
                 }
          }

五、贪吃蛇吃食物(##)

①食物结构体

struct Snake food;  

②初始化食物


     int x=rand()%20;  //取余是为了确保随机值取值范围<20
     int y=rand()%20; 
     food.i=x;
     food.j=y;

}

③判断是否吃到食物

int hasFood(int x,int y)      
{
     if(food.i==x && food.j==y)
      {
        return 1;
      }
     return 0;

}

六、优化贪吃蛇死亡方式

贪吃蛇的两种死亡方式:①撞墙.②咬到自己

int ifSnakeDie()
{
    struct Snake *p;
    p=head;

    if(tail->i<0 ||tail->i==20||tail->j==0||tail->j==20)
     {
      return 1;
     }
     while(p->next !=NULL)
     {
      if(p->i==tail->i && p->j==tail->j)
       {
         return 1;
       }
       p=p->next;
     }
   return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值