【C语言】贪吃蛇项目(2)- 实现代码详解

文章目录

  • 前言
  • 一、游戏开始界面设计
    • 首先 - 打印环境界面
    • 其次 - 游戏地图、蛇身及食物的设计
      • 1、地图
      • 2、蛇身设置及打印
      • 3、食物
  • 二、游戏运行环节
    • 蛇的上下左右移动等功能
    • 蛇的移动
  • 三、结束游戏
  • 代码


前言

在笔者的前一篇博客中详细记载了贪吃蛇项目所需的一些必备知识以及我们进行贪吃蛇项目的整体大致思路,有需要的朋友可以自行看下 https://blog.csdn.net/2301_77954967/article/details/137881771?spm=1001.2014.3001.5501

此外,需要提醒的是,如果你也想要自己写出这样的贪吃蛇程序,你最好进行区块分类,在头文件中将所有方法文件中用到的功能先写出来。

对于等等写着写着忘记原来这个方法干嘛用的,可以点击你想要找的内容,再按 F12 就可以回溯到这个方法的最开始的编写部分了

一、游戏开始界面设计

作为一款雄安游戏我们需要有一个简单的进入界面设计,以及一些简单的游戏规则介绍,并在此之后初始化游戏的主体,包括食物,边框以及蛇身和它所需要的一系列参数。

我们一步一步来

首先 - 打印环境界面

当然为了整体性美观,我们需要将控制台的光标给隐藏起来,并且给这个控制台命名初始化边框,这就需要用到上一篇博客中所介绍的一系列控制台函数
代码如下:

	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);//设置控制台光标状态

紧接着初始化操作,我们就需要在控制台界面上打印一些欢迎游戏的字样,对此,我们需要用到上一篇博客中用到的定位光标的控制台方法,然后再打印,这里再把定位光标的方法展现一下

void SetPos(int x, int y)
{//获得标准输出的设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x,y };SetConsoleCursorPosition(houtput, pos);
}

定位好想要打印的欢迎游戏的位置后,考研选择用宽字符的方法打印,也可以就直接用printf输出,至于要打印什么字就看个人需要了,还需要注意的一点是,我们可以使用**getchar()来暂停这个程序,更好的判断当前部分代码的正确性。
此外我们还可以利用
system(“pause”)**来暂停所运行代码,实现界面与用户交互后发生变化的能力
代码如下

//欢迎界面
void WelcomeToGame()
{SetPos(40, 14);wprintf(L"欢迎来到贪吃蛇小游戏\n");SetPos(42, 20);system("pause");system("cls");//清理屏幕SetPos(28, 14);wprintf(L"用↑.↓.←.→来控制蛇的移动,按F3加速,F4减速\n");SetPos(28, 15);wprintf(L"加速能够得到更高的分数\n");SetPos(42, 20);system("pause");system("cls");//清理屏幕
}
//打印帮助信息
void PrintfHelpInfo()
{SetPos(64, 13);wprintf(L"%ls", L"游戏规则:");SetPos(64, 14);wprintf(L"%ls", L"不能穿墙,不能咬到自己");SetPos(64, 15);wprintf(L"%ls", L"用↑.↓.←.→来控制蛇的移动");SetPos(64, 16);wprintf(L"%ls", L"按F3加速,F4减速");SetPos(64, 17);wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");SetPos(64, 19);wprintf(L"%ls", L"JoknKi制作");
}

运行后的第一个界面
在这里插入图片描述
点击后发生改变
在这里插入图片描述

其次 - 游戏地图、蛇身及食物的设计

既然要进入游戏了,然基本的游戏界面必须搭建好

1、地图

我们先说说这个地图,我们需要知道游戏地图他的本质上还是控制台界面,可以理解为是由无数个高宽2:1的长方形组成的,左上角为原点,向右x轴增,向下y轴增,因此我们通过输出宽字符来创建游戏的边宽,需要注意,每次x加2,y加1,才能实现一个方方正正的边框
在这里插入图片描述
代码如下

