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

贪吃蛇解析

目录

文章结尾有代码可自取

Win32API

光标的隐藏

获取按键信息

控制光标位置

游戏开始前的准备

游戏准备及介绍

加载和欢迎界面

打印游戏指南

运行游戏

打印墙体和说明

设置蛇的各个信息

初始化及打印蛇

创造食物

运行游戏

1)打印得分情况

2)获取按键信息

3)蛇走的下一步

下一步不是食物

下一步是食物

4)检查是否撞到墙或自己

游戏结束

各个文件代码统计

头文件

源文件

源文件


文章结尾有源码可自取

Win32API

此处首先对Win32API进行简单的介绍,在游戏中光标的隐藏,获得用户按键信息的都会用到;

以下操作要包含头文件<windows.h> 

光标的隐藏

//隐藏光标
void Hide_cursor(void)
{//获得输出控制台句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//获得控制台光标信息CONSOLE_CURSOR_INFO cursor_info;GetConsoleCursorInfo(houtput, &cursor_info);//将光标信息中的可见度改为0cursor_info.bVisible = 0;SetConsoleCursorInfo(houtput, &cursor_info);
}

获取按键信息

可以读取用户按下了哪一个键。

//获取按键信息
#define KEY_PRESS(vk) (GetAsyncKeyState(vk)?1:0)
//vk表示按键
//如:Key_Press(VK_UP)如果用户按下↓,则返回1,否则是0

控制光标位置

在有一些情况不希望将光标放在控制台(黑框)的开始默认位置,我们可能需要在控制台中央打印,此时就需要对控制台光标位置进行定位。

//定位光标位置
void SetPos(size_t x, size_t y)
{//获得输出控制台句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);// 获取光标COORD cursorPos = { 0, 0 };//修改光标位置cursorPos.X = x;cursorPos.Y = y;SetConsoleCursorPosition(houtput,cursorPos);
}

关于Win32API的知识不过多讲解,对于初学者可以直接使用上述代码实现功能。有兴趣可以看windows的控制台函数。控制台函数 - Windows Console | Microsoft Learnhttps://learn.microsoft.com/zh-cn/windows/console/console-functions


游戏开始前的准备

游戏进行时我们有时候想要打印图案字符,eg:■◯Ő以及一些汉字(宽字符)。我们就需要将程序修改为本地状态,可以理解为不仅仅能够打印外国的英文,还能够打印我们本地中文汉字。

为了有更好的游戏体验,需要对控制台的大小和名称进行修改

#include<locale.h>
void Prepare(void)
{//先将程序改成本地模式setlocale(LC_ALL, "");//先对控制台的大小进行修改system("mode con cols=150 lines=40");//对名称进行重命名system("title 贪吃蛇");
}

游戏准备及介绍

隐藏完光标后,我们进行游戏开始之前的打印,范围两部分,打印加载和欢迎页面;打印游戏指南,方法。所以在游戏开始运行之前有两个页面需要打印。

加载和欢迎界面

先打印第一个封面,欢迎和加载界面。此处将代码封装成立三个函数:打印进度条函数;打印欢迎界面函数。效果如下。

进度条的实现主要是依靠一个数组,通过循环向数组里添加字符,打印数组从而动态的效果。

//加载界面的打印
void ProcessBar(void)
{wchar_t bar[102] = { '\0' }; //设置一个字符串来当作进度条int cnt = 0;while (cnt <= 100){SetPos(25, 21);  //定位wprintf(GREEN L"[%-101ls]" RESET"[%d%%]", bar, cnt); //打印进度条,并加上颜色Sleep(100);      //休眠100毫秒bar[cnt] = L'█';cnt++;}
}void Print_Page1(void)
{//定位光标,打印欢迎语句SetPos(65, 18);wprintf(YELLOW L"欢迎进入贪吃蛇游戏" RESET);//打印加载的进度条ProcessBar();
}

可以看到,在上买了的代码中添加了控制输出字符串的颜色,定义如下可自取。

//定义一些颜色
#define RESET       L"\033[0m"           // 恢复默认// 前景色 (文本颜色)
#define BLACK        L"\033[30m"
#define RED          L"\033[31m"
#define GREEN        L"\033[32m"
#define YELLOW       L"\033[33m"
#define BLUE         L"\033[34m"
#define MAGENTA      L"\033[35m"
#define CYAN         L"\033[36m"
#define WHITE        L"\033[37m"

打印游戏指南

