c语言利用控制台实现贪吃蛇

使用控制台实现贪吃蛇需要的技能加点:

控制台设置(包含于stdlib.h):

定义命令行窗口高/宽:
system("mode con cols=100 lines=30");

        system() 函数是一个C标准库函数,它允许程序执行操作系统命令。
        mode 是命令的关键字,代表“模式”,用于改变系统设备的配置。
        con 是“console”的缩写,指代命令行界面或控制台窗口。
        cols=100 设置了窗口的列数(宽度)为100个字符。
        lines=30 设置了窗口的行数(高度)为30行。
        长宽(二倍关系);
        合起来为改变控制台(命令行)窗口的配置:高30 宽100.

设置标题:
system("title 贪吃蛇");

        在程序运行结束前设定标题为贪吃蛇;
        在程序结束后标题恢复为默认状态。

控制台窗口坐标:

                                                  坐标图(x轴俩个单位长度等于y轴一个单位)

使用windows.h头文件可以使用该结构体,该结构体有俩个成员:

typedef struct _COORD {
            SHORT X;
            SHORT Y;
    } COORD, *PCOORD;

    通过X,Y可以确定一个控制台上的一个位置,后面会用它来调整光标位置。


    光标

获取/使用句柄(控制台句柄):

        获取句柄

        句柄可以理解为一个中介,通过中介可以操纵使用中介手里的资源。例如你需要找个兼职,但是不知道怎么做才能兼职,于是你找了一个地区的兼职中介,你将需求告诉了中介,最后中介帮你搞定了找兼职的事,这个中介只管他所在地区的资源。
        在这里你需要隐藏掉控制台的光标,毕竟我们不需要在贪吃蛇显示光标。

//获取当前控制台界面的句柄
HANDLE houtput = NULL;
//定义结构体用来接收存放句柄;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);

        GetStdHandle()是Windows API 提供的函数,参数为标准设备(标准输入、标准输出、标准错误),返回值为对应句柄
        STD_OUTPUT_HANDLE 表示标准输出设备(当前控制台)。

        使用句柄

        我们可以通过这个句柄对当前的控制台进行一些操作,这里我们先来一个获取光标信息的操作:

//首先定义一个光标信息的结构体
CONSOLE_CURSOR_INFO cursor_info = {0};

        该结构体是Windows API中用于描述控制台光标属性的数据结构,它包含以下两个成员:

typedef struct {
    DWORD dwSize;
    BOOL  bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

关键词解释:

        dwSize:这是一个DWORD类型的变量,表示光标的大小。这里的“大小”并不是指光标的物理尺寸,而是指光标所覆盖的字符单元的百分比。例如,如果设置为50,则光标将占据半个字符单元的高度。此值必须在1到100之间;
        DWORD类型是Windows API中定义的一种数据类型,全称为Double Word,表示一个32位的无符号整数。

        bVisible:这是一个BOOL类型的变量,指示光标是否可见。如果bVisible为TRUE,则光标可见;如果为FALSE,则光标不可见。


下面演示一下获取句柄->通过该句柄得到光标数据->打印出光标数据

        //获得标准输出设备的句柄
        HANDLE houtput = NULL;
        houtput = GetStdHandle(STD_OUTPUT_HANDLE);
        //定义一个光标信息的结构体
        CONSOLE_CURSOR_INFO cursor_info = {0};
        //获取和houtput句柄相关的控制台上的光标信息,存放在cursor_info中
        GetConsoleCursorInfo(houtput, &cursor_info);
        //打印当前控制台的光标大小,光标隐藏情况
        printf("%u, %d", cursor_info.dwSize, cursor_info.bVisible);

        除了得到句柄,我们也可以定义一个光标属性的结构体,设定好里面的参数,然后将他交给句柄来改变光标属性:
        

        //定义一个光标信息的结构体
        CONSOLE_CURSOR_INFO cursor_info = {0};
        //设定光标显示:否
        cursor_info.bVisible = false;
        //像前面一样,获得当前标准输出设备的句柄
        HANDLE houtput = NULL;
        houtput = GetStdHandle(STD_OUTPUT_HANDLE);
        //设置控制台上的光标信息(通过刚刚获得的句柄,SetConsoleCursorInfo通过句柄可以知道更改的是哪个窗口
        SetConsoleCursorInfo(houtput, &cursor_info);
        //暂停程序以便观察
        system("pause");
        观察发现光标不见了(不显示)

    改变光标位置(实现在控制台不同位置打印文本,而不是默认的从左到右、从上到下):

        获得当前标准输出设备的句柄
        HANDLE houtput = NULL;
        houtput = GetStdHandle(STD_OUTPUT_HANDLE);
        定义并初始化一个窗口坐标结构体,参数为横纵坐标:
        COORD pos = { 10, 20 };
        使用光标位置调整函数(参数为句柄和窗口坐标结构体)
        SetConsoleCursorPosition(houtput, pos);
        测试光标位置
        printf("10,20位置的光标在这里");

检测按键:

        在游戏中,我们需要通过上下左右控制蛇的方向,所以我们还需要一个检测按键的方法:

        以下举例为检测数字5是否被按过:

short ret = GetAsyncKeyState(0x35);
if ((ret & 1) == 1)//这里使用了位与运算符&来检查ret的最低位(第0位)是否为1
    printf("5被按过\n");
    else
    printf("没有被按过\n");

        这里用到了GetAsyncKeyState函数:这是Windows API中的一个函数,用于检查指定虚拟键的当前状态。它接收一个虚拟键码作为参数,返回一个短整型(short)值,该值反映了按键的"状态"包括"是否被按下"。虚拟键码0x35对应于键盘上的数字键"5"。        

        这段代码调用了GetAsyncKeyState函数,传入数字键"5"的虚拟键码,获取其状态并存储在变量ret中

        由于我们此次项目只检测按键是否被按过这一状态,不在乎按键的其他状态(如是否为持续按住),所以我们只会用到ret的最后一位数字,该数字为1表示按过,为0表示没按过。     
        我们可以用做一个宏用来检测某键是否被按过

 #define KEY_PRESS(vk)  ((GetAsyncKeyState(vk)&1)?1:0)

        参数为虚拟键码vk。
        虚拟键码表可以自行网上搜索

本地化:

        有一些特殊的字符需要俩个字节的编码来表示,而使用这些字符需要设置一个本地化:由于世界不同地区的文字差异,时间格式差异,符号差异,货币单位差异...,不同地区有不同的双字节编码方式(同样的编码可能表示不同的字符)。

        以下为查询当前程序的本地化设置。

#include <locale.h>//我们要用的setlocale函数在这个头文件        
    char* ret = setlocale(LC_ALL, NULL);
    printf("%s\n", ret);//打印出当前的本地化设置名称。

        调用setlocale函数,参数LC_ALL是一个宏,代表所有类别,用它的意思是对所有的地区性的东西动手。第二个参数为NULL时,NULL表示不修改地区性的东西。setlocale会返回修改后的本地化设置名称(返回的设置名称被存储在ret指向的字符数组中,随后通过printf打印出来。)

        以下为改变当前程序的本地化设置

    ret = setlocale(LC_ALL, "");

    printf("%s\n", ret);//再次打印出本地化设置名称。

        调用setlocale函数,这次第二个参数是空字符串""。当使用空字符串作为参数时,setlocale会尝试根据环境变量(如LC_ALL, LANG, LC_CTYPE等)来设置本地化信息,通常会选择用户或系统的默认本地化设置。

        再次打印出本地化设置名称,这次是在尝试应用用户默认设置后得到的结果。如果用户的默认设置与之前的查询结果不同,这里应该能看到变化。

        我们先尝试一下本地化后的汉字打印,以及本地的特殊符号打印:

//设置本地化
    setlocale(LC_ALL, "");
    //定义一个字符变量 ,由于该变量占俩个字节,所以叫做宽字符,宽字符的变量类型为wchar_t,赋值的变量前面加上L来说明该字符为宽字符,防止程序将其误会为普通的char类型字符
    wchar_t a = L'本';
    wchar_t b = L'地';
    wprintf(L"%lc\n", wc1);
    wprintf(L"%lc\n", wc2);
    wprintf(L"%lc\n", L'●');
    wprintf(L"%lc\n", L'★');

        打印可以看到宽字符的宽度是普通字符的俩倍。

单链表和结构体知识:

        蛇的身体我们通过单链表来维护。

贪吃蛇游戏实现:

适配本地环境,设定随机数"起点"  

//设置适配本地环境
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));

        我们生成食物需要用到随机数。

