贪吃蛇的实现,基于windows操作系统

news2024/11/23 12:42:49

前言:

贪吃蛇从学习到真正实现花了9天实现,第一二天第一次学习,第三四五天第二次学习,第六七八天一边实现一边思考,才完成了贪吃蛇的代码。实现了贪吃蛇以后已经接近过年,我想自己再根据掌握的知识制作烟花燃烧绽放的场景。贪吃蛇的移动和烟花的移动原理是一样的,贪吃蛇的头插删尾,使我能够处理烟花消失的部分。在我花两天时间写完500行的烟花代码后,对贪吃蛇的实现原理也更加了解了。然后再写下这篇文章。

写作过程中遇到的问题:有很多,但最多的是,写着写着不知道当前要实现什么。思路不清晰。然后去看正确的代码。以及对指针的掌握不够,要使用的函数比较生疏,令人烦躁,静不下心去理解等等。之后通过各种途径一一克服了。

一、分析和规划贪吃蛇的思路

1.一个已经懂得贪吃蛇怎样写的人和一个从没写过贪吃蛇怎么写,第一次上手的思维是不一样的。但是第一部应该都需要了解自己要实现哪些功能。然后划分为不同阶段逐个完成。

2.根据想要制作的成品模样,画出草图和X-mind思维导图。

                                                          成图1,欢迎页面一

                                                        成图2,欢迎页面二

                                                      成图3,游戏页面

     根据贪吃蛇游戏框,绘出墙体食物和蛇。

接下来是X-mind思维导图,写出整个程序的脉络。然后一一落实。

二、落实想法

1.建立三个文件。snake.c  snake.h  test.c.

2.贪吃蛇有两个结构体。

一个是蛇身,它是由一个个结点组成。

这个结点里放蛇身的坐标和下一个结点的指针,记住下一个结点的位置。放坐标是因为蛇在移动的时候要打印蛇身,蛇身的位置是依靠坐标来确定的,这样打印的时候就能找到蛇身的位置了。

这个蛇身由单向不带头不循环链表----单链表构成。

创建一个结构体,以及一个指向结构体的指针并将它们重命名。

typedef struct SnakeNode
{
   int x;
   int y;
   struct SnakeNode * next;
}SnakeNode, *pSnakeNode;

另一个用来维护蛇。如思维导图中写的思路所示。

typedef struct Snake
{
   pSnakeNode sn;  //指向蛇头的指针
   pSnakeNode pfood;//指向食物的指针,本质上和蛇身没什么区别,只是它是单个的,没有链。
   int score;   //分数,每次吃食物要涨粉
   int foodweight;//当前每吃一个食物增加的分数
   enum DIRECTION;//方向
   enum STATUS;//状态
   int sleeptime;//速度
}Snake,*psnake;//重命名

3.三个文件代码附上

snake.h

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#pragma once
#include <locale.h>
#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <math.h>

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

//监测按键
#define KEY_STATE(vkey)   ((GetAsyncKeyState(vkey)&0x1)?(1):(0))
//蛇头方向
enum DIRICTION
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};
//游戏状态
enum STATUS
{
	OK=1,
	KILL_BY_SELF,
	KILL_BY_WALL,
	ESC
};

//蛇身的结点
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

//蛇的维护
typedef struct Snake
{
	pSnakeNode sn;//蛇身,是一个结构体指针
	pSnakeNode pfood;//也是一个结构体指针,它是一个坐标
	enum DIRICTION Dir;//蛇的方向
	enum STATUS Status;//蛇的状态
	int Score;//游戏当前得分
	int Foodweight;//食物的分数
	int Sleeptime;//走一步睡眠时间,和蛇速相关
}Snake,*pSnake;

//游戏开始前的准备工作
void GameStart(pSnake snake);

//玩游戏
void GameRun(pSnake snake);
//
游戏结束
//void GameEnd();