为了让用户有更好的游戏体验,需要对玩法进行说明。第二张画面打印游戏指南。效果如下。

void Print_Page2(void)
{system("cls");//清屏SetPos(58, 17);wprintf(MAGENTA L"用↑↓←→来控制方向,ESC退出,SPACE暂停");SetPos(58, 18);wprintf(L"F1加速,F2减速;");SetPos(58, 19);wprintf(L"加速可以获得更多积分,减速获得积分变慢;" RESET);SetPos(58, 21);system("pause");
}

将page1和page2结合。

void game_prepare(void)
{//隐藏光标Hide_cursor();//设置控制台名称和大小Prepare();//打印第一个画面,进入画面Print_Page1();//打印游戏指南Print_Page2();
}

运行游戏

打印墙体和说明

墙体的长和宽都可以自定义设置。效果如下。

打印墙体的时候要注意临界位置的讨论。

//打印墙体
void Print_Wall(void)
{SetPos(0, 0);//打印横向for (int i = 0; i < LEN; i += 2)  //注意此处是+2,因为宽字符的宽度是2不像普通字符一样wprintf(L"%c", WALL);SetPos(0, WIDTH);for (int i = 0; i < LEN; i += 2)  wprintf(L"%c", WALL);//打印纵向for (int i = 1; i < WIDTH; i++){SetPos(0, i);wprintf(L"%c", WALL);SetPos(LEN-2, i);wprintf(L"%c", WALL);}
}//打印说明
void Print_RULE(void)
{SetPos(100, 8);wprintf(GREEN L"↑↓←→来控制方向");SetPos(100, 9);wprintf(L"ESC退出,SPACE暂停");SetPos(100, 10);wprintf(L"F1加速,F2减速");SetPos(100, 11);wprintf(L"加速可以获得更多积分,减速获得积分变慢" RESET);
}

设置蛇的各个信息

在打印蛇之前,需要考虑设置什么样的信息。此处首先就是蛇身的各个节点,还需要蛇的方向,状态,蛇的得分,以及食物信息,所以要创建多个结构体:蛇的各个信息,蛇的每个节点,关于食物的信息。

通过枚举将蛇的方向,状态一一列举出来。

//设置方向
typedef enum DIR
{RIGHT,LEFT,UP,DOWN
}DIR;
//设置蛇的状态
typedef enum STATE
{FINE,            //正常DEAD_BYWALL,     //撞墙DEAD_BYBODY,     //撞到蛇身QUIT             //退出
}STATE;
//设置蛇节点
typedef struct Snackbody
{size_t _x;size_t _y;struct Snackbody* _next;
}Snackbody;
//食物信息
typedef struct Food
{size_t x;size_t y;size_t _each_score;
}Food;
//设置结构体来存储蛇的各个信息
typedef struct Snack
{Snackbody* _head;size_t _speed;size_t _score;DIR dir;STATE state;Food* pf;
}Snack;

初始化及打印蛇

初始化打印蛇,主要分为两步:创建蛇身的各个节点并将其链接;初始化蛇的状态,得分等信息。

//创造节点
Snackbody* MakeBody()
{Snackbody* newbody = (Snackbody*)malloc(sizeof(Snackbody));newbody->_next = NULL;newbody->_x = 0;newbody->_y = 0;return newbody;
}//对蛇进行初始化
void Init_Snack(Snack* sn)
{//对蛇的各个数据进行初始化sn->_head = NULL;sn->dir = RIGHT;sn->state = FINE;sn->_score = 0;sn->_speed = 200;//创建蛇身Snackbody** snhead = &(sn->_head);  //注意此处需要是二级指针,因为要对其地址进行修改Snackbody* tail = *snhead;for (int i = 0; i < 5; i++){Snackbody* newbody = MakeBody();newbody->_x = 20 - i * 2;newbody->_y = 15;if ((*snhead) == NULL){*snhead = newbody;tail = newbody;}else{(tail)->_next = newbody;tail = (tail)->_next;}}//打印Snackbody* cur = sn->_head;while (cur != NULL){SetPos(cur->_x, cur->_y);wprintf(L"%c", SNACK);cur = cur->_next;}
}

创造食物

此处通过伪随机数,时间戳来确定食物的位置。

注意:创造的食物必须在墙内部,并且不能创作到蛇身上。所以在创建完食物后,要对食物的位置进行检查。

