当前位置: 首页 > news >正文

【项目实践】贪吃蛇

  • 一、游戏效果展示
  • 二、博客目标
  • 三、使用到的知识
  • 四、Win32 API 介绍
    • 4.1 WIn32 API
    • 4.2 控制台程序
    • 4.3 控制屏幕上的坐标COORD
    • 4.4 GetStdHandle
    • 4.5 GetConsoleCursorInfo
      • 4.5.1 CONSOLE_CURSOR_INFO
    • 4.6 SetConsoleCursorInfo
    • 4.7 SetConsoleCursorPosition
    • 4.8 GetAsyncKeyState
  • 五、贪吃蛇游戏设计与分析
    • 5.1 地图
      • 5.1.1 <locale.h>本地化
      • 5.1.2 类项
      • 5.1.3 setlocale函数
      • 5.1.4 宽字符打印
      • 5.1.5 地图坐标
    • 5.2 蛇身与食物
    • 5.3 数据结构设计
    • 5.4 游戏流程设计
  • 六、核心逻辑实现分析
    • 6.1 游戏主逻辑
    • 6.2 游戏开始
      • 6.2.1 打印欢迎界面
      • 6.2.2 创建地图
      • 6.2.3 创建蛇身
      • 6.2.4 创建第一个食物
    • 6.3 游戏运行
      • 6.3.1 KEY_PRESS
      • 6.3.2 打印右侧的帮助信息
      • 6.3.3 蛇身的移动
    • 6.4游戏结束
  • 七、参考代码
  • 八、控制台设置

一、游戏效果展示

贪吃蛇效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、博客目标

使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇。

实现基本的功能:

  • 贪吃蛇地图绘制
  • 蛇吃食物的功能(上、下、左、右方向键控制蛇的动作)
  • 蛇撞墙死亡
  • 蛇撞自身死亡
  • 计算得分
  • 蛇身加速、减速
  • 暂停游戏

三、使用到的知识

C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。

四、Win32 API介绍

本次实现贪吃蛇会使用到的⼀些Win32 API知识,接下来我们就学习⼀下。

4.1Win32 API

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

4.2 控制台程序

平常我们运行起来的黑框程序其实就是控制台程序。能在控制台窗口执行的命令,也可以调用C语言函数system来执行

4.3 控制台屏幕上的坐标COORD

COORD 是Windows API中定义的⼀个结构体,表示⼀个字符在控制台屏幕幕缓冲区上的坐标,坐标系(0,0)的原点位于缓冲区的顶部左侧单元格。
在这里插入图片描述
COORD类型的声明:

typedef struct _COORD 
{SHORT X;SHORT Y;
} COORD, *PCOORD;

4.4 GetStdHandle

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

HANDLE GetStdHandle(DWORD nStdHandle);

4.5 GetConsoleCursorInfo

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

BOOL WINAPI GetConsoleCursorInfo( HANDLE hConsoleOutput,PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);
//PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标.

4.5.1 CONSOLE_CURSOR_INFO

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

typedef struct _CONSOLE_CURSOR_INFO 
{DWORD dwSize;BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
  • dwSize,由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完全填充单元格到单元底部的水平线条。
  • bVisible,游标的可见性。如果光标可见,则此成员为 TRUE。
CursorInfo.bVisible = false; //隐藏控制台光标 

4.6SetConsoleCursorInfo

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

BOOL WINAPI SetConsoleCursorInfo(HANDLE hConsoleOutput,const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);

4.7 SetConsoleCursorPosition

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

BOOL WINAPI SetConsoleCursorPosition(HANDLE hConsoleOutput,COORD pos);

4.8 GetAsyncKeyState

获取按键情况,GetAsyncKeyState的函数原型如下:

SHORT GetAsyncKeyState(int vKey);

将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。
GetAsyncKeyState 的返回值是short类型,在上⼀次调用 GetAsyncKeyState 函数后,如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最高是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1。
虚拟键码

五、贪吃蛇游戏设计与分析

5.1 地图

这里不得不讲⼀下控制台窗口的⼀些知识,如果想在控制台的窗口中指定位置输出信息,我们得知道该位置的坐标,所以首先介绍⼀下控制台窗口的坐标知识。
控制台窗口的坐标,横向的是X轴,从左向右依次增长,纵向是Y轴,从上到下依次增长。
在游戏地图上,我们打印墙体使用宽字符 □ ,打印蛇使用宽字符 ● ,打印食物使用宽字符 ★

普通的字符是占⼀个字节的,这类宽字符是占用2个字节。

这里再简单的讲⼀下C语言的国际化特性相关的知识,过去C语言并不适合非英语国家(地区)使用。
C语言最初假定字符都是单字节的。但是这些假定并不是在世界的任何地方都适用。
C语言字符默认是采用ASCII编码的,ASCII字符集采用的是单字节编码,且只使用了单字节中的低7位,最高位是没有使用的,可表示为0xxxxxxxx;可以看到,ASCII字符集共包含128个字符,在英语国家中,128个字符是基本够用的,但是,在其他国家语言中,比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,⼀些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的 é 的编码为130(⼆进制10000010)。这样⼀来,这些欧洲国家使用的编码体系,可以表示最多256个符号。但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不⼀样。比如,130在法语编码中代表了 é ,在俄语编码中又会代表另⼀个符号。但是不管怎样,所有这些编码方式中,0–127表示的符号是⼀样的,不⼀样的只是128–255的这⼀段。
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。⼀个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达⼀个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示⼀个汉字,所以理论上最多可以表示256 x 256 = 65536 个符号。
后来为了使C语言适应国际化,C语言的标准中不断加入了国际化的至支持。比如:加入了宽字符的类型wchar_t 和宽字符的输⼊和输出函数,加⼊了<locale.h>头文件,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语言的地理区域)调整程序行为的函数。

5.1.1 <locale.h>本地化

<locale.h>提供的函数用于控制C标准库中对于不同的地区会产生不⼀样行为的部分。
在标准中,依赖地区的部分有以下几项:

  • 数字量的格式
  • 货币量的格式
  • 字符集
  • 日期和时间的表示形式

