当前位置: 首页 > news >正文

使用Win32API实现贪吃蛇小游戏

目录

C语言贪吃蛇项目

基本功能

需要的基础内容

Win32API

介绍

控制台程序部分指令

设置控制台窗口的长宽

设置控制台的名字

控制台在屏幕上的坐标位置结构体COORD

检索指定标准设备的句柄(标准输入、标准输出或标准错误)

光标信息结构体类型CONSOLE_CURSOR_INFO

检索有关指定控制台屏幕上的光标大小和可见性的信息

指定的控制台屏幕缓冲区设置光标的大小和可见性

设置指定控制台屏幕缓冲区中的光标位置

获取按键状态

宽字符打印与本地化

宽字符

头文件locale.h与本地化

类项

设置本地化

宽字符的打印

贪吃蛇项目设计

游戏流程设计

贪吃蛇界面的打印

打印游戏欢迎界面

打印游戏规则界面

打印游戏地图界面

打印游戏地图右侧的帮助信息

贪吃蛇结构体设计

贪吃蛇进行过程设计

初始化蛇身以及游戏信息

初始化食物

游戏进行部分

贪吃蛇运行结束

贪吃蛇项目实现


C语言贪吃蛇项目

基本功能

使用C语言在Windows环境的控制台中模拟实现贪吃蛇

  • 贪吃蛇地图绘制
  • 蛇吃食物的功能(使用键盘按键控制蛇)
  • 蛇撞墙死亡
  • 蛇撞自身死亡
  • 计算得分
  • 蛇身加速、减速
  • 暂停游戏

需要的基础内容

函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32API

Win32API

介绍

Windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,它同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序(Application),所以便称之为Application Programming Interface,简称API函数。WIN32API也就是Microsoft Windows 32位平台的应用程序编程接口

控制台程序部分指令

设置控制台窗口的长宽
  1. 在运行中输入cmd后打开命令提示符
  2. 输入mode con cols=100 lines=30后按Enter执行(cols表示列,lines表示行)

如图所示:

参考mode命令文档:mode | Microsoft Learn

设置控制台的名字
  1. 在运行中输入cmd后打开命令提示符
  2. 输入title (需要的名字)后按Enter执行

参考title命令文档:title | Microsoft Learn

可以在C语言中使用system函数执行上述命令

📌

使用system函数需要包含头文件stdlib.h

#define _CRT_SECURE_NO_WARNINGS 1#include <stdlib.h>//使用system函数需要包含头文件stdlib.h
#include <stdio.h>int main()
{//设置控制台窗口的长宽:mode con cols=100 lines=30system("mode con cols=100 lines=30");//设置列为100列,行为30行//设置控制台窗口的名称:title 贪吃蛇system("title 贪吃蛇");getchar();//防止程序提前自动结束//等价于使用system("pause");return 0;
}
控制台在屏幕上的坐标位置结构体COORD

COORDWindowsAPI中定义的⼀个结构体,表示一个字符在控制台屏幕上显示时的坐标,坐标系(0, 0)的原点位于屏幕的顶部左侧单元格

//COORD结构体原型
typedef struct _COORD {SHORT X;SHORT Y;
} COORD, *PCOORD;//对_COORD结构体进行重命名为COORD,以及对_COORD结构体指针重命名为*PCOORD

参考COORD文档:COORD 结构 - Windows Console | Microsoft Learn

使用COORD设置字符显示坐标:

📌

使用COORD结构体需要包含头文件windows.h

COORD pos = {10, 15};//将x轴坐标设置为10,y轴坐标设置为15
检索指定标准设备的句柄(标准输入、标准输出或标准错误)

使用GetStdHandle函数进行获取。

GetStdHandle是⼀个WindowsAPI函数。它用于从⼀个特定的标准设备(标准输入、标准输出或标

准错误)中取得⼀个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备

参考GetStdHandle文档:GetStdHandle 函数 - Windows Console | Microsoft Learn

//函数原型
HANDLE WINAPI GetStdHandle(_In_ DWORD nStdHandle);//参数说明
nStdHandle [in]标准设备。 此参数的取值可为下列值之一
STD_INPUT_HANDLE((DWORD)-10)    标准输入设备。 最初,这是输入缓冲区 CONIN$ 的控制台
STD_OUTPUT_HANDLE((DWORD)-11)    标准输出设备。 最初,这是活动控制台屏幕缓冲区 CONOUT$
STD_ERROR_HANDLE((DWORD)-12)    标准错误设备。 最初,这是活动控制台屏幕缓冲区 CONOUT$返回类型
如果函数执行成功,则返回值为指定设备的句柄
如果函数执行失败,则返回值为 INVALID_HANDLE_VALUE
如果应用程序没有关联的标准句柄(例如在交互式桌面上运行的服务),并且尚未重定向这些句柄,则返回值为 NULL
📌

使用GetStdHandle函数需要包含头文件windows.h

代码实例:

HANDLE hOutput = NULL;//hOutput相当于一个指针类型的变量
//获取标准输出的句柄(用来标识不同的标准输出设备的数值) ,即需要操作的控制台程序
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
💡

对于句柄的简单理解:需要操作(按键响应等)控制台程序时,首先得获取到控制台这个应用,而句柄就是帮助获取控制台应用的一个“把手”

光标信息结构体类型CONSOLE_CURSOR_INFO
//光标信息结构体原型
typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;//光标占比大小BOOL  bVisible;//光标是否可见
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;//将结构体类型重命名为CONSOLE_CURSOR_INFO,并将结构体指针类型重命名为*PCONSOLE_CURSOR_INFOdwSize
由游标填充的字符单元的百分比。 该值介于 1 到 100 之间。 游标外观各不相同,范围从完全填充单元到显示为单元底部的横线。bVisible
光标的可见性。 如果光标可见,则此成员为 TRUE,默认为可见
📌

dwsize的表现形式:默认为25