#define FOOD L'豆'
//检查食物
bool CHECK_food(Snack* sn)
{size_t x = sn->pf->x;size_t y = sn->pf->y;Snackbody* cur = sn->_head;while (cur != NULL){if (cur->_x == x && cur->_y == y)return false;cur = cur->_next;}return true;
}//创造食物
void Make_food(Snack* sn)
{sn->pf = (Food*)malloc(sizeof(Food));sn->pf->_each_score = 10;srand((unsigned int)time(NULL));
again:sn->pf->x = rand()%65+2;   //食物x范围是2-66sn->pf->y = rand()%21+1;   //y的范围是1-21//检查食物是否符合要求//x必须是偶数,食物不能出现在蛇身上if (sn->pf->x % 2 != 0 || !CHECK_food(sn)){goto again;}SetPos(sn->pf->x, sn->pf->y);wprintf(L"%c", FOOD);
}

运行游戏

游戏是要持续进行的,所以肯定要设置循环,此处采用do...while循环,在蛇没有死之前,游戏都要能正常运行,所以循环的条件就是蛇的状态是不是FINE。

在循环中,主要有4个部分需要处理:打印侧栏得分;获取按键信息;打印蛇的下一步;检查状态。具体实现目的如下。

1)打印得分情况

游戏运行过程中要保证实时打印总得分及当前每个食物的得分。如下图所示

SetPos(100, 6);
wprintf(L"总得分:%d", sn->_score);
SetPos(100, 7);
wprintf(L"当前食物分数:%2d", sn->pf->_each_score);

2)获取按键信息

关于按键信息如何获取,前面Win32API已经写了,此处直接使用其宏定义,获取完按键信息后要对蛇的方向进行修改。

对于不同的按键要有不同的响应:上下左右直接改变方向即可;F1需要添加分数,缩短睡眠时间;F2相反;Esc则需要改变蛇的状态,让循环停止;Space需要暂停游戏,这里使用一个死循环来模拟暂停。

	//暂停void SPACE(){while (1){if (KEY_PRESS(VK_SPACE))break;}}//检查按键if (KEY_PRESS(VK_UP) && sn->dir != DOWN)sn->dir = UP;if (KEY_PRESS(VK_DOWN) && sn->dir != UP)sn->dir = DOWN;if (KEY_PRESS(VK_LEFT) && sn->dir != RIGHT)sn->dir = LEFT;if (KEY_PRESS(VK_RIGHT) && sn->dir != LEFT)sn->dir = RIGHT;if (KEY_PRESS(VK_F1)){if (sn->_speed > 40){sn->pf->_each_score += 2;sn->_speed -= 40;}}if (KEY_PRESS(VK_F2)){if (sn->pf->_each_score > 2){sn->pf->_each_score -= 2;sn->_speed += 40;}}if (KEY_PRESS(VK_SPACE))SPACE();if (KEY_PRESS(VK_ESCAPE)){sn->state = QUIT;break;}Sleep(sn->_speed);   //休眠,准备打印下一张图片

3)蛇走的下一步

可以通过蛇的方向来预测蛇的下一个位置,将下一个位置打印成蛇即可实现移动。此处需要考虑下一个位置是不是食物。两种情况是不同的,不是食物的话,就需要将尾部变为空,将下一个位置打印成蛇;是食物的话,就不需要对尾部进行处理,但是需要重新创建食物。

//打印下一张图片
void Print_NEXT(Snack* sn)
{//创建下一个位置的节点Snackbody* pnext = (Snackbody*)malloc(sizeof(Snackbody));switch(sn->dir){case UP:pnext->_x = sn->_head->_x;pnext->_y = sn->_head->_y - 1;break;case DOWN:pnext->_x = sn->_head->_x;pnext->_y = sn->_head->_y + 1;break;case LEFT:pnext->_x = sn->_head->_x - 2;pnext->_y = sn->_head->_y;break;case RIGHT:pnext->_x = sn->_head->_x + 2;pnext->_y = sn->_head->_y;break;}//下一个位置是食物if (pnext->_x == sn->pf->x && pnext->_y == sn->pf->y){IS_FOOD(sn,pnext);}elseNO_FOOD(sn,pnext);
}
下一步不是食物

不是食物的话,就需要将尾部变为空,将下一个位置打印成蛇;

