【C语言实现贪吃蛇】(内含源码)

news2024/11/24 7:52:26

前言:首先在实现贪吃蛇小游戏之前,我们要先了解Win32 API的有关知识

1.Win32 API

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

2.控制台程序

平常我们运用起来的黑框程序就是控制台程序

我们可以通过执行命令来改变控制台的长宽

在我们执行cmd命令之前,我们首先要先做以下事情

点开设置,然后将windows决定改为windows控制台主机,然后点击保存,我们就可以执行有关命令了,(如果控制台主机不可以,可以将其改为windows决定

下面我介绍两个控制台窗口执行命令,可以用C语言函数的system来实现。

1.改变控制台窗口大小(mode命令)

#include<stdio.h>
#include<windows.h>
int main()
{
	system("mode con cols=50 lines=20");//控制台列改为50列,行就改为20行
	return 0;
}

可以发现控制台行为20行,列为50行,但看起来却像个正方形,这是为什么呢?其实是因为控制台窗口行和列的比例并不是1:1的。

其坐标位置如下

2.改变控制台的窗口名字(title命令)

#include<stdio.h>
#include<windows.h>
int main()
{
	system("title 贪吃蛇");
	return 0;
}

3.控制台屏幕上的坐标

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

其中COORD的类型的声明为:

    typedef struct _COORD
    {
        short x;
        short y;
    };

给坐标赋值为:

COORD pos={ 10, 15 };

4.GetStdHandle

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

HANDLE GetStdHandle(DWORD nStdHandle);

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

5.GetConsoleCursorInfo

其函数原型为:

BOOL WINAPI GetConsoleCursorInfo(
  _In_  HANDLE               hConsoleOutput,
  _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo

);

HANDLE为获得的标准输出句柄
PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标

6.CONSOLE_CURSOR_INFO

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

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

其中dwSize为由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完全填充单元格但单元底部的水平线条。

这里的黑色长方型为光标。

那么其占完全填充单元格的百分之多少呢?这里我们就可以通过一个代码来演示

#include<stdio.h>
#include<windows.h>
int main()
{
	HANDLE houtput = NULL;//返回void*的指针
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定义一个光标信息的结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);
	printf("%d", cursor_info.dwSize);
	return 0;
}

可以观察到其占完全填充光标的25%。

完全填充光标:

那如果我们想要将光标信息隐藏该怎么办呢?

bVisible表示游标的可见性。如果光标可见,则此成员为true.

#include<stdio.h>
#include<windows.h>
#include<stdbool.h>
int main()
{
	HANDLE houtput = NULL;//返回void*的指针
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定义一个光标信息的结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);
	cursor_info.bVisible = false;//隐藏光标
	return 0;
}

但这样并不能隐藏光标信息,还需要借助一个函数SetConsoleCursorInfo来设置控制台屏幕缓冲区的光标大小和可见性。

7.SetConsoleCursorInfo

其函数原型为:

BOOL WINAPI SetConsoleCursorInfo (
HANDLE hConsoleOutput,
const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);
#include<stdio.h>
#include<windows.h>
#include<stdbool.h>
int main()
{
	HANDLE houtput = NULL;//返回void*的指针
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定义一个光标信息的结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);
	cursor_info.bVisible = false;//隐藏光标
	SetConsoleCursorInfo(houtput, &cursor_info);
	return 0;
}

我们就可以观察到光标被隐藏了。

8.SetConsoleCursorPosition

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

函数原型:

BOOL WINAPI SetConsoleCursorPosition (
HANDLE hConsoleOutput,
COORD pos
);

 这里我们就用Set_Pos分装一个设置光标的函数

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

9.GetAsyncKeyState

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

SHORT GetAsyncKeyState (
int vKey
);

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

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

这里为了方便判断按键是否被按过,我们可以定义一个宏,来判断GetAsynKeyState返回值最低为是否为1.

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

 参考:虚拟键码 (Winuser.h) - Win32 apps

这里虚拟键码就可以参照上面的链接。

