c语言第一个小游戏:贪吃蛇小游戏05

贪吃蛇脱缰自动向右走:脱缰的野蛇

#include <curses.h>

#include <stdlib.h>

struct snake{

        int hang;

        int lie;

        struct snake *next;

};

struct snake *head;

struct snake *tail;

void initNcurse()

{

        initscr();

        keypad(stdscr,1);

}

int  hasSnakeNode(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;

}

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

                                        printw("[]");

                                 }

                                 else{

                                         printw("  ");

                                 }

                        }

                        printw("\n");

                }

                if(hang == 19){

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

                                 printw("--");

                        }

                        printw("\n");

                }

          }

          printw("by shijintao");

          printw("\n");

   }

void addNode()

{

        struct snake *new;

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

        new->hang=tail->hang;

        new->lie=tail->lie+1;

        tail->next = new;

        tail = new;

        new->next = NULL;

}

void  initSnake()

{

        struct snake *p;

        while(head != NULL){

             p=head;

             head=head->next;

             free(p);

        }

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

        head->hang=2;

        head->lie=2;

        head->next=NULL;

        tail = head;

        addNode();

        addNode();

}

void deleteNode()

{

        struct snake *p;

        p = head;

        head = head->next;

        free(p);

}

void moveSnake()

{

        addNode();

        deleteNode();

        if(tail->hang==0||tail->hang==20||tail->lie==20||tail->lie==0){

                initSnake();

        }

}

int main()

{

                int con;

                initNcurse();

                initSnake();

                gamepic();

                while(1){

                           moveSnake();

                           gamepic();

                           refresh();

                           usleep(100000);

                }

                getch();

                endwin();

                return 0;

}

这边最主要改动的代码地方在于main函数里面的 

        主要是我们要先想一下如果只是一味的想让这个蛇自己往右边走(这个右边就是代码效果,也没有设置方向,就是新节点的插入导致一个向右边走的效果),肯定要把监测右方向键给去掉,然后死循环就可以实现l,不断的moveSnake(不断的addNode,deleteNode)

while(1){

                           moveSnake();

                           gamepic();

                           refresh();

                           usleep(100000);

                }

原始代码中,getch() 函数的调用确实会导致屏幕刷新。这是因为 getch() 在等待用户输入时,会隐式触发一次屏幕刷新

1. getch() 的隐式刷新机制

在 ncurses 中,getch() 不仅用于获取用户输入,还会执行以下操作:

  • 刷新屏幕:在等待按键时,ncurses 会自动将当前缓冲区内容渲染到屏幕上。
  • 处理输入事件:捕获用户按下的键并返回其 ASCII 码(或特殊键的编码,如方向键)。

因此,当你在循环中调用 getch() 时,即使没有显式调用 refresh(),屏幕也会被刷新。这解释了为什么你之前的代码能正常显示画面。

2. 为什么现在需要显式调用 refresh()

在你的新代码中,循环结构发生了变化:

while(1){

    moveSnake();      // 更新蛇位置

    gamepic();        // 绘制画面

    refresh();        // 显式刷新屏幕

    usleep(100000);   // 延时控制速度}

这里的关键区别是:蛇的移动不再依赖用户输入,而是通过定时器自动更新(usleep())。因此:

  • 如果没有 refresh(),绘制操作(gamepic())的结果只会停留在缓冲区,无法显示。
  • usleep() 是纯延时函数,不会触发屏幕刷新,必须手动调用 refresh()

3. 两种实现方式的对比

旧代码(依赖 getch()

新代码(自动移动)

蛇仅在用户按键时移动

蛇自动持续移动

getch() 隐式刷新屏幕

必须显式调用 refresh()

游戏节奏由用户控制

游戏节奏由 usleep() 控制

4. 最佳实践建议

  • 始终显式调用 refresh():即使在有 getch() 的场景中,显式刷新能让代码逻辑更清晰,避免依赖隐式机制。
  • 分离输入处理和画面刷新:在自动移动的游戏中,用户输入(如方向键)和屏幕刷新是独立的操作,需要分别处理。

总结

原始代码之所以能正常显示,是因为 getch() 在等待输入时隐式刷新了屏幕。而在新的自动移动实现中,由于没有依赖 getch(),必须通过 refresh() 手动刷新屏幕才能看到蛇的移动效果。这是 ncurses 编程中的一个常见细节,理解它有助于写出更健壮的代码!

贪吃蛇风骚走位之支持方向变换

        那么问题来了,方向的改变要监测键盘的按键需要一个while(1)循环(不断的检测用户的输入)   还有,蛇的脱缰的走也需要一个while(1)循环支持,就是这两个while(1)需要同时跑,那么我们现在单线程,就是目前无法一心二用,所以我们还做不了,需要学习多线程。

Linux多线程概念引入及编码实现

我们做贪吃蛇的时候不是要需要控制按方向键和moveSnake(脱缰的野蛇)且 要同时运行且都是要用while死循环去进行接收吗,那么我们单线程就无法做到了,这个时候要学习多线程,这样我们就是可以同时执行两个死循环。

所以我们先学习一下多线程的基本原理

想让我们先看个单线程的例子

我想通过这个单线程的返回结果,和等下多线程的返回结果做对比就很容易理解多线程的意思了;

        因为在单线程环境下,函数是顺序执行的。当调用 FUNC1 时,即使里面有sleep操作,它也只是让执行流程暂停一下,并不会切换到其他函数执行,所以会先输出this is FUNC1,等待sleep时间过后再继续输出this is FUNC1

假如现在这段代码变成了多线程

在多线程环境下,当一个线程执行到类似sleep操作时,它会暂停执行一段时间,这时操作系统会调度其他可运行的线程。

假设存在FUNC1FUNC2两个函数分别在不同线程中执行:

  1. FUNC1所在线程执行到sleep时,该线程会进入休眠状态,此时操作系统会寻找其他可运行的线程,很可能就会找到FUNC2所在线程(如果FUNC2线程处于可运行状态),然后开始执行FUNC2
  2. FUNC2执行到类似sleep操作后,它也会暂停,操作系统又会重新调度,此时FUNC1线程休眠时间结束的话,可能会继续执行FUNC1;如果FUNC1还没结束休眠,且还有其他可运行线程,就可能会执行其他线程。

这种线程间的切换和执行顺序,取决于操作系统的线程调度机制,没有办法精确地预测哪个线程会在特定时刻执行,除非使用线程同步机制(如锁、条件变量、信号量等)来控制线程的执行顺序。例如在 Java 中,可以使用join方法让一个线程等待另一个线程执行完毕;或者使用同步块和waitnotify方法来控制线程间的协作和执行顺序。

通过下面的例子学习一下

运行后是这样的  一直循环这个两句话,就是说不一定每次调用的都是一样的(在多核是同时运行,在单核是他们去争夺cpu资源),但是可以保证他们可以同时运行的(这就够了)

  • #include <pthread.h> :引入 POSIX 线程库的头文件,该库用于在 C 语言中实现多线程编程,后续用到的 pthread_t 类型以及 pthread_create 等函数声明都在这个头文件中。
  • func1 和 func2 是两个线程函数。它们的返回类型为 void * ,这是 POSIX 线程库中线程函数的标准返回类型要求。
  • 每个函数内部都有一个无限循环 while(1) 。在循环中,先通过 printf 输出相应的提示信息("this is func1\n" 或 "this is func2\n" ),然后调用 sleep(1) 让线程暂停 1 秒钟。这使得线线程变量声明pthread_t th1; 和 pthread_t th2; 声明了两个 pthread_t 类型的变量,pthread_t 用于标识线程,th1 和 th2 分别用来标识即将创建的两个不同线程。
  • 线程创建
    • pthread_create(&th1,NULL,func1,NULL); 调用 pthread_create 函数创建了一个新线程,该线程的标识符存储在 th1 中。第二个参数 NULL 表示使用默认的线程属性;第三个参数 func1 指明这个新线程要执行的函数是 func1 ;第四个参数 NULL 表示不向 func1 函数传递额外参数 。
    • 同理,pthread_create(&th2,NULL,func2,NULL); 创建了另一个线程,其标识符为 th2 ,执行函数为 func2 。
  • 无限循环与程序结束while(1); 是一个无限循环,这里的作用是防止 main 函数执行结束。因为在多线程程序中,如果主线程(main 函数所在线程)执行完毕退出,那么其他子线程也会被操作系统强制终止。通过这个无限循环,让主线程一直保持运行状态,从而使得创建的 th1 和 th2 线程能持续执行它们各自的任务。return 0; 理论上不会执行到,不过按照 main 函数的规范,需要有返回值声明。

整体功能

这段代码实现了一个简单的多线程程序,创建了两个线程分别执行 func1 和 func2 函数,这两个线程会并发地不断打印各自的提示信息并间隔 1 秒。但代码存在一些不足,比如没有对线程的返回值进行处理,也没有合理的方式来终止线程或主线程,实际应用中可以进一步完善,比如添加信号处理机制来结束程序等 。

  • 程会不断重复打印信息并间隔 1 秒。

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

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

相关文章

react-diff-viewer 如何实现语法高亮

前言 react-diff-viewer 是一个很好的 diff 展示库&#xff0c;但是也有一些坑点和不完善的地方&#xff0c;本文旨在描述如何在这个库中实现自定义语法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…

coco数据集mAP评估

0 coco数据集划分说明 1 用yolo自带的评估 from ultralytics import YOLOmodel YOLO("../spatial-perception/checkpoints/yolo11n.pt")metrics model.val(data"./coco.yaml", save_jsonTrue) ## save_json为True,可以把预测结果存成json文件&#xff…

sensitive-word-admin v2.0.0 全新 ui 版本发布!vue+前后端分离

前言 sensitive-word-admin 最初的定位是让大家知道如何使用 sensitive-word&#xff0c;所以开始想做个简单的例子。 不过秉持着把一个工具做好的原则&#xff0c;也收到很多小伙伴的建议。 v2.0.0 在 ruoyi-vue&#xff08;也非常感谢若依作者多年来的无私奉献&#xff09…

好消息!PyCharm 社区版现已支持直接选择 WSL 终端为默认终端

在过去&#xff0c;PyCharm 社区版虽然提供了链接 Windows 子系统 Linux&#xff08;WSL&#xff09;终端的能力&#xff0c;但用户无法在设置中直接指定 WSL 为默认终端&#xff0c;这一功能仅限于专业版使用者。 而现在&#xff0c;在 PyCharm 2025.1.1 版本中&#xff0c;Je…

【Redis】string 字符串

文章目录 string 字符串常用命令设置和获取setgetmget & mset 计数操作incr & incrbydecr & decrbyincrbyfloat 字符串操作appendstrlengetrangesetrange 应用场景 string 字符串 关于 Redis 的字符串&#xff0c;有几点需要注意 Redis 所有的 key 的类型都是字符…

本地部署firecrawl的两种方式,自托管和源码部署

网上资料很多 AI爬虫黑科技 firecrawl本地部署-CSDN博客 源码部署 前提条件本地安装py&#xff0c;node.js环境,嫌弃麻烦直接使用第二种 使用git或下载压缩包 git clone https://github.com/mendableai/firecrawl.git 设置环境参数 cd /firecrawl/apps/api 复制环境参数 …

(三)毛子整洁架构(Infrastructure层/DapperHelper/乐观锁)

文章目录 项目地址一、Infrastructure Layer1.1 创建Application层需要的服务1. Clock服务2. Email 服务3. 注册服务 1.2 数据库服务1. 表配置Configurations2. Respository实现3. 数据库链接Factory实现4. Dapper的DataOnly服务实现5. 所有数据库服务注册 1.3 基于RowVersion的…

uni-app微信小程序登录流程详解

文章目录 uni-app微信小程序登录流程实战详解微信小程序登录流程概述1. 获取登录凭证&#xff08;code&#xff09;2. 发送登录请求3. 保存登录态4. 登录状态管理5. 应用登录状态请求拦截器中添加 token自动登录页面路由守卫 使用 Vuex 集中管理登录状态登录组件示例登录流程最…

GUC并发编程和SpringCloud,二者之间的关系

一.提问 我认为&#xff0c;Java开发中&#xff0c;如果项目的每一个小模块需要不同人员并行开发时&#xff0c;就需要使用SpringCloud&#xff1b;如果要解决系统用户激增&#xff0c;就是用GUC并发编程。 这个说法对么&#xff1f; 二.解答 你的理解部分正确&#xff0c;但不…

在 Vue 3 中使用 canvas-confetti 插件

&#x1f389; 在 Vue 3 中使用 canvas-confetti 插件 canvas-confetti 是一个轻量、无依赖的 JavaScript 动画库&#xff0c;用于在网页上展示彩带、庆祝动画。非常适合用于抽奖、支付成功、活动庆祝等场景。 本教程将指导你如何在 Vue 3 项目中集成并使用该插件。 &#x1…

深入解析Spring Boot项目目录结构:从新手到规范实践

一、标准项目结构全景图 典型的Spring Boot项目&#xff08;Maven构建&#xff09;目录结构如下&#xff1a; my-spring-project/ ├── src/ │ ├── main/ │ │ ├── java/ # 核心代码 │ │ │ └── com/ │ │ │ └── exa…

【C语言】宏经典练习题,交换奇偶位

交换奇偶位 写一个宏&#xff0c;可以将一个整数的二进制位的奇数位和偶数位交换。 #define Swap(x) x(((x&0x55555555)<<1)((x&0xaaaaaaaa)>>1)) int main() {int a 10;Swap(a);printf("%d\n", a);return 0; } 写宏的思路&#xff1a; 假设…

VSCode-插件:codegeex:ai coding assistant / 清华智普 AI 插件

一、官网 https://codegeex.cn/ 二、vscode 安装插件 点击安装即可&#xff0c;无需复杂操作&#xff0c;国内软件&#xff0c;无需科学上网&#xff0c;非常友好 三、智能注释 输入 // 或者 空格---后边自动出现注释信息&#xff0c;&#xff0c;按下 Tab 键&#xff0c;进…

FFmpeg 与 C++ 构建音视频处理全链路实战(三)—— FFmpeg 内存模型

经过前面文章的 FFmpeg 编程实践&#xff0c;相信你已经对AVPacket和AVFrame这两个核心结构体不再陌生。当我们编写代码时&#xff0c;频繁调用unref系列 API 释放内存的操作&#xff0c;或许让你心生疑惑&#xff1a;这些函数究竟是如何实现内存释放的&#xff1f;又该在何时准…

c 中的哈希表

哈希是一种可以接受各种类型、大小的输入&#xff0c;输出一个固定长度整数的过程。你可以将哈希理解成一种特殊的映射&#xff0c;哈希映射&#xff0c;将一个理论无限的集合A映射到有限整数集合B上。 哈希函数&#xff1a;哈希函数是哈希过程的核心&#xff0c;它决定了哈希映…

【一次成功!】Ubuntu22.04安装cartographer

之前在ubuntu20.04上成功安装cartographer&#xff0c;但是翻遍全网都没找到官方的22.04安装教程&#xff0c;然后找到小鱼的&#xff0c;试了一下&#xff0c;一次成功&#xff0c;连接如下&#xff1a; gd2l-ros2/docs/humble/chapt10/get_started/2.Carto介绍及安装.md at …

【WPF】Opacity 属性的使用

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Opacity 属性是定义一个元素透明度的属性&#xff0c;其值范围是从 0.0&#xff08;完全透明&#xff09;到 1.0&#xff08;完全不透明&#xff09;。由于 Opacity 是在 UIElement 类中定义的&…

8天Python从入门到精通【itheima】-6~10

目录 7节-开发出第一个Python程序: 1.在cmd窗口写下第一个最简单的程序:Hello World!!! 9节: 1.如何卸载python: 2.报错:不是可运行的程序 ​编辑 3.报错:无法初始化设备PRN: 4.报错:语法错误——非法的字符 10节-python解释器: 1.python解释器的原理: 2.解…

Mac 3大好用的复制粘贴管理工具对比

剪贴板管理器是查看复制粘贴历史记录的工具&#xff0c;几乎是每个苹果电脑用户必备工具。市面上的工具很多&#xff0c;我结合了功能丰富、设计简洁、交互便利整理了目前3款头部剪贴板应用 Paste、PasteNow、PasteMe。 Paste 优势&#xff1a;老牌剪切板应用&#xff0c;功能…

2025年全国青少年信息素养大赛初赛模拟测试网站崩了的原因及应对比赛流程

2025年全国青少年信息素养大赛初赛模拟测试昨天开始&#xff0c;由于同一时间涌入太多的人&#xff0c;导致网站的服务器奔了&#xff0c;出现了各种状况&#xff0c;导致很多人没有模拟上&#xff0c;大家今天可以刷新或者提前打开网页。 有的是一直“转圈圈”&#xff0c;有的…