基于C+ncurses 实现的贪吃蛇

news/2024/7/27 9:01:09/文章来源:https://blog.csdn.net/mx_jun/article/details/137154252

游戏说明:


linux 环境下的,基于Ncursse 图形图的C语言小游戏

基础要求:


C语言基础
Linux 基本操作:  
如何编写代码 如何编译代码  如何运行程序  如何创建文件夹


为什么需要应用ncurse 库:


C语言的键盘输入 函数  没法满足实时性 -- scanf gets getchar -- 都需要 回车确认

curse 输入和输出:


键盘响应快 
#include<curses.h>
initscr();
printw("This is a curse window.\n");  // ncurse 模式的printf
getch();  //等待用户输入 ,if 没这句话程序会自动退出,看不到运行结果
endwin(); //程序退出-- 调用函数来恢复shell终端的显示,没有这句话,shell终端就会乱码

nurse 的上下左右 键:
#define  KEY_DOWN 0402
#define KEY_UP  0403
#define KEY_LEFT 0404
#define KEY_RIGHT 0405

上面四个键是功能键 -- 使用的时候需要我们 引入 keypad函数

keypad(stdscr,1);


#include <curses.h>

int main()
{
    int key;
    initscr();
    keypad(stdscr, 1);
    while (1)
    {
      key = getch();
      switch (key)
      {
     // case 0402:
      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;
  
      }

        
    }
    endwin();

    return 0;
}


======================================

贪吃蛇地图:


20 * 20 的格子 :
地图上界"--"
地图左界"|"
苹果(食物)"##"
蛇身 "[][][[]"

void myPrintMap()
{
  int line, row;

  for (line = 0; line < 20; ++line)
  {
    if (line == 0)
    {
      for (row = 0; row < 20; row++)
        printw("--");
      printw("\n");
    }

    if (line >= 0 && line < 20)
    {
      for (row = 0; row <= 20; row++)
      {
        if (row == 0)
          printw("|");
        else if (row == 20)
          printw("|\n");
        else
          printw("  ");
      }
      if (line == 19)
      {
        for (row = 0; row < 20; row++)
        {
          if (row == 19)
            printw("--\nby mxjun");
          else
            printw("--");
        }
      }
    }
  }
}

=========================================


贪吃蛇身子 节点 -- 结构体--使用链表实现

//扫描匹配到贪吃蛇的身体节点 --行列都匹配 
不打空格 ,打出蛇身子
-- printw("[]") --显示出来

//判断是否需要显示蛇的身子 
int haveSnack(int line,int row)
{
struct snack * p=&s1;

 while(p!=NULL)
 {
  if(p->line==line && p->row==row)
   return 1;
  p=p->next;
 }
return 0;

}

=======================================


链表添加蛇的节点:


void addSnack()
{
 //尾插法实现
 struct Snack *new = (struct Snack*)malloc(sizeof(struct Snack));
 new->line=tail->line; 
 new->row=tail->row+1;
 new->next=NULL; //作为新的尾巴
 tail->next=new;
 tail=new;
}

void initSnack()
{
head = (struct Snack*)malloc(sizeof(struct Snack));
head->line=2;
head->row=2;
head->next=NULL;
tail=head;

addSnack();
addSnack();
addSnack();

}

=========================================


实现贪吃蛇的向右移动:

1.右移:
原理 删除第一个, 在最右边一个节点就没添加一个

void delSnack()
{
 struct Snack* new;
 new=head;
 head=head->next;
 free(new);
}

void moveRight()
{
 addSnack();
 delSnack();

}

===============================


贪吃蛇撞墙 ;

void initSnack()
{
struct Snack*new=NULL;
while(head!=NULL)
 {
  new=head;
 head=head->next;
free(new);

 }


head = (struct Snack*)malloc(sizeof(struct Snack));
head->line=2;
head->row=2;
head->next=NULL;
tail=head;

addSnack();
addSnack();
addSnack();

}


void moveRight()
{
 addSnack();
 delSnack();
  //判断是否撞到墙
 if(tail->row==0 || tail->row==20)
   {
   initSnack();

   }

}

usleep () -- 休眠
refresh()  -- 刷新

========================


贪吃蛇的自行游走

 -- while -- 一直执行, move函数 usleep() -- 控制时间

 warning: implicit declaration of function ‘usleep’ [-Wimplicit-function-declaration]
  156 |        usleep(100000);
// 这种警报 可以直接man usleep 查看他的头文件

===============================

方向变化 -- 多线程