讲完了上面的有关知识我们就可以开始实现贪吃蛇了

首先先展示贪吃蛇的大致画面

QQ202453-133347

这里为了实现游戏地图的打印,我们就需要讲解一下控制台有关知识,控制台窗口的坐标如下所示,横向的是x轴,纵向的是y轴,从上到下依次增长。

在游戏地图上,我们打印墙体使用的是宽字符:■,打印蛇使用宽字符●,打印食物使用的宽字符是★

那什么是宽字符呢?

普通字符是占一个字节的,宽字符是占两个字节的。

过去C语言并不适用于非英语国家使用,C语言最初假定字符都是单字节的。但是这些假定并不是在世界的任何地方都适用。后来为了适应C语言国际化·,C语言的标准不断加入了国际化的支持。比如:加入宽字符的类型wchar_t和宽字符的输入和输出函数。加入了<locale.h>头文件,提供了允许程序员针对特定地区调整程序行为的函数。

10.setlocale

函数原型:char*setlocale(int category,const char*locale);

setlocale函数用于修改当前地区,可以针对一个类项,也可以所有类项,如果第一个参数是LC_ALL,就是影响所有的类项。

C标准给出了第二个参数定义了两种可能取值:“C”(正常模式)和“ ”(本地模式)。

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

setlocale(LC_ALL,"C");

如果想切换到本地模式就支持宽字符(汉字)的输出:

setlocale(LC_ALL," ");

11.宽字符的打印

宽字符的字面量必须加上前缀“L”,否则C语言会把字面量当做窄字符类型处理,前缀“L”在单引号面前,表示宽字符,对应wprintf()的占位付为%lc;在双引号面前,表示宽字符串,对应占位付就为%ls.

#include <stdio.h>
#include<locale.h>
int main() {
 setlocale(LC_ALL, "");
 wchar_t ch = L'●';
 
 printf("%c%c\n", 'a', 'b');
 
 wprintf(L"%lc\n", ch);
 return 0;
}

注:这个宽字符的实现要在Windows控制台主机上实现。

这里我们就实现棋盘27行,58列的棋盘,在围绕地图画出墙

这里我们蛇身的初始长度为5,在固定的一个坐标处开始,比如我们在(24,5)处开始打印,连续5个节点。

注:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有一半出现在墙体。

关于食物,就是在墙体内随机生成一个坐标(x的坐标必须是2的倍数),坐标不能和蛇的身体重合,然后打印★。

下面我们开始实现贪吃蛇(代码内含注释)

snake.h

#define  _CRT_SECURE_NO_WARNINGS 1

#define KEY_PRESS(VK) (( GetAsyncKeyState(VK) & 0x1 )? 1 : 0)
#define POS_X 24
#define POS_Y 5
#define WALL L'■'
#define BODY L'●'
#define FOOD L'★'
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<windows.h>
#include<locale.h>
#include<time.h>
#include<conio.h>

int choice;//选择穿墙还是不穿墙

//蛇的方向
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 color(int c); //(每次置为其他颜色时都要将其再置为白色,方便设置其他颜色,(也可以根据自己需求设置))

//定义光标
void Set_Pos(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 pNextNode, pSnake ps);
//吃掉食物
void EatFood(pSnakeNode pNextNode, pSnake ps);
//不是食物
void NoFood(pSnakeNode pNextNode, pSnake ps);
//蛇是否撞墙
bool KillByWall(pSnake ps);
//蛇撞墙不会死
void WallSnakeMove(pSnake ps);
//穿墙
int NoKillByWall(pSnake ps, pSnakeNode pn);
//蛇是否撞到自己
bool KillBySelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);

snake.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"

void color(int c)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
	//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
}

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