//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext)
{//将下一个节点当成蛇头pnext->_next = sn->_head;sn->_head = pnext;//找到最后一个节点并释放Snackbody* cur = sn->_head;while (cur->_next->_next != NULL)cur = cur->_next;//将蛇尾从原本的蛇改为空SetPos(cur->_next->_x, cur->_next->_y);wprintf(L"  ");free(cur->_next);cur->_next = NULL;//将蛇的下一个位置打印成蛇SetPos(pnext->_x, pnext->_y);wprintf(L"%c", SNACK);
}
下一步是食物
//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext)
{//将食物直接当成蛇头pnext->_next = sn->_head;sn->_head = pnext;SetPos(pnext->_x, pnext->_y);wprintf(L"%c", SNACK);//加分sn->_score += sn->pf->_each_score;//创建食物Make_food(sn);
}

4)检查是否撞到墙或自己

此处直接通过坐标进行检查即可。

//检查是不是墙
void IF_WALL(Snack* sn)
{if (sn->_head->_x >= LEN - 2 || sn->_head->_x == 0)sn->state = DEAD_BYWALL;if (sn->_head->_y == WIDTH || sn->_head->_y == 0)sn->state = DEAD_BYWALL;
}//检查是否撞到自己
void IF_SELF(Snack* sn)
{int x = sn->_head->_x;int y = sn->_head->_y;Snackbody* cur = sn->_head->_next;while (cur != NULL){if (cur->_x == x && cur->_y == y)sn->state = DEAD_BYBODY;cur = cur->_next;}
}

游戏结束

游戏可以正常运行了,但是还要对游戏结束进行处理,要将游戏结束的信息反馈给用户,还要对游戏运行过程中动态开辟的空间进行销毁。

//游戏结束
void Game_over(Snack* sn)
{system("cls");SetPos(65, 18);switch (sn->state){case DEAD_BYWALL:wprintf(MAGENTA L"你撞到墙了!!!");break;case DEAD_BYBODY:wprintf(L"你撞到自己了!!!");break; case QUIT:wprintf(L"已退出!!!" RESET);break;}SetPos(0, 35);//释放蛇身各个节点,释放动态开辟的空间Snackbody* cur = sn->_head;while (cur != NULL){Snackbody* next = cur->_next;free(cur);cur = next;}free(sn->pf);
}

各个文件代码统计

<Blog_Snack.h>头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>//定义一些颜色
#define RESET       L"\033[0m"           // 恢复默认// 前景色 (文本颜色)
#define BLACK        L"\033[30m"
#define RED          L"\033[31m"
#define GREEN        L"\033[32m"
#define YELLOW       L"\033[33m"
#define BLUE         L"\033[34m"
#define MAGENTA      L"\033[35m"
#define CYAN         L"\033[36m"
#define WHITE        L"\033[37m"#define WALL L'墙'  //也可以用█,看自己。但是一定要是宽字符
#define SNACK L'蛇'  //可以定义成其他宽字符
#define FOOD L'豆'#define LEN 70
#define WIDTH 22//获取按键信息
#define KEY_PRESS(vk) (GetAsyncKeyState(vk)?1:0)
//vk表示按键
//如:Key_Press(VK_UP)如果用户按下↓,则返回1,否则是0//设置方向
typedef enum DIR
{RIGHT,LEFT,UP,DOWN
}DIR;
//设置蛇的状态
typedef enum STATE
{FINE,            //正常DEAD_BYWALL,     //撞墙DEAD_BYBODY,     //撞到蛇身QUIT             //退出
}STATE;
//设置蛇节点
typedef struct Snackbody
{size_t _x;size_t _y;struct Snackbody* _next;
}Snackbody;
//食物信息
typedef struct Food
{size_t x;size_t y;size_t _each_score;
}Food;
//设置结构体来存储蛇的各个信息
typedef struct Snack
{Snackbody* _head;size_t _speed;size_t _score;DIR dir;STATE state;Food* pf;
}Snack;//设置控制台名称和大小
void Prepare(void);
//打印游戏指南
void Print_Page2(void);//隐藏光标
void Hide_cursor(void);//定位光标位置
void SetPos(size_t x, size_t y);//开始前的准备
void game_prepare(void);//第一个界面
void Print_Page1(void);//加载界面的打印
void ProcessBar(void);//运行游戏
void game_start(void);//打印墙体
void Print_Wall(void);//打印说明
void Print_RULE(void);//打印蛇
void Print_Snack(Snack* sn);//对蛇进行初始化
void Init_Snack(Snack* sn);//创造节点
Snackbody* MakeBody();//创造食物
void Make_food(Snack* sn);//检查食物
bool CHECK_food(Snack* sn);//游戏运行
void start(Snack* sn);//打印下一张图片
void Print_NEXT(Snack* sn);//暂停
void SPACE();//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext);//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext);//检查是不是墙
void IF_WALL(Snack* sn);//检查是否撞到自己
void IF_SELF(Snack* sn);//游戏结束
void Game_over(Snack* sn);

