基于链表实现贪吃蛇游戏

本文中,我们将使用链表和一些Win32 API的知识来实现贪吃蛇小游戏

一、功能

(1)游戏载入界面

(2)地图的绘制

(3)蛇身的移动和变长

(4)食物的生成

(5)死亡判定

(6)计算得分

(7)退出游戏和暂停游戏

实现贪吃蛇小游戏,我们需要创建3个文件来实现不同的部分

  • Snake.c:游戏函数的实现
  • Snake.h:结构体定义、头文件引用、宏定义和函数声明
  • test.c:主函数的实现

二、Snake.h 

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

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

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

//蛇的初始位置
#define POS_X 24
#define POS_Y 5

enum GAME_STATUS
{
	RUN,
	ESC,
	KILL_BY_WALL,
	KILL_BY_SELF,
};

enum DIRECTION
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
};

//蛇⾝节点
typedef struct SnakeNode
{
	short x;
	short y;
	struct SnakeNode* next;
}SnakeNode;

typedef struct Snake
{
	SnakeNode* pSnake;//指向蛇头
	SnakeNode* Food; 
	int SleepTime;
	int Score;
	int FoodScore;
	enum GAME_STATUS Status;
	enum DIRECTION Dir;
}Snake;

void GameStart(Snake* ps);

void GameRun(Snake* ps);

void GameEnd(Snake* ps);

void SetPos(short x, short y);//设置光标坐标

void WelcomeToGame();//欢迎界面

void CreateMap();//创建地图

void InitSnake(Snake* ps);//初始化蛇

void CreateFood(Snake* ps);//生成食物

void PrintHelpInfo();//打印教程

void SnakeMove(Snake* ps);//蛇身移动

void EatFood(Snake* ps, SnakeNode* next);//移动后吃到食物

void NotEatFood(Snake* ps, SnakeNode* next);//移动后没吃到食物

void KillByWall(Snake* ps);//检测是否撞墙

void KillBySelf(Snake* ps);//检测是否撞到自己

三、Snake.c

在Snake.c中,我们将整个游戏拆分成游戏前的准备、游戏运行中和游戏结束后三部分

首先要在头文件中定义蛇的节点等相关信息

enum GAME_STATUS //游戏状态的枚举
{
	RUN, //游戏正常运行中
	ESC, //正常退出游戏
	KILL_BY_WALL, //撞到墙导致游戏结束
	KILL_BY_SELF, //撞到自己导致游戏结束
};

enum DIRECTION //蛇身方向的枚举
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
};

//蛇⾝节点
typedef struct SnakeNode
{
	short x; //x轴坐标
	short y; //y轴坐标
	struct SnakeNode* next;
}SnakeNode;

typedef struct Snake
{
	SnakeNode* pSnake; //指向蛇头
	SnakeNode* Food;  //指向食物
	int SleepTime; //蛇身运动的速度
	int Score; //总分数
	int FoodScore; //每个食物的分数
	enum GAME_STATUS Status; //游戏运行的状态
	enum DIRECTION Dir; //蛇身方向
}Snake;

接下来就可以开始设计游戏前的准备程序了

2.1 游戏前的准备

为了美观,我们可以使用一些cmd命令来设置控制台窗口的长宽等信息

控制台窗口实际上是有坐标的,也就是有行和列的

最左上角的位置的坐标为(0,0),像这样的一个字符高为1 宽为1

所以我们可以通过cmd命令将控制台设置为30行,100列,并将标题改成贪吃蛇

void GameStart(Snake* ps)
{
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得设备句柄
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);      //获得光标信息
	CursorInfo.bVisible = false;                    //设置光标为不可见
	SetConsoleCursorInfo(handle, &CursorInfo);      //保存光标信息
	//欢迎界面
	WelcomeToGame();
	//创建地图
	CreateMap();
	//初始化蛇
	InitSnake(ps);
	//生成食物
	CreateFood(ps);
}