void WelcomeToGame()
{
	Set_Pos(38, 14);
	wprintf(L"欢迎来到贪吃蛇小游戏\n");
	Set_Pos(42, 20);
	system("pause");
	system("cls");
	Set_Pos(25, 14);
	wprintf(L"用↑,↓,←,→来控制蛇的移动,按F3加速,F4减速\n");
	Set_Pos(25,15);
	wprintf(L"加速能够得到更高的分数\n");
	Set_Pos(42, 20);
	system("pause");
	system("cls");
}

void CreateMap()
{
	color(3);
	//UP
	for (int i = 1; i <= 29; i++)
	{
		wprintf(L"%lc",WALL);
	}
	//DOWN
	Set_Pos(0, 26);
	for (int i = 1; i <= 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//LEFT
	for (int i = 1; i < 26; i++)
	{
		Set_Pos(0, i);
		wprintf(L"%lc", WALL);
	}
	//RIGHT
	for (int i = 1; i < 26; i++)
	{
		Set_Pos(56, i);
		wprintf(L"%lc", WALL);
	}
	color(7);
}

void InitSnake(pSnake ps)
{ 
	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)
	{
		Set_Pos(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;
}

void CreateFood(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 && cur->y == 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;

	Set_Pos(x, y);
	color(12); //颜色设置为红色
	wprintf(L"%lc", FOOD);
	color(7); //颜色设置为白色
	ps->_pFood = pFood;
}


void GameStart(pSnake ps)
{	
	//0.设置窗口大小,光标隐藏
	ps->_pSnake = NULL;
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo = { 0 };
	GetConsoleCursorInfo(houtput, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(houtput, &CursorInfo);

	//1.打印欢迎界面
	WelcomeToGame();

	//2.绘制地图
	CreateMap();
	
	//3.创建蛇
	InitSnake(ps);

	//4.创建食物
	CreateFood(ps);
	//5.设置游戏的相关信息
}

void PrintHelpInfo()
{
	color(6);
	Set_Pos(64, 10);
	wprintf(L"%ls", L"不能穿墙,不能咬到自己\n");
	Set_Pos(64, 11);
	wprintf(L"%ls", L"用↑,↓,←,→来控制蛇的移动\n"); 
	Set_Pos(64, 12);
	wprintf(L"%ls", L"按F3加速,F4减速\n");
	Set_Pos(64, 13);
	wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏\n");

	Set_Pos(64, 15);
	wprintf(L"%ls", L"贪吃蛇小游戏");
	color(7);
}

void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
			break;
	}
}

int NextIsFood(pSnakeNode pNextNode, pSnake ps)
{
	return (ps->_pFood->x == pNextNode->x && ps->_pFood->y == pNextNode->y);
}

void EatFood(pSnakeNode pNextNode, pSnake ps)
{
	//头插
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;

	//释放旧的节点
	free(pNextNode);
	pNextNode = NULL;

	pSnakeNode cur = ps->_pSnake;
	//将头置为红色
	color(12); //颜色设置为红色
	Set_Pos(cur->x, cur->y);
	wprintf(L"%lc", BODY);
	cur = cur->next;
	color(7); //颜色设置为白色
	while (cur)
	{
		Set_Pos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_score += ps->_food_weight;
	Set_Pos(76, 8);//定位光标到分数旁边
	printf("+%2d", ps->_food_weight);
	Sleep(100);
	Set_Pos(76, 8);
	printf("   ");
	//重新创建食物
	CreateFood(ps);
}

bool 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 false;
	}
	return true;
}


bool 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;
			return false;
		}
		cur = cur->next;
	}
	return true;
}

void NoFood(pSnakeNode pNextNode, pSnake ps)
{
	//头插
	pNextNode->next = ps->_pSnake;
	ps->_pSnake = pNextNode;
	//撞墙
	if (!KillByWall(ps))
	{
		return;
	}
	//撞到自己
	if (!KillBySelf(ps))
	{
		return;
	}
	pSnakeNode cur = ps->_pSnake;
	//将头置为红色
	color(12); //颜色设置为红色
	Set_Pos(cur->x, cur->y);
	wprintf(L"%lc", BODY);
	cur = cur->next;
	color(7); //颜色设置为白色(每次置为其他颜色时都要将其再置为白色,以便下一次置色)
	while (cur->next->next != NULL)
	{
		Set_Pos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//把最后一个节点打印空格
	Set_Pos(cur->next->x, cur->next->y);
	printf("  ");//要两个空格
	//释放最后一个节点
	free(cur->next);
	//把倒数第二个节点的next置为空
	cur->next = NULL;
}

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);
}

