贪吃蛇游戏C语言破解:成为编程高手的必修课!

news2024/9/19 17:47:36

                                                                                个人主页:秋风起,再归来~

                                                                                文章专栏:C语言实战项目                              

                                                                        个人格言:悟已往之不谏,知来者犹可追

                                                                                        克心守己,律己则安!

1、游戏效果演示

贪吃蛇游戏效果演示

2、win32 API介绍

这里实现贪吃蛇会使⽤到的⼀些Win32 API知识,接下来我介绍一下。

2.1 Win32 API

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

2.2 控制台程序

平常我们运⾏起来的⿊框程序其实就是控制台程序 我们可以使⽤cmd命令来设置控制台窗⼝的⻓宽:

设置控制台窗⼝的⼤⼩,30⾏,100列

mode con cols=100 lines=30

也可以通过命令设置控制台窗⼝的名字:

title 贪吃蛇

 这些能在控制台窗⼝执⾏的命令,也可以调⽤C语⾔函数system来执⾏。例如

#include<stdlib.h>
int main()
{
	//设置控制台相关属性
	//要包含头文件<stdlib.h>
	system("mode con cols=100 lines=25");
	system("title 贪吃蛇");
	//getchar();
	system("pause");
	return 0;
}

2.3 控制台屏幕上的坐标COORD

COORD是WindowsAPI中定义的⼀个结构体,表⽰⼀个字符在控制台屏幕幕缓冲区上的坐标,坐标系 (0,0)的原点位于缓冲区的顶部左侧单元格。

COORD类型的声明:
typedef struct _COORD {
 SHORT X;
 SHORT Y;
} COORD, *PCOORD;

 默认光标的位置~

//修改光标位置:COORD是win32自定义的关于光标在控制台的位置的结构体类型
COORD pos = { 10,20 };

2.4 GetStdHandle

GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标 准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备

HANDLE GetStdHandle(DWORD nStdHandle);
//获得标准输出设备的句柄
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

2.5 GetConsoleCursorInfo

GetConsoleCursorInfo检索有关指定控制台屏幕缓冲区的光标⼤⼩可⻅性的信息

BOOL WINAPI GetConsoleCursorInfo(
 HANDLE hConsoleOutput,
 PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);
PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 

2.5.1 CONSOLE_CURSOR_INFO

  CONSOLE_CURSOR_INFO是win32自定义的一个结构体类型,里面包含了光标的比例和可见度

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

• dwSize,由光标填充的字符单元格的百分⽐。此值介于1到100之间。光标外观会变化,范围从完 全填充单元格到单元底部的⽔平线条。

• bVisible,游标的可⻅性。如果光标可⻅,则此成员为TRUE。 

2.6 SetConsoleCursorInfo

设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。

BOOL WINAPI SetConsoleCursorInfo(
 HANDLE hConsoleOutput,
 const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

示例:

#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<stdbool.h>
int main()
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//CONSOLE_CURSOR_INFO是win32自定义的一个结构体类型,里面包含了光标的比例和可见度
	CONSOLE_CURSOR_INFO cursorInfo = { 0 };
	//获得光标信息
	GetConsoleCursorInfo(houtput, &cursorInfo);


	//获得光标信息
	GetConsoleCursorInfo(houtput, &cursorInfo);
	
	//修改控制台的光标信息
	cursorInfo.dwSize = 100;//比例
	cursorInfo.bVisible = false;//可见度

	//设置光标的信息
	SetConsoleCursorInfo(houtput, &cursorInfo);

	system("pause");
	return 0;
}

光标被隐藏啦!   

2.7 SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调 ⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。

BOOL WINAPI SetConsoleCursorPosition(
 HANDLE hConsoleOutput,
 COORD pos
);

示例: 

int main()
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//修改光标位置:COORD是win32自定义的关于光标在控制台的位置的结构体类型
	COORD pos = { 10,20 };

	//设置光标位置
	SetConsoleCursorPosition(houtput, pos);

	getchar();
	//system("pause");
	return 0;
}

SetPos:封装⼀个设置光标位置的函数

//把设置光标位置的操作封装成为一个函数
void _SetPos(short x, short y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//修改光标位置:COORD是win32自定义的关于光标在控制台的位置的结构体类型
	COORD pos = { x,y };

	//设置光标位置
	SetConsoleCursorPosition(houtput, pos);
}

2.8 GetAsyncKeyState

获取按键情况,GetAsyncKeyState的函数原型如下:

SHORT GetAsyncKeyState(
 int vKey
);

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

GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果 返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬 起;如果最低位被置为1则说明,该按键被按过,否则为0。 如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

虚拟键码 (Winuser.h) - Win32 apps

3、<locale.h>本地化

如上图游戏效果演示所示:

在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇使⽤宽字符●,打印⻝物使⽤宽字符★ 普通的字符是占⼀个字节的,这类宽字符是占⽤2个字节。

这⾥再简单的讲⼀下C语⾔的国际化特性相关的知识,过去C语⾔并不适合⾮英语国家(地区)使⽤。 C语⾔最初假定字符都是单字节的。但是这些假定并不是在世界的任何地⽅都适⽤。

