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

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

                                                                                文章专栏: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、 完结散花

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

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

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

我们下期不见不散~~

​​​​

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

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

相关文章

【深度学习实战(20)】使用torchsummary打印模型结构

一、安装torchsummary库 pip install torchsummary 二、代码 import torchvision.models as models from torchsummary import summarymodel models.AlexNet() model.to(cuda) summary(model,(3,224, 224))

AI智能边缘分析一体机,32T算力,可同时处理32路1080p高清视频

产品概述 XM-AIBOX-32智能边缘分析一体机是一款高性能、低功耗边缘计算产品。搭载BM1684X主芯片&#xff0c;INT8算力高达32TOPS&#xff0c;FP16/BF16算力高达16TFLOPS&#xff0c;FP32算力高达2TFLOPS&#xff0c;可同时处理32路高清视频&#xff0c;支持32路1080P高清视频硬…

【NOI】C++算法设计入门之深度优先搜索

文章目录 前言一、深度优先搜索1.引入2.概念3.迷宫问题中的DFS算法步骤4.特点5.时间、空间复杂度5.1 时间复杂度 (Time Complexity)5.2 空间复杂度 (Space Complexity)5.3 小结 二、例题讲解1.问题&#xff1a;1586 - 扫地机器人问题&#xff1a;1430 - 迷宫出口 三、总结四、感…

docker安装并跑通QQ机器人实践(3)-bs-nonebot搭建

NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架&#xff08;下称 NoneBot&#xff09;&#xff0c;它基于 Python 的类型注解和异步优先特性&#xff08;兼容同步&#xff09;&#xff0c;能够为你的需求实现提供便捷灵活的支持。同时&#xff0c;NoneBot 拥有大…

Rust实战 | 用 RustRover 开发猜数字游戏

#1、概述 从这期开始&#xff0c;我会以实际项目的形式&#xff0c;分享个人学习 Rust 语言的过程。期间&#xff0c;我不会讲太多语法知识&#xff0c;需要各位看官自行查阅文档&#x1f604;。 开始前&#xff0c;需具备 Rust 环境&#xff08;我用的是当前最新版本1.77.2&…

【Transformer】detr之encoder逐行梳理(二)

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 detr之encoder逐行梳理 1. 整体 encoder由encoder layer构成 输入进encoder的特征shape:(hw,b,c)&#xff0c;后文将给出说明 class Transformer(nn.…

Nacos分布式配置中心

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://…

常见的数据抽取工具对比

1.什么是ETL? ETL&#xff0c;是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform&#xff09;、加载&#xff08;load&#xff09;至目的端的过程&#xff0c;是数据仓库的生命线。 …

C#仿QQ抽屉式窗体的设计方法:创建特殊窗体

目录 1.WindowFromPoint函数 2.GetParent函数 3.实例 &#xff08;1&#xff09; 图片集合编辑器 &#xff08;2&#xff09;Form1.Designer.cs &#xff08;3&#xff09;Form1.cs 4.生成效果 QQ软件对于绝大多数的人来说再熟悉不过了&#xff0c;它以使用方便、界面美…

Scala 05 —— 函数式编程底层逻辑

Scala 05 —— 函数式编程底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 Scala 05 —— 函数式编程底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函…

多因素不同水平的正交表设计(并列法)

文章目录 一、问题提出二、举例说明 一、问题提出 参考高等教育课本《实验设计与数据处理》 很多时候&#xff0c;我们要考察的因素水平数不尽相同&#xff0c;这时候一般采用混合水平正交表或者对普通的正交表作修改&#xff0c;其中&#xff0c;混合水平正交表由于水平数不规…

JAVA程序设计-对象设计

无论是根据某马还是某谷的适配教程做项目时候,发现了大部分都是重复的crud,大部分只要做好笔记复习即可,但是却往往忘记了编码设计,所以这里开始复习编码设计,对象设计中,长期使用Mp的那一套导致就是Service Mapper,一套梭哈完了,这样很容易忘记基本功夫 POJO&#xff1a; 简单…

Java、Spring、Dubbo三者SPI机制原理与区别

Java、Spring、Dubbo三者SPI机制原理与区别 什么是SPI SPI全称为Service Provider Interface&#xff0c;是一种动态替换发现的机制&#xff0c;一种解耦非常优秀的思想&#xff0c;SPI可以很灵活的让接口和实现分离&#xff0c;让api提供者只提供接口&#xff0c;第三方来实…

刷题训练之二分查找

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握二分查找算法 > 毒鸡汤&#xff1a;学习&#xff0c;学习&#xff0c;再学习 ! 学&#xff0c;然后知不足。 > 专栏选自&#xff1a;刷题…

网卡技术解密:理解网卡背后的原理

✍✍在这个信息爆炸的时代&#xff0c;网卡承载着无数数据的流动&#xff0c;是我们日常生活和工作不可或缺的一部分。但是&#xff0c;您是否曾经好奇过&#xff0c;这些小小的硬件是如何在瞬息万变的网络世界中稳定地发挥作用的呢&#xff1f; 想象一下&#xff0c;每当我们…

2024中国内燃机展-北京汽车发动机零部件展

2024第二十三届中国国际内燃机与零部件展览会 由中国内燃机工业协会主办、中国机床专用技术设备有限公司、汽车工艺装备成套开发集团协办的2024中国国际内燃机及动力装备博览会&#xff08;简称“动博会”&#xff09;将于2024年10月11日-13日在亦创国际会展中心隆重举办。本届…

智能时代 | 合合信息Embedding模型荣获C-MTEB榜单第一

目录 前言 1. MTEB与C-MTEB 2. acge模型的优势 3. Embedding模型应用 4. 大模型发展的关键技术 结语 前言 随着人工智能的不断发展&#xff0c;大语言模型吸引着社会各界的广泛关注&#xff0c;支撑模型应用落地的Embedding模型成为业内的焦点&#xff0c;大模型的发展给…

Electron 30.0.0 发布,升级 Node 和 V8 引擎

近日&#xff0c;Electron 30.0.0 正式发布&#xff01;你可以通过 npm install electronlatest 进行安装&#xff0c;或者从 Electron 的发布网站下载&#xff0c;继续阅读了解此版本的详细信息。 &#x1f525; 主要更新 Windows 上支持 ASAR 完整性融合。如果未正确配置&am…

【后端】python与django的开发环境搭建指南

安装Git 双击Git 客户端安装文件&#xff0c;在安装页面&#xff0c;单击“Next” 在安装路径选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在功能组件选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在开始菜单文件夹设置页面&#xff0c;保持默认&am…

AI交互数字人对教育领域有何优势?

AI交互数字人不仅能够跨越物理距离的限制&#xff0c;以数字人形象为学生提供“面对面”教学互动体验&#xff0c;还能根据学生的具体需求提供个性化的知识解答。如天津大学推出了数字人老师&#xff0c;以刘艳丽教授形象1&#xff1a;1仿真打造的2.5D数字人&#xff0c;能够应…