将剩下的四个功能分别分装在四个函数中

(1)欢迎界面

void WelcomeToGame()//欢迎界面
{
	SetPos(45, 10);
	printf("欢迎来到贪吃蛇小游戏");
	SetPos(45, 15);
	system("pause");
	system("cls");
	SetPos(35, 10);
	printf("用 ↑.↓.←.→ 控制蛇的行动,F3为加速,F4为减速");
	SetPos(35, 11);
	printf("加速能得到更多的分数");
	SetPos(45, 15);
	system("pause");
	system("cls");
}

其中,SetPos函数是用来设置光标坐标的,因为在控制台中printf中的内容会从光标的位置开始打印。

void SetPos(short x, short y) // 设置光标坐标
{
	COORD pos = {x, y};
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(handle, pos);
}

完成后的效果如下:

(2)创建地图

创建地图时需要用到宽字符的打印,宽字符也就是高为1 宽为2的字符,需要用wprintf函数打印

void CreateMap() // 创建地图
{
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i < 58; i += 2)
	{
		SetPos(i, 0);
		wprintf(L"%lc", L'□');
	}
	for (i = 0; i < 58; i += 2)
	{
		SetPos(i, 26);
		wprintf(L"%lc", L'□');
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", L'□');
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", L'□');
	}
}

使用循环把墙体打印出来即可 

我们也可以用宏定义将 L'□' 替换

#define WALL L'□'

(3)初始化蛇

void InitSnake(Snake *ps) // 初始化蛇
{
	SnakeNode *cur = ps->pSnake = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (SnakeNode *)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake:malloc fail");
			return;
		}
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		if (ps->pSnake == NULL)
			ps->pSnake = cur;
		else
		{
			cur->next = ps->pSnake;
			ps->pSnake = cur;
		}
	}

	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	ps->Food = NULL;
	ps->SleepTime = 200;
	ps->Score = 0;
	ps->FoodScore = 10;
	ps->Status = RUN;
	ps->Dir = RIGHT;
}

对Snake结构体中的各项信息进行初始化即可,主要难点在于蛇身的创建 

这里的蛇身和蛇的初始位置也用了宏定义

#define BODY L'●'
//蛇的初始位置
#define POS_X 24
#define POS_Y 5

 (4)生成食物

void CreateFood(Snake *ps) // 生成食物
{
	int x = 0;
	int y = 0;
	SnakeNode *cur = ps->pSnake;
again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 == 1);

	while (cur)
	{
		if (cur->x == x || cur->y == y)
		{
			goto again;
			break;
		}
		cur = cur->next;
	}

	ps->Food = (SnakeNode *)malloc(sizeof(SnakeNode));
	if (ps->Food == NULL)
	{
		perror("CreateFood:malloc fail");
		return;
	}

	ps->Food->x = x;
	ps->Food->y = y;
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

随机生成食物要用到rand函数,并且食物不能跑出地图外,我们要注意一下细节

1.宽字符占两格,所以x必须为偶数

2.食物要保持在地图内部,x轴范围为2~54,也就是(0~52)+2,y轴同理

3.食物不能和蛇身重合

这里的食物也用了宏定义

#define FOOD L'★'

2.2 游戏运行中

这一部分需要包含分数等信息的打印和按键输入的判定等功能

这里将按键输入的判断封装为了宏

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

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