引入 -- .界面一边在刷新,我们也要求一边能接收我们的按键  -- 多线程 --让两个while 同时进行


怎么实现多线程:


Linux 线程:

创建线程的代码:
#include<pthread.h>
ret = pthread_create(&th,NULL,thread,&arg ); // thread --可以是函数 -- 我们要执行的


#include<stdio.h>
#include<pthread.h>

void* func1()
{
 while(1)
  {
  puts("func1 is running");
  sleep(1);
  }

}

void* func2()
{
 while(1)
  {
  puts("func2 is running");
  sleep(1);
  }

}

int main()
{
 pthread_t th1;
 pthread_t th2;


 pthread_create(&th1,NULL,func1,NULL);
 pthread_create(&th2,NULL,func2,NULL);


 while(1);

}


===================================


蛇的转弯


定义全局 的方向变量dir 
在键入 方向键的时候进行修改 -- 然后在moveSncak()的addSnack() 的时候 方向 被改变


===================================

绝对值优化蛇的不合理走位;

abs 函数 -- 去绝对值

initNcurses() -- noecho -- 不要把无关信息显示在界面上

#define UP    1
#define DOWN  2
#define LEFT 3
#define RIGHT 4

oid turn(int newDir)
{
 if(abs(newDir)!=abs(dir))
   dir=newDir;

}

void *inputKey()
{

  while (1)
  {
    key = getch();
    switch (key)
    {
    case KEY_DOWN:
      turn(DOWN);
      break;
    case KEY_UP:
      turn(UP);
      break;
    case KEY_LEFT:
      turn(LEFT);
      break;
    case KEY_RIGHT:
     turn(RIGHT);
      break;
    }
  }
}

=====================================


苹果 -- 食物

食物可以使用之前的snack 结构体里面的坐标表示

static -- 静态变量 -- 函数被再次调用打的时候他的值不会发生变化

rand()  --实现食物随机生成: % 20 -- 限制范围

==================================


蛇能吃自己 -- 链表尾巴 和 身体的重合

//这里我们再明确一点 ,由于我们使用的是尾插法,so我们得到的链表尾巴就是蛇头

代码整体实现

