贪吃蛇实现

1.资料来源

https://learn.microsoft.com/zh-cn/windows/console/getstdhandle

2.前言

简介

贪吃蛇是久负盛名的游戏,和俄罗斯方块、扫雷等游戏位列于经典游戏的行列。
《贪食蛇》中玩家控制一条不断移动的蛇,在屏幕上吃掉出现的食物。每吃掉一个食物,蛇的身体就会变长。游戏的目标是尽可能长时间地生存下去,同时避免蛇头撞到自己的身体或屏幕边缘。游戏最初是像素风格,后来发展出了3D版本和多人对战模式。玩家需要灵活操作,利用策略在有限的空间内避免碰撞,挑战高分。

实现基本功能

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

技术要点

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

WIN32API

Win32 API是Windows操作系统的核心编程接口,用于与操作系统内核直接交互。

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

控制台程序(Console)

平常运行起来的黑框其实就是控制台程序
win + R输入cmd打开控制台窗口
可以使用cmd命令来设置控制台窗口的长宽:设置控制台窗口的大小,30行,100列

mode con cols=100 lines=30

也可以通过命令命令设置控制台窗口的名字

title 贪吃蛇

这些能在控制台窗口执行的命令,也可以调用C语言函数system来执行,例如:

#include <stdlib.h>
//system函数可以执行系统命令
int main()
{//设置控制台相关属性system("mode con cols=100 lines=30");system("title 贪吃蛇");//暂停//getchar();  system("pause");return 0;
}

3.相关win32API函数

控制台屏幕上的坐标 COORSD

COORD 是Windows API上定义的一个结构体,表示一个字符在控制台屏幕缓冲区上的坐标,坐标系(0, 0)的原点位于缓冲区的顶部左侧单元格。

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

示例

#include <windows.h>
int main()
{COORD pos1 = { 0, 0 };COORD pos2 = { 10, 20 };system("pause");return 0;
}

GetStdHandle 获取句柄

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

HANDLE WINAPI GetStdHandle(_In_ DWORD nStdHandle
);

在这里插入图片描述

HANDLE GetStdHandle(DWORD nStdHandle);
typedef void* HANDLE

CONSOLE_CURSOR_INFO 结构

包含有关控制台游标的信息。

typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;BOOL  bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

在这里插入图片描述


GetConsoleCursorInfo 检索游标信息

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

BOOL WINAPI GetConsoleCursorInfo(_In_  HANDLE               hConsoleOutput,  //句柄_Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo  //指针
);PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关
主机游标(光标)的信息。

SetConsoleCursorInfo 设置游标信息

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

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

示例1

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{//获得标准输出设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定义一个光标信息的结构体CONSOLE_CURSOR_INFO cursor_info = { 0 };//获取和houtput句柄相关的控制台上的光标信息,存储在cursor_info中GetConsoleCursorInfo(houtput, &cursor_info);printf("%d\n", cursor_info.dwSize);system("pause");   //暂停//修改光标的占比cursor_info.dwSize = 50;//设置和houtput句柄相关的控制台上的光标信息SetConsoleCursorInfo(houtput, &cursor_info);system("pause");return 0;
}

SetConsoleCursorPosition 设置控制台光标位置

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

BOOL WINAPI SetConsoleCursorPosition(_In_ HANDLE hConsoleOutput,_In_ COORD  dwCursorPosition
);

在这里插入图片描述
示例:

void set_pos(short x, short y)
{//获得标准输出设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x, y };SetConsoleCursorPosition(houtput, pos);
}
int main()
{//直接定位光标位置set_pos(10, 20);system("pause");return 0;
}

GetAsyncKeyState 获取按键情况

确定调用函数时键是向上还是向下,以及上次调用 GetAsyncKeyState 后是否按下了该键。

SHORT GetAsyncKeyState([in] int vKey
);

将键盘上每个键的虚拟键值传给函数,函数通过返回值来分辨键的状态。

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

参考:虚拟键代码 https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes


分装宏函数

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1) ? 1 : 0)
//结果是1表示按过,结果是0表示没按过

示例:

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1) ? 1 : 0)
int main()
{//按哪个数字打印哪个数字while (1){if (KEY_PRESS(0x30))printf("0\n");else if (KEY_PRESS(0x31))printf("1\n");else if (KEY_PRESS(0x32))printf("2\n");else if (KEY_PRESS(0x33))printf("3\n");else if (KEY_PRESS(0x34))printf("4\n");else if (KEY_PRESS(0x35))printf("5\n");else if (KEY_PRESS(0x36))printf("6\n");else if (KEY_PRESS(0x37))printf("7\n");else if (KEY_PRESS(0x38))printf("8\n");else if (KEY_PRESS(0x39))printf("9\n");}return 0;
}