5.1.2 类项

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

  • LC_COLLATE:影响字符串比较函数 strcoll() 和 strxfrm() 。
  • LC_CTYPE:影响字符处理函数的行为。
  • LC_MONETARY:影响货币格式。
  • LC_NUMERIC:影响 printf() 的数字格式。
  • LC_TIME:影响时间格式 strftime() 和 wcsftime() 。
  • LC_ALL:针对所有类项修改,将以上所有类别设置为给定的语言环境。

5.1.3 setlocale函数

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

setlocale 函数用于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项。
setlocale 的第⼀个参数可以是前面说明的类项中的⼀个,那么每次只会影响⼀个类项,如果第⼀个参数是LC_ALL,就会影响所有的类项。
C标准给第二个参数仅定义了2种可能取值:“C”(正常模式)和" "(本地模式)
当程序运行起来后想改变地区,就只能显示调用 setlocale 函数。用" "作为第2个参数,调用 setlocale 函数就可以切换到本地模式,这种模式下程序会适应本地环境。比如:切换到我们的本地模式后就支持宽字符(汉字)的输出等。

setlocale(LC_ALL, " ");//切换到本地环境 

5.1.4 宽字符的打印

那如果想在屏幕上打印宽字符,怎么打印呢?
宽字符的字面量必须加上前缀“L”,否则 C 语言会把字面量当作窄字符类型处理。前缀“L”在单引号前面,表示宽字符,对应 wprintf() 的占位符为 %lc ;在双引号前面,表示宽字符串,对应wprintf() 的占位符为 %ls 。
在这里插入图片描述

5.1.5 地图坐标

我们假设实现⼀个棋盘27行,58列的棋盘(行和列可以根据自己的情况修改),再围绕地图画出墙,如下:
在这里插入图片描述

5.2 蛇身与食物

初始化状态,假设蛇的蛇度是5,蛇身的每个节点是 ● ,在固定的⼀个坐标处,比如(24,5)处开始出现蛇,连续5个节点。
注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有⼀半儿出现在墙体中,另外⼀般在墙外的现象,坐标不好对齐。
关于食物,就是在墙体内随机生成⼀个坐标(x坐标必须是2的倍数),坐标不能和蛇的身体重合,然后打印★。
在这里插入图片描述

5.3 数据结构设计

在游戏运行的过程中,蛇每次吃⼀个食物物,蛇的身体就会变长⼀节,如果我们使用链表存储蛇的信息,那么蛇的每⼀节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行,所以蛇节点结构如下:

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

要管理整条贪吃蛇,我们再封装⼀个Snake的结构来维护整条贪吃蛇:

typedef struct Snake
{pSnakeNode _pSnake;//维护整条蛇的指针 pSnakeNode _pFood;//维护⻝物的指针 enum DIRECTION _Dir;//蛇头的⽅向,默认是向右 enum GAME_STATUS _Status;//游戏状态 int _Socre;//游戏当前获得分数 int _foodWeight;//默认每个⻝物分数 int _SleepTime;//每⾛⼀步休眠时间 
}Snake, * pSnake;

蛇的方向,可以⼀⼀列举,使用枚举

//⽅向 
enum DIRECTION
{ UP = 1,	//向上DOWN,	//向下LEFT,	//向左RIGHT	//向右
};

游戏状态,可以⼀⼀列举,使用枚举

//游戏状态 
enum GAME_STATUS
{OK,//正常运⾏ KILL_BY_WALL,//撞墙 KILL_BY_SELF,//咬到⾃⼰ END_NOMAL//正常结束 
};

5.4 游戏流程设计

在这里插入图片描述

六、核心逻辑实现分析

6.1 游戏主逻辑

程序开始就设置程序支持本地模式,然后进入游戏的主逻辑。
主逻辑分为3个过程:

  • 游戏开始(GameStart)完成游戏的初始化
  • 游戏运行(GameRun)完成游戏运行逻辑的实现
  • 游戏结束(GameEnd)完成游戏结束的说明,实现资源释放
void game()
{int ch = 0;srand((unsigned int)time(NULL));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');SetPos(0, 27);
}int main()
{//修改当前地区为本地模式,为了支持中文宽字符的打印 setlocale(LC_ALL, "");//测试逻辑 game();return 0;
}

6.2游戏开始(GameStart)

这个模块完成游戏的初始化任务:

  • 控制台窗口大小的设置
  • 控制台窗口名字的设置
  • 光标的隐藏
  • 打印欢迎界面
  • 创建地图
  • 初始化蛇
  • 创建第一个食物
void GameStart(pSnake ps)
{//设置控制台窗口的大小,30行,100列 //mode 为DOS命令 system("mode con cols=100 lines=30");//设置cmd窗口名称 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);
}

6.2.1 打印欢迎界面

在游戏正式开始之前,做⼀些功能提醒.

//设置光标的坐标 
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//获取标准输出的句柄(用来标识不同设备的数值) hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为pos SetConsoleCursorPosition(hOutput, pos);
}
//打印欢迎进入游戏
void WelcomeToGame()
{SetPos(40, 15);printf("欢迎进入贪吃蛇游戏");SetPos(42, 25);//让按任意键继续的出现的位置好看点 system("pause");system("cls");SetPos(20, 12);printf("用 ↑  ↓  ←   → 分别控制蛇的移动, K为加速,L为减速");SetPos(37, 14);printf("加速将能得到更高的分数");SetPos(40, 25);//让按任意键继续的出现的位置好看点 system("pause");system("cls");
}

6.2.2 创建地图

创建地图就是将墙打印出来,因为是宽字符打印,所有使用wprintf函数,打印格式串前使用L。
打印地图的关键是要算好坐标,才能在想要的位置打印墙体。
墙体打印的宽字符:

#define WALL L'□' 
//创建地图
void CreateMap()
{int i = 0;//上(0,0)-(56, 0) SetPos(0, 0);for (i = 0; i < 58; i += 2){wprintf(L"%c", WALL);}//下(0,26)-(56, 26) SetPos(0, 26);for (i = 0; i < 58; i += 2){wprintf(L"%c", WALL);}//左 //x是0,y从1开始增长for (i = 1; i < 26; i++){SetPos(0, i);wprintf(L"%c", WALL);}//x是56,y从1开始增长for (i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);}
}

6.2.3 初始化蛇身