参考CONSOLE_CURSOR_INFO结构体类型文档:CONSOLE_CURSOR_INFO 结构 - Windows Console | Microsoft Learn

📌

使用CONSOLE_CURSOR_INFO需要包含头文件windows.h

代码实例

CONSOLE_CURSOR_INFO CursorInfo.bVisible = false;//隐藏光标 
检索有关指定控制台屏幕上的光标大小和可见性的信息

使用函数GetConsoleCursorInfo进行获取

//函数原型
BOOL WINAPI GetConsoleCursorInfo(_In_ HANDLE hConsoleOutput, _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);//参数说明
第一个参数hConsoleOutput [in],获取指定控制台屏幕缓冲区的句柄。可以通过GetStdHandle函数获取
第二个参数lpConsoleCursorInfo [out],指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关控制台光标的信息返回类型
如果该函数成功,则返回值为非零值
如果函数失败,则返回值为零

参考GetConsoleCursorInfo函数文档:GetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn

📌

使用GetConsoleCursorInfo函数需要包含头文件windows.h

代码实例

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;//使用光标结构体类型定义变量
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
指定的控制台屏幕缓冲区设置光标的大小和可见性

使用SetConsoleCursorInfo 进行设置

//函数原型
BOOL WINAPI SetConsoleCursorInfo(_In_       HANDLE              hConsoleOutput,_In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);//参数说明
第一个参数hConsoleOutput [in],控制台屏幕缓冲区的句柄
第二个参数pConsoleCursorInfo [in],指向 CONSOLE_CURSOR_INFO 结构的指针,该结构为控制台屏幕缓冲区的光标提供新的参数//返回类型
如果该函数成功,则返回值为非零值
如果函数失败,则返回值为零

参考SetConsoleCursorInfo函数文档:SetConsoleCursorInfo 函数 - Windows Console | Microsoft Learn

📌

使用SetConsoleCursorInfo函数需要包含windows.h

代码实例

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//隐藏光标操作 
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 
CursorInfo.bVisible = false; //隐藏控制台光标 
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
设置指定控制台屏幕缓冲区中的光标位置

使用函数SetConsoleCursorPosition 进行设置

//函数原型
BOOL WINAPI SetConsoleCursorPosition(_In_ HANDLE hConsoleOutput,_In_ COORD  dwCursorPosition
);//参数说明
第一个参数为hConsoleOutput [in],获取控制台屏幕缓冲区的句柄
第二个参数为dwCursorPosition [in],指定新光标位置(以字符为单位)的 COORD 结构。 坐标是屏幕缓冲区字符单元的列和行。 坐标必须位于控制台屏幕缓冲区的边界以内//返回类型
如果该函数成功,则返回值为非零值
如果函数失败,则返回值为零

参考SetConsoleCursorPosition文档:SetConsoleCursorPosition 函数 - Windows Console | Microsoft Learn

代码实例

COORD pos = { 10, 5};
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos 
SetConsoleCursorPosition(hOutput, pos);
获取按键状态

使用GetAsyncKeyState 函数获取

将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

GetAsyncKeyState 的返回值是short类型,在上⼀次调用 GetAsyncKeyState 函数后,如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最高是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0

//函数原型
SHORT GetAsyncKeyState([in] int vKey);//参数说明
[in] vKey,类型: int,虚拟密钥代码。

参考GetAsyncKeyState函数文档:getAsyncKeyState 函数 (winuser.h) - Win32 apps | Microsoft Learn

参考虚拟密钥代码文档:虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn

  • 定义宏检测按键是否被按过
#define KEY_PRESS(VK)  ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
📌

使用GetAsyncKeyState函数需要包含头文件windows.h

宽字符打印与本地化

宽字符

在游戏地图上,打印墙体使用宽字符:□,打印蛇使用宽字符●,打印食物使用宽字符★

普通的字符(即窄字符)是占1个字节的,而宽字符是占用2个字节

出现窄字符和宽字符区别的原因:
C语言字符默认是采用ASCII编码的,ASCII字符集采用的是单字节编码,且只使用了单字节中的低7位,最高位是没有使用的,可表示为0xxxxxxx,故ASCII字符集共包含128个字符,在英语国家中,128个字符是基本够用的,但是,在其他国家语言中,例如,在法语中,字母上方有注音符号,它就无法用基本的ASCII码表示。于是,⼀些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。但是,不同的国家有不同的字母,因此,尽管都使用256个符号的编码方式,代表的字母却不⼀样。例如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel,在俄语编码中代表另⼀个符号。但是所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这⼀段。
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,就必须使用多个字节表达⼀个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示⼀个汉字,所以理论上最多可以表示256x256=65536个符号

为了使C语言适应国际化,C语言的标准中不断加入了国际化的支持。例如:加入宽字符的类型wchar_t 和宽字符的输入wscanf()和输出函数wprintf(),并且使用时需要包含头文件locale.h,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语言的地理区域)调整程序行为的函数

头文件locale.h与本地化

locale.h提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分。

在标准可以中,依赖地区的部分有以下几项:

  • 数字量的格式
  • 货币量的格式
  • 字符集
  • 日期和时间的表示形式
类项

通过修改地区,程序可以改变它的行为来适应世界的不同区域。但地区的改变可能会影响库的许多部分,其中一部分可能是不希望修改的。所以C语言支持针对不同的类项进行修改,下面的一个宏,指定一个类项:

类项

影响内容

LC_ALL

以下列出的所有类别

LC_COLLATE

strcoll_stricollwcscoll_wcsicollstrxfrm_strncoll_strnicoll_wcsncoll_wcsnicollwcsxfrm 函数

LC_CTYPE

字符处理函数(不受影响的 isdigitisxdigitmbstowcsmbtowc 除外)

LC_MONETARY

localeconv 函数返回的货币格式信息

LC_NUMERIC