snake.c

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#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(38, 13);
	printf("欢迎来到贪吃蛇小游戏\n");
    
	SetPos(60, 25);
	system("pause");
	system("cls");
	//打印游戏说明
	SetPos(25, 12);
	printf("用↑.↓.←.→分别控制蛇的移动,F3为加速,F4为减速\n");
	SetPos(25, 13);
	printf("加速能得到更高的分数。\n");
	SetPos(60, 25);
	system("pause");
	system("cls");
}

//初始化蛇身
void InitSnake(pSnake snake)
{
	for (int i = 0; i < 5; i++)
	{
		pSnakeNode p = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (p == NULL)
		{
			perror("malloc failed!\n");
			exit(1);
		}
		p->x = 20+2*i;
		p->y = 6;
		p->next = NULL;

		//头插法
		if (snake->sn==NULL)
		{
			snake->sn = p;
		}
		else
		{
			p->next = snake->sn;
			snake->sn = p;
		}

		//打印蛇身,用循环
		/*if (p)
		{
			SetPos(p->x, p->y);
			wprintf(L"%lc", SNAKE);
		}*/
		while (p)
		{
			SetPos(p->x, p->y);
			wprintf(L"%lc", SNAKE);
			p = p->next;
		}
	}
	//其他信息初始化
	snake->Dir = RIGHT;
	snake->Foodweight = 10;
	snake->pfood = NULL;
	snake->Score = 0;
	snake->Sleeptime = 200;
	snake->Status = OK;
	
}

void CreatMap()
{
	int i = 0;
	for (i = 0; i < 57; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	
	for (i = 0; i <= 26; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc\n", WALL);
	}
	SetPos(0, 26);
	for (i = 0; i < 57; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	SetPos(0, 1);
	for (i = 0; i <= 25; i++)
	{
		wprintf(L"%lc\n", WALL);
	}
}

void CreatFood(pSnake snake)
{
	int x = 0;
	int y = 0;
again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);

	//不能在蛇身上
	pSnakeNode cur = snake->sn;
	while (cur)
	{
		if (cur->x != x || cur->y != y)
		{
			cur = cur->next;
		}
		else
		{
			goto again;
		}
	}

	//申请食物的结点
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror("malloc failed!\n");
		exit(1);
	}
	pFood->x = x;
	pFood->y = y;

	snake->pfood = pFood;
	SetPos(x, y);
	wprintf(L"%lc", FOOD);

}
//游戏开始前的准备工作
void GameStart(pSnake snake)
{
	//设置一下控制台大小 
	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();
	//绘制地图
	CreatMap();
	//初始化蛇身
	InitSnake(snake);
	//打印食物
	CreatFood(snake);

}

void PrintHelpInfo()
{
	SetPos(61, 16);
	printf("1.不能撞墙,不能咬到自己\n");
	SetPos(61, 17);
	printf("2.用↑.↓.←.→分别控制蛇的移动\n");
	SetPos(61, 18);
	printf("3.F3为加速,F4为减速\n");
	SetPos(61, 19);
	printf("4.加速可以获得更多分数\n");
	SetPos(80, 22);
	printf("制作者:真白");
}

void EatFood(pSnake snake, pSnakeNode pnext)
{
	//吃食物,则头插
	pnext->next = snake->sn;//食物的下一个结点连接蛇头
	snake->sn = pnext;//把食物的结点给蛇头
	
	//打印蛇身
	pSnakeNode cur = snake->sn;//创建一个cur指针来循环
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", SNAKE);
		cur = cur->next;
	}
	//分数变化
	snake->Score += snake->Foodweight;
	//释放旧的食物结点
	free(snake->pfood);
	
	//创建新的食物结点
	CreatFood(snake);
}