void GameRun(Snake* ps)
{
	PrintHelpInfo();//打印教程
	do
	{
		//打印信息
		SetPos(60, 10);
		printf("当前得分:%2d", ps->Score);
		SetPos(60, 11);
		printf("当前速度:%3d", (400 - ps->SleepTime) / 40);
		SetPos(60, 12);
		printf("每个食物得分:%2d", ps->FoodScore);

		//判断按键
		if (KEY_PRESS(VK_UP) && ps->Dir != DOWN)
		{
			ps->Dir = UP;
		}
		if (KEY_PRESS(VK_DOWN) && ps->Dir != UP)
		{
			ps->Dir = DOWN;
		}
		if (KEY_PRESS(VK_LEFT) && ps->Dir != RIGHT)
		{
			ps->Dir = LEFT;
		}
		if (KEY_PRESS(VK_RIGHT) && ps->Dir != LEFT)
		{
			ps->Dir = RIGHT;
		}
		if (KEY_PRESS(VK_ESCAPE))
		{
			ps->Status = ESC;
			break;
		}
		if (KEY_PRESS(VK_SPACE))
		{
			pause();
		}
		if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime > 40)
			{
				ps->SleepTime -= 40;
				ps->FoodScore += 2;
			}
		}
		if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodScore > 2)
			{
				ps->SleepTime += 40;
				ps->FoodScore -= 2;
			}
		}

		Sleep(ps->SleepTime);
		//蛇身移动
		SnakeMove(ps);

	} while (ps->Status == RUN);
}

(1)打印教程

void PrintHelpInfo() // 打印教程
{
	SetPos(60, 16);
	printf("请按空格键开始游戏");
	SetPos(60, 17);
	printf("不能撞到墙上或者撞到自己");
	SetPos(60, 18);
	printf("用 ↑.↓.←.→ 控制蛇的行动");
	SetPos(60, 19);
	printf("F3为加速,F4为减速");
	SetPos(60, 20);
	printf("ESC:退出游戏  space:暂停游戏");
}

效果如下

(2)暂停游戏

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

这里需要实现按一次空格暂停,再按一次恢复的效果,所以在循环内部还需要设置按键的判定。 

(3)蛇身移动

void SnakeMove(Snake *ps) // 蛇身移动
{
	SnakeNode *next = (SnakeNode *)malloc(sizeof(SnakeNode));
	if (next == NULL)
	{
		perror("SnakeMove:malloc fail");
		return;
	}

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

	if (next->x == ps->Food->x && next->y == ps->Food->y)
		EatFood(ps, next);
	else
		NotEatFood(ps, next);

	KillByWall(ps);

	KillBySelf(ps);
}

malloc一个新节点放到蛇头下一步移动到的位置,替换成新的蛇头。位置的计算根据蛇身的方向来计算坐标即可。

如果蛇的下一步吃到了食物,就不需要尾删,如果没吃到食物则需要尾删一次并且抹除尾巴

并且每一步移动都需要判定是否撞墙或撞到自己 

(4)移动后吃到食物的情况

void EatFood(Snake *ps, SnakeNode *next) // 移动后吃到食物
{
	next->next = ps->pSnake;
	ps->pSnake = next;
	SetPos(next->x, next->y);
	wprintf(L"%lc", BODY);

	ps->Score += ps->FoodScore;
	free(ps->Food);
	ps->Food = NULL;
	CreateFood(ps);
}

将原先的蛇头和新节点相连,更新蛇头的地址并且打印出新蛇头,不要忘了吃到食物后加分,还要创建一个新的食物 

(5)移动后没吃到食物的情况