<Snack.c>源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Blog_Snack.h"//隐藏光标
void Hide_cursor(void)
{//获得输出控制台句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//获得控制台光标信息CONSOLE_CURSOR_INFO cursor_info;GetConsoleCursorInfo(houtput, &cursor_info);//将光标信息中的可见度改为0cursor_info.bVisible = 0;SetConsoleCursorInfo(houtput, &cursor_info);
}//定位光标位置
void SetPos(size_t x, size_t y)
{//获得输出控制台句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);// 获取光标COORD cursorPos = { 0, 0 };//修改光标位置cursorPos.X = x;cursorPos.Y = y;SetConsoleCursorPosition(houtput,cursorPos);
}#include<locale.h>
void Prepare(void)
{//先将程序改成本地模式setlocale(LC_ALL, "");//先对控制台的大小进行修改system("mode con cols=150 lines=40");//对名称进行重命名system("title 贪吃蛇");
}//加载界面的打印
void ProcessBar(void)
{wchar_t bar[102] = { '\0' }; //设置一个字符串来当作进度条int cnt = 0;while (cnt <= 100){SetPos(25, 21);  //定位wprintf(GREEN L"[%-101ls]" RESET"[%d%%]", bar, cnt); //打印进度条,并加上颜色Sleep(1);      //休眠100毫秒bar[cnt] = L'█';cnt++;}
}void Print_Page1(void)
{//定位光标,打印欢迎语句SetPos(65, 18);wprintf(YELLOW L"欢迎进入贪吃蛇游戏" RESET);//打印加载的进度条ProcessBar();
}void Print_Page2(void)
{system("cls");//清屏SetPos(58, 17);wprintf(MAGENTA L"用↑↓←→来控制方向,ESC退出,SPACE暂停;");SetPos(58, 18);wprintf(L"F1加速,F2减速;");SetPos(58, 19);wprintf(L"加速可以获得更多积分,减速获得积分变慢;" RESET);SetPos(58, 21);system("pause");
}//打印墙体
void Print_Wall(void)
{SetPos(0, 0);//打印横向for (int i = 0; i < LEN; i += 2)  //注意此处是+2,因为宽字符的宽度是2不像普通字符一样wprintf(L"%c", WALL);SetPos(0, WIDTH);for (int i = 0; i < LEN; i += 2)  wprintf(L"%c", WALL);//打印纵向for (int i = 1; i < WIDTH; i++){SetPos(0, i);wprintf(L"%c", WALL);SetPos(LEN-2, i);wprintf(L"%c", WALL);}
}//打印说明
void Print_RULE(void)
{SetPos(100, 8);wprintf(GREEN L"↑↓←→来控制方向");SetPos(100, 9);wprintf(L"ESC退出,SPACE暂停");SetPos(100, 10);wprintf(L"F1加速,F2减速");SetPos(100, 11);wprintf(L"加速可以获得更多积分,减速获得积分变慢" RESET);
}//创造节点
Snackbody* MakeBody()
{Snackbody* newbody = (Snackbody*)malloc(sizeof(Snackbody));newbody->_next = NULL;newbody->_x = 0;newbody->_y = 0;return newbody;
}//对蛇进行初始化
void Init_Snack(Snack* sn)
{//对蛇的各个数据进行初始化sn->_head = NULL;sn->dir = RIGHT;sn->state = FINE;sn->_score = 0;sn->_speed = 200;//创建蛇身Snackbody** snhead = &(sn->_head);  //注意此处需要是二级指针,因为要对其地址进行修改Snackbody* tail = *snhead;for (int i = 0; i < 5; i++){Snackbody* newbody = MakeBody();newbody->_x = 20 - i * 2;newbody->_y = 15;if ((*snhead) == NULL){*snhead = newbody;tail = newbody;}else{(tail)->_next = newbody;tail = (tail)->_next;}}//打印Snackbody* cur = sn->_head;while (cur != NULL){SetPos(cur->_x, cur->_y);wprintf(L"%c", SNACK);cur = cur->_next;}
}//检查食物
bool CHECK_food(Snack* sn)
{size_t x = sn->pf->x;size_t y = sn->pf->y;Snackbody* cur = sn->_head;while (cur != NULL){if (cur->_x == x && cur->_y == y)return false;cur = cur->_next;}return true;
}//创造食物
void Make_food(Snack* sn)
{sn->pf = (Food*)malloc(sizeof(Food));sn->pf->_each_score = 10;srand((unsigned int)time(NULL));
again:sn->pf->x = rand()%65+2;   //食物x范围是2-66sn->pf->y = rand()%21+1;   //y的范围是1-21//检查食物是否符合要求//x必须是偶数,食物不能出现在蛇身上if (sn->pf->x % 2 != 0 || !CHECK_food(sn)){goto again;}SetPos(sn->pf->x, sn->pf->y);wprintf(L"%c", FOOD);
}//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext)
{//将食物直接当成蛇头pnext->_next = sn->_head;sn->_head = pnext;SetPos(pnext->_x, pnext->_y);wprintf(L"%c", SNACK);//加分sn->_score += sn->pf->_each_score;//创建食物Make_food(sn);
}//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext)
{//将下一个节点当成蛇头pnext->_next = sn->_head;sn->_head = pnext;//找到最后一个节点并释放Snackbody* cur = sn->_head;while (cur->_next->_next != NULL)cur = cur->_next;//将蛇尾从原本的蛇改为空SetPos(cur->_next->_x, cur->_next->_y);wprintf(L"  ");free(cur->_next);cur->_next = NULL;//将蛇的下一个位置打印成蛇SetPos(pnext->_x, pnext->_y);wprintf(L"%c", SNACK);
}//打印下一张图片
void Print_NEXT(Snack* sn)
{//创建下一个位置的节点Snackbody* pnext = (Snackbody*)malloc(sizeof(Snackbody));switch(sn->dir){case UP:pnext->_x = sn->_head->_x;pnext->_y = sn->_head->_y - 1;break;case DOWN:pnext->_x = sn->_head->_x;pnext->_y = sn->_head->_y + 1;break;case LEFT:pnext->_x = sn->_head->_x - 2;pnext->_y = sn->_head->_y;break;case RIGHT:pnext->_x = sn->_head->_x + 2;pnext->_y = sn->_head->_y;break;}//下一个位置是食物if (pnext->_x == sn->pf->x && pnext->_y == sn->pf->y){IS_FOOD(sn,pnext);}elseNO_FOOD(sn,pnext);
}//暂停
void SPACE()
{while (1){if (KEY_PRESS(VK_SPACE))break;}
}//检查是不是墙
void IF_WALL(Snack* sn)
{if (sn->_head->_x >= LEN - 2 || sn->_head->_x == 0)sn->state = DEAD_BYWALL;if (sn->_head->_y == WIDTH || sn->_head->_y == 0)sn->state = DEAD_BYWALL;
}//检查是否撞到自己
void IF_SELF(Snack* sn)
{int x = sn->_head->_x;int y = sn->_head->_y;Snackbody* cur = sn->_head->_next;while (cur != NULL){if (cur->_x == x && cur->_y == y)sn->state = DEAD_BYBODY;cur = cur->_next;}
}//游戏结束
void Game_over(Snack* sn)
{system("cls");SetPos(65, 18);switch (sn->state){case DEAD_BYWALL:wprintf(MAGENTA L"你撞到墙了!!!");break;case DEAD_BYBODY:wprintf(L"你撞到自己了!!!");break; case QUIT:wprintf(L"已退出!!!" RESET);break;}SetPos(0, 35);//释放蛇身各个节点,释放动态开辟的空间Snackbody* cur = sn->_head;while (cur != NULL){Snackbody* next = cur->_next;free(cur);cur = next;}free(sn->pf);
}//游戏运行
void start(Snack* sn)
{do {SetPos(100, 6);wprintf(L"总得分:%d", sn->_score);SetPos(100, 7);wprintf(L"当前食物分数:%2d", sn->pf->_each_score);//检查按键if (KEY_PRESS(VK_UP) && sn->dir != DOWN)sn->dir = UP;if (KEY_PRESS(VK_DOWN) && sn->dir != UP)sn->dir = DOWN;if (KEY_PRESS(VK_LEFT) && sn->dir != RIGHT)sn->dir = LEFT;if (KEY_PRESS(VK_RIGHT) && sn->dir != LEFT)sn->dir = RIGHT;if (KEY_PRESS(VK_F1)){if (sn->_speed > 40){sn->pf->_each_score += 2;sn->_speed -= 40;}}if (KEY_PRESS(VK_F2)){if (sn->pf->_each_score > 2){sn->pf->_each_score -= 2;sn->_speed += 40;}}if (KEY_PRESS(VK_SPACE))SPACE();if (KEY_PRESS(VK_ESCAPE)){sn->state = QUIT;break;}Sleep(sn->_speed);   //休眠,准备打印下一张图片Print_NEXT(sn);IF_WALL(sn);IF_SELF(sn);} while (sn->state == FINE);//游戏结束Game_over(sn);}

<Snack_main.c>源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Blog_Snack.h"void game_prepare(void)
{//隐藏光标Hide_cursor();//设置控制台名称和大小Prepare();//打印第一个画面,进入画面Print_Page1();//打印游戏指南Print_Page2();
}//运行游戏
void game_start(void)
{system("cls");//打印墙体Print_Wall();//打印说明Print_RULE();Snack sn;//对蛇进行初始化Init_Snack(&sn);//创造食物Make_food(&sn);//游戏运行start(&sn);
}int main()
{game_prepare();game_start();return 0;
}

相关文章:

贪吃蛇解析

目录 文章结尾有代码可自取 Win32API 光标的隐藏 获取按键信息 控制光标位置 游戏开始前的准备 游戏准备及介绍 加载和欢迎界面 打印游戏指南 运行游戏 打印墙体和说明 设置蛇的各个信息 初始化及打印蛇 创造食物 运行游戏 1&#xff09;打印得分情况 2&#…...

vue非组件的初学笔记

1.创建Vue实例&#xff0c;初始化渲染的核心 准备容器引包创建Vue实例new Vue() el用来指定控制的盒子data提供数据 2.插值表达式 作用利用表达式插值&#xff0c;将数据渲染到页面中 格式{{表达式}} 注意点 表达式的数据要在data中存在表达式是可计算结果的语句插值表达式…...

LeetCode 热题 100_单词搜索(60_79_中等_C++)(深度优先搜索(回溯))(初始化二维vector的大小)

LeetCode 热题 100_单词搜索&#xff08;60_79&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;深度优先搜索&#xff08;回溯&#xff09;&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&am…...

js闭包,跨域

js闭包&#xff0c;跨域 闭包 想象一下&#xff0c;你家有个大仓库&#xff08;函数&#xff09;&#xff0c;仓库里放着各种东西&#xff08;变量&#xff09;。一般情况下&#xff0c;你从仓库外面是看不到也拿不到仓库里的东西的。但是&#xff0c;闭包就像是你在仓库里留…...

算法练习(力扣-BFS)——102. 二叉树的层序遍历

题目描述&#xff08;简要概括&#xff09; 题目链接&#xff1a;102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 题目要求对给定的二叉树进行层序遍历&#xff08;从上到下&#xff0c;从左到右&#xff09;&#xff0c;并返回遍历的结果。层序遍历是一种基…...

Jetson Agx Orin平台preferred_stride调试记录--1924x720图像异常

1.问题描述 硬件: AGX Orin 在Jetpack 5.0.1和Jetpack 5.0.2上测试验证 图像分辨率在1920x720和1024x1920下图像采集正常 但是当采集图像分辨率为1924x720视频时,图像输出异常 像素格式:yuv_uyvy16 gstreamer命令如下 gst-launch-1.0 v4l2src device=/dev/video0 ! …...

nlp|微调大语言模型初探索(2),训练自己的聊天机器人

前言 上篇文章记录了具体的微调语言大模型步骤&#xff0c;以及在微调过程中可能遇见的各种报错&#xff0c;美中不足的是只是基于开源数据集的微调&#xff0c;今天来记录一下怎么基于自己的数据集去微调大语言模型&#xff0c;训练自己的智能机器人&#xff01;&#xff01;&…...

win11安装wsl报错:无法解析服务器的名称或地址(启用wsl2)

1. 启用wsl报错如下 # 查看可安装的 wsl --install wsl --list --online此原因是因为没有开启DNS的原因&#xff0c;所以需要我们手动开启DNS。 2. 按照如下配置即可 Google的DNS&#xff08;8.8.8.8和8.8.4.4) 全国通用DNS地址 (114.114.114.114) 3. 运行以下命令来重启 WSL…...

Gentleman:优雅的Go语言HTTP客户端工具包

gentlemen介绍&#xff0c;特点等 插件驱动架构&#xff1a;Gentleman的核心特点是其插件系统&#xff0c;允许用户注册和重用各种自定义插件&#xff0c;如重试策略或动态服务器发现&#xff0c;以增强HTTP客户端的功能。 中间件层&#xff1a;项目内置了一个上下文感知的层次…...

解锁豆瓣高清海报(三)从深度爬虫到URL构造,实现极速下载

脚本地址: 项目地址: Gazer PosterBandit_v2.py 前瞻 之前的 PosterBandit.py 是按照深度爬虫的思路一步步进入海报界面来爬取, 是个值得学习的思路, 但缺点是它爬取慢, 仍然容易碰到豆瓣的 418 错误, 本文也会指出彻底解决旧版 418 错误的方法并提高爬取速度. 现在我将介绍…...

IDEA单元测试插件 SquareTest 延长试用期权限

SquareTest是一款强大的IDEA单元测试生成插件工具&#xff0c;具体使用方法就不过多介绍了&#xff0c;这里主要介绍变更试用期&#xff0c;方便大家使用 配置信息 我的电脑安装前提配置条件 IntelliJ IDEA 2023.2windows 系统 软件安装 IntelliJ IDEA 直接安装插件Squar…...

PLC的五个学习步骤

五个学习步骤详解&#xff1a; 1. 夯实电气基础 (第一步) 核心思想: PLC控制技术是建立在传统电气控制技术之上的&#xff0c;因此扎实的电气基础至关重要。学习内容: 电气元件原理: 深入理解继电器、接触器、按钮、三相异步电机等常用电气元件的工作原理。这是理解电气控制回…...

深度学习05 ResNet残差网络

目录 传统卷积神经网络存在的问题 如何解决 批量归一化BatchNormalization, BN 残差连接方式 ​残差结构 ResNet网络 ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出&#xff0c;斩获当年ImageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得CO…...

卷积神经网络CNN

目录 一、CNN概述 二、图像基础知识 三、卷积层 3.1 卷积的计算 3.2 Padding 3.3 Stride 3.4 多通道卷积计算 3.5 多卷积核卷积计算 3.6 特征图大小计算 3.7 Pytorch 卷积层API 四、池化层 4.1 池化计算 4.2 Stride 4.3 Padding 4.4 多通道池化计算 4.5 Pytorc…...

Android:播放Rtsp视频流的两种方式

一.SurfaceView Mediaplayer XML中添加SurfaceView: <SurfaceViewandroid:id"id/surface_view"android:layout_width"match_parent"android:layout_height"match_parent"/> Activity代码&#xff1a; package com.android.rtsp;impor…...

web信息泄露 ctfshow-web入门web1-web10

01做题思路 判断做题的思路是读取&#xff0c;写入&#xff0c;还是执行判断大概的类型&#xff0c;有登录逻辑就尝试sql注入&#xff0c;有下载逻辑就尝试文件读取&#xff0c;有源码就做源码审计 02信息泄露及利用 robots.txt 以ctfshow的web1为例&#xff0c;访问robots…...

Log4j在Spring项目中的应用与实践

在现代Java开发中&#xff0c;日志记录是不可或缺的一部分。它不仅帮助开发者调试和监控应用程序的运行状态&#xff0c;还能在出现问题时快速定位原因。今天&#xff0c;我们就来探讨如何在Spring项目中使用Log4j进行日志管理&#xff0c;并通过具体的实例来展示其强大的功能。…...

docker安装mysql:8.0

1.docker源 目前docker国内的源基本上用不了了&#xff0c;建议去淘宝找一找&#xff0c;我整了一个大概是10R一个月。 2.拉取镜像 docker pull mysql:8.0 3.启动容器 命令如下&#xff1a; docker run \-p 3306:3306 \-e MYSQL_ROOT_PASSWORD123456 \-v /home/data/mysq…...

搭建一个 Spring Boot 项目,解决jdk与springboot版本不匹配

搭建一个 Spring Boot 项目 方式一&#xff1a;使用 Spring Initializr Spring Initializr 是一个基于 Web 的工具&#xff0c;用于快速生成 Spring Boot 项目的基础结构。 访问 Spring Initializr 网站&#xff1a;https://start.spring.io/配置项目信息&#xff1a; …...

心心相系:十颗心

心心相系&#xff1a;十颗心 【1】心脏&#xff1b;人心&#xff0c;热心 heart //注&#xff1a;h-通c-或k- warmhearted a.热心的&#xff0c;热心肠的&#xff1b;亲切的a warm-hearted person 为人古道热肠 词根cardi(o)-(heart)&#xff0c;例词&#xff1a;cardiology(…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...