void NotEatFood(pSnake snake,pSnakeNode pnext)
{
	//正常走,头插,删尾
	pnext->next = snake->sn;
	snake->sn = pnext;

	//删尾,创建一个指针循环
	pSnakeNode cur = snake->sn;
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);//先设置坐标再打印
		wprintf(L"%lc", SNAKE);//打印蛇身
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);//先设置坐标再打印尾处的空白
	printf("  ");
	free(cur->next);//释放尾结点
	cur->next = NULL;
}

void IsItFood(pSnake snake, pSnakeNode pnext)
{
	if (pnext->x == snake->pfood->x && pnext->y == snake->pfood->y)
	{
		//是食物,吃掉
		EatFood(snake,pnext);
	}
	else
	{
		//不是食物,不吃
		NotEatFood(snake,pnext);
	}
}

void KillByWall(pSnake snake, pSnakeNode pnext)
{
	//下一个结点的位置是不是墙的坐标
	if (pnext->x == 0 || pnext->y == 0 || pnext->x == 56 || pnext->y==26)
	{
		snake->Status = KILL_BY_WALL;
	}
}
void KillBySelf(pSnake snake, pSnakeNode pnext)
{
	//下一个结点是不是蛇身
	pSnakeNode cur = snake->sn->next;
	while (cur)
	{
		if (pnext->x != cur->x || pnext->y != cur->y)
		{
			cur = cur->next;
		}
		else 
		{
			snake->Status = KILL_BY_SELF;
			break;
		}
	}

}

void SleepTime(pSnake snake)
{
	Sleep(snake->Sleeptime);
}
void Snakemove(pSnake snake)
{
	//创建蛇的下一个位置的结点
	pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pnext == NULL)
	{
		perror("Snakemove:: malloc");
		return;
	}
	pnext->next = NULL;//只需要这个结点,不需要它下一个结点的信息

	//安排pnext的坐标
	switch (snake->Dir)
	{
	case UP: pnext->x = snake->sn->x;
		     pnext->y = snake->sn->y - 1;//如果按了上,下一个坐标的位置就在蛇头的上面
		break;
	case DOWN: pnext->x = snake->sn->x;
		       pnext->y = snake->sn->y + 1;//同上,下面同上
		break;
	case LEFT:pnext->x = snake->sn->x - 2;
		      pnext->y = snake->sn->y;
		break;
	case RIGHT:pnext->x = snake->sn->x + 2;
		       pnext->y = snake->sn->y;
		break;
	}

	//判断下一个结点是否是食物
	IsItFood(snake, pnext);

	//判断下一个节点是否撞墙
	KillByWall(snake,pnext);

	//判断下一个节点是否咬到自己
	KillBySelf(snake,pnext);
    
}
void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_STATE(VK_SPACE) == 1)
		{
			break;
		}
	}
}

void F3(pSnake snake)
{
	//休眠时间限制,5档 200,170,140,110,80
	if (snake->Sleeptime <= 200 && snake->Sleeptime >= 110)
	{
		snake->Sleeptime -= 30;
		snake->Foodweight += 2;
	}
}

void F4(pSnake snake)
{
	if (snake->Sleeptime >= 80 && snake->Sleeptime <= 170)
	{
		snake->Sleeptime += 30;
		if (snake->Foodweight >= 4)
		{
			snake->Foodweight -= 2;
		}
	}
}
//玩游戏
void GameRun(pSnake snake)
{
	//打印帮助信息
	PrintHelpInfo();

	do
	{
		//当前分数情况
		SetPos(60, 10);
		printf("得分:%d  ", snake->Score);
		printf("每个食物得分:%02d\n", snake->Foodweight);

		//监测当前按键情况
		
		if (KEY_STATE(VK_UP) == 1 && snake->Dir != DOWN)
		{
			snake->Dir = UP; 
		}
		else if(KEY_STATE(VK_DOWN) == 1&& snake->Dir != UP)
		{
			snake->Dir = DOWN;
		}
		else if (KEY_STATE(VK_LEFT) == 1&& snake->Dir != RIGHT)
		{
			snake->Dir = LEFT;
		}
		else if (KEY_STATE(VK_RIGHT) == 1&& snake->Dir != LEFT)
		{
			snake->Dir = RIGHT;
		}
		else if (KEY_STATE(VK_ESCAPE) == 1)
		{
			snake->Status = ESC;
			break;
		}
		else if (KEY_STATE(VK_SPACE) == 1)
		{
			Pause();
		}
		else if (KEY_STATE(VK_F3) == 1)
		{
			F3(snake);
		}
		else if (KEY_STATE(VK_F4) == 1)
		{
			F4(snake);
		}
		

		
		//蛇的移动
		Snakemove(snake);

		//移动一个位置,休眠一下
		SleepTime(snake);

	} while(snake->Status==OK);

}