初始化游戏:

    1. 打印开始界面(欢迎界面,下一步后记得清屏)

        通过控制控制台坐标来调整光标位置,再打印文字来实现欢迎界面打印

system("cls")//下一步进入操作介绍前记得清屏


    2. 功能介绍

            介绍游戏的操作方式,如何控制移动方向,设置速度,实现方式和上一步同理
    3. 绘制地图

            打印一个墙体,墙用方格子表示,方格子是宽字符,需设置本地化
    4. 创建蛇

            本质是创建一个链表,然后打印出来
    5. 创建食物

            在随机的坐标生成食物,本质为创建一个不和蛇相交的结点并打印

            注意食物的坐标横坐标应该为偶数
    6. 设置游戏的相关信息(开局蛇的方向,分数,食物分数,蛇速,蛇状态为存活)

            创建一个结构体来维护这些信息

运行游戏:

    1.显示打印当前分数和食物的分值
    2.用if else检测刚刚的按键情况并执行相应任务,改变方向、退出、加速、减速、暂停
    3.结算蛇走一步
        根据结算决定是否进行下一个循环:
        啥事没有->继续:蛇身链表头增,尾删实现蛇移动一格
        撞自己->结束 :蛇头遍历蛇身体存在相同坐标,此时代表撞了自己,设置蛇状态为撞自己并结束游戏。
        撞墙->结束:蛇头遍历墙体坐标存在相同坐标,此时代表撞了墙,设置蛇状态为撞墙并结束游戏。
        吃了食物->继续:蛇头坐标与食物坐标相同,蛇身链表不尾删,只头增,同时创建一个新食物,同初始化游戏第五条。
    4.程序休息一点点时间(模拟蛇的速度)

Sleep(time);

结束游戏:

    1.打印结束原因(你撞墙了,你撞自己了...)
    2.释放蛇链表内存
    3.询问是否再来一盘

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

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

相关文章

深度学习Day-14:RNN实现心脏病预测

&#x1f368; 本文为&#xff1a;[&#x1f517;365天深度学习训练营] 中的学习记录博客 &#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制] 要求&#xff1a; 本地读取并加载数据&#xff1b;了解循环神经网络RNN的构建过程&#xff1b;测试集accuracy达到87%…

Linux--Linux常用命令

Linux常用命令 前言Linux命令格式命令讲解1、ls:查看当前目录下所有的内容语法:ls[-al][dir]2、pwd: 查看当前所在目录3、cd : 切换目录4、touch[文件名] : 如果文件不存在新建文件5、mkdir: 创建目录6、rm: 删除指定文件7、rmdir: 删除空目录8、cat:用于显示文件内容9、m…

MySQL8.0.36-社区版:二进制日志(4)

什么是二进制日志&#xff08;binlog&#xff09;&#xff1a;记录了所有的ddl和dml语句&#xff0c;但是不包括查询类的 二进制日志的作用&#xff1a;1.灾难恢复&#xff0c;2.mysql主从复制 查看二进制日志状态 show variables like %log_bin%; 在mysql8中默认是开启的 | l…

Docker - Compose

原文地址&#xff0c;使用效果更佳&#xff01; Docker - Compose | CoderMast编程桅杆Docker - Compose 在部署应用时&#xff0c;常常使用到不止一个容器&#xff0c;那么在部署容器的时候就需要一个一个进行部署&#xff0c;这样的部署过程也相对来说比较繁琐复杂&#xff…

使用 OpenCV 测量物体尺寸

使用 OpenCV 测量物体尺寸 你是否曾经遇到过这样的问题&#xff1a;想要知道计算器的精确尺寸&#xff0c;但手头又没有专业的测量工具&#xff1f;别担心&#xff0c;今天我们就来教大家一个简单又实用的方法&#xff0c;通过一张A4纸就能估算出计算器的宽度和高度&#xff0c…

Python 全栈安全(三)

原文&#xff1a;annas-archive.org/md5/712ab41a4ed6036d0e8214d788514d6b 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十一章&#xff1a;OAuth 2 本章内容 注册 OAuth 客户端 请求对受保护资源的授权 授权而不暴露身份验证凭据 访问受保护的资源 OAuth …

指针的使用以及运算、二级指针、造成野指针的原因以及解决方法、指针和数组相互使用