void CreatMap()
{//上for (int i = 0; i < 29; i++){wprintf(L"%c", L'□');}//下SetPos(0, 26);for (int i = 0; i < 29; i++){wprintf(L"%c", L'□');}//左for (int i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%c", L'□');}//右for (int i = 1; i <= 25; i++){SetPos(56, i);wprintf(L"%c", L'□');}}

这里提一嘴,关于宽字符 ‘□’ 我们可以通过定义宏的方式让它不用反复输入,导致出现失误,同时也可以是边框的灵活性更高,以后想换边框时,更改宏定义里的宽字符就行了

2、蛇身设置及打印

蛇身的整体和各个节点在上一篇文章中已经建好了,这里利用了单链表的知识,我们同样利用宏设定初始点的坐标,然后再利用next成员名不断设置下一节点,之后遍历打印就OK了

当然仅此如此还不够,我们还必须将蛇的整体上的各个部分都设置一下,包括当前状态、食物坐标等,为其封装一个完善的结构,方便后续进行使用和判断。

当然这里的各个数据绝大多数都是先规划好运行要求等,再写进之前创建的蛇整体结构体里,当然后续如果有需要也可以额外补充进来,不要忘了就是了

代码如下

// 4.初始化蛇
void InitSnake(pSnake ps)
{int i = 0;pSnakeNode cur = NULL;for (int i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc()");return;}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", BODY);cur = cur->next;}//设置贪吃蛇的属性ps->_dir = RIGHT;//默认向右ps->_score = 0;ps->_food_weight = 10;//每个食物二点分数ps->_sleep_time = 200;//单位是毫秒ps->_status = OK;//正常状态}

3、食物

食物作为贪吃蛇游戏的核心进行,必须具有随机性与趣味性,因此必须用到随机数,也就是利用C语言的 #include<time.h> 及其相关用法

因此我们不仅要包含头文件还需要,在开头定义 srand 即

	srand((unsigned int)time(NULL));

这样一来就设定好了

然后我们需要保证当前的食物不能与蛇的身体覆盖,这就需要遍历就行了,也还好,不难,这里介绍一下 gotoagain 可以在小范围内实现很便捷的循环功能
最后打印,代码如下

// 5.创建食物
void CreatFood(pSnake ps)
{int x = 0;int y = 0;
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;
}

二、游戏运行环节

作为一个游戏,除了主要的游戏区域,我们还需要提示和记录的部分,像上面的欢迎信息一样打印出来就行了,打印什么内容也是根据个人喜欢所定

蛇的上下左右移动等功能

就像前面欢迎界面所介绍的那样我们除了要让蛇上下左右移动外,我们还需要加速、减速、暂停、退出等,因此我们可以通过 case 来实现。

		//打印总分数和食物的分值SetPos(64, 10);printf("总分数:%2d", ps->_score);SetPos(64, 11);printf("当前食物的分数是:%2d", 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(VK_F3))//加速{if (ps->_sleep_time > 80){ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(VK_F4))//减速{if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}//贪吃蛇走一步

蛇的移动

输入好状态后,首当其冲的就是蛇的移动了

蛇的移动作为重中之重,我们不仅要考虑如何使他位移,嗨哟啊考虑位移后的状态

  • 有食物
  • 撞墙
  • 撞自己
  • 仅移动

首先我们先利用接待你是设进行最基本的唯一,就是说不管最后这么状态,先移动了再说,动完在判断

但是这里的移动不是完全移动,仅仅头节点的移动,也就是说我们创建一个新的接待你,用它根据当前运动趋势确定坐标,至于整个蛇的打印就需要根据具体情况分类讨论了

因此我们可以得出以下代码

	//创建一个节点,表示蛇即将到的下一个节点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;}

紧接着我们需要判断它这个点有没有食物,有没有撞墙,有没有撞到自己
因此我们需要写出下面三个方法

//检测下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{return(ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}
//撞墙
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;return 1;}return 0;
}
//撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;while (cur){if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y){return 1;}cur = cur->next;}return 0;
}

再然后就是写遇到食物和没遇到食物的情况了

关于吃到食物,笔者认为,我们只需要打印当前这个食物的节点为蛇的身体,而后面的内容无需覆盖直接放着就行了,如果你觉得不放心,可以利用遍历的方法照常打印出来就行

//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{//头插法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", BODY);//	cur = cur->next;//}SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);ps->_score += ps->_food_weight;//重新创建食物CreatFood(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", BODY);cur = cur->next;}SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;KillBySelf(ps);//撞自己KillByWall(ps);//撞墙}

这里程序运行的方法都讲清楚了,但是为了使程序真正运行起来,我们必须对上面这一整块内容进行while语句循环,判断条件就是当前状态是否正常即

//游戏运行逻辑
void GameRun(pSnake ps)
{//打印帮助信息PrintfHelpInfo();do{//打印总分数和食物的分值SetPos(64, 10);printf("总分数:%2d", ps->_score);SetPos(64, 11);printf("当前食物的分数是:%2d", 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(VK_F3))//加速{if (ps->_sleep_time > 80){ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(VK_F4))//减速{if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}//贪吃蛇走一步SnakeMove(ps);Sleep(ps->_sleep_time);} while (ps->_status == OK);
}