void NotEatFood(Snake *ps, SnakeNode *next) // 移动后没吃到食物
{
	next->next = ps->pSnake;
	ps->pSnake = next;
	SnakeNode *cur = ps->pSnake;
	while (cur->next->next)
	{
		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; // 不置空会出现问题
}

还是将原先的蛇头和新节点链接,但是这次需要把尾部的节点删除并抹除尾节点 

(6)检测是否撞墙

void KillByWall(Snake *ps) // 检测是否撞墙
{
	if (ps->pSnake->x == 0 ||
		ps->pSnake->x == 56 ||
		ps->pSnake->y == 0 ||
		ps->pSnake->y == 25)
	{
		ps->Status = KILL_BY_WALL;
	}
}

检测蛇头的坐标和墙的坐标是否重合即可 

(7)检测是否撞到自己

void KillBySelf(Snake *ps) // 检测是否撞到自己
{
	SnakeNode *cur = ps->pSnake->next;
	while (cur)
	{
		if (ps->pSnake->x == cur->x && ps->pSnake->y == cur->y)
		{
			ps->Status = KILL_BY_SELF;
		}
		cur = cur->next;
	}
}

 检测蛇头的坐标是否和蛇身任一节点重合即可

2.3 游戏结束后

void GameEnd(Snake *ps)
{
	SetPos(24, 12);

	switch (ps->Status)
	{
	case KILL_BY_SELF:
		printf("你撞到了自己");
		break;
	case KILL_BY_WALL:
		printf("你撞到墙了");
		break;
	case ESC:
		printf("游戏退出中...");
		break;
	}

	SnakeNode *cur = ps->pSnake;
	while (ps->pSnake)
	{
		ps->pSnake = ps->pSnake->next;
		free(cur);
		cur = ps->pSnake;
	}
}

根据游戏的状态打印不同的信息,并且释放蛇身的内存空间 

2.4 完整代码

#include "snake.h"

void SetPos(short x, short y)//设置光标坐标
{
	COORD pos = { x,y };
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(handle, pos);
}

void WelcomeToGame()//欢迎界面
{
	SetPos(45, 10);
	printf("欢迎来到贪吃蛇小游戏");
	SetPos(45, 15);
	system("pause");
	system("cls");
	SetPos(35, 10);
	printf("用 ↑.↓.←.→ 控制蛇的行动,F3为加速,F4为减速");
	SetPos(35, 11);
	printf("加速能得到更多的分数");
	SetPos(45, 15);
	system("pause");
	system("cls");
}

void CreateMap()//创建地图
{
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i < 58; i += 2)
	{
		SetPos(i, 0);
		wprintf(L"%lc", WALL);
	}
	for (i = 0; i < 58; i += 2)
	{
		SetPos(i, 26);
		wprintf(L"%lc", WALL);
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

void InitSnake(Snake* ps)//初始化蛇
{
	SnakeNode* cur = ps->pSnake = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (SnakeNode*)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake:malloc fail");
			return;
		}
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		if (ps->pSnake == NULL)
			ps->pSnake = cur;
		else
		{
			cur->next = ps->pSnake;
			ps->pSnake = cur;
		}
	}

	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	ps->Food = NULL;
	ps->SleepTime = 200;
	ps->Score = 0;
	ps->FoodScore = 10;
	ps->Status = RUN;
	ps->Dir = RIGHT;
}


void CreateFood(Snake* ps)//生成食物
{
	int x = 0;
	int y = 0;
	SnakeNode* cur = ps->pSnake;
again:
	do 
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 == 1);
	
	while (cur)
	{
		if (cur->x == x || cur->y == y)
		{
			goto again;
			break;
		}
		cur = cur->next;
	}

	ps->Food = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (ps->Food == NULL)
	{
		perror("CreateFood:malloc fail");
		return;
	}

	ps->Food->x = x;
	ps->Food->y = y;
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

void GameStart(Snake* ps)
{
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(handle, &CursorInfo);
	
	WelcomeToGame();

	CreateMap();

	InitSnake(ps);

	CreateFood(ps);
}

void PrintHelpInfo()//打印教程
{
	SetPos(60, 16);
	printf("请按空格键开始游戏");
	SetPos(60, 17);
	printf("不能撞到墙上或者撞到自己");
	SetPos(60, 18);
	printf("用 ↑.↓.←.→ 控制蛇的行动");
	SetPos(60, 19);
	printf("F3为加速,F4为减速");
	SetPos(60, 20);
	printf("ESC:退出游戏  space:暂停游戏");
}

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

