基于链表实现贪吃蛇游戏

news2024/9/23 13:25:54

本文中,我们将使用链表和一些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;
}

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

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

相关文章

Utreexo:优化Bitcoin UTXO集合的基于哈希的动态累加器

1. 引言 前序博客&#xff1a; Utreexo&#xff1a;比特币UTXO merkle tree proof以节约节点存储空间 MIT Digital Currency Initiative 的 Thaddeus Dryja 2019年论文 Utreexo: A dynamic hash-based accumulator optimized for the Bitcoin UTXO set。 开源代码实现见&…

如何在DBeaver中重命名数据库

前言 DBeaver是一款强大的开源通用数据库管理和开发工具&#xff0c;支持多种数据库类型。在某些数据库系统中&#xff0c;你可以直接通过DBeaver的图形界面来重命名数据库名称。本文将详细介绍如何在DBeaver中进行数据库重命名操作。 重要提示&#xff1a; 对于不同的数据库…

SSD寻址单元IU对寿命的影响有多大?

随着存储技术的不断进步&#xff0c;固态硬盘SSD的容量正以惊人的速度增长&#xff0c;尤其是采用高密度QLC NAND闪存技术的大容量SSD&#xff0c;如30TB及以上级别的产品。QLC NAND由于每个单元能够存储4比特数据&#xff0c;从而显著提高了存储密度&#xff0c;但同时也带来了…

Web服务器之Tomcat

文章目录 Web 服务器软件简介资源分类访问流程常见的Web服务器软件 Tomcat简介使用步骤使用Tomcat注意事项部署项目的方式方式一方式二方式三 问题中文乱码黑窗口一闪而过启动报错 Web 服务器软件 简介 服务器&#xff1a;安装了服务器软件的计算机服务器软件&#xff1a;接收…

ssm跨域方案?

1、过滤器 2、xml配置 <mvc:cors><mvc:mapping path"/**" /> </mvc:cors>3、注解 CrossOrigin(origins “*”) 说明&#xff1a;三种方案&#xff0c;本质都是一样的、只是方式不一样罢了。

实现SERVLET生命周期事件

实现SERVLET生命周期事件 问题陈述 David Wong是Smart Software Developers的管理员,他希望创建一个应用程序在日志中记录请求和上下文对象初始化及向上下文对象添加属性的时间。同时,该应用程序应该还能在日志中记录删除上下文对象的属性及销毁请求和上下文时的时间。 解决方…

二进制部署promethues

1、定义&#xff1a;promethues是一个开源的系统监控以及报警系统&#xff0c;整合zabbix的功能&#xff08;监控系统、网络、设备&#xff09;&#xff0c;promethues可以兼容网络、设备、容器监控、告警系统。因为其与k8s是一个项目基金开发出来的产品&#xff0c;天生匹配k8…

设计模式第2篇|策略模式

&#x1f680; 作者简介&#xff1a;程序员小豪&#xff0c;全栈工程师&#xff0c;热爱编程&#xff0c;曾就职于蔚来、腾讯&#xff0c;现就职于某互联网大厂&#xff0c;技术栈&#xff1a;Vue、React、Python、Java &#x1f388; 本文收录于小豪的前端系列专栏&#xff0c…

Web3技术革新:重新定义在线体验

互联网的不断演进塑造了我们的数字生活&#xff0c;而Web3技术的涌现正带来一场前所未有的变革。本文将深入探讨Web3技术的创新&#xff0c;以及它如何重新定义和提升我们的在线体验。 Web3技术的基本概念 Web3是互联网的第三个时代&#xff0c;它将去中心化、区块链、智能合约…

Mac苹果电脑玩幻兽帕鲁 Crossover玩Windows游戏

​​ 《幻兽帕鲁》&#xff08;英文&#xff1a;Palworld&#xff09;是一款近期在 Steam 爆红的动作冒险生存游戏&#xff0c;游戏设置在一个居住着「帕鲁」的开放世界中&#xff0c;玩家可以战斗并捕捉帕鲁&#xff0c;也能用它们来建造基地、骑乘和战斗。 不过目前《幻兽帕…

MATLAB绘制电磁场

MATLAB绘制电磁场举例: clc;close all;clear all;warning off;%清除变量 rand(seed, 100); randn(seed, 100); format long g; m12 for k1:m for j1:m if k1 V(j,k)1; elseif((j1)|(jm)|(km)) V(j,k)0; else …

强敌环伺:金融业信息安全威胁分析——整体态势

从早期的Zeus和其他以银行为目标的特洛伊木马程序&#xff0c;到现在的大规模分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;再到新颖的钓鱼攻击和勒索软件&#xff0c;金融服务业已成为遭遇网络犯罪威胁最严重的行业之一。金融服务业的重要性不言而喻&#xff0…

暴雨受邀出席太原市人工智能行业协会年度大会

2024年1月26日&#xff0c;太原市人工智能行业协会第二届二次会员大会暨2024年年会成功召开。太原市委、市工商联、市大数据应用中心、市政协经济委员会以及太原市科技局的专家领导&#xff0c;与三百多名来自各行业的人工智能企业家和协会会员一同参加了本次盛会&#xff0c;共…

ARCGIS PRO SDK 数据库属性域设置与获取

一、数据库创建属性域。 sdk3.1 以下的开发版本不支持&#xff0c;不能使用 Pro SDK 向域添加新的编码值&#xff0c;可以使用地理处理工具&#xff1a; 创建属性域 Dim va As IReadOnlyList(Of String) Dim gpResult As ArcGIS.Desktop.Core.Geoprocessing.IGPResult Dim env…

gitee建库并git

箴言&#xff1a;书山有路勤为径 文章目录 前言一、gitee导入ssh二、gitee建库三、克隆到本地四、关联本地工程到远程仓库五、push流程总结 前言 nodejs每天的学习都有代码产出&#xff0c;转念一想不如在码云上面搞个仓库&#xff0c;也经历了些许波折&#xff0c;往常也建了…

论文阅读-MapReduce

论文名称&#xff1a;MapReduce: Simplified Data Processing on Large Clusters 翻译的效果不是很好&#xff0c;有空再看一遍&#xff0c;参照一下别人翻译的。 MapReduce:Simplified Data Processing on Large Clusters 中文翻译版(转) - 阿洒 - 博客园 (cnblogs.com) 概…

Powershell Install telegraf 实现Grafana Windows 图形展示

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

Dragons

题目链接&#xff1a; Problem - 230A - Codeforces 解题思路&#xff1a; 用结构体排序就好&#xff0c;从最小的开始比较&#xff0c;大于就加上奖励&#xff0c;小于输出NO 下面是c代码&#xff1a; #include<iostream> #include<algorithm> using namespac…

React16源码: React中LegacyContext的源码实现

LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了&#xff0c;就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢&#xff1f;因为它…

什么是Vue Vue入门案例

一、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 构建用户界面 的 渐进式 框架 Vue2官网&#xff1a;Vue.js 1.什么是构建用户界面 基于数据渲染出用户可以看到的界面 2.什么是渐进式 所谓渐进式就是循序渐进&#xff0c;不一定非得把V…