4.贪吃蛇游戏设计与分析

4.1地图

在游戏地图上,打印宽字符。普通的字符是占一个字节的,这类宽字符是占2个字节。
汉字本质上也是宽字符。

这里再简单的讲一下C语言的国际化特性相关的知识,过去C语言并不适合非英语国家(地区)使用。C语言最初假定地址都是单字节的,但是这些假定并不是在世界的任何地方都适用。
后来为了使C语言适应国际化,C语言的标准中不断加入了国际化的支持。比如:加入了宽字符的类型 wchar_t 和宽字符的输入输出函数,加入了 <locale.h> 头文件,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语言的地理区域)调整程序行为的函数。

4.1.1<locale.h>本地化

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

  1. 数字量的格式
  2. 货币量的格式
  3. 字符集
  4. 日期和时间的表示形式

4.1.2类项

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


参考资料:https://learn.microsoft.com/zh-cn/cpp/text/locales-and-code-pages

https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-170

4.1.3 setlocale函数

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

setlocale函数用于修改当前地区,可以针对一个类项修改,也可以针对所有类项。
setlocale的第一个参数可以是前面说明的类项中的一个,那么每次只会影响一个类项,如果第一个参数是LC_ALL,就会影响所有的类项。
C标准给第二个参数仅定义了2种可能取值: “C”(正常模式)和 “”(本地模式)。

在任意程序执行开始,都会隐藏式执行调用:

setlocale(LC_ALL, "C");

调用setlocale函数就可以切换到本地模式,这种模式下程序会适应本地环境。
比如:切换到我们的本地模式后就支持宽字符(汉字)的输出等。

setlocale(LC_ALL, "");

setlocale的返回值是一个字符串指针,表示已经设置好的格式,如果调用失败,则返回空指针NULL。
setlocale()可以用来查询当前的地区,这时第二个参数设为NULL就可以了。


示例:

#include <locale.h>
int main()
{char* ret = setlocale(LC_ALL, NULL);printf("%s\n", ret);ret = setlocale(LC_ALL, "");printf("%s\n", ret);return 0;
}

参考资料:https://legacy.cplusplus.com/reference/clocale/setlocale/?kw=setlocale

/* setlocale example */
#include <stdio.h>      /* printf */
#include <time.h>       /* time_t, struct tm, time, localtime, strftime */
#include <locale.h>     /* struct lconv, setlocale, localeconv */int main ()
{time_t rawtime;struct tm * timeinfo;char buffer [80];struct lconv * lc;time ( &rawtime );timeinfo = localtime ( &rawtime );int twice=0;do {printf ("Locale is: %s\n", setlocale(LC_ALL,NULL) );strftime (buffer,80,"%c",timeinfo);printf ("Date is: %s\n",buffer);lc = localeconv ();printf ("Currency symbol is: %s\n-\n",lc->currency_symbol);setlocale (LC_ALL,"");} while (!twice++);return 0;
}

4.1.3 宽字符的打印

宽字符的字面量必须叫上前缀L,否则C语言会把字面量当作窄字符类型处理。前缀L在单引号前面,表示宽字符。宽字符的打印使用wprintf,对应wprintf()的占位符为 %lc;在双引号前面,表示宽字符串,对应wprintf()的占位符为 %ls

int main()
{//设置本地化setlocale(LC_ALL, "");//宽字符字面量前加 Lwchar_t ch1 = L'你';wchar_t ch2 = L'好';wchar_t ch3 = L'世';wchar_t ch4 = L'界';printf("%c%c\n", 'a', 'b');//格式串前面也要加 Lwprintf(L"%lc\n", ch1);wprintf(L"%lc\n", ch2);wprintf(L"%lc\n", ch3);wprintf(L"%lc\n", ch4);return 0;
}

代码

snake.h

#pragma once#include <stdio.h>
#include <locale.h>
#include <stdbool.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>#define POS_X 24
#define POS_Y 5
#define WALL L'□'
#define BOOY L'●'
#define FOOD L'★'//类型的声明//蛇的方向
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};//蛇的状态
enum GAME_STATUS
{OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END_NORMAL//正常退出
};//蛇身的节点类型
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 _food_weight;//一个食物的分数int _score;//总成绩int _sleep_time;//休息时间,时间越短速度越快,时间越长速度越慢
} Snake, *pSnake;//函数声明//设置光标位置
void SetPos(short x, short y);
//游戏初始化
void GameStart(pSnake ps);
//欢迎界面的打印
void WelcomeToGame();
//绘制地图
void CreateMap();
//初始化蛇身
void InitSnake(pSnake ps);
//创建食物
void CreateFood(pSnake ps);
//运行游戏
void GameRun(pSnake ps);
//蛇的移动-走一步
void SnakeMove(pSnake ps);
//判断下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps);
//下一个位置是食物,吃掉食物
void EatFood(pSnakeNode pn, pSnake ps);
//下一个位置不是食物
void NoFood(pSnakeNode pn, pSnake ps);
//检查是否撞墙
void KillByWall(pSnake ps);
//检查是否撞到自己
void KillBySelf(pSnake ps);
//结束运行-善后工作
void GameEnd(pSnake ps);