格式化输出例程(例如printf)、数据转换例程和localeconv返回的非货币格式设置信息的十进制点字符。 除小数点字符之外,LC_NUMERIC 还设置返回localeconv的千位分隔符和分组控制字符串

LC_TIME

strftimewcsftime 函数

设置本地化

使用函数setlocale函数进行设置

setlocale函数用于修改当前地区,可以针对一个类项修改,也可以针对所有类项

//函数原型
char *setlocale(int category, const char *locale);//参数说明
第一个参数为category,表示受区域设置影响的分类。
第二个参数为locale,表示区域设置说明符//返回类型
如果提供了有效的locale和category,函数会返回指向与指定的locale和category关联的字符串的指针。 如果locale参数为NULL,函数会返回当前区域设置。
如果将无效参数传递给任一函数,则返回值为NULL。

setlocale的第一个参数可以是前面说明的类项中的某一个,那么每次只会影响一个类项,如果第一个参数是LC_ALL,就会影响所有的类项

C标准给第⼆个参数仅定义了2种可能取值:"C"(即NULL)和""

在任意程序执行开始,都会隐藏式执行调用:

setlocale(LC_ALL, "C");//当地区设置为"C"时,库函数按正常方式执行

需要改变地区时,就只能显示调用setlocale函数。将" "作为第2个参数,调用setlocale函数就可以切换到本地模式,这种模式下程序会适应本地环境。

例如:切换到我们的本地模式后则支持宽字符(汉字)的输出等

setlocale(LC_ALL, "");//切换到本地环境

函数参考文档:setlocale | Microsoft Learn

代码实例

#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <locale.h>int main()
{printf("当前环境为:%s\n", setlocale(LC_ALL, NULL));printf("设置后的本地环境为:%s\n", setlocale(LC_ALL, ""));return 0;
}
输出结果:
当前环境为:C
设置后的本地环境为:Chinese (Simplified)_China.936

宽字符的打印

在C语言中,宽字符的字面量前必须加上前缀L,否则C语言会把字面量当作窄字符类型处理。前缀L在单引号前面,表示宽字符。

宽字符的打印使用函数wprintf(),对应wprintf()的占位符为%lc;打印宽字符串时,对应的占位符为%ls并且在双引号前,需要加上前缀L

代码实例

#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <locale.h>int main()
{printf("设置后的本地环境为:%s\n", setlocale(LC_ALL, ""));wchar_t c = L'中';wchar_t c1 = L'国';wchar_t c2 = L'■';wchar_t c3 = L'●';wchar_t c4 = L'★';char* string = "ab";wprintf(L"%lc\n", c);wprintf(L"%lc\n", c1);printf("%s\n", string);wprintf(L"%lc\n", c2);wprintf(L"%lc\n", c3);wprintf(L"%lc\n", c4);return 0;
}
输出结果如下图:

普通字符和宽字符打印的宽度展示如下:

贪吃蛇项目设计

游戏流程设计

贪吃蛇界面的打印

打印游戏欢迎界面
  • 效果图

  • 设计思路

游戏开始界面只需要打印文字,但是需要确定文字的打印位置

  1. 设置光标隐藏,封装成函数void HideCursor();,防止出现打印完成后光标闪烁问题
  2. 设置控制台的大小和标题,使用mode命令和title命令
  3. 设置光标位置,将该操作封装成函数void SetPos(int x, int y);
  4. 将欢迎界面的打印和设置光标位置封装成函数void Welcome();
  5. 调用pause指令显示“请按任意键继续...”,亦可防止程序提前结束
  6. 最后调用cls指令清除当前页面打印的内容,方便打印下一界面,实现页面跳转的效果

代码示例

//隐藏光标
void HideCursor()
{HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//隐藏光标CONSOLE_CURSOR_INFO cursor = { 0 };//先获取光标信息GetConsoleCursorInfo(hOutput, &cursor);cursor.bVisible = false;//设置光标为不可见//再传入新的光标信息SetConsoleCursorInfo(hOutput, &cursor);
}//设置光标位置
void SetPos(int x, int y)
{HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);
}//欢迎界面
void Welcome()
{system("mode con cols=100 lines=30");system("title 贪吃蛇");//定位光标SetPos(33, 12);printf("欢迎来到贪吃蛇的世界,尽情游玩吧!");SetPos(40, 28);system("pause");system("cls");
}
打印游戏规则界面
  • 效果图如下

  • 设计思路

游戏规则界面同样只是打印基本文字信息

  1. 通过调用SetPos函数在每一次打印过程中设置光标位置
  2. 将游戏规则的打印和设置光标位置函数的调用封装成函数void Rules();
  3. 调用pause指令显示“请按任意键继续...”,亦可防止程序提前结束
  4. 最后调用cls指令清除当前页面打印的内容,方便打印下一界面,实现页面跳转的效果
📌

注意设置光标隐藏函数只需要在程序运行起来后执行一次即可,故在游戏界面打印部分只需要调用一次,不需要放置到Welcome()函数内

代码示例

//游戏规则介绍界面
void Rules()
{SetPos(45, 10);printf("游戏规则");SetPos(35, 12);printf("使用WASD或↑↓←→键操作蛇的移动");SetPos(30, 13);//重新设置坐标位置防止出现文字打印覆盖printf("按下空格即可加速,按下左alt或右alt取消加速");SetPos(22, 14);printf("加速可以加分,加速一次+2分,最高加8分,取消加速-2分,直到减到初始值");SetPos(40, 28);system("pause");system("cls");
}
打印游戏地图界面
  • 效果图

  • 设计思路

游戏地图的打印依旧是纯打印字符,但是需要注意打印的是宽字符

参考下图

  1. 地图的打印封装成函数void Map();,在该函数中调用SetPos函数设置每次打印的位置
  2. 在打印地图上半部分边界和下半部分边界时,需要注意因为宽字符横向占两个宽度,故需要确保宽字符的数量尽量保证其为2的倍数,而纵向方向上依旧是占用一个宽度
  3. 在打印左右部分边界时,需要注意此时宽字符纵向是一个宽度