蛇最开始长度为5节,每节对应链表的⼀个节点,蛇身的每⼀个节点都有自己的坐标。
创建5个节点,然后将每个节点存放在链表中进行管理。创建完蛇身后,将蛇的每⼀节打印在屏幕上。

  • 蛇的初始位置从(24,5)开始。再设置当前游戏的状态,蛇移动的速度,默认的方向,初始成绩,每个食物的分数。
  • 游戏状态是:OK
  • 蛇的移动速度:200毫秒
  • 蛇的默认方向:RIGHT
  • 初始成绩:0
  • 每个食物的分数:10
    蛇身打印的宽字符:
#define BODY L'●' 
void InitSnake(pSnake ps)
{pSnakeNode cur = NULL;int i = 0;//创建蛇身节点,并初始化坐标 //头插法 for (i = 0; i < 5; i++){//创建蛇身的节点 cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc()");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;}}//打印蛇的身体 cur = ps->_pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}//初始化贪吃蛇数据 ps->_SleepTime = 200;ps->_Socre = 0;ps->_Status = OK;ps->_Dir = RIGHT;ps->_foodWeight = 10;
}

6.2.4 创建第一个食物

  • 先随机生成食物的坐标
    • x坐标必须是2的倍数
    • 食物的坐标不能和蛇身每个节点的坐标重复
  • 创建食物节点,打印食物
    食物打印的宽字符:
#define FOOD L'★'
//创建食物
void CreateFood(pSnake ps)
{int x = 0;int y = 0;
again://产生的x坐标应该是2的倍数,这样才可能和蛇头坐标对齐。 do{x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2 != 0);//获取指向蛇头的指针 pSnakeNode cur = ps->_pSnake;//食物不能和蛇身冲突 while (cur){if (cur->x == x && cur->y == y){goto again;}cur = cur->next;}//创建食物pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); if (pFood == NULL){perror("CreateFood::malloc()");return;}else{pFood->x = x;pFood->y = y;SetPos(pFood->x, pFood->y);wprintf(L"%c", FOOD);ps->_pFood = pFood;}
}

6.3 游戏运行(GameRun)

游戏运行期间,右侧打印帮助信息,提示玩家。
根据游戏状态检查游戏是否继续,如果是状态是OK,游戏继续,否则游戏结束。
如果游戏继续,就是检测按键情况,确定蛇下一步的方向,或者是否加速减速,是否暂停或者退出游戏。
虚拟按键上面给过。

void GameRun(pSnake ps)
{//打印右侧帮助信息 PrintHelpInfo();do{SetPos(62, 5);printf("得分:%d  ", ps->_Socre);printf("每个食物得分:%d分", ps->_foodWeight);//按键判断,上下左右WSAD或者数字8246,虚拟键if ((KEY_PRESS(0x57) || KEY_PRESS(VK_NUMPAD8)) && ps->_Dir != DOWN){ps->_Dir = UP;}else if ((KEY_PRESS(0x53) || KEY_PRESS(VK_NUMPAD2)) && ps->_Dir != UP){ps->_Dir = DOWN;}else if ((KEY_PRESS(0x41) || KEY_PRESS(VK_NUMPAD4)) && ps->_Dir != RIGHT){ps->_Dir = LEFT;}else if ((KEY_PRESS(0x44) || KEY_PRESS(VK_NUMPAD6)) && ps->_Dir != LEFT){ps->_Dir = RIGHT;}else if (KEY_PRESS(VK_SPACE)){pause();}else if (KEY_PRESS(VK_ESCAPE)){ps->_Status = END_NOMAL;break;}else if (KEY_PRESS(0x4B)){if (ps->_SleepTime >= 50){ps->_SleepTime -= 30;ps->_foodWeight += 2;}}else if (KEY_PRESS(0x4C)){if (ps->_SleepTime < 350){ps->_SleepTime += 30;ps->_foodWeight -= 2;if (ps->_SleepTime == 350){ps->_foodWeight = 1;}}}//蛇每次⼀定之间要休眠的时间,时间短,蛇移动速度就快 Sleep(ps->_SleepTime);SnakeMove(ps);} while (ps->_Status == OK);
}

6.3.1 KEY_PRESS

检测按键状态,我们封装了⼀个宏

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

6.3.2打印右侧的帮助信息

//打印提示信息
void PrintHelpInfo()
{	 SetPos(62, 7);printf("不能穿墙,不能咬到自己");SetPos(62, 9);printf("用 ↑ . ↓ . ← . → 分别控制蛇的移动.");SetPos(62, 11);printf("K 为加速,L 为减速");SetPos(62, 13);printf("ESC:退出游戏.");SetPos(62, 15);printf("space:暂停游戏.");SetPos(84, 20);printf("卡戎-caryon@版权");
}

6.3.3蛇身的移动

先创建下⼀个节点,根据移动方向和蛇头的坐标,蛇移动到下⼀个位置的坐标。
确定了下⼀个位置后,看下⼀个位置是否是食物(NextIsFood),是食物就做吃食物处理(EatFood),如果不是食物则做前进一步的处理(NoFood)。
蛇身移动后,判断此次移动是否会造成撞墙(KillByWall)或者撞上自己蛇身(KillBySelf),从而影响游戏的状态。

//暂停 
void pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
int NextIsFood(pSnakeNode psn, pSnake ps)
{return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
}//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
void EatFood(pSnakeNode psn, pSnake ps)
{//头插法 psn->next = ps->_pSnake;ps->_pSnake = psn;pSnakeNode cur = ps->_pSnake;//打印蛇while (cur){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}ps->_Socre += ps->_foodWeight;free(ps->_pFood);CreateFood(ps);
}
//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
void NoFood(pSnakeNode psn, pSnake ps)
{//头插法 psn->next = ps->_pSnake;ps->_pSnake = psn;pSnakeNode cur = ps->_pSnake;//打印蛇 while (cur->next->next){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}//最后一个位置打印空格,然后释放节点 SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;
}
//pSnake ps 维护蛇的指针 
int 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;return 1;}return 0;
}
//pSnake ps 维护蛇的指针 
int KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;while (cur){if ((ps->_pSnake->x == cur->x)&& (ps->_pSnake->y == cur->y)){ps->_Status = KILL_BY_SELF;return 1;}cur = cur->next;}return 0;
}
//蛇的移动
void SnakeMove(pSnake ps)
{//创建下一个节点 pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("SnakeMove()::malloc()");return;}//确定下一个节点的坐标,下一个节点的坐标根据,蛇头的坐标和方向确定 switch (ps->_Dir){case UP:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y - 1;break;case DOWN:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y + 1;break;case LEFT:pNextNode->x = ps->_pSnake->x - 2;pNextNode->y = ps->_pSnake->y;break;case RIGHT:pNextNode->x = ps->_pSnake->x + 2;pNextNode->y = ps->_pSnake->y;break;}//如果下一个位置就是食物if (NextIsFood(pNextNode, ps)){EatFood(pNextNode, ps);}else//如果没有食物 {NoFood(pNextNode, ps);}KillByWall(ps);KillBySelf(ps);
}