void GameEnd(pSnake snake)
{
	SetPos(15, 12);
	switch (snake->Status)
	{
	case ESC:
		printf("主动退出游戏,正常退出\n");
		break;
	case KILL_BY_WALL:
		printf("很遗憾,你撞墙了,游戏结束\n");
		break;
	case KILL_BY_SELF:
		printf("很遗憾,你咬到自己了,游戏结束\n");
		break;
		}
	pSnakeNode cur = snake->sn;
	pSnakeNode del = NULL;

	while (cur)
	{
		del = cur;
		cur = cur->next;
		free(del);
	}

	free(snake->pfood);
	snake = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include "snake.h"


void test()
{
	int ch = 0;
	do
	{
		Snake s1 = { 0 };
		GameStart(&s1);
		GameRun(&s1);
		GameEnd(&s1);
		SetPos(15, 14);
		printf("再玩一把吗?Y/N :");
		ch = getchar();
		getchar();
	

	} while (ch == 'Y' || ch == 'y');
	
}

//
int main()
{
	setlocale(LC_ALL, "");//本地化,头文件<locale.h>
	test();
	
	
	SetPos(0, 27);
	return 0;

}




三、整个贪吃蛇实现的难点

1.windows系统提供的API的一些接口和功能。

在学C语言的时候没有接触,所以要了解一下需要掌握的函数用法。

如何修改控制台的大小?

通过包含windows.h的库函数,可以使用windows命令提示符的一些命令。来达到修改控制台大小的效果。在程序结束前,都是这个大小。system("mode con cols 100 lines 30")

同样的,修改控制台的标题也是利用windows命令提示符的一些命令来修改。

如何隐藏光标?

首先是要获得控制台的句柄。句柄就相当于控制台的钥匙,获取句柄,就是获取这个控制台的信息,(这些信息使得这个控制台与其他控制台相区别)我们得到的这个控制台的句柄,只能操作这个控制台的独特信息,不能修改其他控制台的独特信息。提供的函数是GetStdHandle,它有一个参数,但可以选择填入的参数有三个,为了获取句柄,填入的是STD_OUTPUT_HANDLE。这个函数返回的类型是HANDLE。因此也要创建一个HANDLE 类型的变量来接收返回的句柄。

    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 
    CONSOLE_CURSOR_INFO Cursorinfo;      创造一个光标的变量,前面是类型,后面是变量,这是在下一句获取光标信息要用到。提前创建一个变量。
    GetConsoleCursorInfo(handle, &Cursorinfo);    传本控制台的句柄和一个光标变量的地址。传入这两个变量以后,就能获取本控制台的光标信息。本控制台的光标信息,会被复制到Cursorinfo这个变量上。
    Cursorinfo.bVisible = false; 这个Cursorinfo是一个结构体变量,它有两个成员,一个是光标占一个坐标的比例(0-100)。第二个是是否可见。把bVisible设置为ture就是可见,false就是不可见。通过这一步,就把光标设置为隐藏。
    SetConsoleCursorInfo(handle, &Cursorinfo);这个函数是设置光标信息。传入本控制台的句柄,再把设置好的光标信息传进去。从而改变本控制台的光标。

如何设置坐标位置?

首先,什么是坐标?

以此图为例,x坐标是横坐标,从左向右延申,y坐标是纵坐标,从上至下延申,第一个位置是原点。

如何设置坐标位置:

void SetPos(int x, int y)  只要传入坐标的位置,就可以把光标移到想要的地方。把他封装成一个函数
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    获取本控制台的句柄,因为把光标移到指定坐标(的函数)也需要本控制台的句柄(作为它的参数)。
    COORD pos = { x,y }; 这是一个坐标的结构体,COORD是一个结构体类型,里面是坐标的参数。这一步是设置pos作为一个结构体坐标变量,成员初始化为x,y。
    SetConsoleCursorPosition(handle, pos);这是一个设置光标位置的函数,需要传两个参数,一个是本控制台句柄,一个是指定的坐标信息。
}