void EatFood(Snake* ps, SnakeNode* next)//移动后正好吃到食物
{
	next->next = ps->pSnake;
	ps->pSnake = next;
	SetPos(next->x, next->y);
	wprintf(L"%lc", BODY);
	
	ps->Score += ps->FoodScore;
	free(ps->Food);
	ps->Food = NULL;
	CreateFood(ps);
}

void NotEatFood(Snake* ps, SnakeNode* next)//移动后没吃到食物
{
	next->next = ps->pSnake;
	ps->pSnake = next;
	SnakeNode* cur = ps->pSnake;
	while (cur->next->next)
	{
		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 KillByWall(Snake* ps)//检测是否撞墙
{
	if (ps->pSnake->x == 0 ||
		ps->pSnake->x == 56 ||
		ps->pSnake->y == 0 ||
		ps->pSnake->y == 25)
	{
		ps->Status = KILL_BY_WALL;
	}
}

void KillBySelf(Snake* ps)//检测是否撞到自己
{
	SnakeNode* cur = ps->pSnake->next;
	while (cur)
	{
		if (ps->pSnake->x == cur->x && ps->pSnake->y == cur->y)
		{
			ps->Status = KILL_BY_SELF;
		}
		cur = cur->next;
	}
}

void SnakeMove(Snake* ps)//蛇身移动
{
	SnakeNode* next = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (next == NULL)
	{
		perror("SnakeMove:malloc fail");
		return;
	}

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

	if (next->x == ps->Food->x && next->y == ps->Food->y)
		EatFood(ps, next);
	else
		NotEatFood(ps, next);

	KillByWall(ps);

	KillBySelf(ps);
}

void GameRun(Snake* ps)
{
	PrintHelpInfo();
	do
	{
		//打印信息
		SetPos(60, 10);
		printf("当前得分:%2d", ps->Score);
		SetPos(60, 11);
		printf("当前速度:%3d", (400 - ps->SleepTime) / 40);
		SetPos(60, 12);
		printf("每个食物得分:%2d", ps->FoodScore);

		//判断按键
		if (KEY_PRESS(VK_UP) && ps->Dir != DOWN)
		{
			ps->Dir = UP;
		}
		if (KEY_PRESS(VK_DOWN) && ps->Dir != UP)
		{
			ps->Dir = DOWN;
		}
		if (KEY_PRESS(VK_LEFT) && ps->Dir != RIGHT)
		{
			ps->Dir = LEFT;
		}
		if (KEY_PRESS(VK_RIGHT) && ps->Dir != LEFT)
		{
			ps->Dir = RIGHT;
		}
		if (KEY_PRESS(VK_ESCAPE))
		{
			ps->Status = ESC;
			break;
		}
		if (KEY_PRESS(VK_SPACE))
		{
			pause();
		}
		if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime > 40)
			{
				ps->SleepTime -= 40;
				ps->FoodScore += 2;
			}
		}
		if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodScore > 2)
			{
				ps->SleepTime += 40;
				ps->FoodScore -= 2;
			}
		}

		Sleep(ps->SleepTime);
		//蛇身移动
		SnakeMove(ps);

	} while (ps->Status == RUN);
}

void GameEnd(Snake* ps)
{
	SetPos(24, 12);

	switch (ps->Status)
	{
	case KILL_BY_SELF:
		printf("你撞到了自己");
		break;
	case KILL_BY_WALL:
		printf("你撞到墙了");
		break;
	case ESC:
		printf("游戏退出中...");
		break;
	}

	SnakeNode* cur = ps->pSnake;
	while (ps->pSnake)
	{
		ps->pSnake = ps->pSnake->next;
		free(cur);
		cur = ps->pSnake;
	}
}

四、test.c

#include "snake.h"

void Test()
{
	srand((unsigned int)time(NULL));
	char ch = 0;
	do
	{
		Snake snake;
		GameStart(&snake);
		GameRun(&snake);
		GameEnd(&snake);
		if (snake.Status == ESC)
			break;
		SetPos(24, 13);
		printf("再来一局吗?(Y/N):");
		scanf(" %c", &ch);
	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 27);
}