6.4 游戏结束

游戏状态不再是OK(游戏继续)的时候,要告知游戏结束的原因,并且释放蛇身节点。

//游戏结束
void GameEnd(pSnake ps)
{pSnakeNode cur = ps->_pSnake;SetPos(24, 12);switch (ps->_Status){case END_NOMAL:printf("您主动退出游戏\n");break;case KILL_BY_SELF:printf("您撞上自己了 ,游戏结束!\n");break;case KILL_BY_WALL:printf("您撞墙了,游戏结束!\n");break;}//释放蛇身的节点 while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

七、参考代码

//test.c
#include"snake.h"
#include"Game_Start.h"#include <locale.h>
void game()
{int ch = 0;srand((unsigned int)time(NULL));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');SetPos(0, 27);
}int main()
{//修改当前地区为本地模式,为了支持中文宽字符的打印 setlocale(LC_ALL, "");//测试逻辑 game();return 0;
}
//snake.h
#pragma once
#include <windows.h>
#include <time.h>
#include <stdio.h>
#include<stdbool.h>
#include<stdlib.h>//判断按键是否被按下过
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)//方向
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};
//游戏状态 
enum GAME_STATUS
{OK,//正常运⾏ KILL_BY_WALL,//撞墙 KILL_BY_SELF,//咬到⾃⼰ END_NOMAL//正常结束 
};#define WALL  L'□' 
#define BODY L'●' 
#define FOOD L'★' 
//蛇的初始位置 
#define POS_X 24
#define POS_Y 5
//蛇身节点 
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;
}SnakeNode, * pSnakeNode;
//蛇
typedef struct Snake
{pSnakeNode _pSnake;//维护整条蛇的指针 pSnakeNode _pFood;//维护食物的指针 enum DIRECTION _Dir;//蛇头的方向默认是向右 enum GAME_STATUS _Status;//游戏状态 int _Socre;//当前获得分数 int _foodWeight;//默认每个食物10分 int _SleepTime;//每走一步休眠时间 
}Snake, * pSnake;//游戏开始前的初始化 
void GameStart(pSnake ps);
//游戏运行过程 
void GameRun(pSnake ps);
//游戏结束 
void GameEnd(pSnake ps);
//snack.c
#include"snake.h"
#include"Game_Start.h"
#include"Game_Run.h"void GameStart(pSnake ps)
{//设置控制台窗口的大小,30行,100列 //mode 为DOS命令 system("mode con cols=100 lines=30");//设置cmd窗口名称 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 GameRun(pSnake ps)
{//打印右侧帮助信息 PrintHelpInfo();do{SetPos(62, 5);printf("得分:%d  ", ps->_Socre);printf("每个食物得分:%d分", ps->_foodWeight);//按键判断,上下左右WSAD或者数字8246,虚拟键if ((KEY_PRESS(0x57) || KEY_PRESS(VK_NUMPAD8)) && ps->_Dir != DOWN){ps->_Dir = UP;}else if ((KEY_PRESS(0x53) || KEY_PRESS(VK_NUMPAD2)) && ps->_Dir != UP){ps->_Dir = DOWN;}else if ((KEY_PRESS(0x41) || KEY_PRESS(VK_NUMPAD4)) && ps->_Dir != RIGHT){ps->_Dir = LEFT;}else if ((KEY_PRESS(0x44) || KEY_PRESS(VK_NUMPAD6)) && ps->_Dir != LEFT){ps->_Dir = RIGHT;}else if (KEY_PRESS(VK_SPACE)){pause();}else if (KEY_PRESS(VK_ESCAPE)){ps->_Status = END_NOMAL;break;}else if (KEY_PRESS(0x4B)){if (ps->_SleepTime >= 50){ps->_SleepTime -= 30;ps->_foodWeight += 2;}}else if (KEY_PRESS(0x4C)){if (ps->_SleepTime < 350){ps->_SleepTime += 30;ps->_foodWeight -= 2;if (ps->_SleepTime == 350){ps->_foodWeight = 1;}}}//蛇每次⼀定之间要休眠的时间,时间短,蛇移动速度就快 Sleep(ps->_SleepTime);SnakeMove(ps);} while (ps->_Status == OK);
}
//游戏结束
void GameEnd(pSnake ps)
{pSnakeNode cur = ps->_pSnake;SetPos(24, 12);switch (ps->_Status){case END_NOMAL:printf("您主动退出游戏\n");break;case KILL_BY_SELF:printf("您撞上自己了 ,游戏结束!\n");break;case KILL_BY_WALL:printf("您撞墙了,游戏结束!\n");break;}//释放蛇身的节点 while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}
//Game_Start.h
#pragma once
#include"snake.h"
//设置光标的坐标 
void SetPos(short x, short y);
//欢迎界面
void WelcomeToGame();
//打印帮助信息 
void PrintHelpInfo();
//创建地图 
void CreateMap();
//初始化蛇 
void InitSnake(pSnake ps);
//创建食物
void CreateFood(pSnake ps);
//Game_Start.c
#include"Game_Start.h"//设置光标的坐标 
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//获取标准输出的句柄(用来标识不同设备的数值) hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为pos SetConsoleCursorPosition(hOutput, pos);
}
//打印欢迎进入游戏
void WelcomeToGame()
{SetPos(40, 15);printf("欢迎进入贪吃蛇游戏");SetPos(42, 25);//让按任意键继续的出现的位置好看点 system("pause");system("cls");SetPos(20, 12);printf("用 ↑  ↓  ←   → 分别控制蛇的移动, K为加速,L为减速");SetPos(37, 14);printf("加速将能得到更高的分数");SetPos(40, 25);//让按任意键继续的出现的位置好看点 system("pause");system("cls");
}
//创建地图
void CreateMap()
{int i = 0;//上(0,0)-(56, 0) SetPos(0, 0);for (i = 0; i < 58; i += 2){wprintf(L"%c", WALL);}//下(0,26)-(56, 26) SetPos(0, 26);for (i = 0; i < 58; i += 2){wprintf(L"%c", WALL);}//左 //x是0,y从1开始增长for (i = 1; i < 26; i++){SetPos(0, i);wprintf(L"%c", WALL);}//x是56,y从1开始增长for (i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);}
}
//初始化蛇,给五节身体
void InitSnake(pSnake ps)
{pSnakeNode cur = NULL;int i = 0;//创建蛇身节点,并初始化坐标 //头插法 for (i = 0; i < 5; i++){//创建蛇身的节点 cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc()");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;}}//打印蛇的身体 cur = ps->_pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}//初始化贪吃蛇数据 ps->_SleepTime = 200;ps->_Socre = 0;ps->_Status = OK;ps->_Dir = RIGHT;ps->_foodWeight = 10;
}
//创建食物
void CreateFood(pSnake ps)
{int x = 0;int y = 0;
again://产生的x坐标应该是2的倍数,这样才可能和蛇头坐标对齐。 do{x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2 != 0);//获取指向蛇头的指针 pSnakeNode cur = ps->_pSnake;//食物不能和蛇身冲突 while (cur){if (cur->x == x && cur->y == y){goto again;}cur = cur->next;}//创建食物pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); if (pFood == NULL){perror("CreateFood::malloc()");return;}else{pFood->x = x;pFood->y = y;SetPos(pFood->x, pFood->y);wprintf(L"%c", FOOD);ps->_pFood = pFood;}
}
//Game_Run.h
#pragma once#include"snake.h"
//打印提示信息
void PrintHelpInfo();
//暂停响应 
void pause();
//下一个节点是食物
int NextIsFood(pSnakeNode psn, pSnake ps);
//吃食物 
void EatFood(pSnakeNode psn, pSnake ps);
//不吃食物
void NoFood(pSnakeNode psn, pSnake ps);
//撞墙检测 
int KillByWall(pSnake ps);
//撞自身检测 
int KillBySelf(pSnake ps);
//蛇的移动 
void SnakeMove(pSnake ps);
//游戏初始化 
void GameStart(pSnake ps);
//游戏运行
void GameRun(pSnake ps);
//游戏结束 
void GameEnd(pSnake ps);
//Game_Run.c
#include"Game_Run.h"
#include"Game_Start.h"
//打印提示信息
void PrintHelpInfo()
{	 SetPos(62, 7);printf("不能穿墙,不能咬到自己");SetPos(62, 9);printf("用 ↑ . ↓ . ← . → 分别控制蛇的移动.");SetPos(62, 11);printf("K 为加速,L 为减速");SetPos(62, 13);printf("ESC:退出游戏.");SetPos(62, 15);printf("space:暂停游戏.");SetPos(84, 20);printf("卡戎-caryon@版权");
}
//暂停 
void pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
int NextIsFood(pSnakeNode psn, pSnake ps)
{return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
}//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
void EatFood(pSnakeNode psn, pSnake ps)
{//头插法 psn->next = ps->_pSnake;ps->_pSnake = psn;pSnakeNode cur = ps->_pSnake;//打印蛇while (cur){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}ps->_Socre += ps->_foodWeight;free(ps->_pFood);CreateFood(ps);
}
//pSnakeNode psn 是下一个节点的地址 
//pSnake ps 维护蛇的指针 
void NoFood(pSnakeNode psn, pSnake ps)
{//头插法 psn->next = ps->_pSnake;ps->_pSnake = psn;pSnakeNode cur = ps->_pSnake;//打印蛇 while (cur->next->next){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}//最后一个位置打印空格,然后释放节点 SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;
}
//pSnake ps 维护蛇的指针 
int 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;return 1;}return 0;
}
//pSnake ps 维护蛇的指针 
int KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;while (cur){if ((ps->_pSnake->x == cur->x)&& (ps->_pSnake->y == cur->y)){ps->_Status = KILL_BY_SELF;return 1;}cur = cur->next;}return 0;
}
//蛇的移动
void SnakeMove(pSnake ps)
{//创建下一个节点 pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("SnakeMove()::malloc()");return;}//确定下一个节点的坐标,下一个节点的坐标根据,蛇头的坐标和方向确定 switch (ps->_Dir){case UP:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y - 1;break;case DOWN:pNextNode->x = ps->_pSnake->x;pNextNode->y = ps->_pSnake->y + 1;break;case LEFT:pNextNode->x = ps->_pSnake->x - 2;pNextNode->y = ps->_pSnake->y;break;case RIGHT:pNextNode->x = ps->_pSnake->x + 2;pNextNode->y = ps->_pSnake->y;break;}//如果下一个位置就是食物if (NextIsFood(pNextNode, ps)){EatFood(pNextNode, ps);}else//如果没有食物 {NoFood(pNextNode, ps);}KillByWall(ps);KillBySelf(ps);
}

