C语言实战:贪吃蛇(万字详解)
💡目录
效果图
界面设计思路
1. 基本布局
2. 视觉元素
游戏机制设计
基本规则
游戏代码
前期准备
游戏代码详解
数据结构设计
宏定义
数据结构定义
函数原型(详见后文)
主函数代码
核心代码
Review
效果图
界面设计思路
1. 基本布局
- 游戏区域:屏幕中央占据大部分面积,用于显示游戏地图,地图通常被设计成一个矩形网格,每个单元格代表蛇可以移动的一个位置
- 得分显示:位于屏幕的顶部或角落,实时更新并显示玩家当前的得分,鼓励玩家追求更高的分数
- 控制提示:初学者友好设计,简短说明如何控制蛇的移动(如使用箭头键),通常在游戏开始前或暂停时显示
2. 视觉元素
- 蛇:使用不同的字符或颜色块来表示蛇的身体和头部,头部可能有特殊标记以区分,蛇移动时,通过改变字符或颜色的位置来模拟动画效果
- 食物:采用醒目的颜色或符号,使其在地图上易于识别,吸引玩家去追逐
- 边界:地图边缘用特殊的符号或颜色加重,提醒玩家避免碰撞
游戏机制设计
基本规则
- 移动:玩家通过键盘控制蛇的前进方向,蛇会在每一帧自动向前移动一格,无法倒退或立即转向180度
- 生长:当蛇头触碰到食物时,蛇的长度增加,同时分数增加,并在地图上的随机空白处生成新的食物
- 死亡条件:蛇碰到自己的身体或地图边界时,游戏结束。此外,可以设定时间限制或生命值系统增加挑战性
- 速度控制:玩家可通过按键调整蛇的移动速度
游戏代码
snake.h
#pragma once#define _CRT_SECURE_NO_WARNINGS 1
#define _KEY_STATE(x) (GetAsyncKeyState(x)&1?1:0)
#define _SNAKE_LENTH_ 5
#define GAME_WIDTH 26
#define GAME_HEIGHT 26#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<stdbool.h>
#include<locale.h>
#include<io.h>
#include<fcntl.h>
#include<time.h>typedef enum DIRECTION//蛇的方向
{UP,DOWN,LEFT,RIGHT
}DIRECTION;typedef enum STATE//蛇的状态
{RUN,PAUSE,KILL_BY_SELF,KILL_BY_WALL
}STATE;typedef struct Psnake //用于记录蛇的各个节点
{int x;int y;struct Psnake* next;
}Psnake;
Psnake* PsnakeNode;typedef struct Food
{int x;int y;int score;
}Food;typedef struct object
{Psnake* psnakenode;Food food;int score;int speed;DIRECTION dir;STATE state;
}object;
object SNAKE;void set_pos(short x, short y);//设置光标坐标void cursor(bool state, int x);//设置光标可见性和大小void map();//打印地图void cover();//打印封面void GameStart();//游戏初始化void printInstruction(int y, const wchar_t* message);//打印提示信息void PrintFood(Food* pos);//打印食物void CreateFood(Psnake* snake);//创建食物bool FoodJudge(Psnake* next);//判断下一节点是否为食物Psnake* buynode(int x, int y);//申请节点Psnake* InitSnake(Psnake** head);//初始化蛇void PrintSnake(Psnake* head);//打印蛇void IsDie();//判断满足结束条件void SnakeMove(Psnake** head);//移动蛇void ClearScreen();//清屏void GameOver(int score);//游戏结束void GameRun(Psnake** head);//运行游戏void GameStart();//游戏前准备
snake.c
#include "snake.h"//设置坐标
void set_pos(short x, short y)
{HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(hConsole, coord);
}//设置坐标//设置光标信息
void cursor(bool state, int x)
{HANDLE op = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO con;con.dwSize = x;con.bVisible = state;SetConsoleCursorInfo(op, &con);
}//打印提示信息
void printInstruction(int y, const wchar_t* message) {set_pos(60, y);wprintf(message);
}//打印地图
void map() {// Draw top borderset_pos(0, 0);for (int i = 0; i < 29; i++) {wprintf(L"□");}// Draw left and right bordersfor (int i = 1; i < 29; i++) {set_pos(0, i);wprintf(L"□");set_pos(28 * 2, i); //右边的坐标(28*2 ,i)wprintf(L"□");}// Draw bottom borderset_pos(0, 29);for (int i = 0; i < 29; i++) {wprintf(L"□");}const wchar_t* instructions[] = {L"操作指南: 使用方向键←↑→↓",L"速度控制: F2 加速, F3 减速",L"暂停游戏: 按 P",L"退出游戏: 按 ESC"};for (int i = 0; i < sizeof(instructions) / sizeof(instructions[0]); ++i) {printInstruction(10 + i, instructions[i]);}
}//打印封面
void cover()
{system("mode con cols=120 lines=50");system("title 贪吃蛇");set_pos(40, 10);wprintf(L" \n");wprintf(L" .-----------------. .-----------------. .----------------. .----------------. .---------------- .\n");wprintf(L" | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |\n");wprintf(L" | | _______ | || | ____ _____ | || | __ | || | ______ | || | ___ ____ | |\n");wprintf(L" | | / ___ | | || ||_ \\ |_ _| | || | / \\ | || | .' ___ | | || | |_ ||_ _| | |\n");wprintf(L" | | | (__ \\_| | || | | \\ | | | || | / /\\ \\ | || | / .' \\_| | || | | |_/ / | |\n");wprintf(L" | | '.___`-. | || | | |\\ \\| | | || | / ____ \\ | || | | | | || | | __'. | |\n");wprintf(L" | | |`\\____) | | || | _| |_\\ | |_ | || | / / \\ \\_ | || | \\ `.___.'\\ | || | _| | \\ \\_ | |\n");wprintf(L" | | |_______.' | || ||_____|\\____| | || ||____| |____|| || | `._____.' | || | |____ || __|_| |\n");wprintf(L" | | | || | | || | | || | | || | | |\n");wprintf(L" | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |\n");wprintf(L" '----------------' '------'---------' '----------------' '------'---------' '--'-------------'\n");set_pos(45, 29);system("pause");system("cls");map();}//打印食物坐标
void PrintFood(Food* pos)
{set_pos((*pos).x, (*pos).y);wprintf(L"◆");
}//创造食物
void CreateFood(Psnake* snake)
{Food* pos = (Food*)malloc(sizeof(Food));if (!pos){perror("MALLOC FAILED:");return;}Psnake* cur = snake;//生成随机数srand((unsigned int)time(NULL));again:(*pos).x = (rand() % GAME_WIDTH + 2) * 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).y = rand() % GAME_HEIGHT + 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).score = 1;while (cur){if ((*pos).x == cur->x && (*pos).y == cur->y)goto again;cur = cur->next;}SNAKE.food = *pos;PrintFood(pos);
}//判断下一个节点是否为食物
bool FoodJudge(Psnake* next)
{return ((next->x == SNAKE.food.x) && (next->y == SNAKE.food.y)) ? true : false;
}//申请一个节点
Psnake* buynode(int x,int y)
{Psnake* newnode = (Psnake*)calloc(1, sizeof(Psnake));if (newnode == NULL){perror("calloc:");return NULL;}newnode->next = NULL;newnode->x = x;newnode->y = y;return newnode;
}//初始化蛇的信息
Psnake* InitSnake(Psnake**head)
{if (!(head&&*head)) {fprintf(stderr, "Memory allocation failed\n");return NULL;}(*head) = (Psnake*)calloc(1, sizeof(Psnake));// 初始化成员(*head)->x = 6;(*head)->y = 10;(*head)->next = NULL;Psnake* cur = *head;if (!cur){perror("head is nullptr");return NULL;}int x = _SNAKE_LENTH_;while (x--){Psnake* newnode= buynode((cur->x)+2, cur->y);newnode->next = cur;cur = newnode;}SNAKE.dir = RIGHT;SNAKE.psnakenode = cur;SNAKE.state = RUN;SNAKE.speed = 150;return cur;
}//打印蛇
void PrintSnake(Psnake* head)
{Psnake* cur = SNAKE.psnakenode;while (cur){set_pos(cur->x, cur->y);wprintf(L"●");cur = cur->next;}
}void IsDie() {Psnake* cur = SNAKE.psnakenode->next; // 从第二个节点开始Psnake* prev = SNAKE.psnakenode; // 保存蛇头节点用于比较while (cur->next) {cur = cur->next;if (cur->x == prev->x && cur->y == prev->y) { // 检查当前节点是否与之前的节点重合SNAKE.state = KILL_BY_SELF;return;}}if (SNAKE.psnakenode->x == 56||SNAKE.psnakenode->x==0 ||SNAKE.psnakenode->y==0||SNAKE.psnakenode->y == 28) {SNAKE.state = KILL_BY_WALL;return;}
}//蛇的移动
void SnakeMove(Psnake** head)
{// 首先检查蛇头的下一个位置Psnake* next = (Psnake*)calloc(1, sizeof(Psnake));if (!next) {perror("Failed to allocate memory for next snake node");exit(EXIT_FAILURE);}// 根据当前方向设置蛇头的下一个位置switch (SNAKE.dir){case UP:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y - 1;break;case DOWN:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y + 1;break;case LEFT:next->x = SNAKE.psnakenode->x - 2;next->y = SNAKE.psnakenode->y;break;case RIGHT:next->x = SNAKE.psnakenode->x + 2;next->y = SNAKE.psnakenode->y;break;default:free(next);return; // 如果方向无效,不移动蛇}// 检查下一个位置是否有食物if (FoodJudge(next)){SNAKE.score += SNAKE.food.score; // 增加得分next->next = (*head); // 将新节点放在头部*head = next; // 更新头指针CreateFood(*head); // 生成新的食物}else{// 没有食物,移动蛇的尾部到头部Psnake* tail = SNAKE.psnakenode;Psnake* prev = NULL;if (!(tail || prev)){perror("POINTER IS NULL:");return;}// 找到尾节点和其前一个节点while (tail->next) {prev = tail;tail = tail->next;}// 将尾部节点移动到头部prev->next = NULL; // 将前一个节点的next设置为NULL,它现在是尾部set_pos(tail->x, tail->y);wprintf(L" ");tail->next = SNAKE.psnakenode; // 将原尾部节点放在头部SNAKE.psnakenode = tail; // 更新头指针// 更新头部位置SNAKE.psnakenode->x = next->x;SNAKE.psnakenode->y = next->y;// 释放之前分配的next节点,因为我们只是移动了尾部节点free(next);}IsDie();PrintSnake(SNAKE.psnakenode);
}//=========================================
//运行游戏void ClearScreen() { // 定义清屏函数,适用于Windows和Linux/macOS
#ifdef _WIN32system("cls");
#elsesystem("clear");
#endif
}void GameOver(int score) {// 游戏结束后的善后处理ClearScreen(); // 清除屏幕wprintf(L"Game Over!\n"); // 打印游戏结束信息wprintf(L"总分: %d\n", score); // 显示玩家得分// 询问玩家是否想再玩一次wprintf(L"再来一次?(y/n): ");system("pause >> nul");if (_KEY_STATE(0x59)){GameStart();fflush(stdin);}else {wprintf(L"感谢游玩\n");exit(0); // 结束程序}
}void GameRun(Psnake**head)
{do{set_pos(60, 6);wprintf(L"当前分数: %d", SNAKE.score);if ((_KEY_STATE(VK_UP) || _KEY_STATE(0x57)) && SNAKE.dir != DOWN){SNAKE.dir = UP;}else if ((_KEY_STATE(VK_DOWN) || _KEY_STATE(0x53)) && SNAKE.dir != UP){SNAKE.dir = DOWN;}else if ((_KEY_STATE(VK_LEFT) || _KEY_STATE(0x41)) && SNAKE.dir != RIGHT){SNAKE.dir = LEFT;}else if ((_KEY_STATE(VK_RIGHT) || _KEY_STATE(0x44)) && SNAKE.dir != LEFT){SNAKE.dir = RIGHT;}else if (_KEY_STATE(0x71)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) -= 30;} else if (_KEY_STATE(0x72)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) += 30;}else if (_KEY_STATE(0x50)){SNAKE.speed = 2147483647;}SnakeMove(head);Sleep(SNAKE.speed);} while (SNAKE.state == RUN);GameOver(SNAKE.score);
}//开始游戏及游戏前准备
void GameStart()
{cover();SNAKE.psnakenode = (Psnake*)malloc(sizeof(Psnake));InitSnake(&SNAKE.psnakenode);PrintSnake(SNAKE.psnakenode);CreateFood(SNAKE.psnakenode);set_pos(40, 36);GameRun(&SNAKE.psnakenode);
}
main.c
#include"snake.h"int main()
{
// 环境配置
//=================================================
// 可打印宽字符_setmode(_fileno(stdout), _O_U16TEXT);//切换到本地setlocale(LC_ALL, "");system("mode con cols=10 lines=20");set_pos(0, 20);cursor(false, 10);
//=================================================
// 正式开始GameStart();set_pos(0, 40);return 0;
}
前期准备
在 visual studio 2022中,默认窗口为cmd终端,无法实现预期效果,我们需要以下设置。若为其他开发环境请自查资料。
游戏代码详解
数据结构设计
snake.h
#pragma once#define _CRT_SECURE_NO_WARNINGS 1
#define _KEY_STATE(x) (GetAsyncKeyState(x)&1?1:0)
#define _SNAKE_LENTH_ 5
#define GAME_WIDTH 26
#define GAME_HEIGHT 26#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<stdbool.h>
#include<locale.h>
#include<io.h>
#include<fcntl.h>
#include<time.h>typedef enum DIRECTION//蛇的方向
{UP,DOWN,LEFT,RIGHT
}DIRECTION;typedef enum STATE//蛇的状态
{RUN,PAUSE,KILL_BY_SELF,KILL_BY_WALL
}STATE;typedef struct Psnake //用于记录蛇的各个节点
{int x;int y;struct Psnake* next;
}Psnake;
Psnake* PsnakeNode;typedef struct Food
{int x;int y;int score;
}Food;typedef struct object
{Psnake* psnakenode;Food food;int score;int speed;DIRECTION dir;STATE state;
}object;
object SNAKE;void set_pos(short x, short y);//设置光标坐标void cursor(bool state, int x);//设置光标可见性和大小void map();//打印地图void cover();//打印封面void GameStart();//游戏初始化void printInstruction(int y, const wchar_t* message);//打印提示信息void PrintFood(Food* pos);//打印食物void CreateFood(Psnake* snake);//创建食物bool FoodJudge(Psnake* next);//判断下一节点是否为食物Psnake* buynode(int x, int y);//申请节点Psnake* InitSnake(Psnake** head);//初始化蛇void PrintSnake(Psnake* head);//打印蛇void IsDie();//判断满足结束条件void SnakeMove(Psnake** head);//移动蛇void ClearScreen();//清屏void GameOver(int score);//游戏结束void GameRun(Psnake** head);//运行游戏void GameStart();//游戏前准备
宏定义
#pragma once
: 用于防止头文件被重复包含。_CRT_SECURE_NO_WARNINGS 1
: 禁用Visual Studio中的某些不安全函数警告,比如使用scanf
等。_KEY_STATE(x) (GetAsyncKeyState(x)&1?1:0)
: 自定义宏,用来检查给定虚拟键是否被按下。GetAsyncKeyState
是Windows API函数( 配合虚拟键码使用 ),用于检查按键状态。_SNAKE_LENTH_ 5
: 定义初始蛇的长度为5个单位。GAME_WIDTH 26
和GAME_HEIGHT 26
: 定义游戏地图的宽度和高度,均为26个单位。
数据结构定义
enum DIRECTION
: 定义蛇的移动方向,包括上、下、左、右。enum STATE
: 定义蛇的状态,包括运行中、暂停、自杀死亡、撞墙死亡。struct Psnake
: 蛇的节点结构体,包含位置坐标(x, y)
和指向下一个节点的指针。struct Food
: 食物结构体,包含位置坐标(x, y)
和分数。struct object
: 游戏对象结构体,整合蛇、食物、得分、速度、方向和状态等游戏所需的所有信息。
将蛇、食物抽象成一个结构体,用宽字符来打印,enum STATE、enum DIRECTION 来表示蛇的状态。将以上数据放在object结构体中进行管理。
函数原型(详见后文)
set_pos(short x, short y)
: 设置控制台光标位置。cursor(bool state, int x)
: 设置控制台光标的可见性和大小。map()
: 打印游戏地图。cover()
: 打印游戏封面。GameStart()
: 游戏初始化入口,可能包含了封面展示、地图绘制、蛇的初始化等。printInstruction(int y, const wchar_t* message)
: 在指定行打印宽字符格式的提示信息。PrintFood(Food* pos)
: 在指定位置打印食物。CreateFood(Psnake* snake)
: 随机生成食物,并确保不与蛇身重叠。FoodJudge(Psnake* next)
: 判断蛇的下一个位置是否为食物。buynode(int x, int y)
: 申请一个新的蛇节点内存。InitSnake(Psnake** head)
: 初始化蛇的链表结构。PrintSnake(Psnake* head)
: 打印蛇的当前位置。IsDie()
: 判断游戏是否结束,即蛇是否撞墙或自相残杀。SnakeMove(Psnake** head)
: 处理蛇的移动逻辑,包括方向改变和吃到食物的处理。ClearScreen()
: 清除屏幕内容。GameOver(int score)
: 游戏结束时的操作,显示分数并可能询问是否重新开始。GameRun(Psnake** head)
: 游戏主循环,处理用户输入和游戏逻辑。
整个框架围绕着贪吃蛇的基本逻辑展开,包括了游戏的初始化、地图绘制、蛇的移动与增长、食物的生成与检测、游戏状态管理以及用户交互。
主函数代码
main.c
#include"snake.h"int main()
{
// 环境配置
//=================================================
// 可打印宽字符_setmode(_fileno(stdout), _O_U16TEXT);//切换到本地setlocale(LC_ALL, "");system("mode con cols=10 lines=20");set_pos(0, 20);cursor(false, 10);
//=================================================
// 正式开始GameStart();set_pos(0, 40);return 0;
}
_setmode(_fileno(stdout) , _O_U16TEXT); 将输出模式设置为 _O_U16TEXT
模式,以便后续打印宽字符。
_setmode
函数来自<io.h>
头文件。_fileno
函数来自<stdio.h>
头文件。_O_U16TEXT
宏定义通常在<fcntl.h>
或<io.h>
中找到
setlocale(LC_ARR,""); 切换到本地模式
LC_ALL
是一个宏,代表所有类别,包括日期和时间格式、数字表示、货币符号、语言等。使用LC_ALL
意味着你想改变所有方面的区域设置。""
作为第二个参数,是一个特殊的值,它告诉函数使用用户默认的或者环境变量(如LANG
或LC_ALL
)指定的区域设置。
system("mode con cols=10 lines=20"); 将窗口设置为20行高,10列宽,在vs控制台中,字符高度一致,但宽度不一致,一般来说宽字符是窄字符的两倍,为了达到预期效果,需按照以上比例设置。
set_pos(0,20); 设置打印字符的位置,这是我自己封装的函数,后续会讲到。
cursor(false,10); 设置光标状态,自己封装的函数,后续会讲到。
GameStart(); 正式进入游戏。跳转到snake.c文件中。
核心代码
snake.c
#include "snake.h"//设置坐标
void set_pos(short x, short y)
{HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(hConsole, coord);
}//设置坐标//设置光标信息
void cursor(bool state, int x)
{HANDLE op = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO con;con.dwSize = x;con.bVisible = state;SetConsoleCursorInfo(op, &con);
}//打印提示信息
void printInstruction(int y, const wchar_t* message) {set_pos(60, y);wprintf(message);
}//打印地图
void map() {// Draw top borderset_pos(0, 0);for (int i = 0; i < 29; i++) {wprintf(L"□");}// Draw left and right bordersfor (int i = 1; i < 29; i++) {set_pos(0, i);wprintf(L"□");set_pos(28 * 2, i); //右边的坐标(28*2 ,i)wprintf(L"□");}// Draw bottom borderset_pos(0, 29);for (int i = 0; i < 29; i++) {wprintf(L"□");}const wchar_t* instructions[] = {L"操作指南: 使用方向键←↑→↓",L"速度控制: F2 加速, F3 减速",L"暂停游戏: 按 P",L"退出游戏: 按 ESC"};for (int i = 0; i < sizeof(instructions) / sizeof(instructions[0]); ++i) {printInstruction(10 + i, instructions[i]);}
}//打印封面
void cover()
{system("mode con cols=120 lines=50");system("title 贪吃蛇");set_pos(40, 10);wprintf(L" \n");wprintf(L" .-----------------. .-----------------. .----------------. .----------------. .---------------- .\n");wprintf(L" | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |\n");wprintf(L" | | _______ | || | ____ _____ | || | __ | || | ______ | || | ___ ____ | |\n");wprintf(L" | | / ___ | | || ||_ \\ |_ _| | || | / \\ | || | .' ___ | | || | |_ ||_ _| | |\n");wprintf(L" | | | (__ \\_| | || | | \\ | | | || | / /\\ \\ | || | / .' \\_| | || | | |_/ / | |\n");wprintf(L" | | '.___`-. | || | | |\\ \\| | | || | / ____ \\ | || | | | | || | | __'. | |\n");wprintf(L" | | |`\\____) | | || | _| |_\\ | |_ | || | / / \\ \\_ | || | \\ `.___.'\\ | || | _| | \\ \\_ | |\n");wprintf(L" | | |_______.' | || ||_____|\\____| | || ||____| |____|| || | `._____.' | || | |____ || __|_| |\n");wprintf(L" | | | || | | || | | || | | || | | |\n");wprintf(L" | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |\n");wprintf(L" '----------------' '------'---------' '----------------' '------'---------' '--'-------------'\n");set_pos(45, 29);system("pause");system("cls");map();}//打印食物坐标
void PrintFood(Food* pos)
{set_pos((*pos).x, (*pos).y);wprintf(L"◆");
}//创造食物
void CreateFood(Psnake* snake)
{Food* pos = (Food*)malloc(sizeof(Food));if (!pos){perror("MALLOC FAILED:");return;}Psnake* cur = snake;//生成随机数srand((unsigned int)time(NULL));again:(*pos).x = (rand() % GAME_WIDTH + 2) * 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).y = rand() % GAME_HEIGHT + 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).score = 1;while (cur){if ((*pos).x == cur->x && (*pos).y == cur->y)goto again;cur = cur->next;}SNAKE.food = *pos;PrintFood(pos);
}//判断下一个节点是否为食物
bool FoodJudge(Psnake* next)
{return ((next->x == SNAKE.food.x) && (next->y == SNAKE.food.y)) ? true : false;
}//申请一个节点
Psnake* buynode(int x,int y)
{Psnake* newnode = (Psnake*)calloc(1, sizeof(Psnake));if (newnode == NULL){perror("calloc:");return NULL;}newnode->next = NULL;newnode->x = x;newnode->y = y;return newnode;
}//初始化蛇的信息
Psnake* InitSnake(Psnake**head)
{if (!(head&&*head)) {fprintf(stderr, "Memory allocation failed\n");return NULL;}(*head) = (Psnake*)calloc(1, sizeof(Psnake));// 初始化成员(*head)->x = 6;(*head)->y = 10;(*head)->next = NULL;Psnake* cur = *head;if (!cur){perror("head is nullptr");return NULL;}int x = _SNAKE_LENTH_;while (x--){Psnake* newnode= buynode((cur->x)+2, cur->y);newnode->next = cur;cur = newnode;}SNAKE.dir = RIGHT;SNAKE.psnakenode = cur;SNAKE.state = RUN;SNAKE.speed = 150;return cur;
}//打印蛇
void PrintSnake(Psnake* head)
{Psnake* cur = SNAKE.psnakenode;while (cur){set_pos(cur->x, cur->y);wprintf(L"●");cur = cur->next;}
}void IsDie() {Psnake* cur = SNAKE.psnakenode->next; // 从第二个节点开始Psnake* prev = SNAKE.psnakenode; // 保存蛇头节点用于比较while (cur->next) {cur = cur->next;if (cur->x == prev->x && cur->y == prev->y) { // 检查当前节点是否与之前的节点重合SNAKE.state = KILL_BY_SELF;return;}}if (SNAKE.psnakenode->x == 56||SNAKE.psnakenode->x==0 ||SNAKE.psnakenode->y==0||SNAKE.psnakenode->y == 28) {SNAKE.state = KILL_BY_WALL;return;}
}//蛇的移动
void SnakeMove(Psnake** head)
{// 首先检查蛇头的下一个位置Psnake* next = (Psnake*)calloc(1, sizeof(Psnake));if (!next) {perror("Failed to allocate memory for next snake node");exit(EXIT_FAILURE);}// 根据当前方向设置蛇头的下一个位置switch (SNAKE.dir){case UP:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y - 1;break;case DOWN:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y + 1;break;case LEFT:next->x = SNAKE.psnakenode->x - 2;next->y = SNAKE.psnakenode->y;break;case RIGHT:next->x = SNAKE.psnakenode->x + 2;next->y = SNAKE.psnakenode->y;break;default:free(next);return; // 如果方向无效,不移动蛇}// 检查下一个位置是否有食物if (FoodJudge(next)){SNAKE.score += SNAKE.food.score; // 增加得分next->next = (*head); // 将新节点放在头部*head = next; // 更新头指针CreateFood(*head); // 生成新的食物}else{// 没有食物,移动蛇的尾部到头部Psnake* tail = SNAKE.psnakenode;Psnake* prev = NULL;if (!(tail || prev)){perror("POINTER IS NULL:");return;}// 找到尾节点和其前一个节点while (tail->next) {prev = tail;tail = tail->next;}// 将尾部节点移动到头部prev->next = NULL; // 将前一个节点的next设置为NULL,它现在是尾部set_pos(tail->x, tail->y);wprintf(L" ");tail->next = SNAKE.psnakenode; // 将原尾部节点放在头部SNAKE.psnakenode = tail; // 更新头指针// 更新头部位置SNAKE.psnakenode->x = next->x;SNAKE.psnakenode->y = next->y;// 释放之前分配的next节点,因为我们只是移动了尾部节点free(next);}IsDie();PrintSnake(SNAKE.psnakenode);
}//=========================================
//运行游戏void ClearScreen() { // 定义清屏函数,适用于Windows和Linux/macOS
#ifdef _WIN32system("cls");
#elsesystem("clear");
#endif
}void GameOver(int score) {// 游戏结束后的善后处理ClearScreen(); // 清除屏幕wprintf(L"Game Over!\n"); // 打印游戏结束信息wprintf(L"总分: %d\n", score); // 显示玩家得分// 询问玩家是否想再玩一次wprintf(L"再来一次?(y/n): ");system("pause >> nul");if (_KEY_STATE(0x59)){GameStart();fflush(stdin);}else {wprintf(L"感谢游玩\n");exit(0); // 结束程序}
}void GameRun(Psnake**head)
{do{set_pos(60, 6);wprintf(L"当前分数: %d", SNAKE.score);if ((_KEY_STATE(VK_UP) || _KEY_STATE(0x57)) && SNAKE.dir != DOWN){SNAKE.dir = UP;}else if ((_KEY_STATE(VK_DOWN) || _KEY_STATE(0x53)) && SNAKE.dir != UP){SNAKE.dir = DOWN;}else if ((_KEY_STATE(VK_LEFT) || _KEY_STATE(0x41)) && SNAKE.dir != RIGHT){SNAKE.dir = LEFT;}else if ((_KEY_STATE(VK_RIGHT) || _KEY_STATE(0x44)) && SNAKE.dir != LEFT){SNAKE.dir = RIGHT;}else if (_KEY_STATE(0x71)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) -= 30;} else if (_KEY_STATE(0x72)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) += 30;}else if (_KEY_STATE(0x50)){SNAKE.speed = 2147483647;}SnakeMove(head);Sleep(SNAKE.speed);} while (SNAKE.state == RUN);GameOver(SNAKE.score);
}//开始游戏及游戏前准备
void GameStart()
{cover();SNAKE.psnakenode = (Psnake*)malloc(sizeof(Psnake));InitSnake(&SNAKE.psnakenode);PrintSnake(SNAKE.psnakenode);CreateFood(SNAKE.psnakenode);set_pos(40, 36);GameRun(&SNAKE.psnakenode);
}
1. set_pos( short x , short y )
void set_pos(short x, short y)
{HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(hConsole, coord);
}//设置坐标
- 作用: 设置光标在控制台上的位置,以便在指定坐标输出字符。
- 思路: 使用GetStdHandle获得控制台光标句柄 ---> 修改值 ---> SetConsoleCursorPosition实现对光标的控制。
注:SetConsoleCursorPosition的使用需一个COORD结构体,将x和y改成目标值。
- 知识点: Windows API使用、句柄概念、坐标系统。
2. cursor ( bool state , short x )
//设置光标信息
void cursor(bool state, int x)
{HANDLE op = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO con;con.dwSize = x;con.bVisible = state;SetConsoleCursorInfo(op, &con);
}
- 作用: 控制光标的显示状态(可见或隐藏)及大小。
- 思路: 使用 GetStdHandle 获得光标句柄 ---> 进行值的修改 ---> 使用SetConsoleCursorInfo进行设置 。
注:SetConsoleCursorInfo需要定义 CONSOLE_CURSOR_INFO类型的结构体
- 知识点: 控制台编程、光标属性设置。
3. printInstruction( int y , const wchar_t* message )
//打印提示信息
void printInstruction(int y, const wchar_t* message) {set_pos(60, y);wprintf(message);
}
- 作用: 在指定的行上打印游戏的指令或提示信息。
- 思路: 调用
set_pos
定位光标,然后使用宽字符输出信息。 - 知识点: 宽字符输出、动态打印信息布局。
4. map( )
//打印地图
void map() {// Draw top borderset_pos(0, 0);for (int i = 0; i < 29; i++) {wprintf(L"□");}// Draw left and right bordersfor (int i = 1; i < 29; i++) {set_pos(0, i);wprintf(L"□");set_pos(28 * 2, i); //右边的坐标(28*2 ,i)wprintf(L"□");}// Draw bottom borderset_pos(0, 29);for (int i = 0; i < 29; i++) {wprintf(L"□");}const wchar_t* instructions[] = {L"操作指南: 使用方向键←↑→↓",L"速度控制: F2 加速, F3 减速",L"暂停游戏: 按 P",L"退出游戏: 按 ESC"};for (int i = 0; i < sizeof(instructions) / sizeof(instructions[0]); ++i) {printInstruction(10 + i, instructions[i]);}
}
- 作用: 绘制游戏的地图边界和游戏说明。
- 思路: 分别绘制上、下、左、右边界,并在固定位置打印游戏规则。
注:宽字符打印需要wprintf,wprintf( L " " ) ;
- 知识点: 循环结构、字符串输出、坐标计算。
5. cover ( )
//打印封面
void cover()
{system("mode con cols=120 lines=50");system("title 贪吃蛇");set_pos(40, 10);wprintf(L" \n");wprintf(L" .-----------------. .-----------------. .----------------. .----------------. .---------------- .\n");wprintf(L" | .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |\n");wprintf(L" | | _______ | || | ____ _____ | || | __ | || | ______ | || | ___ ____ | |\n");wprintf(L" | | / ___ | | || ||_ \\ |_ _| | || | / \\ | || | .' ___ | | || | |_ ||_ _| | |\n");wprintf(L" | | | (__ \\_| | || | | \\ | | | || | / /\\ \\ | || | / .' \\_| | || | | |_/ / | |\n");wprintf(L" | | '.___`-. | || | | |\\ \\| | | || | / ____ \\ | || | | | | || | | __'. | |\n");wprintf(L" | | |`\\____) | | || | _| |_\\ | |_ | || | / / \\ \\_ | || | \\ `.___.'\\ | || | _| | \\ \\_ | |\n");wprintf(L" | | |_______.' | || ||_____|\\____| | || ||____| |____|| || | `._____.' | || | |____ || __|_| |\n");wprintf(L" | | | || | | || | | || | | || | | |\n");wprintf(L" | '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |\n");wprintf(L" '----------------' '------'---------' '----------------' '------'---------' '--'-------------'\n");set_pos(45, 29);system("pause");system("cls");map();}
- 作用: 打印游戏封面。
- 思路: 使用特殊字符和格式化输出来设计一个吸引人的启动画面,随后清除屏幕并进入游戏。
- 知识点: 字符串操作、系统调用(改变窗口尺寸、标题)、多行输出。
6. void PrintFood(Food* pos)
//打印食物坐标
void PrintFood(Food* pos)
{set_pos((*pos).x, (*pos).y);wprintf(L"◆");
}
- 作用: 在指定位置打印食物图标。
- 思路: 移动光标至食物坐标,输出食物的符号。(Food 在 snake.h 会详解)
- 知识点: 结构体使用、函数传参。
7. CreateFood ( Psnake* snake )
//创造食物
void CreateFood(Psnake* snake)
{Food* pos = (Food*)malloc(sizeof(Food));if (!pos){perror("MALLOC FAILED:");return;}Psnake* cur = snake;//生成随机数srand((unsigned int)time(NULL));again:(*pos).x = (rand() % GAME_WIDTH + 2) * 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).y = rand() % GAME_HEIGHT + 2; // rand() % 27 会得到0到26之间的数,加2后变为2到28(*pos).score = 1;while (cur){if ((*pos).x == cur->x && (*pos).y == cur->y)goto again;cur = cur->next;}SNAKE.food = *pos;PrintFood(pos);
}
- 作用: 随机生成食物并确保不与蛇身重叠。
- 思路: 使用 rand 随机生成食物 x、y 坐标,食物遍历贪吃蛇每个节点的坐标,然后更新食物位置信息。
- 知识点: 随机数生成、链表遍历、内存管理(malloc)。
8. FoodJudge(Psnake* next)
//判断下一个节点是否为食物
bool FoodJudge(Psnake* next)
{return ((next->x == SNAKE.food.x) && (next->y == SNAKE.food.y)) ? true : false;
}
- 作用: 判断下一个节点位置是否为食物所在。
- 思路: 比较节点坐标与食物坐标。
- 知识点: 简单逻辑判断、结构体成员访问。
9.buynode(int x, int y)
//申请一个节点
Psnake* buynode(int x,int y)
{Psnake* newnode = (Psnake*)calloc(1, sizeof(Psnake));if (newnode == NULL){perror("calloc:");return NULL;}newnode->next = NULL;newnode->x = x;newnode->y = y;return newnode;
}
- 作用: 创建一个新的蛇节点。
- 思路: 申请内存,初始化节点数据,链接到链表。
蛇按照方向进行移动,需要对蛇头的前,左右进行申请节点
- 知识点: 动态内存分配、链表操作。
10.InitSnake(Psnake**head)
//初始化蛇的信息
Psnake* InitSnake(Psnake**head)
{if (!(head&&*head)) {fprintf(stderr, "Memory allocation failed\n");return NULL;}(*head) = (Psnake*)calloc(1, sizeof(Psnake));// 初始化成员(*head)->x = 6;(*head)->y = 10;(*head)->next = NULL;Psnake* cur = *head;if (!cur){perror("head is nullptr");return NULL;}int x = _SNAKE_LENTH_;while (x--){Psnake* newnode= buynode((cur->x)+2, cur->y);newnode->next = cur;cur = newnode;}SNAKE.dir = RIGHT;SNAKE.psnakenode = cur;SNAKE.state = RUN;SNAKE.speed = 150;return cur;
}
- 作用: 初始化蛇的结构,创建初始蛇身。
- 思路: 设置蛇头位置、按照长度申请节点,并设置蛇其他信息
- 知识点: 指针引用、循环构造链表、内存管理。
11.PrintSnake(Psnake* head)
//打印蛇
void PrintSnake(Psnake* head)
{Psnake* cur = SNAKE.psnakenode;while (cur){set_pos(cur->x, cur->y);wprintf(L"●");cur = cur->next;}
}
- 作用: 打印蛇身在屏幕上。
- 思路: 遍历链表,逐个输出蛇身位置的字符。
- 知识点: 链表遍历、光标移动打印。
12. IsDie()
void IsDie() {Psnake* cur = SNAKE.psnakenode->next; // 从第二个节点开始Psnake* prev = SNAKE.psnakenode; // 保存蛇头节点用于比较while (cur->next) {cur = cur->next;if (cur->x == prev->x && cur->y == prev->y) { // 检查当前节点是否与之前的节点重合SNAKE.state = KILL_BY_SELF;return;}}if (SNAKE.psnakenode->x == 56||SNAKE.psnakenode->x==0 ||SNAKE.psnakenode->y==0||SNAKE.psnakenode->y == 28) {SNAKE.state = KILL_BY_WALL;return;}
}
- 作用: 检查游戏是否结束(撞墙或自食)。
- 思路: 检测是否撞到自身:将蛇头坐标与每个蛇身节点坐标相比较;撞墙:蛇头与游戏边界重叠。之后将蛇的状态修改。
- 知识点: 边界条件检测、循环逻辑。
13. SnakeMove(Psnake** head)
//蛇的移动
void SnakeMove(Psnake** head)
{// 首先检查蛇头的下一个位置Psnake* next = (Psnake*)calloc(1, sizeof(Psnake));if (!next) {perror("Failed to allocate memory for next snake node");exit(EXIT_FAILURE);}// 根据当前方向设置蛇头的下一个位置switch (SNAKE.dir){case UP:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y - 1;break;case DOWN:next->x = SNAKE.psnakenode->x;next->y = SNAKE.psnakenode->y + 1;break;case LEFT:next->x = SNAKE.psnakenode->x - 2;next->y = SNAKE.psnakenode->y;break;case RIGHT:next->x = SNAKE.psnakenode->x + 2;next->y = SNAKE.psnakenode->y;break;default:free(next);return; // 如果方向无效,不移动蛇}// 检查下一个位置是否有食物if (FoodJudge(next)){SNAKE.score += SNAKE.food.score; // 增加得分next->next = (*head); // 将新节点放在头部*head = next; // 更新头指针CreateFood(*head); // 生成新的食物}else{// 没有食物,移动蛇的尾部到头部Psnake* tail = SNAKE.psnakenode;Psnake* prev = NULL;if (!(tail || prev)){perror("POINTER IS NULL:");return;}// 找到尾节点和其前一个节点while (tail->next) {prev = tail;tail = tail->next;}// 将尾部节点移动到头部prev->next = NULL; // 将前一个节点的next设置为NULL,它现在是尾部set_pos(tail->x, tail->y);wprintf(L" ");tail->next = SNAKE.psnakenode; // 将原尾部节点放在头部SNAKE.psnakenode = tail; // 更新头指针// 更新头部位置SNAKE.psnakenode->x = next->x;SNAKE.psnakenode->y = next->y;// 释放之前分配的next节点,因为我们只是移动了尾部节点free(next);}IsDie();PrintSnake(SNAKE.psnakenode);
}
- 作用: 处理蛇的移动逻辑,包括食物检测和身体跟随。
- 思路:申请三个节点(蛇头前左右) 用switch对蛇的方向进行判断(先让蛇头走一步),判断该方向下一节点是否为食物,将蛇身剩下的节点逐个移动一个节点。
移动分两部分,蛇头移动和蛇身移动,先让蛇头走一步,蛇身每个节点进行移动
- 知识点: 方向判断、链表头节点更新、内存释放。
14.ClearScreen( )
void ClearScreen() { // 定义清屏函数,适用于Windows和Linux/macOS
#ifdef _WIN32system("cls");
#elsesystem("clear");
#endif
}
- 作用: 清除屏幕内容。
- 思路: 根据操作系统调用不同的清屏命令。
#ifdef 与 #endif 要成对存在
- 知识点: 条件编译、系统调用。
15.GameOver( int score )
void GameOver(int score) {// 游戏结束后的善后处理ClearScreen(); // 清除屏幕wprintf(L"Game Over!\n"); // 打印游戏结束信息wprintf(L"总分: %d\n", score); // 显示玩家得分// 询问玩家是否想再玩一次wprintf(L"再来一次?(y/n): ");system("pause >> nul");if (_KEY_STATE(0x59)){GameStart();fflush(stdin);}else {wprintf(L"感谢游玩\n");exit(0); // 结束程序}
}
- 作用: 游戏结束时显示分数,并询问是否重新开始。
- 思路: 显示最终得分,根据用户输入决定是否重启游戏。
system("pause>>nul"); 与 system("pause"); 效果类似,后者会打印按任意键继续字样,前者不会
- 知识点: 用户输入读取、流程控制。
16. GameRun(Psnake**head)
void GameRun(Psnake**head)
{do{set_pos(60, 6);wprintf(L"当前分数: %d", SNAKE.score);if ((_KEY_STATE(VK_UP) || _KEY_STATE(0x57)) && SNAKE.dir != DOWN){SNAKE.dir = UP;}else if ((_KEY_STATE(VK_DOWN) || _KEY_STATE(0x53)) && SNAKE.dir != UP){SNAKE.dir = DOWN;}else if ((_KEY_STATE(VK_LEFT) || _KEY_STATE(0x41)) && SNAKE.dir != RIGHT){SNAKE.dir = LEFT;}else if ((_KEY_STATE(VK_RIGHT) || _KEY_STATE(0x44)) && SNAKE.dir != LEFT){SNAKE.dir = RIGHT;}else if (_KEY_STATE(0x71)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) -= 30;} else if (_KEY_STATE(0x72)&&SNAKE.speed>=0&&SNAKE.speed<=500){(SNAKE.speed) += 30;}else if (_KEY_STATE(0x50)){SNAKE.speed = 2147483647;}SnakeMove(head);Sleep(SNAKE.speed);} while (SNAKE.state == RUN);GameOver(SNAKE.score);
}
- 作用: 游戏主循环,处理用户输入和游戏逻辑。
- 思路: 此处do while效果要比while效果要好,通过检测检测按键状态来确定蛇的移动方向。
虚拟键码(Virtual Key Codes)是在计算机键盘输入中使用的一套编码标准,用于标识键盘上的每一个按键,无论键盘的实际物理布局如何。
- 知识点: 主循环控制、事件响应、时间延迟。
17. GameStart ()
//开始游戏及游戏前准备
void GameStart()
{cover();SNAKE.psnakenode = (Psnake*)malloc(sizeof(Psnake));InitSnake(&SNAKE.psnakenode);PrintSnake(SNAKE.psnakenode);CreateFood(SNAKE.psnakenode);set_pos(40, 36);GameRun(&SNAKE.psnakenode);
}
- 作用: 游戏启动入口,包含初始化、封面展示和游戏开始。
- 思路: 先展示封面,初始化游戏环境,然后进入游戏主循环。
将蛇“创建”出来,作为游戏运行的主体
- 知识点: 函数调用顺序、程序入口点。
Review
整理一下思路:
-
需求分析
- 明确游戏目标:控制蛇吃食物变长,避免碰撞自身或边界。
- 确定基本功能:初始化游戏、绘制游戏界面、处理用户输入、蛇的移动与增长、食物的随机生成、碰撞检测、得分计算、游戏结束与重开机制。
-
技术选型与环境搭建
- 准备vs2022
-
核心模块设计
- 游戏界面:设计简洁直观的游戏界面,包括游戏区域、分数显示、游戏说明等。
- 蛇的实现:
- 数据结构:链表或数组来存储蛇身的各个部分。
- 移动逻辑:根据用户输入改变蛇头方向,自动延伸蛇身。
- 食物管理:随机位置生成食物,检测蛇头碰撞并重新生成。
- 碰撞检测:检查蛇头是否触碰边界或自身身体。
- 得分系统:每次吃到食物增加分数,记录最高分。
-
用户交互
- 接收用户输入(键盘/触摸),转换为游戏内操作指令。
- 提供开始新游戏、退出游戏等选项。
-
性能与优化
- 优化游戏循环,确保流畅运行。
- 考虑内存管理,避免内存泄漏。
在这个数字编织的宇宙🌌中,你已悄然绘制了一条属于自己的璀璨轨迹。
每一次思考与实践,都在为你的技能之树浇灌成长的甘露🌱。愿你带着这份宝贵的经历,继续遨游于代码的星辰大海,发现更多的奇遇与美好。
加油,程序员之旅的探险者们,前路广阔,星辰大海,我们共勉!🚀✨
相关文章:
C语言实战:贪吃蛇(万字详解)
💡目录 效果图 界面设计思路 1. 基本布局 2. 视觉元素 游戏机制设计 基本规则 游戏代码 前期准备 游戏代码详解 数据结构设计 宏定义 数据结构定义 函数原型(详见后文) 主函数代码 核心代码 Review 效果图 界面设计思路 1. 基…...
定时器更新界面,线程报错
项目场景: 在javafx框架下使用线程更新UI的时候,出现无法正常更新UI。 问题代码如下: package clock;import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Timer; import java.util.TimerTask;import javaf…...
未来AI大模型的发展趋势
大家好,我是小悟 未来AI大模型的发展趋势无疑将是多元化、高效化、普及化以及人性化。随着技术的飞速进步,AI大模型将在各个领域中展现出更加广泛和深入的应用,成为推动社会进步的重要力量。 多元化是AI大模型发展的重要方向。随着数据量的…...
【JavaScript函数详解】Day04
JavaScript函数详解 JavaScript 基础 - 第4天笔记函数声明和调用声明(定义)调用 参数形参和实参参数默认值 返回值函数补充细节作用域全局作用域局部作用域变量的访问原则 匿名函数函数表达式立即执行函数 逻辑中断小知识(转换为Boolean型&am…...
json和axion结合
目录 java中使用JSON对象 在pom.xml中导入依赖 使用 public static String toJSONString(Object object)把自定义对象变成JSON对象 json和axios综合案例 使用的过滤器 前端代码 响应和请求都是普通字符串 和 请求时普通字符串,响应是json字符串 响应的数据是…...
v1.2.70-FastJson的AutoType机制研究
v1.2.70-FastJson的AutoType机制研究 最近在对接Alexa亚马逊语音技能,Smart Home Skill Apis时,有一个配置的JSON字符串是这样的: { "capabilityResources": {"friendlyNames": [{"type": "asset",…...
老旧机子装linux——Xubuntu
目录 前言 正文 下载系统 编辑 制作系统盘: 安装界面 Xubuntu 编辑 lubuntu 后语 前言 有两台电脑,一台装了Ubuntu22,一台装了debuntu。虽然debuntu界面与乌班图大体一样,但是编译器好像有点区别。由于机子为10年前的老…...
关于Redis中事务
事务的四个特性 Redis到底有没有原子性 Redis中的原子性不同于MySQL,相比于MySQL,Redis中的原子性几乎不值一提。 MySQL中的原子性,不仅仅是“要么全都执行,要么全都不执行”,它还保证了“一旦执行,结果…...
【数据分享】《中国文化文物与旅游统计年鉴》2022
最近老有同学过来询问《中国旅游年鉴》、《中国文化文物统计年鉴》、《中国文化和旅游统计年鉴》、《中国文化文物与旅游统计年鉴》,这四本年年鉴的关系以及怎么获取这四本年鉴。今天就在这里给大家分享一下这四本年鉴的具体情况。 实际上2018年,为适应…...
设计模式及其在软件开发中的应用
一、技术难点 设计模式在软件开发中扮演着至关重要的角色,但它们的应用也伴随着一系列技术难点。 模式选择与识别:在实际项目中,正确识别和选择合适的设计模式是一个挑战。不同的设计模式适用于不同的场景,错误的选择可能导致系统…...
LeetCode72编辑距离
题目描述 解析 一般这种给出两个字符串的动态规划问题都是维护一个二维数组,尺寸和这两个字符串的长度相等,用二维做完了后可以尝试优化空间。这一题其实挺类似1143这题的,只不过相比1143的一种方式,变成了三种方式,就…...
竞拍商城系统源码后端PHP+前端UNIAPP
下载地址:竞拍商城系统源码后端PHP前端UNIAPP...
千益畅行,共享旅游卡,灵活同行,畅游无忧的全方位解析
千益畅行,共享旅游卡,满足您多样化的同行出行需求 近期,关于千益畅行共享旅游卡的咨询热度不减,尤其是关于其同行人数的限制问题。为了给大家一个清晰的解答,我们深入探讨了该旅游卡的特点和优势。 千益畅行共享旅游…...
Web IDE 在线编辑器综合实践(Web IDE 技术探索 三)
前言 前面两篇文章,我们简单讲述了 WebContainer/api 、Terminal 的基本使用,离完备的在线代码编辑器就差一个代码编辑了。今天通过 monaco editor ,来实现初级代码编辑功能,讲述的是整个应用的搭建,并不单独针对monac…...
Less is more VS 精一 [生活感悟]
"Less is More”和王阳明的“精一”思想确实有相似之处。 王阳明的“精一”思想强调的是专注于一件事,将其做到极致,这与"Less is More”中提倡的通过减少数量来提高质量的理念不谋而合。两者都强调了专注和深度的重要性,而不是追…...
函数的概念及图像
注: 判断两函数是否相同,只看定义域和对应法则。 1. 函数的定义 一般的,在一个变化过程中有两个变量 x,y。如果对于x在某个变化范围内的每一个确定值,按照某个对应法则,都有唯一确定的值y和他对应。那么y就…...
Linux中Apache网站基于Http服务的访问限制(基于地址/用户)
🏡作者主页:点击! 👨💻Linux高级管理专栏:点击! ⏰️创作时间:2024年6月3日11点44分 🀄️文章质量:95分 为了更好地控制对网站资源的访问,可…...
滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南
滚动条是用户界面中的图形化组件,用于指示和控制内容区域的可滚动范围。当元素内容超出其视窗边界时,滚动条提供可视化线索,并允许用户通过鼠标滚轮、触屏滑动或直接拖动滑块来浏览未显示部分,实现内容的上下或左右滚动。它在保持…...
06 Linux 设备驱动模型
1、Overview Linux-2.6 引入的新的设备管理机制 - kobject 降低设备多样性带来的 Linux 驱动开发的复杂度,以及设备热拔插处理、电源管理等将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口驱动的开发,就简化为对内核所规定的数据结构的填充和实现驱动模型是 Linu…...
检测五个数是否一样的算法
目录 算法算法的输出与打印效果输出输入1输入2 打印打印1打印2 算法的流程图总结 算法 int main() {int arr[5] { 0 };int i 0;int ia 0;for (i 0; i < 5; i) { scanf("%d", &arr[i]); }for (i 1; i < 5; i) {if (arr[0] ! arr[i]) {ia 1;break;} }…...
java 原生http服务器 测试JS前端ajax访问实现跨域传post数据
后端 java eclipse 字节流转字符 package Httpv3;import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;import java.io.IOException; import java.i…...
【机器学习】消息传递神经网络(MPNN)在分子预测领域的医学应用
1. 引言 1.1. 分子性质预测概述 分子性质预测是计算机辅助药物发现流程中至关重要的任务之一,它在许多下游应用如药物筛选和药物设计中发挥着核心作用: 1.1.1. 目的与重要性: 分子性质预测旨在通过分子内部信息(如原子坐标、原…...
Python Flask实现蓝图Blueprint配置和模块渲染
Python基础学习: Pyhton 语法基础Python 变量Python控制流Python 函数与类Python Exception处理Python 文件操作Python 日期与时间Python Socket的使用Python 模块Python 魔法方法与属性 Flask基础学习: Python中如何选择Web开发框架?Pyth…...
Vue10-事件修饰符
一、示例:<a>标签不执行默认的跳转行为 1-1、方式一 <a href"http://www.baidu.com" onclick"event.preventDefault();">点击我</a> 1-2、方式二 1-3、方式三:事件修饰符 二、Vue的六种事件修饰符 2-1、prevent&…...
oracle中如何查询特定日期?
1. select last_day(to_date(20230101,YYYYMMDD)) from dual; select last_day(to_date(V_END_DATE,YYYYMMDD)) from dual; --查询任意一天 当月的最后一天 2. select to_char(to_date(20230101,YYYYMMDD)-1,YYYYMMDD) INTO V_START_DATE FROM DUAL; select to_char(to_dat…...
Python使用rosbag使用getattr只能获取一层的数据,不能直接获取多层数据例如 a.b.c.d。使用for range写一个递归用来获取多层数据
使用for循环和range来遍历属性列表确实是一个更简单直观的方式,特别是不需要考虑性能优化和异常处理时。以下是使用for循环代替递归的示例代码: python def get_nested_attr(obj, attr_str): attrs attr_str.split(.) for attr in attrs: # 尝试获取下…...
LNWT--篇章三小测
问题1: BERT训练时候的学习率learning rate如何设置? 在训练初期使用较小的学习率(从 0 开始),在一定步数(比如 1000 步)内逐渐提高到正常大小(比如上面的 2e-5),避免模型过早进入…...
【NoSQL】Redis练习
1、redis的编译安装 systemctl stop firewalld systemctl disable firewalld setenforce 0 yum install -y gcc gcc-c make wget cd /opt wget https://download.redis.io/releases/redis-5.0.7.tar.gz tar zxvf redis-5.0.7.tar.gz -C /opt/cd /opt/redis-5.0.7/ # 编译 make…...
Git 和 Github 的使用
补充内容:EasyHPC - Git入门教程【笔记】 文章目录 常用命令配置信息分支管理管理仓库 概念理解SSH 密钥HTTPS 和 SSH 的区别在本地生成 SSH key在 Github 上添加 SSH key 使用的例子同步本地仓库的修改到远程仓库拉取远程仓库的修改到本地仓库拉取远程仓库的分支并…...
学习分享-断路器Hystrix与Sentinel的区别
断路器(Circuit Breaker)简介 断路器(Circuit Breaker)是一种用于保护分布式系统的服务稳定性和容错性的设计模式。它的主要作用是在检测到某个服务的调用出现故障(如超时、异常等)时,快速失败…...
如何做转发文章赚钱的网站/seo北京网站推广
这里是修真院前端小课堂,每篇分享文从 【背景介绍】【知识剖析】【常见问题】【解决方案】【编码实战】【扩展思考】【更多讨论】【参考文献】 八个方面深度解析前端知识/技能,本篇分享的是: 【 css中content属性,有什么作用&a…...
点击最高的模板网站/搜索引擎优化主要包括
先说下我这个有啥好处 主要是能够自定义设置裁剪图片的比例.比如 :今天产品 给你说裁剪 成16:9 的图片. 你做好了 OK 明天 产品又和你说 裁剪成10:7 的图片 ,你是不是要吐血, 我这里只要设置一行代码就可以改变裁剪的比例.OK 还是老规矩 先上效果图 看看啥样子好了 不扯其他的了…...
网站制作产品优化/搜索引擎优化的基本内容
Vue.js入门知识day2添加新品牌删除品牌(根据id删除数据)方法一:方法二根据条件筛选品牌方法一方法二Vue全局过滤器过滤器的定义语法过滤器调用时候的格式定义一个 Vue全局的过滤器:所谓的全局过滤器,就是所有的vm实例都…...
做兼职网站赚钱吗/河北网站建设案例
文章目录1. 背景2. 简介3. 查询3.1 /proc/meminfo的Slab和SReclaimable项3.2 命令slabtop查看slab占用情况3.3 cache查看3.4 系统缓存回收机制的设置项3.5 /proc/slabinfo文件信息3.6 统计Slab占用超过100M的对象slabtop1. 背景 Linux内存管理模式,页式管理适合于大…...
杭州网站建设设计制作/关键词百度指数查询
被tkj大爷艹爆了5555整套模拟赛都是神仙思路题 那么这题题解 还有一个神仙做法,zory巨神在考场上找规律AC,自己都不会证。。我证明了一下(然而这货还是不认可自己的做法) 按照分割点的思路,我们for循环一次,每次找到比当前点小且最…...
重庆商城网站制作报价/网络营销的用户创造价值
目录 docker 安装ElasticSEarch亲测步骤 1:docker search ElasticSearch 2: docker pull elasticsearch:5.1.1 3:下载好后,查看镜像:docker images 4:创建实例启动: 5:查看服务:docker ps 6:…...