int main()
{
	setlocale(LC_ALL, "chs");
	Test();
	return 0;
}

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

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

相关文章

数学建模学习笔记||灰色关联分析

灰色系统 信息绝对透明的是白色系统&#xff0c;信息绝对秘密的是黑色系统&#xff0c;灰色系统介于两者之间 关联分析 即系统的分析因素 包含多种因素的系统中&#xff0c;哪些因素是主要的&#xff0c;哪些因素是次要的&#xff0c;哪些因素影响大&#xff0c;哪些因素影响小…

vue3+typescript+Vite基础简单项目

gitee地址 数据大屏 菜单管理

【Java反序列化】Shiro-550漏洞分析笔记

目录 前言 一、漏洞原理 二、Shiro环境搭建 三、Shiro-550漏洞分析 解密分析 加密分析 四、URLDNS 链 前言 shiro-550反序列化漏洞大约在2016年就被披露了&#xff0c;在上学时期也分析过&#xff0c;最近在学CC链时有用到这个漏洞&#xff0c;重新分析下并做个笔记&…

希尔伯特变换的在信号解调时的示例

1.希尔伯特变换的应用场景 希尔伯特变换&#xff0c;在数学上的含义是清晰的。它是一个数字移相器&#xff0c;可以把通过它的任何一个信号相移-90度。这个数学工具在信号解调时&#xff0c;会有非常有用的特性出现。可以看示例&#xff1a; 解释一下&#xff1a; 1.最上面的…

Nuget包缓存存放位置迁移

本文介绍了如何通过环境变量修改Nuget包缓存的存放位置。 一、背景 默认情况下&#xff0c;NuGet会将项目中使用的包缓存到C盘&#xff0c;随着项目开发积累nuget包越来越多&#xff0c;这会逐渐挤占大量C盘空间&#xff0c;所以我们可以将nuget包缓存位置指定到其他盘中存放…

秋招面试—浏览器原理篇

浏览器原理篇 1.什么是XSS、CSRF,怎么预防&#xff1f; &#xff08;1&#xff09;XSS(跨站脚本攻击)&#xff1a;攻击者将恶意代码植入到浏览器页面中&#xff0c;盗取存储在客户端的Cookie&#xff1b; ​ XSS分为&#xff1a;①存储型&#xff1a;论坛发帖、商品评论、用户…

(2024,CompAgent,LLM,提示分解,基于布局的对象组合)分而治之:语言模型可以规划和自我纠正组合文本到图像的生成

Divide and Conquer: Language Models can Plan and Self-Correct for Compositional Text-to-Image Generation 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 3. 方法 3.1…

Golang 流媒体服务器lalserver使用指南

目录 安装 使用 1.推流 2.播放 官方地址 安装 1.下载源码 wget https://github.com/q191201771/lal/releases/download/v0.36.7/lal_v0.36.7_linux.zipunzip lal_v0.36.7_linux.zip cd lal_v0.36.7_linux 2.启动 ./bin/lalserver -c ./conf/lalserver.conf.json 使用 …

C语言第十三弹---VS使用调试技巧

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 VS调试技巧 1、什么是bug 2、什么是调试&#xff08;debug&#xff09;&#xff1f; 3、Debug和Release​编辑​ 4、VS调试快捷键 4.1、环境准备 4.2、调试…

java.util.LinkedHashSet cannot be cast to java.util.List 的解决方案

出现 “java.util.LinkedHashSet cannot be cast to java.util.List” 的错误&#xff0c;通常是因为你试图将一个 LinkedHashSet 对象直接强制转换为 List 类型。在 Java 中&#xff0c;LinkedHashSet 和 List 是两种不同的集合类型&#xff0c;不能直接进行转换。LinkedHashS…