八、控制台设置

如果同学Win11系统的控制台窗口是这样显示,可以调整⼀下
在这里插入图片描述
调整方式:
在这里插入图片描述
保存后,重新打开cmd即可

相关文章:

【项目实践】贪吃蛇

一、游戏效果展示二、博客目标三、使用到的知识四、Win32 API 介绍 4.1 WIn32 API4.2 控制台程序4.3 控制屏幕上的坐标COORD4.4 GetStdHandle4.5 GetConsoleCursorInfo 4.5.1 CONSOLE_CURSOR_INFO 4.6 SetConsoleCursorInfo4.7 SetConsoleCursorPosition4.8 GetAsyncKeyState 五…...

将exe文件添加到注册表中,实现开机时自动运行

目录 一、前言 二、代码 三、使用步骤 1.编译生成exe文件、 2.以管理员身份运行代码 3.打开注册表&#xff0c;验证结果 一、前言 在Windows操作系统中&#xff0c;将exe文件的路径添加到注册表下&#xff0c;主要用于实现程序的开机自动运行功能。 注册表路径为&#xf…...

SQL使用注意事项

作为开发人员日常最为熟悉的工具sql。但是在实际使用中&#xff0c;有一些坑需要尽量避免&#xff0c;本文是对一些常用注意事项的总结 查询需要的。不要全部都查询。禁止使用存储过程&#xff0c;禁止使用外键。使用sql进行计算&#xff0c;要小心。&#xff08;数据量大的情况…...

