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

贪吃蛇项目的意义

  • 承上启下:从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. 最终效果

    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ELK创建仪表盘

仪表盘 一、保存search二、生成饼图三、创建仪表盘 一、保存search 首先保存一段时间内的search&#xff0c;可以添加想要的字段&#xff0c;并保存这个search方便下次直接打开该search&#xff0c;并方便在可视化和仪表盘中使用该search. 二、生成饼图 点击Visualize 选择…

C语言——内存函数的实现与模拟

1. memcpy 函数 与strcpy 函数类似 1.头文件 <string.h> 2.基本格式 • 函数memcpy从source的位置开始向后复制num个 字节 的数据到destination指向的内存位置。 • 这个函数在遇到 \0 的时候并不会停下来。 • 如果source和destination有任何的重叠&#xff0…

Redis入门到通关之数据结构解析-动态字符串SDS

文章目录 Redis数据结构-动态字符串动态扩容举例二进制安全SDS优点与C语言中的字符串的区别 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间…

Spring Kafka—— KafkaListenerEndpointRegistry 隐式注册分析

由于我想在项目中实现基于 Spring kafka 动态连接 Kafka 服务&#xff0c;指定监听 Topic 并控制消费程序的启动和停止这样一个功能&#xff0c;所以就大概的了解了一下 Spring Kafka 的几个重要的类的概念&#xff0c;内容如下&#xff1a; ConsumerFactory 作用&#xff1a;…

使用JavaScript及HTML、CSS完成秒表计时器

案例要求 1.界面为一个显示计时面板和三个按钮分别为:开始&#xff0c;暂停&#xff0c;重置 2.点击开始&#xff0c;面板开始计时&#xff0c; 3.点击暂停&#xff0c;面板停止 4.点击重置&#xff0c;计时面板重新为0 案例源码 <!DOCTYPE html> <html lang"…

sqlplus / as sysdba登陆失败,(ORA-01017)

周一上班检查alert log&#xff0c;看到某个库报出大量的错误 提示无法连接到ASM实例&#xff0c;这是某知名MES厂商DBA创建的11G RAC刚刚​转交到我手上的&#xff0c;这又是给我挖了什么坑&#xff1f; 报错为ORA-01017​用户名密码不对&#xff1f;​what&#xff1f; 登陆o…

负载均衡的原理及算法

一、定义 负载均衡&#xff08;Load Balancing&#xff09;是一种计算机网络和服务器管理技术&#xff0c;旨在分配网络流量、请求或工作负载到多个服务器或资源&#xff0c;以确保这些服务器能够高效、均匀地处理负载&#xff0c;并且能够提供更高的性能、可用性和可扩展性。…

OpenCV-复数矩阵点乘ComplexMatrixDotMultiplication

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 需求说明 一般用到FFT&#xff0c;就涉及到复数的计算&#xff0c;为了便于调用&#xff0c;我自行封装了一个简单的复数矩阵点乘…

服务器被CC攻击怎么办

遇到CC攻击时&#xff0c;可采取以下措施&#xff1a;限制IP访问频率、启用防DDoS服务、配置Web应用防火墙、增加服务器带宽、使用负载均衡分散请求压力。 处理服务器遭遇CC攻击的方法如下&#xff1a; 1. 确认攻击 你需要确认服务器是否真的遭受了CC攻击&#xff0c;这可以…

Day10-Java进阶-泛型数据结构(树)TreeSet 集合

1. 泛型 1.1 泛型介绍 package com.itheima.generics;import java.util.ArrayList; import java.util.Iterator;public class GenericsDemo1 {/*泛型介绍 : JDK5引入的, 可以在编译阶段约束操作的数据类型, 并进行检查注意 : 泛型默认的类型是Object, 且只能接引用数据类型泛型…

【STM32+HAL+Proteus】系列学习教程3---GPIO输出模式(LED流水灯、LED跑马灯)

实现目标 1、掌握GPIO 输出模式控制 2、学会STM32CubeMX软件配置GPIO 3、具体目标&#xff1a;1、开发板4个LED实现流水灯&#xff1b;2、开发板4个LED实现跑马灯灯。 一、STM32 GPIO 概述 1、GPIO定义 GPIO&#xff08;General-purpose input/output&#xff09;是通用输入…

牛客NC238 加起来和为目标值的组合【中等 DFS C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/172e6420abf84c11840ed6b36a48f8cd 思路 本题是组合问题&#xff0c;相同元素不同排列仍然看作一个结果。 穷经所有的可能子集&#xff0c;若和等于target&#xff0c;加入最终结果集合。 给nums排序是为了方便…

Ai-WB2 系列模组SDK接入亚马逊云

文章目录 前言一、准备二、亚马逊云物模型建立1. 注册亚马逊账号&#xff0c;登录AWS IoT控制台&#xff0c;[注册地址](https://aws.amazon.com/cn/)2. 创建好之后点击登录3. 创建物品以及下载证书 三、连接亚马逊云demo获取以及配置1. 下载源码2. 按照顺序执行下面指令3. 修改…

IDEA 2021.3.3最新激活破解教程(可激活至2099年,亲测有效)

1、ja-netfilter-all Windows 系统&#xff0c;点击运行 install-current-user.vbs 脚本&#xff0c;为当前用户安装破解补丁 截图是window环境下的激活方式 运行此补丁大约花费几分钟&#xff0c;点击 确定&#xff0c; 等待 Done 完成提示框出现&#xff0c;到这里&#xf…

HarmonyOS ArkUI实战开发-页面跳转(Router、Ability)

页面跳转可以分为页面内跳转和页面间跳转&#xff0c;页面内跳转是指所跳转的页面在同一个 Ability 内部&#xff0c;它们之间的跳转可以使用 Router 或者 Navigator 的方式&#xff1b;页面间跳转是指所跳转的页面属与不同的 Ability &#xff0c;这种跳转需要借助 featureAbi…

51单片机数字温度报警器_DS18B20可调上下限(仿真+程序+原理图)

数字温度报警器 1 **主要功能&#xff1a;*****\*资料下载链接&#xff08;可点击&#xff09;&#xff1a;\**** 2 **仿真图&#xff1a;**3 **原理图&#xff1a;**4 **设计报告&#xff1a;**5 **程序设计&#xff1a;**主函数外部中断函数DS18B20驱动 6 讲解视频7 **资料清…

完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城

源码下载地址&#xff1a;完美运营版商城.zip 后台可以自由拖曳修改前端UI页面 还支持虚拟商品自动发货等功能 挺不错的一套源码 前端UNIAPP 后端PHP 一键部署版本

Linux 终止进程命令—sudo kill -9 <进程号>

一、查找占用端口的进程&#xff1a;使用以下命令找到占用了该端口的进程&#xff1a; sudo lsof -i :<端口号> 该命令将显示占用该端口的进程的详细信息。 二、结束占用端口的进程&#xff1a;根据上一步得到的进程信息&#xff0c;使用以下命令结束该进程&#xff1a…

CSS-vminvmax单位

vmin 和 vmax 单位 vmin 是相对于视口宽度和高度中较小值进行计算&#xff0c;它的值为视口宽度和高度中的较小值的百分比。 例如&#xff0c;如果视口宽度为 800px&#xff0c;高度为 1000px&#xff0c;那么 1vmin 等于 8px&#xff08;800px 的 1%&#xff09;。 vmax 是…

【Linux】权限(shell运行原理、概念,Linux权限)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12625432.html 目录 shell命令以及运行原理 创建和删除用户 创建新普通用户 删除用户 Linux权…