第七章&#xff0c;指针的学习 目录 前言 一、指针的概念 二、指针的类型 三、野指针 四、指针的运算 五、指针和数组的关系以及使用 六、指针数组 七、二级指针 总结 前言 这章主要学习的是指针方面的知识&#xff0c;这节只是简单了解一下指针&#xff0c;并不会深…

判断水仙花数(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int n 0;int b 0;int s 0;int g 0;int m 0;//提示用户&#xff1b;printf("请输入…

java-Spring-bean的生命周期

定义 程序中的每个对象都有生命周期&#xff0c;对象的创建、初始化、应用、销毁的整个过程称之为对象的生命周期&#xff1b; 在对象创建以后需要初始化&#xff0c;应用完成以后需要销毁时执行的一些方法&#xff0c;可以称之为是生命周期方法&#xff1b; 在spring中&…

Azure AD统一认证及用户数据同步开发指导

本文主要目的为&#xff1a;指导开发者进行自有服务与Azure AD统一认证的集成&#xff0c;以及阐述云端用户数据同步的实现方案。本文除了会介绍必要的概念、原理、流程外&#xff0c;还会包含Azure门户设置说明&#xff0c;以及使用Fiddler进行全流程的实操验证&#xff0c;同…

学习笔记-数据结构-线性表(2024-04-17)

设计一个算法实现在单链表中删除值相同的多余节点的算法。 设计思想&#xff1a;双指针 变量说明&#xff1a; head - 参数变量&#xff0c;代表链表的头节点。在调用DelSameNum函数时&#xff0c;需要传递链表的头节点的地址给这个参数&#xff0c;从而允许函数对链表进行操作…

21.leetcode---用栈列实现队列(Java版)

题目链接: https://leetcode.cn/problems/implement-queue-using-stacks/ 题解: 代码: 测试:

C# WPF布局

布局&#xff1a; 1、Grid: <Window x:Class"WpfApp2.MainWindow" xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d"http://schemas.microsoft.com…

SpringBoot---------Lombook

Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具&#xff0c;通过使用对应的注解&#xff0c;可以在编译源码的时候生成对应的方法&#xff0c;也就是简化咱们之前pojo&#xff0c;实体类里面臃肿的get/set有参无参。 首先查看一…

LiveNVR监控流媒体Onvif/RTSP常见问题-如何对比监控摄像头延时视频流延时支持webrtc视频流播放超低延时播放

LiveNVR如何对比监控摄像头延时视频流延时支持webrtc视频流播放超低延时播放 1、问题场景2、如何对比延时&#xff1f;3、WEBRTC延时对比4、LiveNVR支持WEBRTC输出5、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 1、问题场景 需要低延时的视频流监控播放&#xff0c;之前可以用rtmp…

react合成事件与原生事件区别备忘

朋友问起在做一个下拉框组件&#xff0c;下拉的点击事件是用react的onClick触发&#xff0c;外部区域点击关闭则用dom的原生点击事件绑定&#xff0c;问题是下拉的点击事件无法阻止冒泡到dom的原生事件。 我说&#xff0c;react的合成事件 和 原生事件是不一样的&#xff0c;尽…

前端表单input的简单使用

1.代码结构介绍 2.实战效果

【嵌入式linux】Ubuntu 修改用户名

第一次打开Ubuntu时不小心把初始用户名“siriusiot”写成“siriousiot”&#xff08;多了一个o&#xff09; 。作为技术人&#xff0c;我们要保持严谨&#xff0c;我们要纠正过来&#xff08;其实就是单词拼错了怕被笑话&#xff09;。 打开终端&#xff0c;输入&#xff1a; …

TypeError: Cannot read property ‘forceUpdate‘ of undefined

今天给大家展示一个 我自己在写项目的时候遇到的保存 其实很简单就是没有修改addid 把自己的小程序appid填上去就好了 学习记录笔记&#xff01;

【高校科研前沿】东北地理所孙敬轩博士为一作在《中国科学:地球科学(中英文版)》发文:气候变化下东北地区农业绿水安全风险评估

目录 01 文章简介 02 研究内容 03 文章引用 04 期刊简介 01 文章简介 论文名称&#xff1a;Risk assessment of agricultural green water security in Northeast China under climate change&#xff08;气候变化下东北地区农业绿水安全风险评估&#xff09; 第一作者及…