uniapp小程序IOS端,uni.createInnerAudioContext()无声音

可能的问题 路径中有中文字符需要使用uni.getBackgroundAudioManager()播放其他问题 解决办法 首先我的路径中没有中文字符&#xff0c;如果有的&#xff0c;可能需要转义一下或者干脆不使用中文字符&#xff0c;第二个也是从其他博客中看到的&#xff0c;我这边分享一下我的…...

第二节-K8s词汇表

关键字词汇表 https://kubernetes.io/zh-cn/docs/reference/glossary/?fundamentaltrue API Group (API 组)Kubernetes API 中的一组相关路径。 API 服务器亦称作:kube-apiserver API 服务器是 Kubernetes 控制平面的组件&#xff0c; 该组件负责公开了 Kubernetes API&…...

命令行运行git reflog(reference log)报错的解决办法

文章目录 1. 检查 Git 是否已安装2. 检查 PATH 环境变量3. 重新安装 Git 在Git中&#xff0c; reflog的英文全称是 “ reference log”。意思是 引用日志&#xff08;参考日志&#xff09;。它记录了本地仓库中HEAD和分支引用所指向的提交的变更历史。这包括了你所有的提交&…...

python3 imwrite 中文路径不成功解决方法

filename 中文路径 #cv2.imwrite(filename, frame) cv2.imencode(.jpg, frame)[1].tofile(filename)...

tapd 与国内外主流的8大项目管理软件大对比

对比Tapd与8大项目管理工具&#xff1a;PingCode、Worktile、Redmine、Teambition、广联达、Jira、禅道、飞书。 Tapd 是腾讯推出的一款敏捷开发管理工具&#xff0c;特别适合那些需要高效协作和快速迭代的敏捷开发团队。它支持多种敏捷方法论&#xff0c;包括Scrum和Kanban&am…...

IP地址配置

1.为虚拟机配置IP地址&#xff0c;网关&#xff0c;DNS 例如&#xff1a;手动给虚拟机配置IP地址为 192.168.5.50/24&#xff1b;网关地址为&#xff1a;192.168.5.2&#xff1b;DNS地址为&#xff1a;192.168.5.2 解题步骤如下&#xff1a; #配置IP地址 [rootlocalhost ~]#…...

【C#】ProgressBar进度条异步编程思想

1.控件介绍 进度条通常用于显示代码的执行进程进度&#xff0c;在一些复杂功能交互体验时告知用户进程还在继续。 在属性栏中&#xff0c;有三个值常用&#xff1a; Value表示当前值&#xff0c;Minimum表示进度条范围下限&#xff0c;Maximum表示进度条范围上限。 2.简单实…...

深入浅出3D感知中的优化与基于学习的技术1(原创系列)

近期几乎看了所有有关NERF技术论文&#xff0c;本身我研究的领域不在深度学习技术方向&#xff0c;是传统的机器人控制和感知。所以总结了下这部分基于学习的感知技术&#xff0c;会写一个新的系列教程讲解这部分三维感知技术的发展到最新的技术细节&#xff0c;并支持自己最近…...

【CentOS 7 上安装 Oracle JDK 8u333】

文章目录 下载 Oracle JDK 8u333&#xff1a;上传 RPM 包到服务器安装 Oracle JDK设置 JAVA_HOME 环境变量验证 下载 Oracle JDK 8u333 访问 https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html 找到 JDK 8u333 版本&#xff0c;并下载适用于 L…...

Nginx 常用配置与应用

Nginx 常用配置与应用 官网地址&#xff1a;https://nginx.org/en/docs/ 目录 Nginx 常用配置与应用 Nginx总架构 正向代理 反向代理 Nginx 基本配置反向代理案例 负载均衡 Nginx总架构 进程模型 正向代理 反向代理 Nginx 基本配置反向代理案例 负载均衡 Nginx 基本配置…...

基于Springboot的智慧养老中心管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Springboot的智慧养老中心管理系统,…...

数据结构笔记第3篇:双向链表

1、双向链表的结构 注意&#xff1a;这里的 "带头" 跟前面我们说的 "头结点" 是两个概念&#xff0c;实际前面的在单链表阶段称呼不严谨&#xff0c;但是为了同学们更好的理解就直接称为单链表的头结点。 带头链表里的头结点&#xff0c;实际为 "哨兵…...

详细对比Java SPI、Spring SPI 和 Dubbo SPI

SPI&#xff08;Service Provider Interface&#xff09;概述 定义&#xff1a;SPI是一种动态替换发现机制&#xff0c;用于实现接口与实现的解耦&#xff0c;提高框架的可扩展性。核心思想&#xff1a;解耦和方便扩展。 Java SPI 约定规范&#xff1a; 扩展类文件放在META-…...

CPU的核心数和线程数

CPU的核心数和线程数 一、关系&#xff1a; 1、线程数可以模拟出不同的CPU核心数。 CPU的核心数指的是硬件上存在着几个核心&#xff0c;而线程数可以模拟出多个核心数的功能。线程数越多&#xff0c;越有利于同时运行多个程序&#xff0c;因为线程数等同于在某个瞬间CPU能同…...

电脑游戏录屏,3款实用软件推荐给你

在电竞游戏热潮席卷全球的今天&#xff0c;电脑游戏录屏早已不再是简单的画面捕捉&#xff0c;它成为了记录电竞风采、打造专属游戏记忆的重要手段。通过游戏录屏&#xff0c;我们可以定格游戏中的精彩瞬间&#xff0c;重温那些令人热血沸腾的电竞时刻。那么&#xff0c;在进行…...

C#桌面应用开发:番茄定时器

C#桌面应用开发&#xff1a;番茄定时器 1、环境搭建和工程创建&#xff1a; 步骤一&#xff1a;安装visual studio2022 步骤二&#xff1a;新建工程 2、制作窗体部件 *踩过的坑&#xff1a; &#xff08;1&#xff09;找不到工具箱控件&#xff0c;现象如下&#xff1a;…...

PHP智慧门店微信小程序系统源码