#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2struct Snack //定义蛇类
{int line;int row;struct Snack *next; //链表实现贪吃蛇变长
};
// 定义蛇头,蛇尾
struct Snack *head = NULL;
struct Snack *tail = NULL;
struct Snack food; //定义食物类
int key;
int dir; //记录方向void initNcurse() //对Ncurse初始化
{initscr();keypad(stdscr, 1);noecho(); //避免键入方向键的时候屏幕错乱,返回值
}void initFood() //初始化食物
{
int x=rand()%20; //随机生成0-19的位置
int y=rand()%20;food.line=x;
food.row=y;}
int haveFood(int line, int row) //判断这个格子上是否存在食物
{if(food.line==line && food.row==row)return 1;return 0;
}int haveSnack(int line, int row)//判断这个格子上是否存在蛇
{struct Snack *p = head;while (p != NULL){if (p->line == line && p->row == row)return 1;p = p->next;}return 0;
}void addSnack() //增加蛇的长度
{// 尾插法实现struct Snack *new = (struct Snack *)malloc(sizeof(struct Snack));switch (dir) //依据当前的方向判断{case UP:new->line = tail->line-1;new->row = tail->row;new->next = NULL; // 作为新的尾巴break;case DOWN:new->line = tail->line+1;new->row = tail->row;new->next = NULL; // 作为新的尾巴break;case LEFT:new->line = tail->line;new->row = tail->row-1;new->next = NULL; // 作为新的尾巴break;case RIGHT:new->line = tail->line;new->row = tail->row+1;new->next = NULL; // 作为新的尾巴break;}tail->next = new;tail = new;
}void initSnack() //初始化蛇 - 死后 复活
{dir=RIGHT;initFood();struct Snack *new = NULL;while (head != NULL){new = head;head = head->next;free(new);}head = (struct Snack *)malloc(sizeof(struct Snack));head->line = 2;head->row = 2;head->next = NULL;tail = head;addSnack();addSnack();addSnack();
}void myPrintMap() //打印地图
{int line, row;move(0, 0); //每次新的地图都移动到(0,0),覆盖之前的地图实现实时性for (line = 0; line < 20; ++line){if (line == 0){for (row = 0; row < 20; row++)printw("--");printw("\n");}if (line >= 0 && line < 20){for (row = 0; row <= 20; row++){if (row == 0)printw("|");else if (row == 20)printw("|\n");else if (haveSnack(line, row))printw("[]");else if (haveFood(line, row))printw("##");elseprintw("  ");}if (line == 19){for (row = 0; row < 20; row++){if (row == 19)printw("--\nby mxjun");elseprintw("--");}}}}
}void delSnack()
{struct Snack *new;new = head;head = head->next;free(new);
}
int ifSnackDead() //判断边界
{if(tail->line<0 || tail->line==20 || tail->row==0 || tail->row==20)return 1;struct Snack *p=head;while(p->next!=NULL){if(p->line==tail->line && p->row==tail->row)return 1;p=p->next;} 
return 0;}void moveSnack()
{ if(tail->line==food.line && tail->row==food.row) //吃到食物蛇变长{addSnack();initFood();}else //不然只是移动{addSnack();delSnack();}// 判断是否撞到墙if (ifSnackDead()){initSnack();//死了重开}
}void *refreshInterface() //线程之一,实现蛇的自动行走
{while (1){moveSnack();myPrintMap();refresh();usleep(100000);}
}void turn(int newDir) //防止蛇出行头变为尾巴这样的逆天走位
{if(abs(newDir)!=abs(dir))dir=newDir;}void *inputKey() //第二个线程,时刻等待键盘输入
{while (1){key = getch();switch (key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}int main()
{
//定义两个线程pthread_t th1;pthread_t th2;
//初始化蛇和NcurseinitNcurse();initSnack();pthread_create(&th1, NULL, refreshInterface, NULL);pthread_create(&th2, NULL, inputKey, NULL);while (1) //避免程序退出{}getch();endwin(); //结束使用ncurse,退出程序,避免乱码getch();return 0;
}

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

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

相关文章

鸿蒙原生应用开发-网络管理HTTP数据请求

一、场景介绍 应用通过HTTP发起一个数据请求&#xff0c;支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。 二、接口说明 HTTP数据请求功能主要由http模块提供。 使用该功能需要申请ohos.permission.INTERNET权限。 涉及的接口如下表&#xff0c;具体的…

bugku-web-秋名山车神

发现问题 每一次刷新后&#xff0c;得到的算式都不一样 但是按照他的说法在两秒内多次进行页面刷新&#xff0c;这时的算式是相同的 现在就要找到&#xff0c;是以何种方式将答案反馈给他 Give me value post about 965096517*4378809482019677711-1518023767539874227-528320…

【中文视觉语言模型+本地部署 】23.08 阿里Qwen-VL:能对图片理解、定位物体、读取文字的视觉语言模型 (推理最低12G显存+)

项目主页&#xff1a;https://github.com/QwenLM/Qwen-VL 通义前问网页在线使用——&#xff08;文本问答&#xff0c;图片理解&#xff0c;文档解析&#xff09;&#xff1a;https://tongyi.aliyun.com/qianwen/ 论文v3. : 一个全能的视觉语言模型 23.10 Qwen-VL: A Versatile…

Linux:入门篇

文章目录 前言1. Linuxd的安装环境2.Linux的简单介绍2.1 新建目录2.2 新建文件 3.指令到底是什么&#xff1f;4.shell命令以及运行原理5.总结 前言 很多人对于Linux的学习总是感觉无法下手&#xff0c;不知道从何开始学习&#xff0c;相信这篇文章将会为你提供一个清晰的思路。…

Python程序设计 多重循环(二)

1.打印数字图形 输入n&#xff08;n<9)&#xff0c;输出由数字组成的直角三角图形。例如&#xff0c;输入5&#xff0c;输出图形如下 nint(input("")) #开始 for i in range(1,n1):for j in range(1,i1):print(j,end"")print()#结束 2.打印字符图形 …

vlanif三层交换机实现不同网络通信

实验目的&#xff1a;通过三层交换机实现不同 网络通信&#xff0c;之前都是路由器进行不同网络转发 拓扑图 内容&#xff1a;左边vlan10&#xff0c;右边vlan20 lsw1接口通过所有vlan lsw2网路vlan10 lsw3网络vlan20 问题点&#xff1a;开始只是配置了最上面LSW1的交换机…

Leetcode 118. 杨辉三角

心路历程&#xff1a; 其实最直观的想法是可以直接按照从上到下的顺序遍历数组&#xff0c;依次计算数组的值即可&#xff0c;相当于DP表格的初始化。 但是也可以从递归的角度进行思考&#xff0c;cache yyds。 注意的点&#xff1a; 1、注意第二层循环col应该是[0, line1) …

C语言中入门到实战————动态内存管理

目录 前言 一、为什么要有动态内存分配 二、 malloc和free 2.1 malloc 2.2 free 三、calloc和realloc 3.1 calloc 3.2 realloc 四. 常见的动态内存的错误 4.1 对NULL指针的解引用操作 4.2 对动态开辟空间的越界访问 4.3 对非动态开辟内存使用free释放 4.4 使…

网络协议学习——HTTPS

目录 ​编辑 一&#xff0c;认识HTTPS 二&#xff0c;加密方式 1&#xff0c;对称式加密 2&#xff0c;非对称式的加密 3&#xff0c;数据指纹&#xff08;数据摘要&#xff09; 4&#xff0c;数据签名 三&#xff0c;HTTPS的工作原理 实现方式 数字证书 一&#xff0c…

5-规范设计(下):commit信息风格迥异、难以阅读,如何规范?

我们在做代码开发时&#xff0c;经常需要提交代码&#xff0c;提交代码时需要填写 Commit Message&#xff08;提交说明&#xff09;&#xff0c;否则就不允许提交。 所以在 Go 项目开发时&#xff0c;一个好的 Commit Message 至关重要&#xff1a; 可以使自己或者其他开发人…

基于单片机三路信号故障诊断仿真设计

单片机设计介绍&#xff0c;基于单片机三路信号故障诊断仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机三路信号故障诊断仿真设计概要主要涵盖了系统设计的整体框架、关键模块功能、仿真方法以及预期实现的目…

嵌入式硬件中常见的面试问题与实现

1 01 请列举您知道的电阻、电容、电感品牌(最好包括国内、国外品牌) ▶电阻 美国:AVX、VISHAY威世 日本:KOA兴亚、Kyocera京瓷、muRata村田、Panasonic松下、ROHM罗姆、susumu、TDK 台湾:LIZ丽智、PHYCOM飞元、RALEC旺诠、ROYALOHM厚生、SUPEROHM美隆、TA-I大毅、TMT…

ARM FVP平台的terminal窗口大小如何设置

当启动ARM FVP平台时&#xff0c;terminal窗口太小怎么办&#xff1f;看起来非常累眼睛&#xff0c;本博客来解决这个问题。 首先看下ARM FVP平台对Host主机的需求&#xff1a; 通过上图可知&#xff0c;UART默认使用的是xterm。因此&#xff0c;我们需要修改xterm的默认字体设…

【战略前沿】与中国达成生产协议后,飞行汽车即将起飞

【原文】Flying cars edge towards takeoff after Chinese production deal 【作者】Thomas Macaulay 斯洛伐克公司KleinVision签署了一项协议&#xff0c;将大规模生产AirCar。 一辆获得航空认证的飞行汽车向商业化又迈出了一大步。 空中汽车的创造者KleinVision今天宣布出售…

Java进阶-反射的详解与应用

本文深入探讨了Java反射机制的核心概念、应用实例及其在现代Java开发中的重要性。文章首先介绍了反射的基本原理和能力&#xff0c;包括在运行时动态获取类信息、操作对象字段和方法的能力。随后&#xff0c;通过具体代码示例&#xff0c;展示了如何利用反射进行字段访问、方法…

为什么有些网站会提示不安全,提示您与此网站之间建立的连接不安全

有时候当我们尝试访问一个网站时&#xff0c;浏览器会弹出一个警告&#xff0c;提示“您与此网站之间建立的连接不安全”。这是什么意思&#xff1f;这种网站真的不安全吗&#xff1f; 理解HTTP与HTTPS HTTP&#xff08;超文本传输协议&#xff09;是互联网上用于传输数据的基…

点点数据K参数加密逆向分析(RPC方案跟加密算法还原)

文章目录 1. 写在前面2. 接口分析3. 断点分析4. RPC调用5. 算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长…

苹果上架经验分享

苹果上架要求是苹果公司对于提交应用程序到苹果商店上架的要求和规定。这些要求主要是为了保证用户体验、应用程序的质量和安全性。以下是苹果上架要求的详细介绍&#xff1a;1. 应用程序的内容和功能必须符合苹果公司的规 苹果上架要求是苹果公司对于提交应用程序到苹果商店上…

备战蓝桥杯---数论基础刷题1

数论在蓝桥杯上考的不多&#xff0c;但是这不能否定它的重要性。 1.简单的GCD的应用&#xff1a; 分析一下&#xff0c;由等差数列的性质&#xff0c;个数&#xff08;an-a1)/d1&#xff0c;其中an与a1是固定的&#xff0c;因此我们就是让dmax,我们先排一下序&#xff0c;d就是…

《HelloGitHub》第 96 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …