贪吃蛇(C语言超详细版)

目录

前言:

总览:

API:

控制台程序(Console):

设置坐标: 

COORD:

GetStdHandle:

STD_OUTPUT_HANDLE参数:

SetConsoleCursorPosition: 

隐藏光标: 

GetConsoleCursorInfo:

CONSOLE_CURSOR_INFO参数:

SetConsoleCursorInfo:

获取按键信息: 

GetAsyncKeyState:

设置宽字符: 

setlocale函数:

宽字符的打印:

贪吃蛇的实现: 

初始化游戏: 

初始化蛇结构体:

初始化游戏:

打印地图: 

初始化蛇并打印: 

创建食物: 

游戏运行:

打印帮助信息:

判断按键: 

蛇移动的函数:

判断蛇的状态:

游戏善后工作: 

所有代码: 

总结:


前言:

        C语言到底能不能用来做游戏?今天我就要告诉你们,完全可以!但是用C语言来完成一个游戏,我们还需要很多预备知识才能将游戏运行。

        在我们要去完成一个项目时,都需要先想好它的逻辑,我们将一个大问题分为若干个子问题去解决,接下来我们就开始正式开始完成贪吃蛇。

总览:

        我们先来看游戏的运行过程:

贪吃蛇运行过程

        可以看到控制台不是很大,而且有标题:贪吃蛇。最重要的是我们每次看到的光标不见了。这是为什么呢?

        实现贪吃蛇会使用一些Win32 API知识,我们需要学习一下。

API:

        什么是API?我喜欢先把它的全称说一下:Application Programming Interface。简称 API 函数。

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

        说人话就是函数,我们能直接使用(应该就是使用Windows系统都可以使用这些函数)。

控制台程序(Console):

        平时我们运行起来的黑框程序其实就是控制台程序。我们可以使用cmd命令来设置控制台窗口的长度:比如设置控制台窗口的大小, 30行,100列。

mode con cols=100 lines=30

        我们使用C语言在 Windows环境下模拟实现贪食蛇小游戏。

        注意,我们在控制终端以前一定要做到以下修改:

        游戏是否能正常运行起来,这一步至关重要! 

         我们可以修改控制台的标题,这些都是控制台的命令,我们都可以用C语言的 system 函数来执行这些系统命令,我们要包含stdlib.h头文件。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	system("mode con cols=50 lines=20");
	system("title 贪吃蛇");
	return 0;
}

         可以发现,控制台终端标题没有变成贪吃蛇,这是因为程序运行结束了,程序没有运行结束之前,标题是会变成我们设置的贪吃蛇的。

        所以为了方便观察,我们加上getchar来防止程序运行结束。

         当然,我们也可以使用系统命令pause来暂行程序。

system("pause");//暂停

设置坐标: 

COORD:

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

GetStdHandle:

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

        也就是说,你要操作这个控制台程序,你要获得这个控制台权限。而这个GetStdHandle函数,就可以让我们从一个特定设备获取句柄。

        好比我们现在打开多个控制台程序:

         我们此时就可以在我们写的程序中获取自己控制台的句柄,以至于不会去干扰其他控制台。

STD_OUTPUT_HANDLE参数:

        当我们在控制台中输出,要使用STD_OUTPUT_HANDLE参数。

        我们可以看到GetStdHandle函数返回的是一个HANDLE,所以我们要接收一下。

SetConsoleCursorPosition: 

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

        也就是说这是一套组合拳,我们都要一起配合使用,我们要先使用GetStdHandle获取当前控制台句柄,并用HANDLE类型接收,之后使用COORD定义一个结构体设置光标的坐标,最后使用SetConsoleCursorPositon正式把坐标设置到控制台中(记得引入头文件)。

#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
	//获取句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//设置光标的第一个参数

	//根据句柄设置光标位置
	COORD pos = { 20, 5 };
	SetConsoleCursorPosition(handle, pos);//设置光标位置

	printf("hehe\n");
	return 0;
}

         但是每次这样写很繁琐,我们为了方便定位,可以直接将其封装为一个函数。

void SetPos(int x, int y)
{
	//获取句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//设置光标的第一个参数

	//根据句柄设置光标位置
	COORD pos = { x, y };
	SetConsoleCursorPosition(handle, pos);//设置光标位置
}

int main()
{
	SetPos(20, 5);
	printf("hehe\n");
	SetPos(25, 5);
	printf("haha\n");
	return 0;
}

隐藏光标: 

GetConsoleCursorInfo:

        GetConsoleCursorInfo:检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息。

        因为我们在运行程序时,是不会出现光标的,所以我们要通过此函数来隐藏光标。

         第一个参数是句柄,指定那个控制台;第二个参数是PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标(光标)的信息。

CONSOLE_CURSOR_INFO参数:

        CONSOLE_CURSOR_INFO:这个结构体,包含有关控制台光标信息。

        dwSize:由游标填充的字符单元的百分比。 该值介于 1 到 100 之间。 游标外观各不相同,范围从完全填充单元到显示为单元底部的横线。 

        bVisible:游标的可见性。 如果游标可见,则此成员为 TRUE。 

        我们可以将bVisible参数设置为false,这样就隐藏了光标。

        但是当我们修改完以后光标的信息,就像设置光标位置一样,还需要设置才能生效,此时就需要用到 SetConsoleCursorInfo 函数。

SetConsoleCursorInfo:

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

        和设置光标是一样的,也是一套组合拳: 

#include<stdio.h>
#include<windows.h>
int main()
{
	//COORD pos = { 40, 10 };//要包含头文件
	//设置坐标
	
	//获取句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//设置光标的第一个参数

	CONSOLE_CURSOR_INFO cursor_info = { 0 };//设置光标的第二个参数
	//包含有关控制台的游标信息

	GetConsoleCursorInfo(handle, &cursor_info);//设置光标信息
	cursor_info.dwSize = 100;
	SetConsoleCursorInfo(handle, &cursor_info);

	return 0;
}

        比如此时我们将dwSize设置为100。 

        小总结: 我们使用API不用去纠结很多细节,我们可以直接使用,要做到会用,至于更多细节的东西,我们以后还会慢慢学习到的,决不能因噎废食!

获取按键信息: 

        我们运行贪吃蛇,肯定是根据按键来控制的,所以我们此时就来学习和按键有关的API函数。

GetAsyncKeyState:

        GetAsyncKeyState:获取按键情况。

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

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

        如果我们要判断一个件是否被按过,可以检测GetAsyncKeyState返回值的最低位是否为1。

        因为按键只很多,不方便展示,各位可以直接点击链接进入(虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn)。

//获取键盘按键情况
#define KEY_PRESS(vk) (GetAsyncKeyState(vk) & 0x1 ? 1 : 0)
//封装成宏,检测这个键有没有被按过

int main()
{
	while (1)
	{
		if (KEY_PRESS(0x30))
		{
			printf("0\n");
		}
		else if (KEY_PRESS(0x31))
		{
			printf("1\n");
		}
		else if (KEY_PRESS(0x32))
		{
			printf("2\n");
		}
		else if (KEY_PRESS(0x33))
		{
			printf("3\n");
		}
		else if (KEY_PRESS(0x34))
		{
			printf("4\n");
		}
		else if (KEY_PRESS(0x35))
		{
			printf("5\n");
		}
	}
	//可以监控别人键盘
	return 0;
}

设置宽字符: 

        为了方便贪吃蛇运行,我们可以发现,平时我们在控制台中的光标是长方形,但是贪吃蛇的身体每个节点都是一个规则的正方形,所以我们要使用宽字符。

        我们知道C语言默认的使用ASCII字符集编码的,采用的是单字节编码,但是ASCII字符集只包含128个字符,和明显不够用。

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

        比如,简体中文常见的编码格式是GB2312,使用两个字节表示一个汉字。所以理论上最多可以表示256 × 256 = 65536 个符号。

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

        比如表示钱:¥(人民币) $(美元)。还有符号和日期等。

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

setlocale函数:

        这个函数也是API函数,它是设置本地化的函数,我们先来观察它的源码:

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

        第一个参数是控制哪些种类(比如符号、时间等),当然参数是以宏的方式传递的。

        如果读不懂的话,你只需要记住一点LC_ALL影响所有的就行了,至于其他的,可以用到再慢慢研究。

        第二个参数是决定了当前编译模式。

        分为: “C”(正常模式) 和 “”(本地模式)。

        在任意程序执行开始时,都会隐式执行调用 setlocale(LC_ALL, "C");

#include<locale.h>
//适应本地化
//宽字符占据两个字节
int main()
{
	char* loc;
	loc = setlocale(LC_ALL, NULL);
	printf("默认的本地信息:%s\n", loc);

	loc = setlocale(LC_ALL, "");
	printf("设置后的本地信息:%s\n", loc);
	return 0;
}

宽字符的打印:

        如果在屏幕上打印宽字符,宽字符的字面量必须加上前缀 L ,否则C语言会把字面量当做窄字符类型处理。前缀L在单引号前面,表示宽字符,宽字符的打印使用 wprintf ,对应 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);
	printf("ab\n");
	return 0;
}

        可以发现,宽字符占据两个位置。

贪吃蛇的实现: 

        因为贪吃蛇是最终的目标,我们先将其分为若干个子问题。

        为了方便,我们还是分为3个文件(一个头文件,两个源文件)。

        test.c:贪吃蛇游戏的测试

        snake.c:函数的实现

        snake.h:贪吃蛇游戏中的类型声明,函数的声明。

初始化游戏: 

初始化蛇结构体:

        我们如何去维护一个“蛇”呢?我们可以用链表的方式。所以我们先去创建一个蛇身节点的结构体。

typedef struct SnakeNode
{
	int x;//横坐标
	int y;//纵坐标
	struct SnakeNode* next;
}SnakeNode, *pSnakeNode;

//相当于
typedef struct SnakeNode* pSnakeNode;

        但是此时我们只维护了一个蛇身节点,所以此时我们在定义一个Snake结构体。

        而这个Snake结构体中,我们需要找到蛇头的节点,维护食物,分数,方向,蛇的状态,蛇的速度,蛇的当前分数,和食物的分数。

        这里注意,其实食物也是蛇身上的节点,因为我们最终会把它吃掉;蛇的速度其实就是休眠时间。

//游戏状态
enum GAME_STATUS
{
	OK = 1,//正常运行
	ESC = 2,//正常退出
	KILL_BY_WALL,//撞墙
	KILL_BY_SELF,//撞到自身
	VICTORY//获胜
};

//蛇的方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};
typedef struct Snake
{
	pSnakeNode pSnake;//维护整条蛇的指针
	pSnakeNode pFood;//指向食物的指针
	int Score;//当前累计分数
	int FoodWeight;//一个食物的分数
	int SleepTime;//蛇休眠的时间,休眠时间越短,速度越快;反之越慢
	enum GAME_STATUS status;//游戏当前的状态
	enum DIRECTION dir;//蛇当前的方向
}Snake, *pSnake;

        这里还需要注意蛇的状态有5种(其实也是游戏的运行状态):

  1. 正常运行(OK)
  2. 正常退出(ESC)
  3. 撞墙而死(KILL_BY_WALL)
  4. 咬到自己而死(KILL_BY_SELF)
  5. 游戏获胜(VICTORY) 

        蛇的行驶方向:

  1. 向上(UP)
  2. 向下(DOWN)
  3. 向左(LEFT)
  4. 向右(RIGHT)

        为了方便使用,我们使用枚举类型来定义。

        此时我们主函数中要先适应本地化,之后创建一条“蛇”,之后初始化游戏。

初始化游戏:

void test()
{
	//分为3大步

	//我们先创建贪吃蛇
	Snake snake = { 0 };

	GameStart(&snake);//游戏开始前的初始化
	//GameRun();//玩游戏的过程
	//GameEnd();//善后工作
}

int main()
{
	//首先适应本地化
	setlocale(LC_ALL, "");

	test();//完成贪吃蛇游戏的测试
	return 0;
}

        首先我们要设置控制台大小和标题,之后我们要隐藏光标信息并且打印欢迎信息。

//设置坐标
void SetPos(int x, int y)
{
	//获取设备句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//根据句柄设置光标位置
	COORD pos = { x, y };
	SetConsoleCursorPosition(handle, pos);
}

//打印欢迎信息
void WelcomeToGame()
{
	//打印欢迎信息
	SetPos(35, 10);
	printf("欢迎来到贪吃蛇小游戏\n");
	SetPos(36, 20);
	system("pause");
	system("cls");//清屏

	//打印功能介绍信息
	SetPos(15, 10);
	printf("用↑,↓,←,→来控制蛇的移动,F3是加速,F4是减速\n");
	SetPos(15, 11);
	printf("加速能得到更高的分数...\n");
	SetPos(38, 20);
	system("pause");
	system("cls");//清屏
}

//游戏开始前的初始化
void GameStart(pSnake ps)
{
	//设置控制台的信息:窗口大小,窗口名
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	//隐藏光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄
	CONSOLE_CURSOR_INFO cursorInfor = { 0 };
	GetConsoleCursorInfo(handle, &cursorInfor);//获取控制台光标信息
	cursorInfor.bVisible = false;//包含stdbool头文件
	SetConsoleCursorInfo(handle, &cursorInfor);//设置光标信息

	//打印欢迎信息
	WelcomeToGame();


	//getchar();//暂停程序,方便观察
}

 

打印地图: 

        我们适应本地化以后,就可以使用宽字符来打印地图了。我们先来总览一下地图:

         初始状态,假设蛇的长度是5,蛇身的每个节点是●,在固定的一个坐标处,比如(26,4)处开始出现蛇,连续5个节点。

        注意,蛇的每个节点x坐标必须是2的倍数,否则蛇身节点可能会出现在墙体中,坐标没有对齐出现故障。

        食物也必须在墙体内生成(x坐标必须是2的倍数),坐标不能和蛇的身体重合,之后打印★。

        因为我们需要蛇的身体节点是正方形,所以一个节点占据两个空间,为了方便使用,我们将墙体定义为宏。我们要先设置光标位置之后用宽字符打印字符: 

#define WALL L'□'
//绘制地图
void CreateMap()
{
	//上
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}

	//下
	SetPos(0, 26);
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}

	//左
	for (i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for (i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
	getchar();
}

初始化蛇并打印: 

        之后根据我们之前的图形去初始化一个蛇。我们再看一眼就会爆炸的地图:

         这个蛇的节点是头插实现,我们设置一个宏先来固定其实坐标,之后创建5个节点,每次设置好横纵坐标后顺带打印节点。之后设置蛇的其他初始状态。

        和之前一样,为了方便打印,我们将蛇的身体节点设为一个宏。

#define BODY L'●'

//设默认的起始坐标
#define POS_X 26
#define POS_Y 4

//初始化蛇
void InitSnake(pSnake ps)
{
	pSnakeNode cur = (pSnake)malloc(sizeof(SnakeNode));
	//创建5个蛇身节点
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		cur = (pSnake)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake()::malloc()");
			return;
		}

		cur->x = POS_X + (i * 2);
		cur->y = POS_Y;
		cur->next = NULL;
		//创建同时顺便打印
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		
		//头插
		if (ps->pSnake == NULL)
		{
			ps->pSnake = cur;
		}
		else
		{
			cur->next = ps->pSnake;
			ps->pSnake = cur;
		}
	}

	//贪吃蛇其他初始化信息
	ps->dir = RIGHT;//方向默认向右
	ps->FoodWeight = 10;//食物的分数
	ps->pFood = NULL;//默认食物信息
	ps->Score = 0;//默认0分
	ps->SleepTime = 200;//默认休眠200毫秒
	ps->status = OK;//开始游戏默认状态
	//getchar();
}

创建食物: 

        之后我们就要来初始化食物,这个食物必须是随机出现的,坐标就是随机的;坐标必须在墙体内部;坐标不能出现在蛇身上。

#define FOOD L'★'

//创建食物
void CreateFood(pSnake ps)
{
	//随机生成坐标
	//观察墙体,必须在墙体内部生成
	//x : 2 ~ 54  0 ~ 52 + 2
	//y : 1 ~ 25  0 ~ 24 + 1
	int x = 0;
	int y = 0;

	again:
	//必须保证 x 是偶数
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);
	
	//和蛇的每个节点比较
	pSnakeNode cur = ps->pSnake;
	while(cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}
	
	//创建食物
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreateFood()::malloc()");
		return;
	}
	food->x = x;
	food->y = y;
	food->next = NULL;
	ps->pFood = food;

	//打印食物
	SetPos(x, y);//记住设置光标位置
	wprintf(L"%lc", FOOD);
	//getchar();

}

        注意这里的食物横坐标必须是2的倍数! 

游戏运行:

打印帮助信息:

        这个就是纯打印,没啥说的。

//打印帮助信息
void PrintfHelpInfo()
{
	SetPos(65, 15);
	printf("1.不能穿墙,不能咬到自己");
	SetPos(65, 16);
	printf("2.用↑,↓,←,→来控制蛇的移动");
	SetPos(65, 17);
	printf("3.F3是加速,F4是减速");
	SetPos(65, 18);
	printf("ESC:退出游戏 space:暂停游戏");
	SetPos(65, 20);
	printf("作者:假油淦");
	getchar();
}

        注意这里的getchar是为了暂停程序方便我们观察,当我们正式运行游戏时一定要把所有的getchar函数去除。 

判断按键: 

         因为是循环判断按键,所以我们使用do while语句来检测按键。

        注意我们比如当前是向右走,就不能向左走;向上走就不能向下走,所以要判断一下。

        这里我们检测暂停时封装一个函数,只来用于睡眠,如果再次按到空格,则跳出循环。

        F3是加速,意味着睡眠时间变短,食物分数变高,但是要有上限。

//玩游戏的过程
void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintfHelpInfo();

	do
	{
		//当前的分数情况
		SetPos(65, 10);
		printf("总分:%5d\n", ps->Score);
		SetPos(65, 11);
		printf("食物的分值:%02d\n", ps->FoodWeight);

		//检测按键
		//上、下、左、右、ESC、空格、F3、F4
		//虚拟键码
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
		{//按了上键当前贪吃蛇方向不朝下走
			ps->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = ESC;//正常退出
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//游戏要暂停
			pause();//暂停和恢复暂停
		}
		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;
			}
		}
		//睡眠一下
		Sleep(ps->SleepTime);

		//走一步
	} while (ps->status == OK);
}

        · 此时,我们已经完成了二分之一了,就差蛇移动的函数未完成了(加把油!)。

蛇移动的函数:

        我们先判断下一个节点是不是食物。

        注意:因为我们是先检测按键的,所以我们判断当前蛇的行驶方向之后在特定的位置创建一个pNext节点让蛇吃掉,之后判断是不是食物。

        如果是食物,则把开辟的pNext节点吃掉,并采用头插,此时多出了食物节点,所以我们销毁食物节点。并把总分加上去。

//判断蛇头的下一步要走的位置处是否是食物
int NextIsFood(pSnake ps, pSnakeNode pNext)
{
	if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
	{
		return 1;//下一个坐标是食物
	}
	else
	{
		return 0;
	}
}
//下一步要走的位置就是食物
void EatFood(pSnake ps, pSnakeNode pNext)
{
	//头插
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;

	//打印蛇身
	pSnakeNode cur = ps->pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//把next节点吃掉了,此时分数要变
	ps->Score += ps->FoodWeight;

	//所以要释放食物节点
	free(ps->pFood);
	//还要创建食物
	CreateFood(ps);
}

        如果不是食物,则把pNext节点挂在蛇身上,也就是把pNext头插,之后尾删一个元素。 此时我们可以头插完以后顺便打印蛇身,一定记住设置光标位置之后打印。

        我们将最后一个节点(就是删除的尾节点打印成两个空格)。

//下一步要走的位置不是食物
void NotEatFood(pSnake ps, pSnakeNode pNext)
{
	//头插并尾删
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;

	pSnakeNode prev = ps->pSnake;
	pSnakeNode cur = ps->pSnake;
	while (cur->next)
	{
		//顺带打印
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		prev = cur;
		cur = cur->next;
	}
	//先将尾节点打印为空
	SetPos(cur->x, cur->y);
	printf("  ");//注意这里是两个空格

	//此时释放
	prev->next = NULL;
	free(cur);
}

判断蛇的状态:

        蛇每走一步,就需要判断游戏的状态,因为蛇有可能撞墙而死;也有可能撞到自己;也有可能是游戏胜利。所以我们还是需要分情况讨论。

        我们先判断蛇是不是撞墙而死。

//蛇撞墙
void KillByWall(pSnake ps)
{
	//直接判断蛇头位置即可
	if (ps->pSnake->x == 0 || ps->pSnake->x == 56
		|| ps->pSnake->y == 0 || ps->pSnake->y == 26)
	{
		ps->status = KILL_BY_WALL;
	}
}

        之后我们判断蛇是不是咬到自身,因为头是不可能碰到头的,所以我们从第二个节点开始判断。 

//蛇撞到自己
void KillBySelf(pSnake ps)
{
	//判断蛇头是否与蛇自身其他节点重合
	//直接从第二个节点开始判断
	pSnakeNode cur = ps->pSnake->next;
	while (cur)
	{
		if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
		{
			ps->status = KILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
}

        最后就是获胜,我们通过蛇身节点来判断是否获胜,通过观察,我们发现一个是675个宽字符占位符。 

//计算蛇身节点个数
int SnakeNodeCount(pSnake ps)
{
	pSnakeNode cur = ps->pSnake;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

//判断获胜
void IsVictory(pSnake ps)
{
	//我们一共有675个宽字符的格子
	//我们直接去判断蛇身节点有多少个去判断输赢
	int ret = SnakeNodeCount(ps);
	if (ret == 675)
	{
		ps->status = VICTORY;
	}
	else
	{
		return;
	}
}

        此时我们就需要去完成最后的善后工作了。

游戏善后工作: 

        使用switch语句来判断是那种情况结束了游戏,之后逐个释放蛇身节点。

//善后工作
void GameEnd(pSnake ps)
{
	SetPos(18, 12);
	switch (ps->status)
	{
	case ESC:
		printf("主动退出游戏,正常退出游戏\n");
		break;
	case KILL_BY_WALL:
		printf("很遗憾,撞墙了,游戏结束\n");
		SetPos(22, 13);
		printf("最终得分为:%d\n", ps->Score);
		break;
	case KILL_BY_SELF:
		printf("很遗憾,咬到自己了,游戏结束\n");
		SetPos(22, 13);
		printf("最终得分为:%d\n", ps->Score);
		break;
	case VICTORY:
		printf("恭喜你,获得胜利!\n");
		break;
	}

	//释放贪吃蛇的链表资源
	pSnakeNode cur = ps->pSnake;
	pSnakeNode nextPos = ps->pSnake;
	while (cur)
	{
		nextPos = cur->next;
		free(cur);
		cur = nextPos;
	}
	free(ps->pFood);
	ps->pSnake = NULL;
}

所有代码: 

snake.h头文件:

#pragma once
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>

#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'

//设默认的起始坐标
#define POS_X 26
#define POS_Y 4

//检测按键
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)

//游戏状态
enum GAME_STATUS
{
	OK = 1,//正常运行
	ESC = 2,//正常退出
	KILL_BY_WALL,//撞墙
	KILL_BY_SELF,//撞到自身
	VICTORY//获胜
};

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

typedef struct SnakeNode
{
	int x;//横坐标
	int y;//纵坐标
	struct SnakeNode* next;
}SnakeNode, * pSnakeNode;

//相当于
//typedef struct SnakeNode* pSnakeNode;

//贪吃蛇
typedef struct Snake
{
	pSnakeNode pSnake;//维护整条蛇的指针
	pSnakeNode pFood;//指向食物的指针
	int Score;//当前累计分数
	int FoodWeight;//一个食物的分数
	int SleepTime;//蛇休眠的时间,休眠时间越短,速度越快;反之越慢
	enum GAME_STATUS status;//游戏当前的状态
	enum DIRECTION dir;//蛇当前的方向
}Snake, *pSnake;

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

//打印欢迎信息
void WelcomeToGame();

//绘制地图
void CreateMap();

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

//创建食物
void CreateFood(pSnake ps);

//玩游戏的过程
void GameRun(pSnake ps);

//打印帮助信息
void PrintfHelpInfo();

//蛇移动的函数
void SnakeMove(pSnake ps);

//判断蛇头的下一步要走的位置处是否是食物
int NextIsFood(pSnake ps, pSnakeNode pNext);

//下一步要走的位置就是食物
void EatFood(pSnake ps, pSnakeNode pNext);

//下一步要走的位不是食物
void NotEatFood(pSnake ps, pSnakeNode pNext);

//蛇撞墙
void KillByWall(pSnake ps);

//蛇撞到自己
void KillBySelf(pSnake ps);

//判断获胜
void IsVictory(pSnake ps);

//善后工作
void GameEnd(pSnake ps);

test.c源文件:

#define  _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"

void test()
{
	int ch = 0;
	do
	{
		//创建贪吃蛇
		Snake snake = { 0 };
		GameStart(&snake);//游戏开始前的初始化
		GameRun(&snake);//玩游戏过程
		GameEnd(&snake);//善后工作
		SetPos(20, 15);
		printf("再来一局吗?(Y/N):");
		ch = getchar();
		getchar();//清理\n
	} while (ch == 'Y' || ch == 'y');
}

int main()
{
	//首先适应本地化
	setlocale(LC_ALL, "");

	//设置时间种子
	srand((unsigned int)time(NULL));

	test();//完成贪吃蛇游戏的测试
	SetPos(0, 26);
	return 0;
}

snake.c源文件:

#define  _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"

//设置坐标
void SetPos(int x, int y)
{
	//获取设备句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//根据句柄设置光标位置
	COORD pos = { x, y };
	SetConsoleCursorPosition(handle, pos);
}

//打印欢迎信息
void WelcomeToGame()
{
	//打印欢迎信息
	SetPos(35, 10);
	printf("欢迎来到贪吃蛇小游戏\n");
	SetPos(36, 20);
	system("pause");
	system("cls");//清屏

	//打印功能介绍信息
	SetPos(15, 10);
	printf("用↑,↓,←,→来控制蛇的移动,F3是加速,F4是减速\n");
	SetPos(15, 11);
	printf("加速能得到更高的分数...\n");
	SetPos(38, 20);
	system("pause");
	system("cls");//清屏
}

//绘制地图
void CreateMap()
{
	//上
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}

	//下
	SetPos(0, 26);
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}

	//左
	for (i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for (i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
	//getchar();
}

//初始化蛇
void InitSnake(pSnake ps)
{
	pSnakeNode cur = (pSnake)malloc(sizeof(SnakeNode));
	//创建5个蛇身节点
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		cur = (pSnake)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake()::malloc()");
			return;
		}

		cur->x = POS_X + (i * 2);
		cur->y = POS_Y;
		cur->next = NULL;
		//创建同时顺便打印
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		
		//头插
		if (ps->pSnake == NULL)
		{
			ps->pSnake = cur;
		}
		else
		{
			cur->next = ps->pSnake;
			ps->pSnake = cur;
		}
	}

	//贪吃蛇其他初始化信息
	ps->dir = RIGHT;//方向默认向右
	ps->FoodWeight = 10;//食物的分数
	ps->pFood = NULL;//默认食物信息
	ps->Score = 0;//默认0分
	ps->SleepTime = 200;//默认休眠200毫秒
	ps->status = OK;//开始游戏默认状态
	//getchar();
}

//创建食物
void CreateFood(pSnake ps)
{
	//随机生成坐标
	//观察墙体,必须在墙体内部生成
	//x : 2 ~ 54  0 ~ 52 + 2
	//y : 1 ~ 25  0 ~ 24 + 1
	int x = 0;
	int y = 0;

	again:
	//必须保证 x 是偶数
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);
	
	//和蛇的每个节点比较
	pSnakeNode cur = ps->pSnake;
	while(cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}
	
	//创建食物
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreateFood()::malloc()");
		return;
	}
	food->x = x;
	food->y = y;
	food->next = NULL;
	ps->pFood = food;

	//打印食物
	SetPos(x, y);//记住设置光标位置
	wprintf(L"%lc", FOOD);
	//getchar();

}

//游戏开始前的初始化
void GameStart(pSnake ps)
{
	//设置控制台的信息:窗口大小,窗口名
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	//隐藏光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄
	CONSOLE_CURSOR_INFO cursorInfor = { 0 };
	GetConsoleCursorInfo(handle, &cursorInfor);//获取控制台光标信息
	cursorInfor.bVisible = false;//包含stdbool头文件
	SetConsoleCursorInfo(handle, &cursorInfor);//设置光标信息

	//打印欢迎信息
	WelcomeToGame();

	//绘制地图
	CreateMap();
	
	//初始化蛇
	InitSnake(ps);
	
	//创建食物
	CreateFood(ps);

	//getchar();//暂停程序,方便观察
}

//打印帮助信息
void PrintfHelpInfo()
{
	SetPos(65, 15);
	printf("1.不能穿墙,不能咬到自己");
	SetPos(65, 16);
	printf("2.用↑,↓,←,→来控制蛇的移动");
	SetPos(65, 17);
	printf("3.F3是加速,F4是减速");
	SetPos(65, 18);
	printf("ESC:退出游戏 space:暂停游戏");
	SetPos(65, 20);
	printf("作者:假油淦");
	//getchar();
}

//暂停和恢复暂停
void pause()
{
	while (1)
	{
		Sleep(100);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

//判断蛇头的下一步要走的位置处是否是食物
int NextIsFood(pSnake ps, pSnakeNode pNext)
{
	if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
	{
		return 1;//下一个坐标是食物
	}
	else
	{
		return 0;
	}
}

//下一步要走的位置就是食物
void EatFood(pSnake ps, pSnakeNode pNext)
{
	//头插
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;

	//打印蛇身
	pSnakeNode cur = ps->pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//把next节点吃掉了,此时分数要变
	ps->Score += ps->FoodWeight;

	//所以要释放食物节点
	free(ps->pFood);
	//还要创建食物
	CreateFood(ps);
}

//下一步要走的位置不是食物
void NotEatFood(pSnake ps, pSnakeNode pNext)
{
	//头插并尾删
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;

	pSnakeNode prev = ps->pSnake;
	pSnakeNode cur = ps->pSnake;
	while (cur->next)
	{
		//顺带打印
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		prev = cur;
		cur = cur->next;
	}
	//先将尾节点打印为空
	SetPos(cur->x, cur->y);
	printf("  ");//注意这里是两个空格

	//此时释放
	prev->next = NULL;
	free(cur);
}

//蛇移动的函数
void SnakeMove(pSnake ps)
{
	//创建一个节点
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNext == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}
	pNext->next = NULL;

	switch (ps->dir)
	{
	case UP:
		pNext->x = ps->pSnake->x;
		pNext->y = ps->pSnake->y - 1;
		break;
	case DOWN:
		pNext->x = ps->pSnake->x;
		pNext->y = ps->pSnake->y + 1;
		break;
	case LEFT:
		pNext->x = ps->pSnake->x - 2;
		pNext->y = ps->pSnake->y;
		break;
	case RIGHT:
		pNext->x = ps->pSnake->x + 2;
		pNext->y = ps->pSnake->y;
		break;
	}

	//判断下一个坐标是否是食物
	if (NextIsFood(ps, pNext))
	{
		//是食物吃掉
		EatFood(ps, pNext);
	}
	else
	{
		//不是食物
		NotEatFood(ps, pNext);
	}

}

//蛇撞墙
void KillByWall(pSnake ps)
{
	//直接判断蛇头位置即可
	if (ps->pSnake->x == 0 || ps->pSnake->x == 56
		|| ps->pSnake->y == 0 || ps->pSnake->y == 26)
	{
		ps->status = KILL_BY_WALL;
	}
}

//蛇撞到自己
void KillBySelf(pSnake ps)
{
	//判断蛇头是否与蛇自身其他节点重合
	//直接从第二个节点开始判断
	pSnakeNode cur = ps->pSnake->next;
	while (cur)
	{
		if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
		{
			ps->status = KILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
}

//计算蛇身节点个数
int SnakeNodeCount(pSnake ps)
{
	pSnakeNode cur = ps->pSnake;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

//判断获胜
void IsVictory(pSnake ps)
{
	//我们一共有675个宽字符的格子
	//我们直接去判断蛇身节点有多少个去判断输赢
	int ret = SnakeNodeCount(ps);
	if (ret == 675)
	{
		ps->status = VICTORY;
	}
	else
	{
		return;
	}
}

//玩游戏的过程
void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintfHelpInfo();

	do
	{
		//当前的分数情况
		SetPos(65, 10);
		printf("总分:%5d\n", ps->Score);
		SetPos(65, 11);
		printf("食物的分值:%02d\n", ps->FoodWeight);

		//检测按键
		//上、下、左、右、ESC、空格、F3、F4
		//虚拟键码
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
		{//按了上键当前贪吃蛇方向不朝下走
			ps->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = ESC;//正常退出
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//游戏要暂停
			pause();//暂停和恢复暂停
		}
		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;
			}
		}

		//睡眠一下
		Sleep(ps->SleepTime);

		//走一步
		SnakeMove(ps);

		//此时蛇已经走了一步了,我们需要判断游戏是否结束
		//蛇撞墙
		KillByWall(ps);

		//蛇撞到自己
		KillBySelf(ps);

		//判断获胜
		IsVictory(ps);

	} while (ps->status == OK);
}

//善后工作
void GameEnd(pSnake ps)
{
	SetPos(18, 12);
	switch (ps->status)
	{
	case ESC:
		printf("主动退出游戏,正常退出游戏\n");
		break;
	case KILL_BY_WALL:
		printf("很遗憾,撞墙了,游戏结束\n");
		SetPos(22, 13);
		printf("最终得分为:%d\n", ps->Score);
		break;
	case KILL_BY_SELF:
		printf("很遗憾,咬到自己了,游戏结束\n");
		SetPos(22, 13);
		printf("最终得分为:%d\n", ps->Score);
		break;
	case VICTORY:
		printf("恭喜你,获得胜利!\n");
		break;
	}

	//释放贪吃蛇的链表资源
	pSnakeNode cur = ps->pSnake;
	pSnakeNode nextPos = ps->pSnake;
	while (cur)
	{
		nextPos = cur->next;
		free(cur);
		cur = nextPos;
	}
	free(ps->pFood);
	ps->pSnake = NULL;
}

总结:

        如果你通过该文章完成了一次,其实你会发现,也没那么难,无非就是根据游戏的进程一步一步去实现每个最微小的步骤。其实所有的难题,都是聚沙成塔的,只要你一步一个脚印去实现,真的没有难题!

        API帮助了我们实现了贪吃蛇,我们可能不知道其内部的实现,但是我们只需要去学会使用它即可。如果你可以独立完成一次,那么恭喜你,你已经彻底学会了C语言,接下来就可以往更高的地点出发。

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

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

相关文章

Springboot测试找不到bean

1.没有加注解 service类上需要加注解 2.Test类引用错误 3.测试类与我们使用的包名不同&#xff0c;两个都是com.travel才可以&#xff0c;否则扫描不到 4.引入的启动类错误 5.不是很确定&#xff0c;但是也是我犯的错误 6.没有配置好XML文件 有的话再补充

【并发编程】锁相关公平锁和非公平锁?可重入锁锁的升级乐观锁和悲观锁版本号机制CAS 算法乐观锁有哪些问题?

目录 ​编辑 锁相关 公平锁和非公平锁&#xff1f; 可重入锁 锁的升级 乐观锁和悲观锁 版本号机制 CAS 算法 乐观锁有哪些问题&#xff1f; 锁相关 公平锁和非公平锁&#xff1f; 公平锁 : 锁被释放之后&#xff0c;先申请的线程先得到锁。性能较差一些&#xff0c;因…

Nacos介绍和统一配置管理

Nacos&#xff08;全称为 Alibaba Cloud Nacos&#xff0c;或简称为 Nacos&#xff09;是一个开源的分布式服务发现和配置管理系统。它由阿里巴巴集团开发并开源&#xff0c;旨在帮助开发人员简化微服务架构下的服务注册、发现和配置管理。 一、Nacos 提供了以下主要功能&…

Deconstructing Denoising Diffusion Models for Self-Supervised Learning

开头说点题外话&#xff1a;这篇可谓是大咖云集啊&#xff0c;刘壮、谢赛宁、何凯明这些耳熟能详的名字&#xff0c;并且这篇论文一些人也觉得分析特别到位&#xff0c;不愧是大佬视角&#xff0c;配得上“解构”两个字&#xff1b;很巧的是&#xff0c;本科阶段的团队导师也是…

什么是虚拟继承

由于C支持多继承&#xff0c;除了public、protected和private三种继承方式外&#xff0c;还支持虚拟&#xff08;virtual&#xff09;继承&#xff0c;举个例子&#xff1a; #include <iostream> using namespace std;class A {}; class B : virtual public A {}; class…

大学形势与政策~秋试题及答案,分享几个实用搜题和学习工具 #媒体#笔记

大学生必备&#xff0c;这条笔记大数据一定定要推给刚上大学的学弟学妹&#xff01;&#xff01; 1.白鸽搜题 这个是公众号 超强题库资源&#xff0c;涵盖行业单位、学历提升等各类考试。解题更轻松&#xff0c;学习更高效。 下方附上一些测试的试题及答案 1、外部招募的主…

【数据库】聚合函数

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;清灵白羽 漾情天…

虚拟机Ubuntu无法识别U盘

1. 现象 虚拟机插入U盘后&#xff0c;无反应。系统中也找不到U盘。 2. 原因 VMware USB Arbitration Service服务别关闭了&#xff0c;任务管理器中找不到该任务。 服务中也没有运行&#xff08;services.msc&#xff09; 3. 解决 打开服务设置窗口&#xff08;services.…

温湿度项目设计V1.0——PCB布线及打板

PCB布线 画好原理图之后&#xff0c;通过工具——标注所有器件&#xff0c;当前原理图上的器件编号会被自动标注号。点击设置——Update PCB Document文件名&#xff0c;弹出的界面先点击生效变更&#xff0c;然后点击执行变更。界面会跳转到PCB界面。如果生效变更有问题&…

城管智慧执法系统源码,基于微服务+java+springboot+vue开发

城管智慧执法系统源码&#xff0c;基于微服务javaspringbootvue开发 城管智慧执法系统源码有演示&#xff0c;自主研发&#xff0c;功能完善&#xff0c;正版授权&#xff0c;可商用上项目。 一套数字化的城管综合执法办案系统源码&#xff0c;提供了案件在线办理、当事人信用…

开学考核复现

不要尝试爆破哟!(misc) 下载图片&#xff0c;解析图片 拿到密码&#xff0c;解压压缩包&#xff0c;生成图片 扫描二维码 留个后门(misc) D盾扫描 打开文件寻找 师姐的旅游照片(misc) 随波逐流&#xff0c;然后base64解码 修改后缀为zip&#xff0c;解压 修改高度 Fuck! (C…

「媒体宣传」企业活动发布会邀请媒体报道的好处与优势?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 企业活动发布会邀请媒体报道具有多种好处与优势&#xff0c;这些都有助于提升企业的知名度、形象和影响力。以下是一些主要的好处与优势&#xff1a; 提升品牌知名度&#xff1a;媒体报道…

MATLAB环境下基于离散小波变换和主成分平均的医学图像融合方法

随着计算机技术和生物影像工程的日趋成熟&#xff0c;医学图像为医疗诊断提供的信息越来越丰富。目前&#xff0c;由于医学成像的设备种类繁多&#xff0c;导致医生获得的图像信息差异较大。如何把这些信息进行整合供医生使用成为当务之急。基于此&#xff0c;医学图像融合技术…

Windows下安装QT,遇到下载组件中没有指定版本(提供解决方式) + 5.15详细安装步骤版

Windows下安装QT 5.15详细安装问题详解 前情提要一、QT 5.15及之后版本的下载问题二、QT 5.15及之后版本的下载方式&#xff1a;下载QT(在线安装版本)三、详细安装步骤遇到<下载组件>中没有指定版本的解决方式 前情提要 嵌入式设备搭载的QT版本是5.15&#xff0c;所以PC…

【C语言】预处理编译链接调试技巧详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.预处理 1.1 预定义符号 1.2 #define 1.2.1 #define 定义标识符 1.2.2 #define 定义宏 1.2.3 #define 替换规则 1.2.4 #和## …

23. UE5 RPG制作属性面板(一)

随着角色的属性越来越多&#xff0c;我们不能每次都进行showdebug abilitysystem进行查看&#xff0c;而且玩家也需要查看角色属性&#xff0c;所以需要一个查看玩家角色属性的面板。 在前面&#xff0c;我们创建三种类型的属性 Primary Attributes&#xff08;主要属性&#…

prettier + eslint 配置

vue-cli 新建项目选择 ESLint Prettier 会自动下载相关包 settings.json {"editor.formatOnSave": true, // 开启保存文件自动格式化代码"editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的代码格式化工具// "prettier.r…

11 Games101 - 笔记 - 几何(曲线与曲面)

11 几何&#xff08;曲线与曲面&#xff09; 贝塞尔曲线 定义 贝塞尔曲线&#xff1a;由控制点和线段组成的曲线&#xff0c;控制点是可拖动的支点。 如图&#xff0c;蓝色为贝塞尔曲线&#xff0c;p1, p2, p3为控制点&#xff0c;曲线和初始与终止端点相切&#xff0c;并且…

数字乡村引领新风尚:科技赋能农村实现全面进步

随着信息技术的迅猛发展&#xff0c;数字乡村正成为引领农村全面进步的新风尚。科技作为推动农村发展的强大引擎&#xff0c;正在深刻改变着传统农业的生产方式、农村的社会结构以及农民的生活方式&#xff0c;为农村经济社会的全面进步注入了新的活力和动力。本文将从数字乡村…

【深度学习】四种天气分类 模版函数 从0到1手敲版本

引入该引入的库 import torch import torch.nn as nn import matplotlib.pyplot as plt import torch.nn.functional as F import torchvision import torch.optim as optim %matplotlib inline import os import shutil import glob os.environ["KMP_DUPLICATE_LIB_OK&q…