int NoKillByWall(pSnake ps, pSnakeNode pn)
{
	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNextNode == NULL)
	{
		perror("SnakeMove()::malloc()");
		exit(1);
	}
	if (pn->x == 0)
	{
		//将头节点穿墙
		pNextNode->x = 54;
		pNextNode->y = ps->_pSnake->y;
		pNextNode->next = NULL;
		//判断下一个节点是否为食物
		if (NextIsFood(pNextNode, ps))
		{
			EatFood(pNextNode, ps);
		}
		else
		{
			NoFood(pNextNode, ps);
		}
		free(pn);
		return 0;
	}
	else if (pn->x == 56)
	{
		//将头节点穿墙
		pNextNode->x = 2;
		pNextNode->y = ps->_pSnake->y;
		pNextNode->next = NULL;
		//判断下一个节点是否为食物
		if (NextIsFood(pNextNode, ps))
		{
			EatFood(pNextNode, ps);
		}
		else
		{
			NoFood(pNextNode, ps);
		}
		free(pn);
		pn = NULL;
		return 0;
	}
	else if (pn->y == 0)
	{
		//将头节点穿墙
		pNextNode->y = 25;
		pNextNode->x = ps->_pSnake->x;
		pNextNode->next = NULL;
		//判断下一个节点是否为食物
		if (NextIsFood(pNextNode, ps))
		{
			EatFood(pNextNode, ps);
		}
		else
		{
			NoFood(pNextNode, ps);
		}
		free(pn);
		pn = NULL;
		return 0;
	}
	else if (pn->y == 26)
	{
		//将头节点穿墙
		pNextNode->y = 1;
		pNextNode->x = ps->_pSnake->x;
		pNextNode->next = NULL;
		//判断下一个节点是否为食物
		if (NextIsFood(pNextNode, ps))
		{
			EatFood(pNextNode, ps);
		}
		else
		{
			NoFood(pNextNode, ps);
		}
		free(pn); 
		pn = NULL;
		return 0;
	}
	return 1;
}

void WallSnakeMove(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 (NoKillByWall(ps, pNextNode))
	{
		if (NextIsFood(pNextNode, ps))
		{
			EatFood(pNextNode, ps);
		}
		else
		{
			NoFood(pNextNode, ps);
		}
	}
	//检测下一个坐标是否为食物
	//撞到自己
	KillBySelf(ps);
}

void GameRun(pSnake ps)
{
	PrintHelpInfo();
	do
	{
		color(6);
		//打印总分数和食物的分值
		Set_Pos(64, 8);
		printf("总分数:%d\n", ps->_score);
		Set_Pos(64, 9);
		printf("当前食物的分数:%2d\n", ps->_food_weight);
		color(7);

		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; 
			}
		}
		if (choice == '1')
		{
			WallSnakeMove(ps);//蛇可以穿墙
		}
		else
		{
			SnakeMove(ps);//蛇走一步的过程
		}
		Sleep(ps->_sleep_time);
	} while (ps->_status == OK);
}