代码示例

//打印游戏地图界面
void Map()
{//设置地区setlocale(LC_ALL, "");//打印地图上部分边界SetPos(0, 0);for (int i = 0; i < 29; i++){wprintf(L"%lc", MAP);}//打印地图下部分边界SetPos(0, 26);for (int i = 0; i < 29; i++){wprintf(L"%lc", MAP);}//打印地图左部分边界for (int i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", MAP);}//打印地图右部分边界for (int i = 1; i <= 25; i++){SetPos(56, i);//注意此处是56而不是28,因为宽字符横向占两个宽度wprintf(L"%lc", MAP);}getchar();
}
打印游戏地图右侧的帮助信息
  • 效果图

  • 设计思路

帮助信息依旧是纯打印文字,只需要设置好打印位置即可

  1. 将帮助信息的打印封装成函数void Help();
//打印游戏地图右侧帮助信息
void Help()
{SetPos(60, 20);printf("不能穿墙,不能咬到自己");SetPos(60, 21);printf("用WASD或↑↓←→控制蛇的移动方向");SetPos(60, 22);printf("按下空格进入加速,按下左alt或右alt取消加速");SetPos(60, 23);printf("使用ESC退出游戏,使用P暂停游戏");SetPos(60, 26);printf("Copy Right@EPSDA");
}

贪吃蛇结构体设计

需要进行设计的结构体类型有:

  1. 蛇身节点
  2. 游戏状态
  3. 蛇的移动方向
  4. 游戏信息
📌

设计时需要注意,蛇吃掉食物,食物变成自身节点,这一过程相当于食物也是蛇身节点类型

//枚举游戏状态
enum state
{Normal = 1,//正常状态Error,//错误状态KillBySelf,//自己咬到自己KillbByWall//撞墙
};//枚举蛇的方向
enum dir
{Up = 1,Down,Left,Right
};//定义蛇身节点
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;
}Snode;//定义游戏信息结构体
typedef struct GameInfo
{Snode* pSnake;//指向蛇头的指针Snode* pFood;//指向食物的指针int FoodWeight;//一个食物的得分int score;//当前总得分enum dir direct;//蛇的移动方向,默认向右enum state Srun;//游戏状态int sleepTime;//蛇的移动速度,睡眠时间越短,蛇越快,默认为200
}Sinfo;

贪吃蛇进行过程设计

