贪吃蛇

贪吃蛇

1.ncurse

1.为什么要用ncures,在c语言自带库函数中获取字符的方式有。

scanf();
getchar();
gets();//回去字符串 gets(*char)
他们在输入数据结束后都需要按一下回车键,表示输入结束。对于操控贪吃蛇,对实时性的要求高,所以按方向键的同时不可能再去按回车键,因此我们引用ncures。

2.ncures的使用

#include <curses.h>
int main()
{
        initscr(); //初始化ncures页面
        printw("wo shi xu bo zheng \n"); //在ncurse模式下的printf
        getch(); //等待用户输入,如果没有这句话,程序就退出了,看不到运行的结果,也就看不到上面这句话。
        endwin(); //程序退出,调用该函数来恢复shell终端的显示,如果没有这就话,shell终端会乱码。
      	return 0;
}

image-20221127122350930

此时进入ncures页面,当随便输入键盘上的按键时,程序回到Linux页面。

在编译ncurse的时候要输入 **gcc cursedemo1.c -lcurses**(切记要加入-lcurses)cursesdemo1.c是创建的.c文件,名字自取。
#include <curses.h>
int main()
{
        char i;
        initscr(); //初始化ncurse页面
        i = getch(); //结构输入的字符
        printw("you input %c \n",i); //打印输入的字符
        getch(); //等待用户输入,没有这就话程序直接退出
        endwin(); //退出程序
        return 0;
}

效果如下

image-20221127124509912

只需按下想输入的按键即可,不在按下回车键

3.ncurse获取上下左右键

#include <curses.h>

int main()
{
        int key;
        initscr(); //初始化ncurse
        //keypad设置了可以在stdscr中接受键盘的功能键(快捷键),我们需要在后面的程序中定义F1,F2以及移动的光标键
        //作用是识别功能键,否则得不到我们想要的键值
        keypad(stdscr,1); //(stdscr表示从stdsur中获取功能码,1表示确定接收)
        while(1)
        {
                key = getch();
                switch(key)
                {
                        case 0402:
                                printw("dowm\n");
                                break;
                        case 0403:
                                printw("up\n");
                                break;
                        case 0404:
                                printw("lelf\n");
                                break;
                        case 0405:
                                printw("right\n");
                                break;
                }
                if((char)key == '\n') //如果按回车键则退出循环
                {
                        break;
                }
        }
        endwin(); //退出程序
        return 0;
}
vi /usr/include/curses.h 可查看各功能码的宏定义

上下左右功能码的宏定义:

image-20221127144844573

效果

image-20221127150431073

4.地图规划

image-20221127153420579

地图规划的基本逻辑:

1.要打印一个20行*20列的一个方格
2.第0行要打印 "|-----------|"(要打印一排-- 再加上两个|)。
3.第1到18行只需再列的头和尾部打印“|”即可。
4.第19行需要也要打印“|---------|”。
void gamePic() //地图规划函数
{
        int hang;
        int lie;
        for(hang=0;hang<20;hang++)
        {
                if(hang == 0) //先打印第一行 第一行要先打印“----------” 然后再下一行 打印两个 “|”(分别是在第0列和第20列)
                {
                        for(lie=0;lie<20;lie++)
                        {
                                printw("--");
                        }
                        printw("\n");
                        for(lie=0;lie<=20;lie++)
                        {
                                if(lie==0 || lie == 20)
                                {
                                        printw("|");
                                }
                                else
                                {
                                        printw("  ");
                                }
                        }
                }
                else if (hang<19) //1-18行 只需要在第0列和第20列打印两个“|”即可。
                {
                        printw("\n");
                        for(lie=0;lie<=20;lie++)
                        {
                                if(lie == 0||lie == 20)
                                {
                                        printw("|");
                                }
                                else
                                {
                                        printw("  ");
                                }
                        }

                }
                else //第19行要先打印两个“|”然后再下一行打印“--------”即可。
                {
                        printw("\n");
                        for(lie=0;lie<=20;lie++)
                        {
                                if(lie == 0||lie == 20)
                                {
                                        printw("|");

                                }
                                else
                                {
                                        printw("  ");
                                }
                        }
                        printw("\n");
                        for(lie=0;lie<20;lie++)
                        {
                                printw("--");
                        }
                }

        }

}

最终效果图

image-20221127162242016

5.贪吃蛇身子的显示

贪吃蛇身子显示

如何显示蛇身子的一个节点:设该节点 : 行坐标2 , 列坐标2.
if(hang == x.hang&&lie == x.lie)
{
	printw("[]");
}

创建并且定义结构体

image-20221128100930758

打印蛇的一个节点

image-20221128101005790

如图,如果找到结构体的行和列,则打印[ ] ,其余情况开始打印 空 。
贪吃蛇显示多个节点
1.构建一个打印函数
2.根据链表的遍历,遍历链表

image-20221128103809836

类似于链表的遍历,如果传递过来的链表的行等于目前的行等于目前的列,则返回1,返回值位于if语句中。

image-20221128104001479

在if语句中使用该函数。

6.链表动态添加蛇的节点

和链表的动态创建相同

image-20221128114854577

创建这两个指针函数,在main函数中调用 initSnake 函数

逻辑如下

1.首先定义链表的头部,将链表头部的下一个设置为NULL。
2.然后再创建一个一直指向链表尾部的结构体指针(因为要从尾部插入)。
3.因为一开始链表只有一个节点,所以teil = head(链表头和链表尾部相同)
4.将teil结构体指针当作addCode函数的参数。(该函数的作用是再链表尾部插入新节点)
5.让当前节点的下一个指向新的节点
6.再让teil指向新的节点,返回忒teil的值。

7.蛇向右移动

image-20221128130555243