snake.c

#define _CRT_SECURE_NO_WARNINGS 1#include "snake.h"//设置光标位置
void SetPos(short x, short y)
{//获取标准输出设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x, y };SetConsoleCursorPosition(houtput, pos);
}//欢迎界面的打印
void WelcomeToGame()
{SetPos(38, 14);wprintf(L"欢迎来到贪吃蛇小游戏\n");SetPos(40, 20);system("pause");system("cls");SetPos(30, 14);wprintf(L"用↑.↓.→.←来控制蛇的移动,按z加速,按x减速\n");SetPos(30, 15);wprintf(L"加速能够获得更高的分数\n");SetPos(40, 20);system("pause");system("cls");
}//绘制地图
void CreateMap()
{int i = 0;//上for (i = 0; i < 29; i++)wprintf(L"%lc", WALL);//左右for (i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%lc", WALL);SetPos(56, i);wprintf(L"%lc", WALL);}//下SetPos(0, 26);for (i = 0; i < 29; i++)wprintf(L"%lc", WALL);
}//初始化蛇身
void InitSnake(pSnake ps)
{int i = 0;pSnakeNode cur = NULL;//创建蛇身节点for (i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake():malloc()");exit(1);}cur->next = NULL;cur->x = POS_X + 2 * i;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"%lc", BOOY);cur = cur->next;}//设置贪吃蛇属性ps->_dir = RIGHT;//默认向右ps->_score = 0;ps->_food_weight = 10;ps->_sleep_time = 200;//单位是毫秒ps->_status = OK;
}//创建食物
void CreateFood(pSnake ps)
{int x = 0;int y = 0;//生成x是2的倍数// x: 2-54// y: 1-25//x = 2 * (rand() % 27) + 2;
again:do{x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2 != 0);//x和y的坐标不能和设的身体坐标冲突pSnakeNode cur = ps->_pSnake;while (cur){if (x == cur->x && y == cur->y)goto again;cur = cur->next;}//创建食物的节点pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("CreateFood():malloc()");return;}pFood->x = x;pFood->y = y;pFood->next = NULL;SetPos(x, y);wprintf(L"%lc", FOOD); ps->_pFood = pFood;
}//1.游戏初始化
void GameStart(pSnake ps)
{//0.先设置窗口信息,再光标隐藏system("mode con cols=100 lines=30");//设置窗口大小system("title 贪吃蛇");        HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄CONSOLE_CURSOR_INFO CursorInfo;//光标信息GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false;//隐藏控制台光标SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态//1.打印环境界面和功能介绍WelcomeToGame();//2.绘制地图CreateMap();//3.初始化蛇身InitSnake(ps);//4.创建食物CreateFood(ps);
}//打印帮助信息
void printHelpInfo()
{SetPos(64, 14);wprintf(L"%ls", L"不能穿墙,不能咬到自己");SetPos(64, 15);wprintf(L"%ls", L"用↑.↓.→.←来控制蛇的移动");SetPos(64, 16);wprintf(L"%ls", L"按z加速,按x减速");SetPos(64, 17);wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");
}
//检测虚拟键值
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1) ? 1 : 0)
//暂停
void Pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}
//判断下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{//是食物返回1,不是食物返回0//return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);if (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y)return 1;elsereturn 0;
}
//下一个位置是食物,吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{//在CreateFood()里创建了pFood和SnakeMove()里创建了pNextNode两个食物节点,需要连接一个,释放一个//头插法ps->_pFood->next = ps->_pSnake;ps->_pSnake = ps->_pFood;//释放下一个位置的节点free(pn);pn = NULL;//打印pSnakeNode cur = ps->_pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}ps->_score += ps->_food_weight;//重新创建食物CreateFood(ps);
}
//下一个位置不是食物
void NoFood(pSnakeNode pn, pSnake ps)
{//头插法pn->next = ps->_pSnake;ps->_pSnake = pn;pSnakeNode cur = ps->_pSnake;while (cur->next->next != NULL){SetPos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}//最后一个节点要打印空白字符(两个空格)覆盖原来的蛇身节点BOOYSetPos(cur->next->x, cur->next->y);printf("  ");//释放最后一个节点free(cur->next);cur->next = NULL;
}
//检查是否撞墙
void 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;}
}
//检查是否撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;while (cur){if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y){ps->_status = KILL_BY_SELF;break;}cur = cur->next;}
}
//蛇的移动-走一步
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);
}//2.运行游戏
void GameRun(pSnake ps)
{//打印帮助信息printHelpInfo();do{//打印总分数和食物的分值SetPos(64, 10);printf("总分数:%d\n", ps->_score);SetPos(64, 11);printf("当前食物的分数:%2d\n", ps->_food_weight);if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)ps->_dir = UP;else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)ps->_dir = DOWN;else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)ps->_dir = LEFT;else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)ps->_dir = RIGHT;else if (KEY_PRESS(VK_SPACE)){//暂停Pause();}else if (KEY_PRESS(VK_ESCAPE)){//正常退出游戏ps->_status = END_NORMAL;}else if (KEY_PRESS(0x5A)){//加速if (ps->_sleep_time > 80)//分四档{ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(0x58)){//减速if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}//蛇的移动-走一步SnakeMove(ps);Sleep(ps->_sleep_time);} while (ps->_status == OK);
}//3.结束运行-善后工作
void GameEnd(pSnake ps)
{SetPos(24, 12);switch (ps->_status){case END_NORMAL:printf("正常退出游戏\n");break;case KILL_BY_WALL:printf("撞到墙上,游戏结束\n");break;case KILL_BY_SELF:printf("撞到自己,游戏结束\n");break;}//释放蛇身链表pSnakeNode cur = ps->_pSnake;while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include "snake.h"//游戏的测试逻辑
void test()
{int ch = 0;do{system("cls");//清屏//创建贪吃蛇Snake snake = { 0 };GameStart(&snake);//运行游戏GameRun(&snake);//结束运行-善后工作GameEnd(&snake);SetPos(20, 15);printf("再来一局吗?(Y/N)");ch = getchar();while (getchar() != '\n');//清理\n} while (ch == 'Y' || ch == 'y');SetPos(0, 27);
}int main()
{//设置适配本地环境setlocale(LC_ALL, "");srand((unsigned int)time(NULL));test();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计Python动漫推荐系统 漫画推荐系统 动漫视频推荐系统 机器学习 bilibili动漫爬虫 数据可视化 数据分析 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

MyBatis-Plus笔记-快速入门

大家在日常开发中应该能发现&#xff0c;单表的CRUD功能代码重复度很高&#xff0c;也没有什么难度。而这部分代码量往往比较大&#xff0c;开发起来比较费时。 因此&#xff0c;目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是…

《OpenCV》——图像透视转换

图像透视转换简介 在 OpenCV 里&#xff0c;图像透视转换属于重要的几何变换&#xff0c;也被叫做投影变换。下面从原理、实现步骤、相关函数和应用场景几个方面为你详细介绍。 原理 实现步骤 选取对应点&#xff1a;要在源图像和目标图像上分别找出至少四个对应的点。这些对…

克隆OpenAI(基于openai API和streamlit)

utils.py&#xff1a; from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationChain import osdef get_chat_response(api_key,prompt,memory): # memory不能是函数的内部局部变量&…

用 HTML、CSS 和 JavaScript 实现抽奖转盘效果

顺序抽奖 前言 这段代码实现了一个简单的抽奖转盘效果。页面上有一个九宫格布局的抽奖区域&#xff0c;周围八个格子分别放置了不同的奖品名称&#xff0c;中间是一个 “开始抽奖” 的按钮。点击按钮后&#xff0c;抽奖区域的格子会快速滚动&#xff0c;颜色不断变化&#xf…

【Linux】使用管道实现一个简易版本的进程池

文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程&#xff1a; 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…

Elasticsearch的索引生命周期管理

目录 说明零、参考一、ILM的基本概念二、ILM的实践步骤Elasticsearch ILM策略中的“最小年龄”是如何计算的&#xff1f;如何监控和调整Elasticsearch ILM策略的性能&#xff1f; 1. **监控性能**使用/_cat/thread_pool API基本请求格式请求特定线程池的信息响应内容 2. **调整…

MQTT知识

MQTT协议 MQTT 是一种基于发布/订阅模式的轻量级消息传输协议&#xff0c;专门针对低带宽和不稳定网络环境的物联网应用而设计&#xff0c;可以用极少的代码为联网设备提供实时可靠的消息服务。MQTT 协议广泛应用于物联网、移动互联网、智能硬件、车联网、智慧城市、远程医疗、…

LabVIEW如何高频采集温度数据?

在LabVIEW中进行高频温度数据采集时&#xff0c;选择合适的传感器&#xff08;如热电偶或热电阻&#xff09;和采集硬件是关键。下面是一些建议&#xff0c;帮助实现高效的温度数据采集&#xff1a; 1. 传感器选择&#xff1a; 热电偶&#xff08;Thermocouple&#xff09;&am…

前端 | 深入理解Promise

1. 引言 JavaScript 是一种单线程语言&#xff0c;这意味着它一次仅能执行一个任务。为了处理异步操作&#xff0c;JavaScript 提供了回调函数&#xff0c;但是随着项目处理并发任务的增加&#xff0c;回调地狱 (Callback Hell) 使异步代码很难维护。为此&#xff0c;ES6带来了…

gesp(C++六级)(10)洛谷:P10722:[GESP202406 六级] 二叉树

gesp(C六级)&#xff08;10&#xff09;洛谷&#xff1a;P10722&#xff1a;[GESP202406 六级] 二叉树 题目描述 小杨有⼀棵包含 n n n 个节点的二叉树&#xff0c;且根节点的编号为 1 1 1。这棵二叉树任意⼀个节点要么是白色&#xff0c;要么是黑色。之后小杨会对这棵二叉树…

【UE】 APlayerState

APlayerState 定义和功能 APlayerState用于保存关于游戏玩家状态的信息&#xff0c;例如得分、玩家名称和其他统计数据。这些信息通常在多人游戏中被用来持续跟踪玩家的表现。设计理念 APlayerState的目的是提供一个存储和传输玩家特定信息的方法&#xff0c;这样即使玩家的控…

如何用微信小程序写春联

​ 生活没有模板,只需心灯一盏。 如果笑能让你释然,那就开怀一笑;如果哭能让你减压,那就让泪水流下来。如果沉默是金,那就不用解释;如果放下能更好地前行,就别再扛着。 一、引入 Vant UI 1、通过 npm 安装 npm i @vant/weapp -S --production​​ 2、修改 app.json …

C# Winform enter键怎么去关联button

1.关联按钮上的Key事件按钮上的keypress&#xff0c;keydown&#xff0c;keyup事件随便一个即可private void textBox1_KeyDown(object sender, KeyEventArgs e){if (e.KeyCode Keys.Enter){this.textBox2.Focus();}}2.窗体上的事件private void textBox2_KeyPress(object sen…

FPGA 使用 CLOCK_DEDICATED_ROUTE 约束

使用 CLOCK_DEDICATED_ROUTE 约束 CLOCK_DEDICATED_ROUTE 约束通常在从一个时钟区域中的时钟缓存驱动到另一个时钟区域中的 MMCM 或 PLL 时使 用。默认情况下&#xff0c; CLOCK_DEDICATED_ROUTE 约束设置为 TRUE &#xff0c;并且缓存 /MMCM 或 PLL 对必须布局在相同…

Ollama+OpenWebUI部署本地大模型

OllamaOpenWebUI部署本地大模型 前言 Ollama是一个强大且易于使用的本地大模型推理框架&#xff0c;它专注于简化和优化大型语言模型&#xff08;LLMs&#xff09;在本地环境中的部署、管理和推理工作流。可以将Ollama理解为一个大模型推理框架的后端服务。 Ollama Ollama安…

SpringBoot 整合 SpringMVC:SpringMVC的注解管理

分类&#xff1a; 中央转发器(DispatcherServlet)控制器视图解析器静态资源访问消息转化器格式化静态资源管理 中央转发器&#xff1a; 中央转发器被 SpringBoot 自动接管&#xff0c;不需要我们在 web.xml 中配置&#xff1a; <servlet><servlet-name>chapter2&l…

Zemax 中带有体素探测器的激光谐振腔

激光谐振腔是激光系统的基本组成部分&#xff0c;在光的放大和相干激光辐射的产生中起着至关重要的作用。 激光腔由两个放置在光学谐振器两端的镜子组成。一个镜子反射率高&#xff08;后镜&#xff09;&#xff0c;而另一个镜子部分透明&#xff08;输出耦合器&#xff09;。…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.5 高级索引应用:图像处理中的区域提取

2.5 高级索引应用&#xff1a;图像处理中的区域提取 目录/提纲 #mermaid-svg-BI09xc20YqcpUam7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BI09xc20YqcpUam7 .error-icon{fill:#552222;}#mermaid-svg-BI09xc20…

[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…