&#x1f50d;【引领未来零售新风尚】&#x1f50d; &#x1f680;升级启航&#xff0c;智慧零售新篇章&#x1f680; 告别传统门店的束缚&#xff0c;智慧门店v3微信小程序携带着前沿科技与人性化设计&#xff0c;正式启航&#xff01;这个版本不仅是对过往功能的全面优化&a…...

SerDes介绍以及原语使用介绍(2)OSERDESE2原语仿真

文章目录 前言一、SDR模式1.1、设计代码1.2、testbench代码1.3、仿真分析 二、DDR模式下2.1、设计代码2.2、testbench代码2.3、仿真分析 三、OSERDES2级联3.1、设计代码3.2、testbench代码3.3、代码分析 前言 上文通过xilinx ug471手册对OSERDESE有了简单的了解&#xff0c;接…...

【稳定检索/投稿优惠】2024年教育、人文发展与艺术国际会议(EHDA 2024)

2024 International Conference on Education, Humanities Development and Arts 2024年教育、人文发展与艺术国际会议 【会议信息】 会议简称&#xff1a;EHDA 2024 大会时间&#xff1a;点击查看 截稿时间&#xff1a;点击查看 大会地点&#xff1a;中国北京 会议官网&#…...

Docker拉取失败,利用 Git将 Docker镜像重新打 Tag 推送到阿里云等其他公有云镜像仓库里

目录 一、开通阿里云容器镜像服务 二、Git配置 三、去DockerHub找镜像 四、编写images.txt文件 ​五、演示 六、其他注意事项 最近一段时间 Docker 镜像一直是 Pull 不下来的状态&#xff0c;想直连 DockerHub 是几乎不可能的。更糟糕的是&#xff0c;很多原本可靠的国内…...

【区分vue2和vue3下的element UI Breadcrumb 面包屑组件,分别详细介绍属性,事件,方法如何使用,并举例】

在 Vue 2 中&#xff0c;Element UI 提供了 el-breadcrumb 面包屑组件&#xff0c;而在 Vue 3 中&#xff0c;Element UI 的官方版本并没有直接更新以支持 Vue 3&#xff0c;但有一个类似的库叫做 Element Plus&#xff0c;它是为 Vue 3 设计的。 Vue 2 Element UI 在 Vue 2…...

gdb调试命令大全

基本命令 #gdb test test是要调试的程序&#xff0c;由gcc test.c -g -o test生成。进入后提示符变为(gdb) 。 start &#xff1a; 指令会执行程序至main() 主函数的起始位置&#xff0c;即在main() 函数的第一行语句处停止执行&#xff08;该行代码尚未执行&#xff09; cont…...

ESP32之arduino环境安装及点灯

目录 前言 前两天安装了VS&#xff43;&#xff4f;&#xff44;&#xff45;&#xff0c;奈何资源找的困难&#xff0c;于是咨询淘宝客服&#xff0c;他说&#xff41;&#xff52;&#xff44;&#xff55;&#xff49;&#xff4e;&#xff4f;用的多,资源多.然后就安装了a…...

查看VUE中安装包依赖的版本号

查看VUE中安装包依赖的版本号 全部依赖包版本查看某个依赖的例&#xff1a;查看stompjs 应用命令npm ls stompjs 全部依赖包版本 使用npm命令 使用 npm ls 命令可以列出项目中所有已安装的依赖包及其版本。 使用 npm list --depth1 命令可以列出项目中直接依赖的包及其版本&a…...

博途通讯笔记1:1200与1200之间S7通讯

目录 一、添加子网连接二、创建PUT GET三、各个参数的意义 一、添加子网连接 二、创建PUT GET 三、各个参数的意义...

Kafka搭建(集群版)

Kafka单机版 部署前提 VMware环境 : 两台centos系统 Jdk包:jdk-8u202-linux-x64.tar.gz Kafka包:kafka_2.12-3.5.0.tgz Zookeeper包:apache-zookeeper-3.7.2-bin.tar.gz 百度网盘自取: 链接: https://pan.baidu.com/s/11EWuhBoSmH3musd_3Rgodw?pwde32t 提取码: e32t Kafka搭建…...

【康复学习--LeetCode每日一题】3115. 质数的最大距离

题目&#xff1a; 给你一个整数数组 nums。 返回两个&#xff08;不一定不同的&#xff09;质数在 nums 中 下标 的 最大距离。 示例 1&#xff1a; 输入&#xff1a; nums [4,2,9,5,3] 输出&#xff1a; 3 解释&#xff1a; nums[1]、nums[3] 和 nums[4] 是质数。因此答案是…...

【yolov8系列】ubuntu上yolov8的开启训练的简单记录

前言 yolov8的广泛使用&#xff0c;拉取yolov8源码工程&#xff0c;然后配置环境后直接运行&#xff0c;初步验证自己数据的检测效果&#xff0c;在数据集准备OK的情况下 需要信手拈来&#xff0c;以保证开发过程的高效进行。 本篇博客更注意为了方便自己使用时参考。顺便也记录…...

Scala学习笔记15: 文件和正则表达式

目录 第十五章 文件和正则表达式1- 读取行2- 从URL或者其它源读取3- 写入文本文件4- 序列化5- 正则表达式6- 正则表达式验证输入数据格式end 第十五章 文件和正则表达式 1- 读取行 在Scala中读取文件中的行可以通过不同的方法实现 ; 一种常见的方法是使用 scala.io.Source 对…...

外卖员面试现状

说明&#xff1a; 以下身份角色用符号代替 # 面试官 $ 求职者 # 看了您的简历你有两年半的送外卖经验&#xff0c;可以简单说一下您平时是怎么送外卖的吗? $ 我首先在平台接单然后到店里取餐&#xff0c;取到餐后到顾客留下的地址&#xff0c;再通知顾客取餐 # 你们也用电动…...

异步加载与动态加载

异步加载和动态加载在概念上有相似之处&#xff0c;但并不完全等同。 异步加载&#xff08;Asynchronous Loading&#xff09;通常指的是不阻塞后续代码执行或页面渲染的数据或资源加载方式。在Web开发中&#xff0c;异步加载常用于从服务器获取数据&#xff0c;而不需要用户等…...

MUNIK解读ISO26262--什么是DFA

