基于Linux的Ncurse库的贪吃蛇项目

news2024/11/20 20:29:01

贪吃蛇项目的意义

  • 承上启下:从C语言基础的学习:数据结构链表基础、C变量、流程控制、函数、指针、结构体等。过渡到Linux系统编程:文件编程、进程、线程、通信、第三方等。

Linux终端图形库curses

curses的名字起源于"cursor optimization",即光标优化。它最早由美国伯克利大学的Bill Joy和Ken Arnold编写的,用来处理一个游戏rogue的屏幕显示。后来贝尔实验室的Mark Horton在system III Unix中重新编写了curses。

现在几乎所有的Unix,Linux操作系统都带了curses函数库,curses也加入了对鼠标的支持,一些菜单和面板的处理。可以说,curses是Linux终端图形编程的不二选择。

#include <curses.h>

int main()
{
        initscr();//ncurse界面的初始化函数
        printw("this is a curses window\n");//ncurse模式下的printf
        getch();//等待用户输入,如果没有这句话,程序就退出了,看不到运行结果
        endwin();//程序退出,调用该函数来恢复shell终端的显示,如果没有这句话,shell终端字乱码,坏掉
        return 0;
}
  1. 绘制地图

    void init_map(struct snake_node s)
    {
            int hang;
            int lie;
            for(hang=0;hang<20;hang++){
                    if(hang == 0 ){
                            for(lie=0;lie<20;lie++){
                                    printw("--");
                            }
                            printw("\n");
                    }
                    for(lie=0;lie<20;lie++){
                            if(lie == 0 || lie == 19){
                                    printw("|");
                            }else{
                                    printw("  ");
                            }
                    }
                    if(hang == 19 ){
                            printw("\n");
                            for(lie=0;lie<20;lie++){
                                    printw("--");
                            }
                    }
    			}
    			printw("This is Snaker");
    }                     
    

    效果:

    在这里插入图片描述

  2. 初始化贪吃蛇身体

    void add_node()//增加一个节点
    {
            struct snake_node *new = (struct snake_node *)malloc(sizeof(struct snake_node));
            new->hang = tail->hang;
            new->lie = tail->lie + 1;
            new->next = NULL;
            tail->next = new;
            tail = new;
    }
    void init_sanke()//初始化身体
    {
            head = (struct snake_node*)malloc(sizeof(struct snake_node));
            head->hang = 5;
            head->lie = 5;//初始化头部的位置
            head->next = NULL;
            tail = head;
            add_node();
            add_node();//初始化身体,想长一点就加一个节点
    }
    
  3. 贪吃蛇向右移动

    void delet_node()//删除一个节点
    {
            struct snake_node * p;
            p = head;
    
            head = head->next;
            free(p);//注意:删除节点需要free掉,所以创建一个p来承接原head的空间,避免内存泄漏
    }
    void move_snake()//删掉头,加一个尾
    {
            add_node();
            delet_node();
    }
    //主函数中判断按键
    while(1){
                    dir = getch();
                    switch(dir){
                            case KEY_RIGHT://如果是右方向键
                                    move_snake();
                                    break;
                    }
                    init_map();//刷新地图,注意用:move(int x,int y)函数重置光标的位置
            }
    
    
  4. 贪吃蛇撞墙死掉

    void move_snake()
    {
            add_node();
            delet_node();
    //加入判断条件,判断tail的行和列,和墙体重合就死掉,重新生成
            if(tail->hang == 0 || tail->lie == 0 || tail->hang == 21 || tail->lie == 21){
                    init_snake();
            }
    }
    void init_snake()
    {
    //加入判断条件,如果不是第一次创建蛇,把之前的蛇free掉,防止内存溢出
            struct snake_node *p;
            while(head != NULL){
                    p = head;
                    head = head->next;
                    free(p);
            }
            head = (struct snake_node*)malloc(sizeof(struct snake_node));
            head->hang = 20;
            head->lie = 1;
            head->next = NULL;
            tail = head;
            add_node();
            add_node();
    }
    
    
  5. 双线程实现刷新界面和响应按键

    int main()
    {
            init_ncurse();
            init_snake();
            init_map();
            while(1){
                    move_snake();
                    init_map();
                    refresh();
                    usleep(200000);
            }
            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:
                   }
            }
            endwin();
            return 0;
    }
    

    上面main函数中存在两个while(1),常规方法无法实现该函数的功能,因此引入线程。

        #include <pthread.h>  // 头文件
    
        pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;
        如:pthread_t t1;  //多线程定义
    
        pthread_create(&t1,NULL,fun,NULL);
        参数1:传出参数,保存系统为我们分配好的线程ID
        参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
        参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
        参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装。
    
        使用多线程的函数必须返回指针型,如void *fun()
    
        注:gcc xxx.c -lcurses -lpthread  //编译需要连接pthread库
    
    
  6. 线程demo

    #include <stdio.h>
    #include <pthread.h>
    
    void * fun1()
    {
            while(1){
                    printf("this is fun1\n");
                    usleep(300000);
            }
    }
    void * fun2()
    {
            while(1){
                    printf("this is fun2\n");
                    usleep(300000);
            }
    }
    int main()
    {
            pthread_t t1;//定义线程
            pthread_t t2;
    
            pthread_create(&t1,NULL,fun1,NULL);//创建线程
            pthread_create(&t2,NULL,fun2,NULL);
            while(1);
            return 0;
    }
    

    效果:

    在这里插入图片描述

  7. 双线程实现刷新屏幕和按键改变方向

    void* refresh_screen()//刷新屏幕的线程
    {
            while(1){
                    move_snake();
                    init_map();
                    refresh();
                    usleep(200000);
            }
    
    }
    void* change_dir()//响应按键改变方向的线程
    {
            while(1){
    
                    dir = getch();
                    switch(dir){
                            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()
    {
            init_ncurse();
            init_snake();
            init_map();
    
            pthread_t t1;//定义两个线程
            pthread_t t2;
    
            pthread_create(&t1,NULL,refresh_screen,NULL);//启动这两个线程
            pthread_create(&t2,NULL,change_dir,NULL);
    
            while(1);//保证程序不退出
            endwin();
            return 0;
    }
    
  8. 贪吃蛇上下左右的移动

    void add_node()//实现贪吃蛇的上下左右移动
    {
            struct snake_node *new = (struct snake_node *)malloc(sizeof(struct snake_node));
            new->hang = tail->hang;
            switch(dir){
                    case UP:
                            new->hang = tail->hang - 1;
                            new->lie = tail->lie;
                            new->next = NULL;
                            break;
                    case DOWN:
                            new->hang = tail->hang + 1;
                            new->lie = tail->lie;
                            new->next = NULL;
                            break;
                    case LEFT:
                            new->hang = tail->hang;
                            new->lie = tail->lie - 1;
                            new->next = NULL;
                            break;
                    case RIGHT:
                            new->hang = tail->hang ;
                            new->lie = tail->lie + 1;
                            new->next = NULL;
                            break;
            }
            tail->next = new;
            tail = new;
    }
    void turn(int direction)//用绝对值来避免从上直接到下和从左直接到右的方向不合理转换
    {
            if(abs(dir) != abs(direction)){
                    dir = direction;
            }
    }
    
  9. 实物的生成和吃食物变长

    void init_food()//随机生成食物
    {
            //1---20
            int x = rand()%20 + 1;
            int y = rand()%20 + 1;
            food.hang = x;
            food.lie = y;
    }
    void move_snake()
    {
            add_node();
            if(tail->hang == food.hang && tail->lie == food.lie){
                    init_food();//如果吃掉食物,长度+1,同时刷新食物位置
            }else{
                    delet_node();
            }
            if(tail->hang == 0 || tail->lie == 0 || tail->hang == 21 || tail->lie == 21){
                    init_snake();
            }
    }
    
  10. 贪吃蛇死亡

    int snake_die()//贪吃蛇死亡的两种方式
    {
            struct snake_node *p;
            p = head;
            //撞墙死亡
            if(tail->hang == 0 || tail->lie == 0 || tail->hang == 21 || tail->lie == 21){
                    return 1;
            }
            //自杀,撞到身体
            while(p->next != NULL){
                    if(p->hang == tail->hang && p->lie == tail->lie){
                            return 1;
                    }
                    p = p->next;
            }
            return 0;
    }
    void move_snake()
    {
            add_node();
            if(tail->hang == food.hang && tail->lie == food.lie){
                    init_food();
            }else{
                    delet_node();
            }
            if(snake_die()){//判断是否满足死亡条件
                    init_snake();
            }
    }
    
  11. 最终效果

    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1624052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法学习笔记Day8——回溯算法

本文解决几个问题&#xff1a; 回溯算法是什么&#xff1f;解决回溯算法相关的问题有什么技巧&#xff1f;回溯算法代码是否有规律可循&#xff1f; 一、介绍 1.回溯算法是什么&#xff1f; 回溯算法就是个多叉树的遍历问题&#xff0c;关键在于在前序和后序时间点做一些操作…

大学生在线考试|基于SprinBoot+vue的在线试题库系统系统(源码+数据库+文档)

大学生在线考试目录 基于SprinBootvue的在线试题库系统系统 一、前言 二、系统设计 三、系统功能设计 试卷管理 试题管理 考试管理 错题本 考试记录 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…

wps屏幕录制怎么用?分享使用方法!

数字化时代&#xff0c;屏幕录制已成为我们学习、工作和娱乐中不可或缺的一部分。无论是制作教学视频、分享游戏过程&#xff0c;还是录制网络会议&#xff0c;屏幕录制都能帮助我们轻松实现。WPS作为一款功能强大的办公软件&#xff0c;其屏幕录制功能也备受用户青睐。本文将详…

【LLM多模态】Qwen-VL模型结构和训练流程

note 观点&#xff1a;现有很多多模态大模型是基于预训练&#xff08;和SFT对齐&#xff09;的语言模型&#xff0c;将视觉特征token化并对齐到语言空间中&#xff0c;利用语言模型得到多模态LLM的输出。如何设计更好的图像tokenizer以及定位语言模型在多模态LLM中的作用很重要…

LeetCode54. 螺旋矩阵

LeetCode54.螺旋矩阵 题解思路 代码 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> res;int n matrix.size();// 行int m matrix[0].size(); // 列vector<vector<bool>> st(n, v…

分步搭建HF区块链网络--部署fabric区块链--加入channel

一.搭建网络规划 采用容器环境&#xff0c;搭建1个排序节点(Orderer)、2个对等节点(Peer)&#xff0c;另外用 一个fabric-cli容器。实训中的绝大部分命令是通过该容器执行的。 容器名称设置 二. 配置HF网络证书 首先docker ps 检查镜像&#xff0c;确保镜像为空 1.生成crypto…

leaftjs+turfjs+idw纯前端实现等值面绘图

最近有个绘制等值面图的需求。我们一般的实现路径是&#xff1a; 1.后台绘图&#xff0c;用surfer绘制好&#xff0c;给前端调用叠加到地图。 2.后台用python绘图&#xff0c;绘制好给前端调用&#xff0c;叠加到地图。 3.后台进行插值计算、地图裁剪、最终生成geojson文件或…

[集群聊天项目] muduo网络库

目录 网络服务器编程常用模型什么是muduo网络库什么是epoll muduo网络库服务器编程 网络服务器编程常用模型 【方案1】 &#xff1a; accept read/write 不是并发服务器 【方案2】 &#xff1a; accept fork - process-pre-connection 适合并发连接数不大&#xff0c;计算任…

第一篇:Python简介:开启你的编程之旅

Python简介&#xff1a;开启你的编程之旅 在这个系列文章中&#xff0c;我将带领大家深入了解Python——一个极具魅力的编程语言。如果你对编程感兴趣&#xff0c;想要掌握一门既实用又强大的语言&#xff0c;那么Python无疑是一个绝佳的选择。本篇文章是这个系列的序章&#…

文件包含漏洞基础

php 中的文件包含函数&#xff1a; incude &#xff1a; require incude_once require_once 为了减少重复性代码的编写&#xff1b; 任意后缀的文件当中只要存在 php 代码就会被当作 php 执行&#xff1b; 本质&#xff1a;由于包含的文件不可控&#xff0c;导致文件包含…

S32K的JLINK与PE接线方法与刷程序失败问题

S32K的JLINK与PE接线方法与刷程序失败问题 1、PE的接线方法2、JLINK的接线方法3、刷程序失败问题 1、PE的接线方法 2、JLINK的接线方法 3、刷程序失败问题 出现如下问题&#xff1a; Secure Debug might be enabled on this device.lf so.please unlock the device via PEmic…

Maven基础篇4

跳过测试(了解) 1.背景 你的模块还没完成&#xff0c;项目经理就已经完成测试模块的开发 自己的模块写好提交测试&#xff0c;看看功能有没有完成&#xff0c;完成了那么就成功&#xff0c;否则失败&#xff1b; 使用背景&#xff1a; 1.测试用例写好了&#xff0c;自己模…

【计算机网络】成功解决 ARP项添加失败:请求的操作需要提升

最近在用Wireshark做实验时候&#xff0c;需要清空本机ARP表和DNS缓存&#xff0c;所以在cmd窗口输入以下命令&#xff0c; 结果发生了错误&#xff1a;ARP项添加失败&#xff1a;请求的操作需要提升 一开始我还以为是操作的命令升级了&#xff0c;但是后面发现其实只是给的权…

C++面向对象程序设计 - 继承与派生

面向对象技术强调软件的可重用性&#xff08;software reusability&#xff09;&#xff0c;C语言提供了类的继承机制&#xff0c;解决了软件重用问题。 C中所谓“继承”就是在一个已存在的类的基础上建立一个新类&#xff0c;从已有的类那里获得已有特性&#xff0c;叫做类的继…

网络协议深度解析:SSL、 TLS、HTTP和 DNS(C/C++代码实现)

在数字化时代&#xff0c;网络协议构成了互联网通信的基石。SSL、TLS、HTTP和DNS是其中最关键的几种&#xff0c;它们确保了我们的数据安全传输、网页的正确显示以及域名的正常解析。 要理解这些协议&#xff0c;首先需要了解网络分层模型。SSL和TLS位于传输层之上&#xff0c…

说说2024年暑期三下乡社会实践工作新闻投稿经验

作为一名在校大学生,我有幸自去年起参与学院组织的暑期大学生三下乡社会实践团活动。这项活动不仅是我们深入基层、服务社会的重要平台,也是展现当代大学生风采、传递青春正能量的有效途径。然而,如何将这些生动鲜活的实践故事、感人至深的瞬间传播出去,让更多人了解并受到启发…

在PostgreSQL中如何创建和使用自定义函数,包括内置语言(如PL/pgSQL)和外部语言(如Python、C等)?

文章目录 一、使用内置语言 PL/pgSQL 创建自定义函数示例代码使用方法 二、使用外部语言 Python 创建自定义函数安装 PL/Python 扩展示例代码使用方法 三、使用外部语言 C 创建自定义函数编写 C 代码编译为共享库在 PostgreSQL 中注册函数注意事项 总结 PostgreSQL 是一个强大的…

前端H5动态背景登录页面(下)

最近正好有点儿时间&#xff0c;把之前没整理完的前端动态背景登录页面给整理一下&#xff01;这是之前的连接前端H5动态背景登录页面&#xff08;上&#xff09;&#xff0c;这主要是两个登陆页面&#xff0c;一个彩色气泡&#xff0c;一个动态云朵&#xff0c;感兴趣的可以点…

08 内核开发-避免冲突和死锁-mutex

08 内核开发-避免冲突和死锁-mutex 课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中。 …

U盘无法正常格式化?教你一个强力的办法

前言 电脑格式化U盘或者移动硬盘的操作&#xff0c;相信各位小伙伴都是有一定经历的。 如果设备正常&#xff0c;那么进入到【此电脑】&#xff0c;在对应的分区点击【鼠标右键】-【格式化】就可以把对应的存储设备恢复到初始状态。 但凡事都会有例外&#xff0c;比如在格式化…