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

news2024/12/27 13:08:06

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

控制台设置(包含于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.询问是否再来一盘

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

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

相关文章

Java中创建对象内存分析

package day31; ​ public class Pet {String name;int age;public void shout(){System.out.println("叫了一声");} } ​ package day31; ​ public class Application {public static void main(String[] args) {Pet cat new Pet();cat.name"肥波";cat…

Linux——网络管理nmcli

nmcli 不能独立使用&#xff0c;需要对应的服务启动 1. NetworkManager.service 2. 网络配置和服务不相关 3. 通过 nmcl &#xff49; 建立网络配置和网卡之前的映射关系 网卡 简称&#xff1a;nmcli d DEVICE &#xff1a;物理设备 TYPE: 物理设备类型 ethernet 以太网…

螺纹滑牙的原因有哪些——SunTorque智能扭矩系统

螺纹滑牙的原因&#xff0c;通常是由于在旋紧或旋松过程中&#xff0c;螺纹副之间的摩擦力不足以维持所需的预紧力或工作载荷&#xff0c;导致螺纹副的相对位置发生变化。这种现象可能由多种因素引起&#xff0c;包括材料选择不当、设计不合理、制造工艺缺陷、环境因素以及使用…

AI大模型探索之路-实战篇3:基于私有模型GLM-企业级知识库开发实战

文章目录 前言概述一、本地知识库核心架构回顾&#xff08;RAG&#xff09;1. 知识数据向量化2. 知识数据检索返回 二、大模型选择1. 模型选择标准2. ChatGLM3-6B 三、Embedding模型选择四、改造后的技术选型五、资源准备1. 安装git-lfs2. 下载GLM模型3. 下载Embeding模型 六、…

Java、Spring、Dubbo三者SPI机制原理与区别

Java、Spring、Dubbo三者SPI机制原理与区别 什么是SPI SPI全称为Service Provider Interface&#xff0c;是一种动态替换发现的机制&#xff0c;一种解耦非常优秀的思想&#xff0c;SPI可以很灵活的让接口和实现分离&#xff0c;让api提供者只提供接口&#xff0c;第三方来实…

【嵌入式】keil5安装(同时兼容C51和STM32)

最近在开发STM32的时候&#xff0c;安装Keil5&#xff0c;遇到STM32和C51的共存的问题&#xff0c;在网上找了很多方法&#xff0c;又遇到一些bug&#xff0c;最终还是弄好了。因此将处理的过程记录下来&#xff0c;希望对遇到相同问题的朋友一些启发。 1、下载安装包 Keil P…

判断水仙花数(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("请输入…

贪吃蛇游戏实现(VS编译环境)

贪吃蛇游戏 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C语言&#x1f353; &#x1f33c;文章目录&#x1f33c; 0. 前言 1. 游戏背景 2. 实现后游戏画面展示 3. 技术要求 4. Win32 API介绍 4.1 Win32 API 4.2 控制台程序 4.…

开启农业新篇章:山海鲸智慧农业解决方案全面解析

在农业领域&#xff0c;数字化转型已经成为推动农业发展的重要力量。山海鲸&#xff0c;作为农业科技创新的引领者&#xff0c;推出了全新的智慧农业解决方案&#xff0c;通过运用先进的物联网、大数据和人工智能等技术&#xff0c;为农业生产提供智能化、精准化的管理服务&…

c++模拟实现list——详讲双链表--链表

在C语言中我们已经模拟实现了list&#xff0c;现在对比c看看二者的区别 双链表————详讲 个人博客主页&#xff1a; 个人主页 个人码云 码云代码 文章目录 目录 文章目录 ​编辑 前言 一、list是什么&#xff1f; 二、list的使用 三、模拟实现list和搭建list的结构 1.节点结…

孩子用什么样的灯对眼睛没有伤害?分享五款防近视护眼台灯

随着生活条件逐渐提升&#xff0c;对台灯的需求也越来越大&#xff0c;不管在生活中还是工作中&#xff0c;灯具是必不可少的照明工具了&#xff0c;尤其是对于学生而言。很多家长都在寻找孩子用什么样的灯对眼睛没有伤害&#xff1f;建议最好选择一款合格、专业的护眼台灯&…

SpringBootWeb请求

文章目录 前言一、Postman介绍 二、简单参数三、实体参数四、数组集合参数五、日期参数六、JSON参数七、路径参数 前言 在上一篇文章中&#xff0c;已经基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello Wor…

STM32之HAL开发——FSMC—扩展外部SRAM

SRAM读写时序 对SRAM进行读写数据时&#xff0c;它各个信号线的时序流程如下图 &#xff08;图一&#xff09;SRAM的读时序 &#xff08;图二&#xff09;SRAM的写时序 流程解释 主机使用地址信号线发出要访问的存储器目标地址&#xff1b;控制片选信号CS1#及CS2#使能存储器…

力扣HOT100 - 25. K 个一组翻转链表

解题思路&#xff1a; class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dum new ListNode(0, head);ListNode pre dum;ListNode end dum;while (end.next ! null) {for (int i 0; i < k && end ! null; i) {end end.next;}if …

生成式AI产品图谱全览:投资人、产品经理必备指南

以下是生成式AI产品图谱的核心要点&#xff0c;供投资人、产品经理等关注生成性AI领域的专业人士参考&#xff1a; 技术领域细分&#xff1a;依据AI技术所处理的媒介类型进行划分&#xff0c;包括文本处理、代码生成、图像处理、语音识别、视频分析、3D模型构建、音乐创作和游戏…

深度学习项目设置超参数 parser or dictionary

见惯了parser 有的人却是用字典读的&#xff1a; 将配置文件config.yaml读取到一个dictionary类型的变量cfg中&#xff0c; 后面出现了这样的语句&#xff1a;cfg["trainer"].get("sup_only_epoch", 1): 意思是&#xff1a;在config.yaml文件里key为trai…

Java客户端如何直接调用es的API

Java客户端如何直接调用es的API 一. 问题二. withJson 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 问题 今天做项目的时候&#xff0c;想要直接通过java客户端调用es的api…

PHP反序列化漏洞原理(附带pikachu靶场演示)

1.反序列化概念 序列化:是将变量转换为可保存或传输的字符串的过程;实现函数是serialize()反序列化:就是在适当的时候把这个字符串再转化成原来的变量使用&#xff0c;就是序列化的逆过程。实现函数是unserialize() 直白一点就是&#xff1a;序列化是把对象转换成字节流&#…

基于java+springboot+vue实现的图书借还管理系统小程序(文末源码+Lw+ppt)23-1

摘 要 随着社会的发展&#xff0c;图书借还的管理形势越来越严峻。越来越多的借阅者利用互联网获得信息&#xff0c;但图书借还信息量大。为了方便借阅者更好的获得本图书借还信息&#xff0c;因此&#xff0c;设计一种安全高效的“共享书角”图书借还管理系统极为重要。 为…

python安装pytorch@FreeBSD

先上结论&#xff0c;最后在conda下安装成功了&#xff01; PyTorch是一个开源的人工智能深度学习框架&#xff0c;由Facebook人工智能研究院&#xff08;FAIR&#xff09;基于Torch库开发并维护。PyTorch提供了一个高效、灵活且易于使用的工具集&#xff0c;用于构建和训练深…