这里简单展现一下到目前为止游戏的界面
在这里插入图片描述

三、结束游戏

还记得我们之前定义的额状态吗

//蛇的状态
//正常、撞墙、撞到自己、正常退出
enum GAME_STATUS
{OK,//正常状态KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END_NORMAL//正常退出
};

这里我们就需要根据当前的状态输出不同的结果,所以就可以得到下面这个部分(记得释放空间)

//游戏结束
void GameEnd(pSnake ps)
{pSnakeNode cur = ps->_pSnake;SetPos(24, 12);switch (ps->_status){case END_NORMAL: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);}
}

但是为了优化程序,使游戏能够循环运行,也就是询问玩家要不要再玩一遍,我们可以这样做,对整个区域块进行 do while 循环

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');} while (ch == 'Y' || ch == 'y');SetPos(0, 27);
}

代码

好了,到此为止所有的内容都讲完了,有兴趣的朋友们自己拿去玩玩吧
我把我的码云链接放在下面了
链接: https://gitee.com/JohnKingW/target-xiamen-university/tree/master/snake/snake
在这里插入图片描述

snake.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)
#include<stdio.h>
#include<locale.h>
#include<windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include<time.h>#define POS_X 24
#define POS_Y 5#define BODY L'●'
#define	WALL L'□'
#define FOOD L'◆'#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)//通过虚拟键盘来判断某个按键是否被使用//类型的声明
//蛇的方向
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(int x, int y);//游戏初始化
void GameStart(pSnake ps);//打印介绍
void WelcomeToGame();//创建地图
void CreatMap();// 4.初始化蛇
void InitSnake(pSnake ps);// 5.创建食物
void CreatFood(pSnake ps);//游戏运行
void GameRun(pSnake ps);//游戏结束
void GameEnd(pSnake ps);//蛇走一步
void SnakeMove(pSnake ps);//检测下一个坐标是否是食物
int NextIsFood(pSnakeNode pn,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);

snake.c