小白水平理解面试经典题目LeetCode 455 Assign Cookies【Java实现】

455 分配cookies 小白渣翻译&#xff1a; 假设你是一位很棒的父母&#xff0c;想给你的孩子一些饼干。但是&#xff0c;你最多应该给每个孩子一块饼干。 每个孩子 i 都有一个贪婪因子 g[i] &#xff0c;这是孩子满意的 cookie 的最小大小&#xff1b;每个 cookie j 都有一个…

sv program module

为了避免races&#xff0c;在验证中引入program&#xff1b; Similarities between program and module block A program block can instantiate another program block in the way how the module is instantiated another module block.Both can have no or more inputs, …

知识推理的多重途径

目录 前言1 逻辑及推理简介2 演绎推理&#xff1a;Top-Down Logic2.1 肯定前件假言推理2.2 否定后件假言推理2.3 演绎推理的逻辑链条 3 归纳推理&#xff1a;Bottom-Up Logic3.1 从特例到一般3.2 逐步推导的过程 4 溯因推理&#xff1a;结果的可解释逻辑4.1 推断过程的回溯4.2 …

vue 使用echarts-gl实现3d旋转地图

之前也有使用过echarts开发项目中涉及到的地图功能&#xff0c;当时使用geo来实现地图轮廓&#xff0c;看上去有种3d的感觉。最近闲来无事看了一份可视化大屏的UI设计图&#xff0c;感觉3d旋转地图挺好玩的&#xff0c;今天就来尝试实现下。 首先安装下echarts和echarts-gl依赖…

关于paddleocr的predict_system按高度顺序画图

关于paddleocr的predict_system按高度顺序画图&#xff0c;&#xff08;coco格式&#xff09; 增加adjust_res函数&#xff1a; 实现代码&#xff1a; def adjust_res(res):res_cp deepcopy(res)res_cp sorted(res_cp, keylambda x: x[bbox][1], reverseFalse)return res …

Android Studio项目——TCP客户端

目录 一、TCP客户端UI 1、UI展示 2、xml代码 二、TCP客户端数据发送 三、TCP客户端数据接收 一、TCP客户端UI 1、UI展示 2、xml代码 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.…

【算法专题】贪心算法

贪心算法 贪心算法介绍1. 柠檬水找零2. 将数组和减半的最少操作次数3. 最大数4. 摆动序列(贪心思路)5. 最长递增子序列(贪心算法)6. 递增的三元子序列7. 最长连续递增序列8. 买卖股票的最佳时机9. 买卖股票的最佳时机Ⅱ(贪心算法)10. K 次取反后最大化的数组和11. 按身高排序12…

leetcode514. 自由之路【线性dp】

原题链接&#xff1a;leetcode514. 自由之路 题目描述 电子游戏“辐射4”中&#xff0c;任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘&#xff0c;并使用表盘拼写特定关键词才能开门。 给定一个字符串 ring &#xff0c;表示刻在外环上的编码&…

CHS_03.2.3.2_2+进程互斥的硬件实现方法

CHS_03.2.3.2_2进程互斥的硬件实现方法 知识总览中断屏蔽方法TestAndSet指令Swap指令 知识回顾 进程互斥的四种软件实现方法 知识总览 这个小节我们会介绍另外的三种进程互斥的硬件实现方法 那么 这个小节的学习过程当中 大家需要注意理解各个方法的原理 并且要稍微的了解各个…

OpenGL ES 渲染 NV21、NV12 格式图像有哪些“姿势”?

使用2个纹理实现 NV21 格式图像渲染 前文提到渲染 NV21 格式图像需要使用 2 个纹理,分别用于保存 Y plane 和 UV plane 的数据,然后在片段着色器中分别对 2 个纹理进行采样,转换成 RGB 数据。 OpenGLES 渲染 NV21或 NV12 格式图像需要用到 GL_LUMINANCE 和 GL_LUMINANCE_A…