我们在学习功能安全过程中&#xff0c;经常会听到很多安全分析方法&#xff0c;有我们熟知的FMEA(Failure Modes Effects Analysis)和FTA(Fault Tree Analysis)还有功能安全产品设计中几乎绕不开的FMEDA(Failure Modes Effects and Diagnostic Analysis)&#xff0c;相比于它们…...

启动spring boot项目停止 提示80端口已经被占用

可能的情况: 检查并结束占用进程: 首先,你需要确定哪个进程正在使用80端口。在Windows上,可以通过命令行输入netstat -ano | findstr LISTENING | findstr :80来查看80端口的PID,然后在任务管理器中结束该进程。在...

SOLIDWORKS分期许可(订阅形式),降低前期的投入成本!

SOLIDWORKS 分期许可使您能够降低前期软件成本&#xff0c;同时提供对 SOLIDWORKS 新版本和升级程序的即时访问&#xff0c;以及在每个期限结束时调整产品的灵活性&#xff0c;帮助您跟上市场需求和竞争压力的步伐。 目 录&#xff1a; ★ 1 什么是SOLIDWORKS分期许可 ★ 2 …...

详细分析Spring Boot 数据源配置的基本知识(附配置)

目录 前言1. 基本知识2. 模版3. 实战经验前言 对于Java的基本知识推荐阅读: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)【Java项目】实战CRUD的功能整理(持续更新)1. 基本知识 包括数据源的概念、连接池的作用、多数据源的实现与管理、Druid 连接池…...

海思SD3403/SS928V100开发(15)9轴IMU ICM-20948模块SPI接口调试

1.前言 芯片平台: 海思SD3403/SS928V100 操作系统平台: Ubuntu20.04.05【自己移植】 9轴IMU模块:ICM-20948 通讯接口: SPI 模块datasheet手册: https://download.csdn.net/download/jzwjzw19900922/89517096 2. 调试记录 2.1 pinmux配置 #spi0 bspmm 0x0102F01D8 …...

大力出奇迹:大语言模型的崛起与挑战

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;特别是在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;大语言模型&#xff08;LLM&#xff09;的出现与应用&#xff0c;彻底改变了我们与机器互动的方式。本文将探讨ChatGPT等大语言模型的定义、…...

【算法 - 哈希表】两数之和

这里写自定义目录标题 两数之和题目解析思路解法一 &#xff1a;暴力枚举 依次遍历解法二 &#xff1a;使用哈希表来做优化 核心逻辑为什么之前的暴力枚举策略不太好用了&#xff1f;所以&#xff0c;这就是 这道题选择 固定一个数&#xff0c;再与其前面的数逐一对比完后&…...

【深度学习】图形模型基础(5):线性回归模型第一部分:认识线性回归模型

1. 回归模型定义 最简单的回归模型是具有单一预测变量的线性模型&#xff0c;其基本形式如下&#xff1a; y a b x ϵ y a bx \epsilon yabxϵ 其中&#xff0c; a a a 和 b b b 被称为模型的系数或更一般地&#xff0c;模型的参数。 ϵ \epsilon ϵ 代表误差项&#…...

2024 年第十四届 APMCM 亚太地区大学生数学建模竞赛B题超详细解题思路+数据预处理问题一代码分享

B题 洪水灾害的数据分析与预测 亚太中文赛事本次报名队伍约3000队&#xff0c;竞赛规模体量大致相当于2024年认证杯&#xff0c;1/3个妈杯&#xff0c;1/10个国赛。赛题难度大致相当于0.6个国赛&#xff0c;0.8个妈杯。该比例仅供大家参考。 本次竞赛赛题难度A:B:C3:1:4&…...

Yarn有哪些功能特点

Yarn是一个由Facebook团队开发&#xff0c;并联合Google、Exponent和Tilde等公司推出的JavaScript包管理工具&#xff0c;旨在提供更优的包管理体验&#xff0c;解决npm&#xff08;Node Package Manager&#xff09;的一些痛点。Yarn的功能特点主要包括以下几个方面&#xff1…...

深度学习算法bert

bert 属于自监督学习的一种&#xff08;输入x的部分作为label&#xff09; 1. bert是 transformer 中的 encoder &#xff0c;不同的bert在encoder层数、注意力头数、隐藏单元数不同 2. 假设我们有一个模型 m &#xff0c;首先我们为某种任务使用大规模的语料库预训练模型 m …...

PyTorch - 神经网络基础

神经网络的主要原理包括一组基本元素&#xff0c;即人工神经元或感知器。它包括几个基本输入&#xff0c;例如 x1、x2… xn &#xff0c;如果总和大于激活电位&#xff0c;则会产生二进制输出。 样本神经元的示意图如下所述。 产生的输出可以被认为是具有激活电位或偏差的加权…...

docker-compose搭建minio对象存储服务器

docker-compose搭建minio对象存储服务器 最近想使用oss对象存储进行用户图片上传的管理&#xff0c;了解了一下例如aliyun或者腾讯云的oss对象存储服务&#xff0c;但是呢涉及到对象存储以及经费有限的缘故&#xff0c;决定自己手动搭建一个oss对象存储服务器&#xff1b; 首先…...

vue3使用pinia中的actions,需要调用接口的话

actions&#xff0c;需要调用接口的话&#xff0c;假如页面想要调用actions中的方法获取数据&#xff0c; 必须使用try catch async await 进行包裹&#xff0c;详情看下面代码 import {defineStore} from pinia import {reqCode,reqUserLogin} from ../../api/hospital/i…...

Python酷库之旅-第三方库Pandas(003)

目录 一、用法精讲 4、pandas.read_csv函数 4-1、语法 4-2、参数 4-3、功能 4-4、返回值 4-5、说明 4-6、用法 4-6-1、创建csv文件 4-6-2、代码示例 4-6-3、结果输出 二、推荐阅读 1、Python筑基之旅 2、Python函数之旅 3、Python算法之旅 4、Python魔法之旅 …...

社交电商中的裂变营销利器,二级分销模式,美妆家具成功案例分享

二级分销返佣模式是一种帮助商家迅速扩大市场覆盖的有效营销策略&#xff0c;不仅能降低营销成本&#xff0c;还能提升品牌知名度。下面通过两个具体的案例来说明这种模式的好处和优势。 某知名美妆品牌在市场竞争日益激烈的情况下&#xff0c;决定采用二级分销返佣模式进行市场…...