#include "snake.h"void SetPos(int x, int y)
{//获得标准输出的设备的句柄HANDLE houtput = NULL;houtput = GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos = { x,y };SetConsoleCursorPosition(houtput, pos);
}//欢迎界面
void WelcomeToGame()
{SetPos(40, 14);wprintf(L"欢迎来到贪吃蛇小游戏\n");SetPos(42, 20);system("pause");system("cls");//清理屏幕SetPos(28, 14);wprintf(L"用↑.↓.←.→来控制蛇的移动,按F3加速,F4减速\n");SetPos(28, 15);wprintf(L"加速能够得到更高的分数\n");SetPos(42, 20);system("pause");system("cls");//清理屏幕
}//绘制地图
void CreatMap()
{//上for (int i = 0; i < 29; i++){wprintf(L"%c", L'□');}//下SetPos(0, 26);for (int i = 0; i < 29; i++){wprintf(L"%c", L'□');}//左for (int i = 1; i <= 25; i++){SetPos(0, i);wprintf(L"%c", L'□');}//右for (int i = 1; i <= 25; i++){SetPos(56, i);wprintf(L"%c", L'□');}}// 4.初始化蛇
void InitSnake(pSnake ps)
{int i = 0;pSnakeNode cur = NULL;for (int i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc()");return;}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", BODY);cur = cur->next;}//设置贪吃蛇的属性ps->_dir = RIGHT;//默认向右ps->_score = 0;ps->_food_weight = 10;//每个食物二点分数ps->_sleep_time = 200;//单位是毫秒ps->_status = OK;//正常状态}// 5.创建食物
void CreatFood(pSnake ps)
{int x = 0;int y = 0;
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;
}//初始化游戏
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.打印环境界面// 2.功能介绍WelcomeToGame();// 3.绘制地图CreatMap();// 4.初始化蛇InitSnake(ps);// 5.创建食物CreatFood(ps);// 6.设置游戏的相关信息}//打印帮助信息
void PrintfHelpInfo()
{SetPos(64, 13);wprintf(L"%ls", L"游戏规则:");SetPos(64, 14);wprintf(L"%ls", L"不能穿墙,不能咬到自己");SetPos(64, 15);wprintf(L"%ls", L"用↑.↓.←.→来控制蛇的移动");SetPos(64, 16);wprintf(L"%ls", L"按F3加速,F4减速");SetPos(64, 17);wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");SetPos(64, 19);wprintf(L"%ls", L"JoknKi制作");
}//暂停 -> 睡眠
void pause()
{while (1){if (KEY_PRESS(VK_SPACE)){break;}}
}//检测下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{return(ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{//头插法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", BODY);//	cur = cur->next;//}SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);ps->_score += ps->_food_weight;//重新创建食物CreatFood(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", BODY);cur = cur->next;}SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;KillBySelf(ps);KillByWall(ps);}//撞墙
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;return 1;}return 0;
}//撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;while (cur){if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y){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);}}//游戏运行逻辑
void GameRun(pSnake ps)
{//打印帮助信息PrintfHelpInfo();do{//打印总分数和食物的分值SetPos(64, 10);printf("总分数:%2d", ps->_score);SetPos(64, 11);printf("当前食物的分数是:%2d", 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(VK_F3))//加速{if (ps->_sleep_time > 80){ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(VK_F4))//减速{if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}//贪吃蛇走一步SnakeMove(ps);Sleep(ps->_sleep_time);} while (ps->_status == OK);
}//游戏结束
void GameEnd(pSnake ps)
{pSnakeNode cur = ps->_pSnake;SetPos(24, 12);switch (ps->_status){case END_NORMAL: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"//完成的是游戏的测试逻辑
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');} while (ch == 'Y' || ch == 'y');SetPos(0, 27);
}int main()
{//设置适配本地环境setlocale(LC_ALL, "");srand((unsigned int)time(NULL));test();return 0;
}

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

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

相关文章

MySQL面试题 3

问题1&#xff1a;char、varchar的区别是什么&#xff1f; varchar是变长而char的长度是固定的。如果你的内容是固定大小的&#xff0c;你会得到更好的性能。 问题2: TRUNCATE和DELETE的区别是什么&#xff1f; DELETE命令从一个表中删除某一行&#xff0c;或多行&#xff0…

上位机图像处理和嵌入式模块部署(树莓派4b实现动态插件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 和上位机一样&#xff0c;我们的智能硬件如果想应用到更多的场景&#xff0c;那么势必需要实现更多的算法。这些算法和算法之间最好是松散耦合的插…

存储过程的使用(二)

目录 带 OUT 参数的存储过程 输入一个编号&#xff0c;查询数据表 emp中是否有这个编号&#xff0c;如果有返回对应员工姓名&#xff0c;如果没有&#xff0c;则提示没有对应员工 使用 EXEC 命令或者 PRINT执行含有 OUT参数的存储过程 使用 PL/SQL 块编辑程序调用含有 OUT …

智慧公厕是如何诞生的?

在城市化进程中&#xff0c;公共卫生设施的建设一直是重要议题之一。而随着科技的不断发展&#xff0c;智慧公厕作为一种创新的解决方案&#xff0c;逐渐成为了现代城市管理的亮点。那么&#xff0c;智慧公厕是如何产生的呢&#xff1f; 一、城市化进程的推动 城市人口的增加和…

排序 “壹” 之插入排序

目录 ​编辑 一、排序的概念 1、排序&#xff1a; 2、稳定性&#xff1a; 3、内部排序&#xff1a; 4、外部排序&#xff1a; 二、排序的运用 三、插入排序算法实现 3.1 基本思想 3.2 直接插入排序 3.2.1 排序过程&#xff1a; 3.2.2 代码示例&#xff1a; 3.2.3…

基于通达信---做T专用算法

什么是做T? 股票做T是股票市场中常见的一种投资策略,也就是股票进行T+0操作,通过当天买进的股票,在当天卖出,是股市中常见的一种超短线的操作。其中T就是指交易日,利用交易日中的股票涨跌来赚取差价。股票做T常见的类型就是正T和倒T。 1、正T 股票做正t就是指先买后卖,…

【Java框架】Spring框架(一)——Spring基本核心(IOC/DI)

目录 Java企业级框架企业级系统EJB概念解析EJB与Spring的恩怨情仇 Spring系统架构1. Data Access/Integration&#xff08;数据访问&#xff0f;集成&#xff09;2. Web 模块3. Core Container&#xff08;Spring 的核心容器&#xff09;4. AOP、Aspects、Instrumentation 和 M…

javaWeb项目-智能仓储系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JSP技术 JSP(Jav…

一文讲透彻Redis 持久化

文章目录 ⛄1.RDB持久化&#x1fa82;&#x1fa82;1.1.执行时机&#x1fa82;&#x1fa82;1.2.RDB原理&#x1fa82;&#x1fa82;1.3.小结 ⛄2.AOF持久化&#x1fa82;&#x1fa82;2.1.AOF原理&#x1fa82;&#x1fa82;2.2.AOF配置&#x1fa82;&#x1fa82;2.3.AOF文件…

浅谈 刷算法题时遇到运行超时异常 的解决办法

文章目录 一、背景介绍二、解决办法2.1 C/C 语言2.2 Java 语言2.2.1 ACM模式下 Java的I/O原理 三、模板详情 一、背景介绍 最近在牛客、leetcode 刷算法题时发现一个奇怪的问题&#xff0c;明明解题思路、所用算法与题解一致&#xff0c;并且在本地IDE运行是通过的&#xff0c…

【C语言__结构体__复习篇5】

目录 前言 一、结构体基础知识 1.1 结构体的语法形式 1.2 创建结构体变量 1.3 结构体变量的初始化 1.4 点(.)操作符和箭头(->)操作符 二、匿名结构体 三、结构体自引用 四、结构体内存对齐 4.1 内存对齐的规则 4.2 出现结构体内存对齐的原因 4.3 修改默认对齐数 五、结…

Flutter 热修复(Shorebird)

Shorebird&#xff1a;https://docs.shorebird.dev/ 我们都知道安卓原生开发&#xff0c;热修复已经不是什么难题。阿里云&#xff0c;腾讯云已经都有现成的SDK可以接入。 然而Flutter开发还一直没有类似热修复的开发库&#xff0c;无意中看到了Shorebird这个平台&#xff0c…

数据结构 -- 二叉树二叉搜索树

二叉树 二叉树是这么一种树状结构&#xff1a;每个节点最多有两个孩子&#xff0c;左孩子和右孩子 重要的二叉树结构 完全二叉树&#xff08;complete binary tree&#xff09;是一种二叉树结构&#xff0c;除最后一层以外&#xff0c;每一层都必须填满&#xff0c;填充时要遵…

C++ 协程 学习笔记

协程的优势就是比线程切换的时间少很多&#xff0c;协程的切换时间是纳秒&#xff0c;而进行切换的时间是微秒 单线程用协程可以轻松的处理并发任务 co_yield和co_await可以将协程暂停下来 resume又把协程激活 如果c函数里有co_await、co_return、co_yield就会自动判定为协程…

【实测】记一次用windows11家庭中文版部署django+vue项目的踩坑之旅

整个过程下来&#xff0c;给我的感觉就和吃了翔一样难受&#xff0c;窒息的感觉&#xff0c;全程没少骂win11的设计者... 因为要调试一些只有在windows才会出现的测试平台bug&#xff0c;于是我耗重金购入了一台顶配windows笔记本。以下是配置&#xff1a; 配置还蛮高的对吧&am…

链表经典算法OJ题目

1.单链表相关经典算OJ题目1&#xff1a;移除链表元素 思路一 直接在原链表里删除val元素&#xff0c;然后让val前一个结点和后一个节点连接起来。 这时我们就需要3个指针来遍历链表&#xff1a; pcur —— 判断节点的val值是否于给定删除的val值相等 prev ——保存pcur的前…

Spring Boot后端与Vue前端融合:构建高效旅游管理系统

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

【网络编程】TCP流套接字编程(TCP实现回显服务器)

一.TCP流套字节相关API. Socket(既能给客户端使用,也能给服务器使用) 构造方法 基本方法: ServerSocket(只能给服务器使用) 构造方法: 基本方法: 二.TCP实现回显服务器. 客户端代码示例: package Demo2;import java.io.IOException; import java.io.InputStream; import j…

一个char类型数字转字符串后引起的惨案

问题现象 嵌入式开发平台&#xff0c;交叉编译链比较老&#xff0c;还不支持 C11 的 to_string 写法&#xff0c;此时想通过模板方式&#xff0c;构造一个通用的 toString 接口&#xff0c;采用了 ostringstream &#xff0c;就出现了问题。 模板接口如下 template <type…

2 逻辑斯蒂回归(分类)

目录 1 理论 逻辑回归假设数据服从伯努利分布&#xff08;二分类&#xff09;,通过极大化似然函数的方法&#xff0c;运用梯度下降来求解参数&#xff0c;来达到将数据二分类的目的。 逻辑斯蒂回归&#xff08;Logistic Regression&#xff09;是一种用于解决分类问题的…