初始化蛇身以及游戏信息
//初始化蛇身以及除食物以外的游戏信息
void Init(Sinfo* ps)
{//确保有蛇对象assert(ps);//优先蛇身的尾结点//通过头插实现蛇身整体Snode* cur = NULL;for (int i = 0; i <= 4; i++){//创建蛇节点cur = (Snode*)malloc(sizeof(Snode));assert(cur);cur->x = 24 + 2 * i;cur->y = 5;cur->next = NULL;//将节点连起来if (ps->pSnake == NULL){ps->pSnake = cur;//使游戏信息中维护蛇身的指针指向蛇尾}else{cur->next = ps->pSnake;ps->pSnake = cur;}}//打印蛇(遍历链表)//使用指向头结点的ps->pSnake指针Snode* pcur = ps->pSnake;setlocale(LC_ALL, "");while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//初始化其他信息ps->FoodWeight = 10;ps->sleepTime = 200;ps->score = 0;ps->Srun = Normal;ps->direct = Right;
}
初始化食物
//初始化食物
//食物本质也是蛇身,所以依旧可以使用蛇身结构体
void FoodInit(Sinfo* pf)
{assert(pf);//随机生成食物Snode* food = (Snode*)malloc(sizeof(Snode));assert(food);pf->pFood = food;//设置食物坐标//但是食物坐标不能与蛇坐标重合
create:do{//确保食物坐标不会超出地图范围pf->pFood->x = rand() % 53 + 2;pf->pFood->y = rand() % 24 + 1;} while (pf->pFood->x % 2 != 0);//确保食物的x坐标为2的倍数//确保食物所在位置不是蛇的位置,并且蛇身每个节点都需要比较Snode* cur = pf->pSnake;while (cur){if (pf->pFood->x == cur->x && pf->pFood->y == cur->y){goto create;}cur = cur->next;}//打印食物setlocale(LC_ALL, "");SetPos(pf->pFood->x, pf->pFood->y);wprintf(L"%lc", FOOD);
}
游戏进行部分
//判断下一个坐标是不是食物
bool NextIsFood(Sinfo* snake, Snode* pfood)
{assert(snake);assert(pfood);if (snake->pSnake->x == snake->pFood->x && snake->pSnake->y == snake->pFood->y){return true;}else{return false;}
}//吃食物
void EatFood(Sinfo* snake, Snode* pfood)
{assert(snake);assert(pfood);//是食物进行头插pfood->next = snake->pSnake;snake->pSnake = pfood;//打印新蛇身Snode* pcur = snake->pSnake;setlocale(LC_ALL, "");while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}snake->score += snake->FoodWeight;//释放原来旧的食物free(snake->pFood);//重新创建食物FoodInit(snake);
}//不是食物则正常移动
void NotEatFood(Sinfo* snake, Snode* pnext)
{assert(snake);assert(pnext);pnext->next = snake->pSnake;snake->pSnake = pnext;//不是食物时,蛇移动不会增加身体的长度Snode* pcur = snake->pSnake;setlocale(LC_ALL, "");while (pcur->next->next != NULL){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//释放掉最后一个节点,防止蛇身长度增加SetPos(pcur->next->x, pcur->next->y);printf("  ");free(pcur->next);pcur->next = NULL;
}//撞到自身
void Kill_Self(Sinfo* snake)
{assert(snake);//不可撞到自身相当于不可以撞到自身的第二个之后的所有节点Snode* pcur = snake->pSnake->next;while (pcur){if (pcur->x == snake->pSnake->x && pcur->y == snake->pSnake->y){snake->Srun = KillBySelf;return;}pcur = pcur->next;}
}//撞到墙
void Kill_Wall(Sinfo* snake)
{assert(snake);//不可以撞到墙相当于头结点不可以等于墙的坐标Snode* pcur = snake->pSnake;if (pcur->x == 0 || pcur->x == 56 || pcur->y == 0 || pcur->y == 26){snake->Srun = KillByWall;return;}
}//蛇的移动
void Move(Sinfo* snake)
{//默认移动方向向右assert(snake);//创建新节点用于移动时存储新坐标Snode* psnake = (Snode*)malloc(sizeof(Snode));assert(psnake);psnake->next = NULL;switch (snake->direct){case Up:psnake->x = snake->pSnake->x;psnake->y = snake->pSnake->y - 1;break;case Down:psnake->x = snake->pSnake->x;psnake->y = snake->pSnake->y + 1;break;case Left:psnake->x = snake->pSnake->x - 2;psnake->y = snake->pSnake->y;break;case Right:psnake->x = snake->pSnake->x + 2;psnake->y = snake->pSnake->y;break;default:break;}//移动过程中是否遇到食物if (NextIsFood(snake, psnake))//遇到食物{EatFood(snake, psnake);}else{NotEatFood(snake, psnake);}Kill_Self(snake);Kill_Wall(snake);
}//游戏暂停
void SleepStatus()
{while (1){//循环睡眠Sleep(200);//同时检测按键if (KEYPRESSED(0x50)){break;}}
}//游戏过程部分
void GameRun(Sinfo* snake)
{//读取键盘输入控制蛇的移动do{//打印得分情况SetPos(60, 10);printf("一个食物%02d分", snake->FoodWeight);SetPos(60, 11);printf("当前得分:%02d", snake->score);//向上走,原方向不能向下if (KEYPRESSED(VK_UP) && snake->direct != Down){snake->direct = Up;}//向下走,原方向不能向上else if (KEYPRESSED(VK_DOWN) && snake->direct != Up){snake->direct = Down;}//向左走,原方向不能向右else if (KEYPRESSED(VK_LEFT) && snake->direct != Right){snake->direct = Left;}//向右走,原方向不能向左else if (KEYPRESSED(VK_RIGHT) && snake->direct != Left){snake->direct = Right;}else if (KEYPRESSED(VK_ESCAPE)){snake->Srun = Quit;break;}else if (KEYPRESSED(0x50)){SleepStatus();//暂停游戏}else if (KEYPRESSED(VK_SPACE)){//加速,最多加到320if (snake->sleepTime < 320){snake->sleepTime += 30;snake->FoodWeight += 2;}}else if (KEYPRESSED(VK_LMENU) || KEYPRESSED(VK_RMENU)){//减速if (snake->sleepTime > 200){snake->sleepTime -= 30;snake->FoodWeight -= 2;}}//蛇的移动Move(snake);Sleep(snake->sleepTime);} while (snake->Srun == Normal);
}

贪吃蛇运行结束

//游戏结束
void GameEnd(Sinfo* snake)
{assert(snake);//释放开辟过的空间SetPos(15, 12);switch (snake->Srun){case Quit:printf("游戏退出");break;case KillByWall:printf("墙可不是食物嗷(头部出现大包)");break;case KillBySelf:printf("没有闹饥荒嗷,自己不好吃");break;}while (snake->pSnake){Snode* BlockToFree = snake->pSnake;snake->pSnake = snake->pSnake->next;free(BlockToFree);}free(snake->pFood);snake = NULL;
}

贪吃蛇项目实现

//测试文件
#define _CRT_SECURE_NO_WARNINGS 1#include "Snake_game.h"void gameTest()
{int ch = 0;do{//游戏开始GameStart();//游戏进行srand((unsigned int)time(NULL));Sinfo snake = { 0 };Init(&snake);//初始化蛇身以及除食物以外的游戏信息//初始化食物FoodInit(&snake);GameRun(&snake);//游戏结束GameEnd(&snake);SetPos(15, 10);printf("还要继续嘛?(Y/N)");ch = getchar();getchar();//清理缓冲区的\n} while (ch == 'Y' || ch == 'y');}int main()
{gameTest();SetPos(0, 28);return 0;
}//头文件
#pragma once#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>
#include <stdbool.h>
#include <time.h>//定义宏检测按键是否被按过
#define KEYPRESSED(vk) (GetAsyncKeyState(vk) & 0x1 ? 1 : 0)
//定义蛇身图案
#define BODY L'●'
//定义食物图案
#define FOOD L'★'
//定义地图图案
#define MAP L'□'//枚举游戏状态
enum state
{Normal = 1,//正常状态Quit,//退出状态KillBySelf,//自己咬到自己KillByWall//撞墙
};//枚举蛇的方向
enum dir
{Up = 1,Down,Left,Right
};//定义蛇身节点
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;
}Snode;//定义游戏信息结构体
typedef struct GameInfo
{Snode* pSnake;//指向蛇头的指针Snode* pFood;//指向食物的指针int FoodWeight;//一个食物的得分int score;//当前总得分enum dir direct;//蛇的移动方向,默认向右enum state Srun;//游戏状态int sleepTime;//蛇的移动速度,睡眠时间越短,蛇越快,默认为200
}Sinfo;//游戏开始部分
void GameStart();
//隐藏光标
void HideCursor();
//设置光标位置决定打印内容位置
void SetPos(int x, int y);
//打印欢迎界面
void Welcome();
//打印游戏规则介绍界面
void Rules();
//打印游戏地图界面
void Map();
//打印游戏地图右侧帮助信息
void Help();//游戏进行
void GameRun(Sinfo* snake);
//初始化蛇身以及游戏信息
void Init(Sinfo* ps);
//初始化食物
void FoodInit(Sinfo* pf);
//判断下一个坐标是不是食物
bool NextIsFood(Sinfo* snake, Snode* pfood);
//吃食物
void EatFood(Sinfo* snake, Snode* pfood);
//不是食物则正常移动
void NotEatFood(Sinfo* snake, Snode* pfood);
//游戏暂停
void SleepStatus();
//蛇的移动
void Move(Sinfo* snake);
//撞到自身
void Kill_Self(Sinfo* snake);
//撞到墙
void Kill_Wall(Sinfo* snake);//游戏结束
void GameEnd(Sinfo* snake);//项目文件
#define _CRT_SECURE_NO_WARNINGS 1#include "Snake_game.h"//隐藏光标
void HideCursor()
{HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//隐藏光标CONSOLE_CURSOR_INFO cursor = { 0 };//先获取光标信息GetConsoleCursorInfo(hOutput, &cursor);cursor.bVisible = false;//设置光标为不可见//再传入新的光标信息SetConsoleCursorInfo(hOutput, &cursor);
}//设置光标位置
void SetPos(int x, int y)
{HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);
}//欢迎界面
void Welcome()
{system("mode con cols=105 lines=30");system("title 贪吃蛇");//定位光标SetPos(33, 12);printf("欢迎来到贪吃蛇的世界,尽情游玩吧!");SetPos(40, 28);system("pause");system("cls");
}//游戏规则介绍界面
void Rules()
{SetPos(45, 10);printf("游戏规则");SetPos(35, 12);printf("使用↑↓←→键操作蛇的移动");SetPos(30, 13);//重新设置坐标位置防止出现文字打印覆盖printf("按下空格即可加速,按下左alt或右alt取消加速");SetPos(22, 14);printf("加速可以加分,加速一次+2分,最高加8分,取消加速-2分,直到减到初始值");SetPos(40, 28);system("pause");system("cls");
}//打印游戏地图界面
void Map()
{//设置地区setlocale(LC_ALL, "");//打印地图上部分边界SetPos(0, 0);for (int i = 0; i < 29; i++){wprintf(L"%lc", MAP);}//打印地图下部分边界SetPos(0, 26);for (int i = 0; i < 29; i++){wprintf(L"%lc", MAP);}//打印地图左部分边界for (int i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", MAP);}//打印地图右部分边界for (int i = 1; i <= 25; i++){SetPos(56, i);//注意此处是56而不是28,因为宽字符横向占两个宽度wprintf(L"%lc", MAP);}
}//打印游戏地图右侧帮助信息
void Help()
{SetPos(60, 20);printf("不能穿墙,不能咬到自己");SetPos(60, 21);printf("用↑↓←→控制蛇的移动方向");SetPos(60, 22);printf("按下空格进入加速,按下左alt或右alt取消加速");SetPos(60, 23);printf("使用ESC退出游戏,使用P暂停游戏");SetPos(60, 26);printf("Copy Right@EPSDA");
}//游戏开始部分
void GameStart()
{//隐藏光标HideCursor();//打印欢迎界面Welcome();//打印游戏规则界面Rules();//打印游戏地图界面Map();//打印游戏地图右侧帮助信息Help();
}//初始化蛇身以及除食物以外的游戏信息
void Init(Sinfo* ps)
{//确保有蛇对象assert(ps);//优先蛇身的尾结点//通过头插实现蛇身整体Snode* cur = NULL;for (int i = 0; i <= 4; i++){//创建蛇节点cur = (Snode*)malloc(sizeof(Snode));assert(cur);cur->x = 24 + 2 * i;cur->y = 5;cur->next = NULL;//将节点连起来if (ps->pSnake == NULL){ps->pSnake = cur;//使游戏信息中维护蛇身的指针指向蛇尾}else{cur->next = ps->pSnake;ps->pSnake = cur;}}//打印蛇(遍历链表)//使用指向头结点的ps->pSnake指针Snode* pcur = ps->pSnake;setlocale(LC_ALL, "");while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//初始化其他信息ps->FoodWeight = 10;ps->sleepTime = 200;ps->score = 0;ps->Srun = Normal;ps->direct = Right;
}//初始化食物
//食物本质也是蛇身,所以依旧可以使用蛇身结构体
void FoodInit(Sinfo* pf)
{assert(pf);//随机生成食物Snode* food = (Snode*)malloc(sizeof(Snode));assert(food);pf->pFood = food;//设置食物坐标//但是食物坐标不能与蛇坐标重合
create:do{//确保食物坐标不会超出地图范围pf->pFood->x = rand() % 53 + 2;pf->pFood->y = rand() % 24 + 1;} while (pf->pFood->x % 2 != 0);//确保食物的x坐标为2的倍数//确保食物所在位置不是蛇的位置,并且蛇身每个节点都需要比较Snode* cur = pf->pSnake;while (cur){if (pf->pFood->x == cur->x && pf->pFood->y == cur->y){goto create;}cur = cur->next;}//打印食物setlocale(LC_ALL, "");SetPos(pf->pFood->x, pf->pFood->y);wprintf(L"%lc", FOOD);
}//判断下一个坐标是不是食物
bool NextIsFood(Sinfo* snake, Snode* pfood)
{assert(snake);assert(pfood);if (snake->pSnake->x == snake->pFood->x && snake->pSnake->y == snake->pFood->y){return true;}else{return false;}
}//吃食物
void EatFood(Sinfo* snake, Snode* pfood)
{assert(snake);assert(pfood);//是食物进行头插pfood->next = snake->pSnake;snake->pSnake = pfood;//打印新蛇身Snode* pcur = snake->pSnake;setlocale(LC_ALL, "");while (pcur){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}snake->score += snake->FoodWeight;//释放原来旧的食物free(snake->pFood);//重新创建食物FoodInit(snake);
}//不是食物则正常移动
void NotEatFood(Sinfo* snake, Snode* pnext)
{assert(snake);assert(pnext);pnext->next = snake->pSnake;snake->pSnake = pnext;//不是食物时,蛇移动不会增加身体的长度Snode* pcur = snake->pSnake;setlocale(LC_ALL, "");while (pcur->next->next != NULL){SetPos(pcur->x, pcur->y);wprintf(L"%lc", BODY);pcur = pcur->next;}//释放掉最后一个节点,防止蛇身长度增加SetPos(pcur->next->x, pcur->next->y);printf("  ");free(pcur->next);pcur->next = NULL;
}//撞到自身
void Kill_Self(Sinfo* snake)
{assert(snake);//不可撞到自身相当于不可以撞到自身的第二个之后的所有节点Snode* pcur = snake->pSnake->next;while (pcur){if (pcur->x == snake->pSnake->x && pcur->y == snake->pSnake->y){snake->Srun = KillBySelf;return;}pcur = pcur->next;}
}//撞到墙
void Kill_Wall(Sinfo* snake)
{assert(snake);//不可以撞到墙相当于头结点不可以等于墙的坐标Snode* pcur = snake->pSnake;if (pcur->x == 0 || pcur->x == 56 || pcur->y == 0 || pcur->y == 26){snake->Srun = KillByWall;return;}
}//蛇的移动
void Move(Sinfo* snake)
{//默认移动方向向右assert(snake);//创建新节点用于移动时存储新坐标Snode* psnake = (Snode*)malloc(sizeof(Snode));assert(psnake);psnake->next = NULL;switch (snake->direct){case Up:psnake->x = snake->pSnake->x;psnake->y = snake->pSnake->y - 1;break;case Down:psnake->x = snake->pSnake->x;psnake->y = snake->pSnake->y + 1;break;case Left:psnake->x = snake->pSnake->x - 2;psnake->y = snake->pSnake->y;break;case Right:psnake->x = snake->pSnake->x + 2;psnake->y = snake->pSnake->y;break;default:break;}//移动过程中是否遇到食物if (NextIsFood(snake, psnake))//遇到食物{EatFood(snake, psnake);}else{NotEatFood(snake, psnake);}Kill_Self(snake);Kill_Wall(snake);
}//游戏暂停
void SleepStatus()
{while (1){//循环睡眠Sleep(200);//同时检测按键if (KEYPRESSED(0x50)){break;}}
}//游戏过程部分
void GameRun(Sinfo* snake)
{//读取键盘输入控制蛇的移动do{//打印得分情况SetPos(60, 10);printf("一个食物%02d分", snake->FoodWeight);SetPos(60, 11);printf("当前得分:%02d", snake->score);//向上走,原方向不能向下if (KEYPRESSED(VK_UP) && snake->direct != Down){snake->direct = Up;}//向下走,原方向不能向上else if (KEYPRESSED(VK_DOWN) && snake->direct != Up){snake->direct = Down;}//向左走,原方向不能向右else if (KEYPRESSED(VK_LEFT) && snake->direct != Right){snake->direct = Left;}//向右走,原方向不能向左else if (KEYPRESSED(VK_RIGHT) && snake->direct != Left){snake->direct = Right;}else if (KEYPRESSED(VK_ESCAPE)){snake->Srun = Quit;break;}else if (KEYPRESSED(0x50)){SleepStatus();//暂停游戏}else if (KEYPRESSED(VK_SPACE)){//加速,最多加到320if (snake->sleepTime < 320){snake->sleepTime += 30;snake->FoodWeight += 2;}}else if (KEYPRESSED(VK_LMENU) || KEYPRESSED(VK_RMENU)){//减速if (snake->sleepTime > 200){snake->sleepTime -= 30;snake->FoodWeight -= 2;}}//蛇的移动Move(snake);Sleep(snake->sleepTime);} while (snake->Srun == Normal);
}//游戏结束
void GameEnd(Sinfo* snake)
{assert(snake);//释放开辟过的空间SetPos(15, 12);switch (snake->Srun){case Quit:printf("游戏退出");break;case KillByWall:printf("墙可不是食物嗷(头部出现大包)");break;case KillBySelf:printf("没有闹饥荒嗷,自己不好吃");break;}while (snake->pSnake){Snode* BlockToFree = snake->pSnake;snake->pSnake = snake->pSnake->next;free(BlockToFree);}free(snake->pFood);snake = NULL;
}

相关文章:

使用Win32API实现贪吃蛇小游戏

目录 C语言贪吃蛇项目 基本功能 需要的基础内容 Win32API 介绍 控制台程序部分指令 设置控制台窗口的长宽 设置控制台的名字 控制台在屏幕上的坐标位置结构体COORD 检索指定标准设备的句柄&#xff08;标准输入、标准输出或标准错误&#xff09; 光标信息结构体类型CONSOLE_CUR…...

力扣0114——二叉树展开为链表

[二叉树展开为链表] 难度&#xff1a;中等 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单…...

FPGA硬件架构

1.Xilinx FPGA是异构计算平台&#xff08;所谓异构&#xff0c;就是有很多不同的部分组成&#xff09;&#xff1a;CLB,BRAM,DSP 2. 软核&#xff1a; 把经过功能验证的、可综合的、实现后电路结构总门数在五千门以上的Verilog HDL模型称为软核(soft core)。 硬核: 把在某一…...

spring boot 嵌入chatGPT步骤

一、需要良好的网络 二、需要在OpenAI官网https://openai.com/注册用户&#xff0c;并获取一个api-key&#xff0c;sk开头的 验证是否可用网站&#xff1a;http://tools.lbbit.top/check_key_valid/ 三、spring boot 配置文件 openai.proxyHost127.0.0.1 openai.proxyPort7890…...

博云科技与中科可控全面合作,探索前沿金融科技新机遇

2024年1月26日&#xff0c;博云科技与中科可控在昆山高新区成功举办合作签约仪式。昆山市委常委、昆山高新区党工委书记孙道寻、中科可控董事长聂华、博云科技董事长花磊等领导出席了本次签约仪式。 中科可控将利用其在先进计算和智造领域的优势&#xff0c;为博云科技提供有关…...

十一、常用API——练习

常用API——练习 练习1 键盘录入&#xff1a;练习2 算法水题&#xff1a;练习3 算法水题&#xff1a;练习4 算法水题&#xff1a;练习5 算法水题&#xff1a; 练习1 键盘录入&#xff1a; 键盘录入一些1~100之间的整数&#xff0c;并添加到集合中。 直到集合中所有数据和超过2…...

基于ssm和微信小程序的健身房私教预约管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…...

微服务架构

微服务&#xff08;Microservices&#xff09;是一种将应用程序作为独立服务套件的架构风格&#xff0c;这些服务围绕业务功能构建&#xff0c;可以通过网络调用进行交互。微服务架构使得可以独立开发、测试、部署和缩放各个服务。 微服务的核心原则包括&#xff1a; 高内聚&…...

山体滑坡在线安全监测预警系统(解决方案)

在近年来&#xff0c;随着全球气候变化的影响&#xff0c;山体滑坡等自然灾害频发&#xff0c;给人们的生命财产安全带来了严重威胁。为了有效预防和减少山体滑坡带来的危害&#xff0c;许多地方开始在山上安装山体滑坡在线安全监测预警系统&#xff08;解决方案&#xff09;。…...

StarRocks -- 基础概念(数据模型及分区分桶)

1. 数据模型 StarRocks提供四种数据模型&#xff1a; Duplicate Key, Aggregate Key, Unique Key, Primary Key 1.1 Duplicate Key 适用场景&#xff1a; 分析原始数据&#xff0c;如原始日志和原始操作记录。可以使用多种方法查询数据&#xff0c;不受预聚合方法的限制。加…...

Unity 状态模式(实例详解)

文章目录 简介示例1&#xff1a;基础角色状态切换示例2&#xff1a;添加更多角色状态示例3&#xff1a;战斗状态示例4&#xff1a;动画同步状态示例5&#xff1a;状态机管理器示例6&#xff1a;状态间转换的条件触发示例7&#xff1a;多态行为与上下文类 简介 Unity 中的状态模…...

力扣hot100 分割回文串 集合 dfs

Problem: 131. 分割回文串 文章目录 思路Code&#x1f496; DP预处理版 思路 &#x1f468;‍&#x1f3eb; 参考题解 Code import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List;public class Solution {int n;//字符…...

C# 一个快速读取写入操作execl的方法封装

这里封装了3个实用类ExcelDataReaderExtensions&#xff0c;ExcelDataSetConfiguration&#xff0c;ExcelDataTableConfiguration和一个实用代码参考&#xff1a; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.T…...

axios结合ts使用,取消请求,全局统一获取数据,抛出错误信息

通常在开发时&#xff0c;后端向前端返回的数据可以如下&#xff1a; 1 使用restful api充分利用http状态码&#xff0c;然后在data中追加code字段&#xff0c;请求成功返回200,请求失败返回404,401,500等状态码&#xff0c;并且在code字段中给出详细的字符串信息2 再包一层&a…...

MongoDB:从容器使用到 Mongosh、Python/Node.js 数据操作(结构清晰万字长文)

文章目录 1. 容器与应用之间的关系介绍2. 使用 Docker 容器安装 MongoDB3. Mongosh 操作3.1 Mongosh 连接到 MongoDB3.2 基础操作与 CRUD 4. Python 操作 MongoDB5. Nodejs 操作 MongoDB5.1 Mongodb 和 Mongoose5.2 推荐在项目中使用 Mongoose 参考文献 1. 容器与应用之间的关系…...

超越传统—Clean架构打造现代Android架构指南

超越传统—Clean架构打造现代Android架构指南 1. 引言 在过去几年里&#xff0c;Android应用开发经历了巨大的变革和发展。随着移动设备的普及和用户对应用的期望不断提高&#xff0c;开发人员面临着更多的挑战和需求。传统的Android架构在应对这些挑战和需求时显得有些力不从…...

WebGL开发项目的类型

WebGL&#xff08;Web Graphics Library&#xff09;是一种用于在Web浏览器中渲染交互式3D和2D图形的JavaScript API。使用WebGL&#xff0c;可以开发各种类型的项目&#xff0c;包括但不限于以下几种&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业…...

CUDA编程- - GPU线程的理解 thread,block,grid - 学习记录

GPU线程的理解 thread,block,grid 一、从 cpu 多线程角度理解 gpu 多线程1、cpu 多线程并行加速2、gpu多线程并行加速2.1、cpu 线程与 gpu 线程的理解&#xff08;核函数&#xff09;2.1.1 、第一步&#xff1a;编写核函数2.1.2、第二步&#xff1a;调用核函数&#xff08;使用…...

yum 报错 ZLIB_1.2.3.3 not defined in file libz.so.1

这篇记录工作中发现的&#xff0c;库文件被修改导致 yum 无法正常使用的问题排查过程 问题描述 1&#xff09;执行yum 报错说python2.7.5 结构异常&#xff0c;发现/usr/bin/yum 的解释器被修改过&#xff0c;恢复成/usr/bin/python即可 2&#xff09;恢复后&#xff0c;发现…...

数字孪生智慧能源电力Web3D可视化云平台合集

前言 能源电力的经济发展是中国式现代化的强大动力&#xff0c;是经济社会发展的必要生产要素&#xff0c;电力成本变化直接关系到工业生产、交通运输、农业生产、居民生活等各个方面&#xff0c;合理、经济的能源成本能够促进社会用能服务水平提升、支撑区域产业发展&#xf…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...