通过这些代码就可以把光标移动到指定位置,然后在指定位置打印出想要的信息。

2.绘制地图

地图的墙是宽字符,打印宽字符要先本地化。使用的函数是wprintf,w表示wide的意思。

3.初始化蛇身

初始化蛇身需要做到申请蛇的结点,每个结点的坐标关系是怎样的,在于你想要蛇一开始在地图的哪个位置出现。蛇的结点之间的连接采用头插法。把蛇的结点都创建出来以后,还要打印一遍蛇身。最后再初始化蛇的状态、速度、食物分数、分数等信息。

4.打印食物

食物的特征,本质上是一个结点。坐标要随机生成,不能生成在墙上,x坐标得是2的倍数,因为和蛇对称,和墙也要对称。坐标不能在蛇的身上。设置好相关条件就可以创建结点了。然后把它初始化为已经生成的坐标。

5.蛇的移动

蛇是走一步移动一步,它要监测是否有按上下左右,如果按了,蛇头就要转变方向。蛇的移动本质上是下一个结点的位置在哪。所以要安排下一个结点的位置。下一个结点的位置有多种可能,撞墙,撞自己,吃食物和正常进行。

如果是吃食物,就是头插。

如果是正常走,那么就是头插以后再删尾,这个过程还要打印一遍蛇身。那么尾巴部分的结点被释放以后,在尾结点的原坐标上打印两个空格来代替。这个过程在地图上显示就是蛇走了一格。

蛇移动的速度越快,休眠的时间越短。所以速度方面设置休眠时间就可以了。这个休眠时间Sleep的函数也是包含windows的库函数来实现的。

撞墙和撞自己都需要修改游戏状态。修改的游戏状态就在于停止游戏。所以外面要套个循环。游戏只在状态是OK的时候进行,其他情况都分别打印出对应的信息。

6.暂停功能如何实现?

暂停的功能可以通过死循环,一直在睡眠。只有重新按了空格键,再跳出循环继续运行。

7.最后的收尾

收尾部分主要是游戏玩了一把game over以后,因为各种原因结束而打印不同信息。打印完了要把蛇的结点依次释放。然后再把食物释放,把传来的维护蛇的指针置为空。

如果想设置再来一把的消息,可以在test.c文件里进行。

要注意两个getchar。第一个getchar用来读取信息,通过一个变量来接收,用于判断玩家到底要不要开下一把。第二个getchar用来接收读取回车字符,但是没有变量接收它,也就是它不产生实际作用。因为它的目的只是用来吸收回车,使这个回车键不至于影响到下一次的输入判定。

8.贪吃蛇的结构体维护

贪吃蛇有两个结构体,第一个结构体是蛇身,第二个是蛇的各种信息,里面也包含了蛇身。