逻辑思路 实现贪吃蛇向右移动
1.首先贪吃蛇的为尾巴要向后多加一个节点
2.头要向后走一位

image-20221128130819927

代码所示
1.首先建立一个新的节点,将新的节点插在链表的末尾
2.将头节点向后瞬移一位,并且释放掉之前的头节点(free)
3.为什么会有二级指针?
因为我们将head和feil都定义在了main函数中,我们要实时的回去头部和尾部的位置,但是一个函数只可以return一个值,所以因为一个二级指针用来指向结构体feil的地址,通过*取值符,对feil里面的内容进行修改。

image-20221128131508444

再main函数中调用该函数。每按一次按键,要刷新一次地图

image-20221128131621913

出现这种情况的原因是因为ncurse光标的问题,ncurse的打印是根据光标的位置打印,所以我们要想将之前打印的结果覆盖掉,需要重置光标位置。

image-20221128132125407

在gemePic中加入 move(0,0)重置光标

效果如下

image-20221128132225207

8.贪吃蛇撞墙死亡

image-20221128145538494

在moveSnake中进行一个判断 如过贪吃蛇的头(链表的末尾),末尾结构体的 行或列等于0 或者 行或列等于20 怎回到初始化的位置。

image-20221128145909362

在贪吃蛇初始化中,在最上面添加了一个判断语句,可以做到内存的释放,防止造成内存泄漏的现象。

9.贪吃蛇自行行走

image-20221128151839560

moveSnake函数可以实现贪吃蛇向右移动,如果放到while(1)中会发现。

image-20221128152026905

没有任何显示,原因是因为while(1)显示速度过于快。

image-20221128152351095

改写成上图形式
refrsh 是ncurse自带的函数表示刷新页面。
usleep(100000) 表示程序在此等待100000微秒(即100毫秒)
sleep(1) 表示程序在此等待一秒。

10.贪吃蛇方向移动

因为涉及到方向的移动就会涉及到方向检测,要用循环实时的检测用户输入的数据,可以我们让贪吃蛇自主行走已经在main函数中执行了一个while(1)循环,那么在Linux中怎么同时执行两个while(1)那???这时我们引用Linux线程。
举一个简单的例子。。

image-20221128154931481

此程序有两个while1,由于main函数从到上到小运行函数,所以在运行的时候,只会运行fun1函数。

image-20221128155122815

引入Linux线程概念

image-20221128160337732
如图所示,我们可以创建一个线程,来专门执行第二个while(1)函数,这些就相当这个程序的两个while分别用两个通道来执行,互不干扰
那我们怎么样来创建一个Linux线程那???(可以用创建线程的专有函数)
https://www.cnblogs.com/lonely-little-boy/p/16726452.html(创建Linux线程)

程序

#include <stdio.h>
#include <pthread.h> //线程的头文件
void* fun1()
{
        while(1){
                printf("this is fun1\n");
                sleep(1);
        }
}

void fun2()
{
        while(1){
                printf("this is fun2\n");
                sleep(1);
        }
}

int main()
{
        pthread_t th;  //定义一个线程叫做 th
        pthread_create(&th,NULL,fun1,NULL); //创建一个线程(取th的地址,一般都为NULL,为需要执行函数的名字,函数传递的参数)
        fun2();
        return 0;
}
它的返回值表示的是,如果创建成功则返回0,失败则返回错误码。第一个参数是一个无符号长整型,它是一个输出型参数,输出线程的id。第二个参数表示的是线程的属性,只不过我们暂时先不需要关心,置为NULL即可。第三个参数是一个返回值为void*,参数为void*的函数,它代表线程要去执行的函数。最后一个参数是传给执行函数的参数。
由于使用了第三方库,因此在编译的时候需要加上-lpthread的选项:
gcc mythread.c  -lpthread

解决问题

image-20221128164455820

image-20221128164510945

利用线程来执行检测用户输入的循环,让贪吃蛇一直向右走的while循环则在main函数中运行。

贪吃蛇蛇皮走位

image-20221129110306139

首先定义一个全局遍历dir 来保存方向
1.实现向左运动:要将头节点删除,新的节点和尾巴同一行,但是列要-1.
2.实现向右运动:要将头节点删除,新的节点和尾巴同一行,但是列要+1.
3.实现向上运动:要将头节点删除,新的节点和尾巴同一列,但是行-1.
4.实现向下运动:要将头节点删除,新的节点和尾巴同一列,当时行要+1.

image-20221129110724059

11.贪吃蛇进食

首先下定义一个食物(food)的结构体,然后类似于显示贪吃蛇,在gamePic中显示食物的位置。
定义结构体food

image-20221129124435511

打印、显示food

image-20221129124512067

如何实现贪吃蛇吃掉食物后增长那???并且食物会换一个位置。

image-20221129124710555

在这里实现,当链表尾巴(蛇头)等于食物所在位置时,则执行createFood函数,来从新定义结构体food,并且蛇尾巴(链表头)不在减少,即实现了吃一个食物加一个关节的效果。

image-20221129125417888

createFood 函数 关键字static用于局部变量的修饰符,通常是某个函数体内,只能在该函数内被调用。这样定义的变量通常被称为局部静态变量,他的值不会因为函数调用的结束而被清除,当函数在此被调用时,他的值是上一次调用结束后的值。
但是这样实现的食物的产生位置,是有规律的(行+2,列+2)我们怎么实现随机产生食物那????并且让他在区间内

12.随机产生食物

用C语言自带的随机生成数字的函数rand 该函数可以生成一个数字,但是随机生成的数字可能会大于20从而超出图像的范围,所以对rand随生成的函数对20取余操作(即生成数字只在0-20之间)
改后的createFood函数

image-20221129132059446

posted @ 2023-09-01 13:57  徐博正  阅读(66)  评论(0)    收藏  举报