void GameEnd(pSnake ps)
{
	Set_Pos(24, 12);
	color(6);
	switch (ps->_status)
	{
	case END_NORMAL:
		printf("你主动结束游戏\n");
		break;
	case KILL_BY_WALL:
		printf("你寄了\n");
		break;
	case KILL_BY_SELF:
		printf("一不小心撞到自己了\n");
		break;
	}
	color(7);
	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"
#include<locale.h>


void test()
{
	int ch = 0;
	do
	{
	    color(6); //颜色设置为土黄色
		system("cls");
		//创建贪吃蛇
		Snake snake = { 0 };
		//初始化游戏
		//1.打印欢迎界面
		//2.功能介绍
		//3.绘制地图
		//4.创建蛇
		//5.创建食物
		//6.设置游戏的相关信息

		Set_Pos(38, 15);
		printf("1.穿墙");
		Set_Pos(38, 16);
		printf("2.不穿墙");
		Set_Pos(38, 18);
		printf("请选择模式:>");
		choice = getchar();
		while (getchar() != '\n');
		system("cls");
		GameStart(&snake);
		//运行游戏
		GameRun(&snake);
		//结束游戏
		GameEnd(&snake);
		while (_kbhit())
		{
			// 使用 _getch() 获取按下的键,不阻塞程序
			_getch();
			// 处理按键事件,可以根据需要进行相应的操作
		}
		Set_Pos(20, 15);
		color(6);
		printf("再来一句不老铁?>(Y/N):");
		ch = getchar();
		color(7);
		while (getchar() != '\n');

	} while (ch=='Y'||ch=='y');
	Set_Pos(0, 27);
}

int main()
{
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	test();

	return 0;
}

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

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

相关文章

前端面试和一些建议

最近公司在招前端&#xff0c;我有跟着一起参与面试。我们主要负责面试的人&#xff0c;不会问那些什么闭包&#xff0c;原型链&#xff0c;他觉得那些东西在我们日常开发中用不到&#xff0c;问的基本都是一些工作中的问题。这些问题不是每次都问&#xff0c;但也就问这些了。…

基于Unity+Vue通信交互的WebGL项目实践

unity-webgl 是无法直接向vue项目进行通信的&#xff0c;需要一个中间者 jslib 文件 jslib当作中间者&#xff0c;unity与它通信&#xff0c;前端也与它通信&#xff0c;在此基础上三者之间进行了通信对接 看过很多例子&#xff1a;介绍的都不是很详细&#xff0c;不如自己写&…

(39)4.29数据结构(栈,队列和数组)栈

#include<stdlib.h> #include<stdio.h> #define MaxSize 10 #define Elemtype int 1.栈的基本概念 2.栈的基本操作 typedef struct { Elemtype data[MaxSize]; int top; }Sqstack;//初始化栈 void InitStack(Sqstack& S) { S.top -1; //初始化…

4G小车的公网直播推流

一直想做一个小车, 可以通过4G推流, 没想到现在很多云服务提供商, SRS云服务器已经可以一键搭建了. 硬件方面, 就是一个1126驮着一个3516, 1126负责4G连接, 转流到Intenet, 3516负责vi_venc_rtsp 思路如下, 我的1126的摄像头一直没能横过来, 所以就不用1126的摄像头了, 先用35…

Redis-概述-安装-基本知识

Redis概述 Redis是什么 Redis&#xff08;Remote Dictionary Server 远程字段服务&#xff09;是一个开源的使用ANSI C语言编写、支持网 络、内存亦可持久化的key-value数据库&#xff0c;并提供多种语言的API。Redis是一个key-value存储系统&#xff0c;它支持存储的value类型…

好用的AI工具推荐与案例分析

你用过最好用的AI工具有哪些&#xff1f; 简介&#xff1a;探讨人们在使用AI工具时&#xff0c;最喜欢的和认为最好用的工具是哪些&#xff0c;展示AI技术的实际应用和影响。 方向一&#xff1a;常用AI工具 在选择常用AI工具时&#xff0c;可以根据不同的应用场景和需求来挑选…

华为平板手机如何清理应用市场的存储空间

如何清理应用市场的存储空间 适用产品&#xff1a; 手机&#xff0c;平板 适用版本&#xff1a;不涉及系统版本 如果您的应用市场显示应用的数据较大&#xff0c;可能是下载的安装包没有安装成功&#xff0c;导致安装包未自动删除。&#xff08;可参考&#xff1a;应用市场下…

【QT】初始QT

目录 一.背景1.GUI开发的各种技术方案2.什么是框架3.QT支持的系统4.QT的版本5.QT的优点6.QT的应用常见 二.环境搭建1.认识QTSDK中的重要工具2.使用QT Creator创建项目3.项目解释(1)main.cpp(2)widget.h(3)widget.cpp(4)widget.ui(5)Empty.pro(6)临时文件 三.初始QT1.Hello Worl…

STM32单片机通过串口控制DDSM210 直驱伺服电机

1 电机介绍 官方资料&#xff1a;https://www.waveshare.net/wiki/DDSM210 DDSM210 直驱伺服电机是基于一体化开发理念&#xff0c;集外转子无刷电机、编码器、伺服驱动于一体的高可靠性永磁同步电动机&#xff0c;其结构紧凑&#xff0c;安装方便&#xff0c;运行稳定&#x…

飞致云开源社区月度动态报告(2024年4月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。值得注意的是&…

服务器数据恢复—多块磁盘离线导致阵列瘫痪,上层lun不可用的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌MSA2000存储&#xff0c;该存储中有一组由8块SAS硬盘&#xff08;其中有一块热备盘&#xff09;组建的RAID5阵列&#xff0c;raid5阵列上层划分了6个lun&#xff0c;均分配给HP-Unix小型机使用&#xff0c;主要数据为oracle数据库和O…

链表面试题及其解析

1.返回倒数第k个节点 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 示例&#xff1a; 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说明&#xff1a; 给定的 k 保证是有效的。 1.1快慢指针 即慢指针一次走一步…

[C++][数据结构]二叉搜索树:介绍和实现

二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c;它是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也…

JavaScript中的Math对象方法、Date对象方法

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f31f;Math对象方法&#x1f344;1 Math静态属性&#x1f344;2 Math…

Vue阶段练习:组件拆分

页面开发思路 分析页面&#xff0c;按模块拆分组件&#xff0c;搭架子&#xff08;局部或全局注册&#xff09;根据设计图&#xff0c;编写html结构css样式拆分封装通用小组件&#xff08;局部或全局注册&#xff09;将来通过js动态渲染实现功能 BaseBrandItem.vue <templ…

A5资源网有哪些类型的资源可以下载?

A5资源网提供了广泛的资源下载&#xff0c;包括但不限于以下类型&#xff1a; 设计素材&#xff1a;包括各类图标、矢量图、背景素材、UI界面元素等&#xff0c;适用于网页设计、平面设计等领域。 图片素材&#xff1a;提供高质量的照片、插图、摄影作品等&#xff0c;可用于…

使用STM32F103C8T6与蓝牙模块HC-05连接实现手机蓝牙控制LED灯

导言: 在现代智能家居系统中,远程控制设备变得越来越普遍和重要。本文将介绍如何利用STM32F103C8T6单片机和蓝牙模块HC-05实现远程控制LED灯的功能。通过这个简单的项目,可以学会如何将嵌入式系统与蓝牙通信技术相结合,实现远程控制的应用。 目录 导言: 准备工作: 硬…

ue引擎游戏开发笔记(27)——解决角色移动及转动存在卡顿掉帧小技巧

1.需求分析&#xff1a; 随之游戏越来越大&#xff0c;难免出现部分时候移动出现卡顿&#xff0c;能否进行一定优化。 2.操作实现&#xff1a; 1.思路&#xff1a;采取捕获最后deltaseconds来逐帧进行旋转或移动&#xff0c;使动作显得不那么卡顿。 .2.首先在引擎中建立映射&a…

【深度学习】第一门课 神经网络和深度学习 Week 4 深层神经网络

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

DRF解析器源码分析

DRF解析器源码分析 1 解析器 解析请求者发来的数据&#xff08;JSON&#xff09; 使用 request.data 获取请求体中的数据。 这个 reqeust.data 的数据怎么来的呢&#xff1f;其实在drf内部是由解析器&#xff0c;根据请求者传入的数据格式 请求头来进行处理。 drf默认的解…