那么就是创建一条贪吃蛇的结构体,来玩这个贪吃蛇游戏。传的参数就是这个贪吃蛇的结构体的地址,因为传地址,才能改变贪吃蛇的值。这里的值包括状态,蛇的结点的指针,蛇的方向,当前分数等等。蛇的结点的头指针是常常需要改变的,因为它要不断移动。如果传的是结点指针,那么要传二级指针。但是本次情况中蛇的结点的头指针在贪吃蛇结构体里只是一个值,这里既然传了贪吃蛇的结构体地址,那么就能随便改变蛇的结点的头指针了。这个是要注意的关于指针的细节。以便在进行与贪吃蛇相似的项目中能够复用。

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

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

相关文章

XEX数字货币交易平台:量化交易策略与市场趋势解析

量化交易&#xff0c;一个结合金融市场知识与计算机科学的领域&#xff0c;通过执行一系列复杂的算法策略&#xff0c;自动化地进行交易决策。它的常见策略包括动量交易、对冲策略、算法套利等&#xff0c;旨在通过分析历史数据和市场模式来预测未来趋势&#xff0c;从而实现盈…

1.CVAT建项目步骤

文章目录 1. 创建project2. 创建task2.1. label 标签详解2.2.高级配置 Advanced configuration 3. 分配任务4. 注释者规范 CVAT的标注最小单位是Task&#xff0c;每个Task为一个标注任务。 1. 创建project 假设你并不熟悉cvat的标注流程&#xff0c;这里以图像2D目标检测为例进…

新版UI界面影视小程序亲测无问题带详细搭建教程

新版UI界面影视小程序亲测无问题带详细搭建教程 环境php7.0 — fileinfo–redis–sg11 mysql5.5 apache2.4 添加站点php7.0—-创建ftp—-上传后端文件《后端文件修改&#xff0c;/maccms/wxapi/config/dbs.php–修改当前数据库》—-设置ssl—-打开数据库安装cms 安装好后管…

LeetCode--代码详解 21.合并两个有序链表

21.合并两个有序链表 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], …

电商小程序02数据源设计

上一篇我们讲解了电商小程序的需求分析&#xff0c;分析了需要具备的功能并且绘制了系统原型。有了原型之后下一步的事情就是根据原型来设计数据源。 数据源就像盖房子打地基一样&#xff0c;地基打不好&#xff0c;楼可能就盖不高&#xff0c;盖起来要再想调整就比较困难。 …

EasyExcel动态列导出

测试代码地址&#xff1a;https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn 官方文档&#xff1a;https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write 一、实现方式 1、根据需要导出的列…

获IROS最佳移动操作论文提名|通研院提出首个实现连续操作任务的空中具身智能机器人CORVUS(渡鸦)

论文导读 本文介绍了通研院机器人实验室发表于2023年国际机器人顶级会议IROS上的论文&#xff0c;题为《Sequential Manipulation Planning for Over-actuated Unmanned Aerial Manipulators》[1]。文章介绍了一种可以实现空中全向平稳飞行的过驱动空间机械臂平台Coordinated …

在计算机/移动设备上恢复已删除视频的 10 个数据恢复工具

视频在网上疯传&#xff0c;我们都观看或创建视频&#xff0c;并将我们最喜欢的视频保存在硬盘上。如果我们丢失了一些重要的视频&#xff0c;那将是非常令人心碎的。但是今天&#xff0c;恢复已删除的视频变得更加容易。删除的视频在被新数据覆盖之前并没有真正从您的存储驱动…

创建你的第一个Vue项目(小白专享版本)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

算法竞赛进阶指南——基本算法(倍增)

ST表 可以求区间最大、最小、gcd、lcm&#xff0c;符合 f(a, a) a都可以 求区间最值&#xff0c;一个区间划分成两段 f[i][j]: 从i开始&#xff0c;长度为2^j的区间最值 #include<iostream> #include<cmath> using namespace std; const int N 1e6 10; int n,…

【书生·浦语实战营】体系介绍