C语⾔字符默认是采⽤ASCII编码的,ASCII字符集采⽤的是单字节编码,且只使⽤了单字节中的低7 位,最⾼位是没有使⽤的,可表⽰为0xxxxxxxx;可以看到,ASCII字符集共包含128个字符,在英语 国家中,128个字符是基本够⽤的,但是,在其他国家语⾔中,⽐如,在法语中,字⺟上⽅有注⾳符 号,它就⽆法⽤ASCII码表⽰。于是,⼀些欧洲国家就决定,利⽤字节中闲置的最⾼位编⼊新的符 号。⽐如,法语中的é的编码为130(⼆进制10000010)。这样⼀来,这些欧洲国家使⽤的编码体 系,可以表⽰最多256个符号。但是,这⾥⼜出现了新的问题。不同的国家有不同的字⺟,因此,哪 怕它们都使⽤256个符号的编码⽅式,代表的字⺟却不⼀样。⽐如,130在法语编码中代表了é,在希 伯来语编码中却代表了字⺟Gimel,在俄语编码中⼜会代表另⼀个符号。但是不管怎样,所有这 些编码⽅式中,0--127表⽰的符号是⼀样的,不⼀样的只是128--255的这⼀段。 ⾄于亚洲国家的⽂字,使⽤的符号就更多了,汉字就多达10万左右。⼀个字节只能表⽰256种符号, 肯定是不够的,就必须使⽤多个字节表达⼀个符号。⽐如,简体中⽂常⻅的编码⽅式是GB2312,使 ⽤两个字节表⽰⼀个汉字,所以理论上最多可以表⽰256x256=65536个符号。 

后来为了使C语⾔适应国际化,C语⾔的标准中不断加⼊了国际化的⽀持。⽐如:加⼊了宽字符的类型 wchar_t 和宽字符的输⼊和输出函数,加⼊了头⽂件,其中提供了允许程序员针对特定 地区(通常是国家或者说某种特定语⾔的地理区域)调整程序⾏为的函数。

提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。在标准中,依赖地区的部分有以下⼏项:

• 数字量的格式

• 货币量的格式

• 字符集

• ⽇期和时间的表⽰形式

3.1 类项

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

• LC_COLLATE:影响字符串⽐较函数 strcoll() 和 strxfrm() 。

• LC_CTYPE:影响字符处理函数的⾏为。

• LC_MONETARY:影响货币格式。

• LC_NUMERIC:影响 printf() 的数字格式。

• LC_TIME:影响时间格式 strftime() 和 wcsftime() 。

• LC_ALL-针对所有类项修改,将以上所有类别设置为给定的语⾔环境

3.2 setlocale函数

char* setlocale (int category, const char* locale);

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

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

C标准给第⼆个参数仅定义了2种可能取值:"C"(正常模式)和" "(本地模式)

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

setlocale(LC_ALL, "C");

当地区设置为"C"时,库函数按正常⽅式执⾏,⼩数点是⼀个点。

当程序运⾏起来后想改变地区,就只能显⽰调⽤setlocale函数。⽤" "作为第2个参数,调⽤setlocale 函数就可以切换到本地模式,这种模式下程序会适应本地环境。

⽐如:切换到我们的本地模式后就⽀持宽字符(汉字)的输出等。 

setlocale(LC_ALL, " ");//切换到本地环境 
#include<locale.h>
int main()
{
	//C语言标准模式
	char* ret = setlocale(LC_ALL, NULL);
	printf("%s\n", ret);

	//本地化之后的模式
	ret = setlocale(LC_ALL, "");
	printf("%s\n", ret);
	return 0;
}

3.3宽字符的打印

那如果想在屏幕上打印宽字符,怎么打印呢?

宽字符的字⾯量必须加上前缀“L”,否则C语⾔会把字⾯量当作窄字符类型处理。

前缀“L”在单引 号前⾯,表⽰宽字符,对应 wprintf() 的占位符为 %lc ;在双引号前⾯,表⽰宽字符串,对应 wprintf() 的占位符为 %ls 。

#include<locale.h>
int main()
{
	//本地化
	setlocale(LC_ALL, "");
	//打印宽字符
	wchar_t ch1 = L'●';
	wchar_t ch2 = L'□';
	wchar_t ch3 = L'★';
	wprintf(L"%lc\n", ch1);
	wprintf(L"%lc\n", ch2);
	wprintf(L"%lc\n", ch3);
	return 0;
}

从输出的结果来看,我们发现⼀个普通字符占⼀个字符的位置 但是打印⼀个汉字字符,占⽤2个字符的位置,那么我们如果 要在贪吃蛇中使⽤宽字符,就得处理好地图上坐标的计算。

 4、游戏实现的整体思路

1. (第一界面)通过贪吃蛇游戏的视频演示我们可以知道我们首先要在屏幕上打印欢迎界面

2. (第二界面)然后再在屏幕上打印帮助手册

3. (第三界面)接着在屏幕上打印游戏运行时的界面

4. (第四界面)游戏结束后再在屏幕上打印结束原因和最后得分

5. (第五界面)打印玩家是否想在来一局

 4.1 我们要用到的数据结构

实现这些步骤前,我们先要思考并完成我们要用到的数据结构

在游戏运⾏的过程中,蛇每次吃⼀个⻝物,蛇的⾝体就会变⻓⼀节,如果我们使⽤链表存储蛇的信 息,那么蛇的每⼀节其实就是链表的每个节点。每个节点只要记录好蛇⾝节点在地图上的坐标就⾏,

所以蛇节点结构如下:

//蛇身的节点类型
typedef struct SnakeNode
{
	//蛇的身体的每个节点的坐标
	int x;
	int y;
	//指向下一个节点的指针
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

要管理整条贪吃蛇,我们再封装⼀个Snake的结构来维护整条贪吃蛇:

//创建一个结构体类型来维护蛇的各种信息
typedef struct Snake
{
	pSnakeNode _pSnakeHead;//维护蛇头的指针
	pSnakeNode _pFood;//维护食物的指针
	enum DIRECTION _dri;//维护蛇的方向
	enum GAME_STATE _state;//维护蛇的状态
	int _score;//维护当前游戏的总分
	int _foodWeight;//维护一个食物默认的分数
	int _sleepTime;//维护蛇的速度
}Snake,*pSnake;

 蛇的⽅向,可以⼀⼀列举,使⽤枚举:

//枚举蛇的四种方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

游戏状态,可以⼀⼀列举,使⽤枚举:

//枚举四种游戏状态
enum GAME_STATE
{
	OK,//正常运行
	END_NORMAL,//正常退出
	KILL_BY_WALL,//撞到墙了
	KILL_BY_SELF//撞到自己了
};

 4.2 封装三个文件

1.test.h(游戏测试逻辑)

2. snake.h(所有头文件的包含和函数的声明)

3 snake.c(函数的具体实现)

4.3 test.c

这里是游戏的整体逻辑,具体实现在snake.c中

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"

//完成游戏的测试逻辑
void test()
{
	int ch = 0;
	do
	{
		//创建贪吃蛇
		Snake snake = { 0 };
		pSnake ps = &snake;
		//初始化游戏
		//0. 光标隐藏
		//1. 打印欢迎界面
		//2. 绘制地图
		//3. 蛇身初始化
		//4. 食物初始化
		GameStart(&snake);
		//运行游戏
		GameRun(&snake);
		//结束游戏(善后工作)
		GameEnd(&snake);
		/*SetPos(62, 18);
		printf("您要再来一局吗?(Y/N):");*/
		ch = getchar();
		//清理缓冲区里面的内容
		getchar(); 
		system("cls");
	} while (ch == 'Y' || ch == 'y');
}

int main()
{
	//设置适配本地环境
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	test();
	return 0;
}

4.4 snake.h

我先把会用到的头文件,结构类型和函数声明放到这里(看完这些之后后面的代码就更容易看懂)

下面有很详细的注释,先不需要知道函数具体怎么实现,这里只需要知道它们的功能即可。

当然,还有一些函数并没在这里声明(这些函数大多都是为实现某个功能为具体的另一个函数(被这个函数调用)服务却没有在整体上都用到)

#pragma once
//包含所需要的头文件
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>
#include<conio.h>

#define WALL L'□'
#define POS_X 24
#define POS_Y 5
#define BODY L'●'
#define FOOD L'★'
//定义宏判断键盘上的按键是否被按过
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&0x1)?1:0)

//类型的声明

//枚举蛇的四种方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

//枚举四种游戏状态
enum GAME_STATE
{
	OK,//正常运行
	END_NORMAL,//正常退出
	KILL_BY_WALL,//撞到墙了
	KILL_BY_SELF//撞到自己了
};

//蛇身的节点类型
typedef struct SnakeNode
{
	//蛇的身体的每个节点的坐标
	int x;
	int y;
	//指向下一个节点的指针
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
//typedef struct SnakeNode* pSnakeNode;

//创建一个结构体类型来维护蛇的各种信息
typedef struct Snake
{
	pSnakeNode _pSnakeHead;//维护蛇头的指针
	pSnakeNode _pFood;//维护食物的指针
	enum DIRECTION _dri;//维护蛇的方向
	enum GAME_STATE _state;//维护蛇的状态
	int _score;//维护当前游戏的总分
	int _foodWeight;//维护一个食物默认的分数
	int _sleepTime;//维护蛇的速度
}Snake,*pSnake;


//函数的声明

//游戏初始化
void GameStart(pSnake ps);

//定位光标位置
void SetPos(int x, int y);

//打印地图
void CreatMap();

//初始化蛇身
void InitSnake(pSnake ps);

//食物初始化
void CreateFood(pSnake ps);

//游戏运行
void GameRun(pSnake ps);

//游戏结束(善后工作)
void GameEnd(pSnake ps);

//蛇走一步
void SnakeMove(pSnake ps);

//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps);

//下一个位置不是食物
void NoFood(pSnake ps,pSnakeNode pn);

//检查是否撞墙
void KillByWall(pSnake ps);

//检查是否撞到自己
void KillBySelf(pSnake ps);

//检测是否有按键被按下
void KeyFun();

 4.5 GameStart(游戏初始化)

1.SetPos(定位光标位置)

//把设置光标位置的操作封装成为一个函数
void SetPos(int x, int y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//修改光标位置:COORD是win32自定义的关于光标在控制台的位置的结构体类型
	COORD pos = { x,y };

	//设置光标位置
	SetConsoleCursorPosition(houtput, pos);
}

2.游戏初始化逻辑 

//游戏初始化
void GameStart(pSnake ps)
{
	//0. 先设置窗口的大小再隐藏光标
	system("mode con cols=150 lines=40");
	system("title 贪吃蛇");
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//CONSOLE_CURSOR_INFO是win32自定义的一个结构体类型,里面包含了光标的比例和可见度
	CONSOLE_CURSOR_INFO cursorInfo = { 0 };
	//获得光标信息
	GetConsoleCursorInfo(houtput, &cursorInfo);
	//修改控制台的光标信息
	cursorInfo.dwSize = 100;//比例
	cursorInfo.bVisible = false;//可见度
	//设置光标的信息
	SetConsoleCursorInfo(houtput, &cursorInfo);

	//1. 打印欢迎界面和游戏功能介绍
	WelcomeToGame();
	//2. 绘制地图
	CreatMap();
	//3. 蛇身初始化
	InitSnake(ps);
	//4. 食物初始化
	CreateFood(ps);
}

 4.5.1 WelcomeToGame(打印欢迎界面)

//打印欢迎界面
void WelcomeToGame()
{
	//第一界面(欢迎界面)
	SetPos(62, 14);
	printf("欢迎来到贪吃蛇小游戏!");
	SetPos(64, 16);
	system("pause");
	system("cls");//清理屏幕
	//第二界面(游戏功能介绍)
	SetPos(50, 12);
	printf("1、用 ↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速!");
	SetPos(50, 13);
	printf("2、加速将可以得到更高的分数!");
	SetPos(65, 15);
	system("pause");
	system("cls");//清理屏幕
}

4.5.2 CreatMap(绘制地图)

//打印地图
void CreatMap()
{
	int i = 0;
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	SetPos(0, 36);
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i ++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i++)
	{
		SetPos(100, i);
		wprintf(L"%lc", WALL);
	}
}

4.5.3 InitSnake(蛇身初始化)

//初始化蛇身
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	//创建5个蛇身的节点
	for (int i = 1; i <= 5; i++)
	{
		cur = CreatSnakeNode();
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		//将5个蛇身节点串起来
		if (ps->_pSnakeHead == NULL)
		{//蛇头为空就直接插入
			ps->_pSnakeHead = cur;
		}
		else//头插
		{
			cur->next = ps->_pSnakeHead;
			ps->_pSnakeHead = cur;
		}
	}
	cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x,cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_dri = RIGHT;//方向默认向右
	ps->_foodWeight = 10;
	ps->_score = 0;
	ps->_sleepTime = 200;
	ps->_state = OK;
}

4.5.4 CreateFood(食物初始化)

//初始化食物
void CreateFood(pSnake ps)
{
	//先随机生成食物的坐标
	int x = 0;
	int y = 0;
again:
	//生成的位置必须在地图内部
	do
	{
		x = rand() % 97 + 2;
		y = rand() % 35 + 1;
	} while (x % 2 != 0);
	pSnakeNode cur = ps->_pSnakeHead;
	//判断食物位置是否与蛇身重叠
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	pSnakeNode food = CreatSnakeNode();
	food->x = x;
	food->y = y;
	food->next = NULL;
	//将食物节点放到ps中维护起来
	ps->_pFood = food;
}

4.6 GameRun(游戏运行)

1.检测按键是否被按过

//定义宏判断键盘上的按键是否被按过
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&0x1)?1:0)

2.检测是否有按键被按下

//检测是否有按键被按下
void KeyFun()
{
	while (_kbhit())
	{
		//使用_getch()获取按下的键,不阻塞程序
		int key = _getch();
	}
}

3.游戏暂停 

//游戏暂停
void Pause()
{
	while(1)
	{
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		Sleep(200);
		KeyFun();
	}
}

游戏运行逻辑 

//游戏运行
void GameRun(pSnake ps)
{
	PrintHelpInfo();
	do 
	{
		SetPos(104, 12);
		printf("按空格键开始游戏!");
		SetPos(104, 13);
		printf("您当前的总分是%3d", ps->_score);
		SetPos(104, 14);
		printf("当前每个食物的总分是%3d", ps->_foodWeight);
		if (KEY_PRESS(VK_UP) && ps->_dri != DOWN)
		{
			ps->_dri = UP;//上
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dri != UP)
		{
			ps->_dri = DOWN;//下
		}	
		else if(KEY_PRESS(VK_LEFT) && ps->_dri != RIGHT)
		{
			ps->_dri = LEFT;//左
		}
		else if(KEY_PRESS(VK_RIGHT) && ps->_dri != LEFT)
		{
			ps->_dri = RIGHT;//右
		}
		else if (KEY_PRESS(VK_SPACE) )
		{
			Pause();//暂停
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->_state = END_NORMAL;//正常退出
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleepTime>80)
			{
				ps->_sleepTime -= 30;
				ps->_foodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_foodWeight>2)
			{
				ps->_sleepTime += 30;
				ps->_foodWeight -= 2;
			}
		}
		//走一步
		SnakeMove(ps);
		//每走一步休息一下
		Sleep(ps->_sleepTime);
		KeyFun();
	} while (ps->_state == OK);
}

 4.6.1 PrintHelpInfo(右侧打印帮助手册)

//打印帮助手册
void PrintHelpInfo()
{
	SetPos(104, 16);
	printf("--------------------------------------------");
	SetPos(104, 17);
	printf("|1、不能撞墙,不能咬到自己!               |");
	SetPos(104, 18);
	printf("|2、用 ↑ . ↓ . ← . → 分别控制蛇的移动! |");
	SetPos(104, 19);
	printf("|3、F3为加速,F4为减速!                   |");
	SetPos(104, 20);
	printf("|4、按Esc退出游戏,按空格暂停游戏 !        |");
	SetPos(104, 21);
	printf("--------------------------------------------");
	SetPos(60, 17);
}

  4.6.2 SnakeMove(蛇走一步)

1.蛇走一步逻辑

//蛇走一步
void SnakeMove(pSnake ps)
{
	//创建一个节点来记录蛇头的下一个位置
	pSnakeNode nextHead = CreatSnakeNode();
	nextHead->next = NULL;
	switch (ps->_dri)
	{
	case UP:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y - 1;
		break;
	case DOWN:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y +1;
		break;
	case LEFT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x - 2;
		break;
	case RIGHT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x + 2;
		break;
	default:
		break;
	}
	//判断下一个节点是不是食物
	if((ps->_pFood->x == nextHead->x) && (ps->_pFood->y == nextHead->y))
	{
		//下一个是食物那就吃掉食物
		EatFood(nextHead, ps);
	}
	else
	{
		//下一个位置不是食物
		NoFood(ps, nextHead);
	}
	//
	KillByWall(ps);
	KillBySelf(ps);
}

 2. EatFood(吃掉食物)

//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{
	//头插吃掉食物
	ps->_pFood->next = ps->_pSnakeHead;
	ps->_pSnakeHead = ps->_pFood;
	ps->_score += ps->_foodWeight;//分数增加
	//打印蛇身
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//释放刚刚malloc的位置
	free(pn);
	pn = NULL;
	//食物被吃掉了,那就再创建一个食物
	CreateFood(ps);
}

3. NoFood往前走一步(不是食物)

//下一个位置不是食物
void NoFood(pSnake ps,pSnakeNode pn)
{
	//头插
	pn->next = ps->_pSnakeHead;
	ps->_pSnakeHead = pn;
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur->next->next!=NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc",BODY);
		cur = cur->next;
	}
	//把最后一个节点打印成两个空格
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放最后一个节点
	free(cur->next);
	cur->next = NULL;
}

 4. KillByWall(检测是否撞墙)

//检查是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnakeHead->x == 0
		|| ps->_pSnakeHead->x == 98
		|| ps->_pSnakeHead->y == 0
		|| ps->_pSnakeHead->y == 36)
	{
		ps->_state = KILL_BY_WALL;
		return;
	}
	return;
}

5.  KillBySelf(检测是否撞到自己)

//检查是否撞到自己
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead->next;
	while (cur)
	{
		if ((cur->x == ps->_pSnakeHead->x) &&
			(cur->y == ps->_pSnakeHead->y))
		{
			ps->_state = KILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
	return;
}

4.7 GameEnd(游戏善后)

//游戏善后
void GameEnd(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	system("cls");
	SetPos(62, 16);
	switch (ps->_state)
	{
	case END_NORMAL:
		printf("您主动退出游戏!\n");
		break;
	case KILL_BY_WALL:
		printf("您撞墙了!\n");
		break;
	case KILL_BY_SELF:
		printf("您撞到自己了!\n");
		break;
	}
	SetPos(62, 17);
	printf("您最终的成绩是:%d", ps->_score);
	Sleep(2000);
	KeyFun();
	system("cls");
	SetPos(62, 18);
	printf("您要再来一局吗?(Y/N):");
}

 4.8 snake.c(完整代码)

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"

//检测是否有按键被按下
void KeyFun()
{
	while (_kbhit())
	{
		//使用_getch()获取按下的键,不阻塞程序
		int key = _getch();
	}
}

//把设置光标位置的操作封装成为一个函数
void SetPos(int x, int y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//修改光标位置:COORD是win32自定义的关于光标在控制台的位置的结构体类型
	COORD pos = { x,y };

	//设置光标位置
	SetConsoleCursorPosition(houtput, pos);
}

//打印地图
void CreatMap()
{
	int i = 0;
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	SetPos(0, 36);
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i ++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i++)
	{
		SetPos(100, i);
		wprintf(L"%lc", WALL);
	}
}

//打印欢迎界面
void WelcomeToGame()
{
	//第一界面(欢迎界面)
	SetPos(62, 14);
	printf("欢迎来到贪吃蛇小游戏!");
	SetPos(64, 16);
	system("pause");
	system("cls");//清理屏幕
	//第二界面(游戏功能介绍)
	SetPos(50, 12);
	printf("1、用 ↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速!");
	SetPos(50, 13);
	printf("2、加速将可以得到更高的分数!");
	SetPos(65, 15);
	system("pause");
	system("cls");//清理屏幕
}

//创建1个蛇身的节点
pSnakeNode CreatSnakeNode()
{
	pSnakeNode ret = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (ret == NULL)
	{
		perror("CreatSnakeNode::fail\n");
		return NULL;
	}
	return ret;
}

//初始化蛇身
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	//创建5个蛇身的节点
	for (int i = 1; i <= 5; i++)
	{
		cur = CreatSnakeNode();
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		//将5个蛇身节点串起来
		if (ps->_pSnakeHead == NULL)
		{//蛇头为空就直接插入
			ps->_pSnakeHead = cur;
		}
		else//头插
		{
			cur->next = ps->_pSnakeHead;
			ps->_pSnakeHead = cur;
		}
	}
	cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x,cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_dri = RIGHT;//方向默认向右
	ps->_foodWeight = 10;
	ps->_score = 0;
	ps->_sleepTime = 200;
	ps->_state = OK;
}

//初始化食物
void CreateFood(pSnake ps)
{
	//先随机生成食物的坐标
	int x = 0;
	int y = 0;
again:
	//生成的位置必须在地图内部
	do
	{
		x = rand() % 97 + 2;
		y = rand() % 35 + 1;
	} while (x % 2 != 0);
	pSnakeNode cur = ps->_pSnakeHead;
	//判断食物位置是否与蛇身重叠
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	pSnakeNode food = CreatSnakeNode();
	food->x = x;
	food->y = y;
	food->next = NULL;
	//将食物节点放到ps中维护起来
	ps->_pFood = food;
}

//打印帮助手册
void PrintHelpInfo()
{
	SetPos(104, 16);
	printf("--------------------------------------------");
	SetPos(104, 17);
	printf("|1、不能撞墙,不能咬到自己!               |");
	SetPos(104, 18);
	printf("|2、用 ↑ . ↓ . ← . → 分别控制蛇的移动! |");
	SetPos(104, 19);
	printf("|3、F3为加速,F4为减速!                   |");
	SetPos(104, 20);
	printf("|4、按Esc退出游戏,按空格暂停游戏 !        |");
	SetPos(104, 21);
	printf("--------------------------------------------");
	SetPos(60, 17);
}

//游戏暂停
void Pause()
{
	while(1)
	{
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		Sleep(200);
		KeyFun();
	}
}

//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{
	//头插吃掉食物
	ps->_pFood->next = ps->_pSnakeHead;
	ps->_pSnakeHead = ps->_pFood;
	ps->_score += ps->_foodWeight;//分数增加
	//打印蛇身
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//释放刚刚malloc的位置
	free(pn);
	pn = NULL;
	//食物被吃掉了,那就再创建一个食物
	CreateFood(ps);
}

//下一个位置不是食物
void NoFood(pSnake ps,pSnakeNode pn)
{
	//头插
	pn->next = ps->_pSnakeHead;
	ps->_pSnakeHead = pn;
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur->next->next!=NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc",BODY);
		cur = cur->next;
	}
	//把最后一个节点打印成两个空格
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放最后一个节点
	free(cur->next);
	cur->next = NULL;
}

//蛇走一步
void SnakeMove(pSnake ps)
{
	//创建一个节点来记录蛇头的下一个位置
	pSnakeNode nextHead = CreatSnakeNode();
	nextHead->next = NULL;
	switch (ps->_dri)
	{
	case UP:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y - 1;
		break;
	case DOWN:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y +1;
		break;
	case LEFT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x - 2;
		break;
	case RIGHT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x + 2;
		break;
	default:
		break;
	}
	//判断下一个节点是不是食物
	if((ps->_pFood->x == nextHead->x) && (ps->_pFood->y == nextHead->y))
	{
		//下一个是食物那就吃掉食物
		EatFood(nextHead, ps);
	}
	else
	{
		//下一个位置不是食物
		NoFood(ps, nextHead);
	}
	//
	KillByWall(ps);
	KillBySelf(ps);
}

//检查是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnakeHead->x == 0
		|| ps->_pSnakeHead->x == 98
		|| ps->_pSnakeHead->y == 0
		|| ps->_pSnakeHead->y == 36)
	{
		ps->_state = KILL_BY_WALL;
		return;
	}
	return;
}

//检查是否撞到自己
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead->next;
	while (cur)
	{
		if ((cur->x == ps->_pSnakeHead->x) &&
			(cur->y == ps->_pSnakeHead->y))
		{
			ps->_state = KILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
	return;
}

//游戏初始化
void GameStart(pSnake ps)
{
	//0. 先设置窗口的大小再隐藏光标
	system("mode con cols=150 lines=40");
	system("title 贪吃蛇");
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//CONSOLE_CURSOR_INFO是win32自定义的一个结构体类型,里面包含了光标的比例和可见度
	CONSOLE_CURSOR_INFO cursorInfo = { 0 };
	//获得光标信息
	GetConsoleCursorInfo(houtput, &cursorInfo);
	//修改控制台的光标信息
	cursorInfo.dwSize = 100;//比例
	cursorInfo.bVisible = false;//可见度
	//设置光标的信息
	SetConsoleCursorInfo(houtput, &cursorInfo);

	//1. 打印欢迎界面和游戏功能介绍
	WelcomeToGame();
	//2. 绘制地图
	CreatMap();
	//3. 蛇身初始化
	InitSnake(ps);
	//4. 食物初始化
	CreateFood(ps);
}

//游戏运行
void GameRun(pSnake ps)
{
	PrintHelpInfo();
	do 
	{
		SetPos(104, 12);
		printf("按空格键开始游戏!");
		SetPos(104, 13);
		printf("您当前的总分是%3d", ps->_score);
		SetPos(104, 14);
		printf("当前每个食物的总分是%3d", ps->_foodWeight);
		if (KEY_PRESS(VK_UP) && ps->_dri != DOWN)
		{
			ps->_dri = UP;//上
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dri != UP)
		{
			ps->_dri = DOWN;//下
		}	
		else if(KEY_PRESS(VK_LEFT) && ps->_dri != RIGHT)
		{
			ps->_dri = LEFT;//左
		}
		else if(KEY_PRESS(VK_RIGHT) && ps->_dri != LEFT)
		{
			ps->_dri = RIGHT;//右
		}
		else if (KEY_PRESS(VK_SPACE) )
		{
			Pause();//暂停
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->_state = END_NORMAL;//正常退出
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleepTime>80)
			{
				ps->_sleepTime -= 30;
				ps->_foodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_foodWeight>2)
			{
				ps->_sleepTime += 30;
				ps->_foodWeight -= 2;
			}
		}
		//走一步
		SnakeMove(ps);
		//每走一步休息一下
		Sleep(ps->_sleepTime);
		KeyFun();
	} while (ps->_state == OK);
}

//游戏善后
void GameEnd(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	system("cls");
	SetPos(62, 16);
	switch (ps->_state)
	{
	case END_NORMAL:
		printf("您主动退出游戏!\n");
		break;
	case KILL_BY_WALL:
		printf("您撞墙了!\n");
		break;
	case KILL_BY_SELF:
		printf("您撞到自己了!\n");
		break;
	}
	SetPos(62, 17);
	printf("您最终的成绩是:%d", ps->_score);
	Sleep(2000);
	KeyFun();
	system("cls");
	SetPos(62, 18);
	printf("您要再来一局吗?(Y/N):");
}

5、 完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

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

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

相关文章

广州大学《软件工程》实验报告三软件设计

广州大学学生实验报告&#xff08;三&#xff09; 开课学院及实验室&#xff1a; 学院 年级/专业/班 姓名 学号 实验课程名称 软件工程导论实验 成绩 实验项目名称 软件设计 指导老师 一、实验目的 掌握软件设计建模技术&#xff0c;能够撰写软件设计文…

解线性方程组——(Jacobi)雅克比迭代法 | 北太天元

一、Jacobi迭代法 n 3 n3 n3 , 阶数为 3 时 A ( a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ) , b ( b 1 b 2 b 3 ) , A\begin{pmatrix} a_{11} & a_{12} &a_{13}\\ a_{21} & a_{22} &a_{23}\\ a_{31} & a_{32} &a_{33}\\ \end{pmatrix} ,\qua…

武汉大学博士,华为上班5年多,月薪多少。。。

最近&#xff0c;一位来自武汉大学的博士研究生透露了自己在华为公司工作五年后的薪酬情况。 据他透露&#xff0c;他在2018年加入华为时的月薪为2.4万&#xff0c;随着时间的推移&#xff0c;到了2023年&#xff0c;他的月薪已经增长至4.4万&#xff01;此外&#xff0c;他还透…

微信小程序webview和小程序通讯

1.背景介绍 1.1需要在小程序嵌入vr页面&#xff0c;同时在vr页面添加操作按钮与小程序进行通信交互 1.2 开发工具&#xff1a;uniapp开发小程序 1.3原型图 功能&#xff1a;.点击体验官带看跳转小程序的体验官带看页面 功能&#xff1a;点击立即咨询唤起小程序弹窗打电话 2.…

React-RTK

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-RTK 目录 1、介绍 2、安装 3、编写RTK使用示例 4、官方提供项目包示例 创建 Redux …

uniapp中vue写微信小程序的生命周期差别

根据uniapp官网里的生命周期&#xff0c;感觉不太对劲&#xff0c;就自己测试了几个&#xff0c;发现有所差别。 红字数字 为 实际测试生命周期顺序。 因为需要页面传参 后再 初始化数据&#xff0c;而onLoad(option)接收参数后&#xff0c;就已经过了create()了&#xff0c;所…

tokio多任务绑定cpu(绑核)

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f; 首先我们先写一段简单的多任务程序。 use tokio; use tokio::runtime; use core_affinity;fn tokio_sample() {let rt runtime::Builde…

3.SpringCloud版本

1.SpringCloud与SpringBoot之间版本对应 2.服务拆分的注意事项 1.不同微服务&#xff0c;不要重复开发相同业务。 2.微服务的数据独立&#xff0c;每个微服务都有自己独立的数据库&#xff0c;不要访问其他微服务的数据库。 3.微服务可以将自己的的业务暴露为接口&#xff…

C++:基础语法

一、命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或名字污染&#xff0c;n…

24V转2.8V2A降压芯片WT6030

24V转2.8V2A降压芯片WT6030 WT6030是一种高效同步整流降压开关模式转换器&#xff0c;集成内部功率MOSFET。该器件在宽输入电源范围内提供3A峰值输出电流&#xff0c;展现出卓越的负载和线路调节性能。其设计仅需要最小数量的外部现成组件&#xff0c;并且采用了节省空间的ESO…

GITHUB的VB代码无法加载的问题解决

GITHUB里有不少好的VB代码&#xff0c;但是下载之后&#xff0c;经常出现工程加载出错的问题&#xff0c;例如&#xff1a; LOG文件为&#xff1a; 不能加载 0 行 0: 不能加载文件 D:\xxxx\Semi VB API Loader\frmMain.frm 。 原因其实很简单&#xff0c;github里的换行符是u…

如何在PostgreSQL中使用索引覆盖扫描提高查询性能?

文章目录 解决方案1. 创建合适的索引2. 确保查询能够使用索引覆盖扫描3. 调整查询以利用索引覆盖扫描4. 监控和调优 示例代码1. 创建索引2. 编写查询3. 检查是否使用索引覆盖扫描4. 调整索引 总结 在PostgreSQL中&#xff0c;索引是提高查询性能的关键工具之一。索引允许数据库…

C# 字面量null对于引用类型变量✓和值类型变量×

编译器让相同的字符串字面量共享堆中的同一内存位置以节约内存。 在C#中&#xff0c;字面量&#xff08;literal&#xff09;是指直接表示固定值的符号&#xff0c;比如数字、字符串或者布尔值。而关键字&#xff08;keyword&#xff09;则是由编程语言定义的具有特殊含义的标…

积极探索新质生产力,九河云携手华为云技术交流引领数智跃迁

4月18日&#xff0c;九河云携手华为云举办了华为云SA技术培训会议&#xff0c;培训邀请到华为云技术人员作为主讲人&#xff0c;通过理论讲解与案例结合的方式&#xff0c;围绕ECS和EBS之间的联动&#xff0c;调优和数据保护等方面展开&#xff0c;深入浅出地讲解了基于EBS部署…

Python从0到100(十四):高级函数及函数使用进阶

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

【机器学习】分类与预测算法的评价与优化

以实际案例解析F1值与P-R曲线的应用 一、分类算法与性能评价的重要性二、F1值与P-R曲线的概念与意义三、实例解析&#xff1a;以垃圾邮件检测为例四、代码实现与结果分析五、结论与展望 在数据驱动的时代&#xff0c;机器学习算法以其强大的数据处理和分析能力&#xff0c;成为…

day07 51单片机-18B20温度检测

18B20温度检测 1.1 需求描述 本案例讲解如何从18B20传感器获取温度信息并显示在LCD上。 1.2 硬件设计 1.2.1 硬件原理图 1.2.3 18B20工作原理 可以看到18B20有两根引脚负责供电&#xff0c;一根引脚负责数据交换。18B20就是通过数据线和单片机进行数据交换的。 1&#xf…

PROSAIL模型前向模拟与植被参数遥感提取代码实现

原文链接&#xff1a;PROSAIL模型前向模拟与植被参数遥感提取代码实现https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247602140&idx7&sn7c4ca9239865d536ba81ba4c26a34031&chksmfa820e3bcdf5872d540c0dfe8c533c8696c8b4658427aab254f246a739f96b36bc37…

GPT 在目标设定中的应用:实现梦想的技术方法

在技术快速进步的时代&#xff0c;我们设定和实现目标的方式正在不断发展。 该领域最重要的创新之一是引入生成式预训练 Transformer (GPT)。 本文将探讨 GPT 技术如何彻底改变目标设定的艺术&#xff0c;提供实用的见解和案例研究来展示其影响。 GPT 和目标设定简介 ​ 了解 …

Ansible安装基本原理及操作(初识)

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月23日15点18分 Ansible 是一款功能强大且易于使用的IT自动化工具&#xff0c;可用于配置管理、应用程序部署和云端管理。它使用无代理模式&#xff08;agentles…