文章目录 从大模型到应用的路径开放体系内容万卷预训练微调部署智能体 从大模型到应用的路径 开放体系 内容 数据&#xff1a;书生万卷预训练&#xff1a;interLM-Train微调&#xff1a;XTuner&#xff0c;全参数、低成本微调部署&#xff1a;LMDeploy评测&#xff1a;OpenCo…

Windows快捷键大全(包含语音输入、剪切板历史快捷键)

最近发现了微软官网上给出的快捷键大全&#xff0c;并且使用了其中几个新的键盘快捷键&#xff08;语音输入、剪切板历史&#xff09;&#xff0c;确实方便快捷&#xff0c;所以写个博客记录分享一下。 注&#xff1a;windows快捷键大全微软官方已经给出&#xff0c;此处不再赘…

让Python遇上Office--从编程入门到自动化办公实践

最近仔细的学习了这本《让Python遇上Office》的书&#xff0c;同时把我的学习进程与心得录制了同步视频。 到今天终于把全部90集完成&#xff0c;并且上传到下面的视频平台了&#xff0c;欢迎大家观看并指正&#xff01; 西瓜视频&#xff1a;https://www.ixigua.com/7300628…

服务异步通信

服务异步通信 消息队列在使用过程中&#xff0c;面临着很多实际问题需要思考&#xff1a; 1.消息可靠性 消息从发送&#xff0c;到消费者接收&#xff0c;会经理多个过程&#xff1a; 其中的每一步都可能导致消息丢失&#xff0c;常见的丢失原因包括&#xff1a; 发送时丢失…

ClickHouse时区

clickhouse数据库的时间是UTC时间。服务器默认的是上海时间。 sudo vim /etc/clickhouse-server/config.xml clickhouse默认的时区是注释的就是UTC时间 %F 表示日期&#xff0c;格式为 YYYY-MM-DD。%T 表示时间&#xff0c;格式为 HH:MM:SS。 因此&#xff0c;formatDateT…

three.js 细一万倍教程 从入门到精通(一)

目录 一、three.js开发环境搭建 1.1、使用parcel搭建开发环境 1.2、使用three.js渲染第一个场景和物体 1.3、轨道控制器查看物体 二、three.js辅助设置 2.1、添加坐标轴辅助器 2.2、设置物体移动 2.3、物体的缩放与旋转 缩放 旋转 2.4、应用requestAnimationFrame …

windows11 MSYS2下载安装教程

MSYS2 可以理解为在windows平台上模拟linux编程环境的开源工具集 当前环境&#xff1a;windows11 1. 下载 官网地址可下载最新版本&#xff0c;需要科学上网 https://www.msys2.org/ 2. 安装 按照正常安装软件流程一路next就可以 打开 3. 配置环境 网上很多教程提到需…

回归预测模型:MATLAB岭回归和Lasso回归

1. 岭回归和Lasso回归的基本原理 1.1 岭回归&#xff1a; 岭回归&#xff08;Ridge Regression&#xff09; 是一种用于共线性数据分析的技术。共线性指的是自变量之间存在高度相关关系。岭回归通过在损失函数中添加一个L2正则项&#xff08; λ ∑ j 1 n β j 2 \lambda \s…

【维生素C语言】附录:strlen 函数详解

写在前面&#xff1a;本篇将专门为 strlen 函数进行讲解&#xff0c;总结了模拟实现 strlen 函数的三种方法&#xff0c;并对其进行详细的解析。手写库函数是较为常见的面试题&#xff0c;希望通过本篇博客能够加深大家对 strlen 的理解。 0x00 strlen函数介绍 【百度百科】str…

【原创 附源码】Flutter安卓及iOS海外登录--Facebook登录最详细流程

最近接触了几个海外登录的平台&#xff0c;踩了很多坑&#xff0c;也总结了很多东西&#xff0c;决定记录下来给路过的兄弟坐个参考&#xff0c;也留着以后留着回顾。更新时间为2024年2月12日&#xff0c;后续集成方式可能会有变动&#xff0c;所以目前的集成流程仅供参考&…