贪吃蛇小游戏C语言全实现

C语言实现贪吃蛇小游戏!(超简单详细)详细思路+源码分享 我们的今天的目标就是:用C语言来实现一个贪吃蛇项目,也不用太复杂,初学者能看懂就行! 阅读详情

介绍:

这张图展示的想必是我们再熟悉不过的一个小游戏,想来我们一定听说过贪吃蛇,吃掉食物,增加自身长度,这款游戏一直以来也有着不小的热度;

而我们在玩这款游戏的时候,是否有思考过,我们所操控的这个蛇初始是怎样生成的?又是如何运动、如何增加自身长度的?又是以一种怎样的方式宣告它的结束?

那今天这篇文章,就来像大家展示一个,用C语言实现一个简易版贪吃蛇的全过程,来向大家展示,贪吃蛇最初的长度是如何生成的,它如何移动以及如何吃掉食物等一系列问题,来帮大家更好的了解这个游戏的本质,相信大家在看完这篇文章后,会对贪吃蛇的代码实现有一个基础的认知,

下面先来向大家展示一下我们最终完工后这一简易贪吃蛇的成果:

那接下来我们就正式开始实现了。

1.部分API函数的简单介绍:

要实现贪吃蛇这个小游戏,那就不得不先提到API函数,对于刚刚接触计算机语言的朋友来说,这个函数可能略微有些陌生,那我们就先对其来个简单的介绍:

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

1.1.控制台程序:

平时我们每一次运行代码,代码的结果都会显示在运行结束后弹出来的一个框框里,那这个框框,也就是我们的控制台,我们这次要执行的贪吃蛇项目,也是在我们的控制台上执行

因此,在执行贪吃蛇之前,我们可以将控制台的大小和标题都改至符合项目的情况:

我们可以使用cmd指令来操控控制台的大小:

mode con cols=100 lines=30

cols表示列,lines表示行,在控制台上,每一行的大小是两倍的列的大小,其具体位置和行列的大小关系对应情况如下:

当我们使用这个命令后,控制台的大小就变为了30行、100列;

Mode命令详细可参考https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/modeicon-default.png?t=N7T8https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/modetitle命令可修改控制台的名称

title 贪吃蛇

title命令详细可参考

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/titleicon-default.png?t=N7T8https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/title

1.2.控制台屏幕上的坐标COORD:

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

1.2.1.COORD类型的声明:

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

给坐标赋值:

COORD pos = { 30, 15 };

1.3.GetStdHandle

HANDLE GetStdHandle(DWORD nStdHandle);

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

当我们需要对控制台进行任何操作时,意味着我们要对控制台的输出信息进行调整,因此我们要获得控制台的输出句柄,该句柄就像我们日常生活中使用的遥控器,控制台就相当于一个电视机,有了输出句柄我们才能对控制台上的输出信息进行操作:

那如何获得输出句柄呢,下面是实例操作:

//获得输出句柄
HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);

1.4.GetConsoleCursorInfo

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

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

1.4.1.CONSOLE_CURSOR_INFO

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

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

dwSize,由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完
全填充单元格到单元底部的水平线条。
bVisible,游标的可见性。如果光标可见,则此成员为TRUE。

隐藏光标:

CursorInfo.bVisible = false; //隐藏控制台光标

1.5.SetConsoleCursorInfo

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

前面我们所使用的GetConsoleCursorInfo是为了获得我们正在使用的控制台的光标的信息,而当我们修改了这个信息之后,我们还需要把他重新运用到我们的控制台上,那这个时候就需要使用到SetConsoleCursorInfo这个API函数:

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

实例操作:

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

1.6.SetConsoleCursorPosition

设置光标的位置,该API函数的调用,可以直接使光标的位置移动到指定位置:

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

hConsoleOutput [in]
控制台屏幕缓冲区的句柄。 该句柄必须具有 GENERIC_READ 访问权限。 有关详细信息,请参阅控制台缓冲区安全性和访问权限icon-default.png?t=N7T8https://learn.microsoft.com/zh-cn/windows/console/console-buffer-security-and-access-rightsdwCursorPosition [in]
指定新光标位置(以字符为单位)的 COORD 结构。 坐标是屏幕缓冲区字符单元的列和行。 坐标必须位于控制台屏幕缓冲区的边界以内。

1.7.GetAsyncKeyState

当我们操作贪吃蛇的时候,一定是通过按键盘上的某些按键来操控贪吃蛇,那这个时候就需要使用到API函数GetAsyncKeyState,这个函数会判断我们是否按到过这个按键,我们再来通过这个返回值来进行对应功能的实现,该函数的原型如下:

SHORT GetAsyncKeyState(
int vKey
);

2.贪吃蛇游戏的实现:

在介绍完一部分我们会使用到的API函数后,我们就可以正式开始实现代码了,事实上API函数很多很多,有着成千上万种不同的功能,我们所了解的只是冰山一角,如果大家有兴趣的话当然可以可以自行了解;

贪吃蛇游戏的实现总共分为三个部分:游戏开始(GameStart)、游戏运行(GameRun)、游戏结束(GameOver),我们来一一进行实现:

2.1.GameStart

在该功能中,我们主要要实现的有以下几点:

1.控制台大小和名称的调整;

2.光标的隐藏;

3.欢迎界面的打印;

4.贪吃蛇游戏地图的创建;

5.贪吃蛇的创建和初始化;

6.食物的创建和初始化;

在实现这些功能之前,我们需要先实现两个非常简单的操作,虽然很简单,但是他们非常重要,这其中的一个操作是实现一个函数,这个函数贯穿我们整个贪吃蛇游戏,我们无时无刻不在使用它,他就是我们控制台光标位置的指定函数SetPos();

2.1.01.光标位置的指定SetPos的实现:

为了实现这个函数,我们当然是要结合到我们前面所了解到的API函数GetStdHandle、和SetConsoleCursorPosition,将光标设置到我们指定的x、y位置,实现如下:

void SetPos(short x, short y)
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y }; //设置我们需要位置的坐标
	SetConsoleCursorPosition(Houtput, pos);  
}

在实现完这个函数后,我们就要开始实现第二个操作:

2.1.02.本地化:

这第二个非常简单的操作,当然就是本地化,我们在实现贪吃蛇这一游戏的时候,一定会使用到宽字符,为防止有些朋友对宽字符可能并无了解,这里简单介绍一下:

我们平常使用printf输出在控制台或者是终端上的字符,都是标准字符,站一个字节位,而我们的宽字符,会占据两个字节位,这里给出一张图便于大家直接感受:

a就是我们平常使用的标准字符,而 ● 就是我们在贪吃蛇游戏中要使用到的宽字符,我们可以明显发现,宽字符的站位大小是标准字符的站位大小的两倍,我们也可以发现,我们的中文汉字也是站两个字节位大小的;

但我们编译器本身提供的环境(也就是C环境)是不支持宽字符的(如果对宽字符不曾了解,可关注我后序的文章,会详细讲解,这里只需初步了解),也没有函数能将其输出在控制台上,但我们的当地的环境,也就是中国地区的环境下,是能支持宽字符的打印的,这个时候我们就要将环境调整为本地环境,这个时候,我们就要使用到locale.h头文件中的setlocale函数(其具体使用会和宽字符一同讲解,这里只需初步了解),将环境改为本地环境:

setlocale(LC_ALL, "");

在实现完这两个简单的操作后,我们就可以上手实现GameStart函数中的各种功能了;

2.1.1.控制台大小和名称的调整:

使用前面介绍过的mode和title命令,直接实现对控制台的调整:

void AdjustConsole()
{
	//调整控制台的大小
	system("mode con cols=100 lines=30");
	//修改控制台的名称
	system("title 贪吃蛇");
}

2.1.2.光标的隐藏

先创建出CONSOLE_CURSOR_INFO类型的结构体,利用句柄使其成为我们要使用的这个控制台的光标信息,将其中的是否可见变量设置为false(要包含头文件<stdbool.h>),再使用SetConsoleCursorInfo完成修改:

void HideCursorInfo()
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo = { 0 };
	GetConsoleCursorInfo(Houtput, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(Houtput, &CursorInfo);
}

2.1.3.欢迎界面的打印

void Welcome()

将光标调整到合适位置,打印欢迎界面的信息;

别忘了使用pause命令停顿,使我们贪吃蛇游戏的流程更加清晰明了:

void Welcome()
{
	SetPos(38, 13);
	wprintf(L"欢迎来到贪吃蛇小游戏");
	SetPos(38, 20);
	system("pause");
}

效果展示:

在实现完这个界面后,我们最好给出一个游戏说明的界面,能让玩家在游戏开始前提前了解到游戏的规则,如下:

void PrintHelp()
{
	SetPos(30, 10);
	wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
	SetPos(30, 11);
	wprintf(L"空格(space)表示暂停,Esc表示正常退出游戏");
	SetPos(30, 13);
	wprintf(L"争取获得更高的分数");
	SetPos(30, 22);
	system("pause");
}

效果展示:

2.1.4.贪吃蛇游戏地图的创建

void CreateMap(pSnack ps)

我们整个控制台的大小是100列,30行,在创建地图之前,我们要确定好地图的大小,这里我们以27*56的地图大小来做示范,大家也可根据自身习惯,来决定地图的大小;

地图的样貌(27 * 58)如下图所示:

我们需要用到宽字符 □ 来作为地图的边框,来分别打印出四条地图的边框:

以打印上图来做示范,以此类推,打印出左右下的地图边框:

27*56地图的上边框共有28个 □ ,所以共要循环28次,如下:

SetPos(0, 0);
for (int i = 0; i < 56; i += 2)
{
	wprintf(L"□");
}

以次方法再打印出左右下的边框:

//下
SetPos(0, 26);
for (int i = 0; i < 56; i += 2)
{
	wprintf(L"□");
}
//左
for (int i = 1; i < 26; i++)
{
	SetPos(0, i);
	wprintf(L"□");
}
//右
for (int i = 1; i < 26; i++)
{
	SetPos(54, i);
	wprintf(L"□");
}

效果展示:

为了便于视觉上的体验,我们还能在地图的附近选择合适的位置给出提示信息:

//打印出帮助信息
SetPos(60, 19);
wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
SetPos(60, 21);
wprintf(L"空格(space)表示暂停");
SetPos(60, 23);
wprintf(L"Esc表示正常退出游戏");

2.1.5.贪吃蛇的创建和初始化

贪吃蛇是基于链表实现的,因此贪吃蛇的每一个节点都相当于是一个链表的结点,我们知道,链表中的每一个结点都是由两个部分组成的,一个数节点中储存的数据,另一个是指向下一个结点的指针,而贪吃蛇的一个结点中存储的数据,是该节点的坐标位置,我们可以通过这个坐标,来打印出贪吃蛇的这一个结点,如下所示,我们先定义出贪吃蛇的节点:

//定义的一个结点的坐标
typedef struct CoordinateOfSnackNode
{
	short x;
	short y;
}NodeCoord;

//定义一个蛇的结点————由链表实现
typedef struct SnackNode
{
	NodeCoord coord;
	struct SnakcNode* next;
}SnackNode, * pSnackNode;

在定义出蛇的节点后,我们不妨再把整个贪吃蛇要用到的所有数据同时定义出来,这便是很重要的面向对象的思维如下,整个贪吃蛇应包含如下信息:

//蛇的移动方向
enum DIRECTION
{
	UP,
	DOWN,
	LEFT,
	RIGHT
};

//蛇的状态
enum STATE
{
	OK,
	END_NORMAL,
	HIT_BYSELF,
	HIT_BYWALL
};

//定义一整条蛇————包含如下信息
typedef struct Snack
{
	//蛇头
	pSnackNode _SnackHead;
	//蛇的移动方向
	enum DIRECTION _dire;
	//每走一步的休眠时间
	int _sleeptime;
	//蛇的状态
	enum STATE _state;
	//食物
	pSnackNode _food;
	//一个食物的分数
	int _foodweight;
	//当前获得的总分数
	int _score;
}Snack, * pSnack;

下面来进行蛇的初始化:

我们默认开始的贪吃蛇本身有5个蛇结点,每个结点的申请构造方式如下

2.1.5.1.BuySnackNode
pSnackNode BuySnackNode(NodeCoord coord)

利用malloc申请空间,最后返回申请到的节点:

pSnackNode BuySnackNode(NodeCoord coord)
{
	pSnackNode newnode = (pSnackNode)malloc(sizeof(SnackNode));
	if (newnode == NULL)
	{
		perror("BuySnackNode()::malloc");
		exit(-1);
	}
	newnode->coord = coord;
	newnode->next = NULL;

	return newnode;
}
2.1.5.2.SnackInit

我们假定第一个蛇节点的位置为x=24,y=6(根据个人习惯),来循环创建结点,最后采用头插的方式并接到蛇身上:

void SnackInit(pSnack ps)
{
	//初始蛇有5个结点,假设蛇头的初位置为24,6;
	for (int i = 0; i < 5; i++)
	{
		NodeCoord coord = { POS_X + i * 2, POS_Y };
		if (ps->_SnackHead == NULL)
		{
			ps->_SnackHead = BuySnackNode(coord);
		}
		else
		{
			//头插数据
			pSnackNode newnode = BuySnackNode(coord);
			newnode->next = ps->_SnackHead;
			ps->_SnackHead = newnode;
		}
	}
}

既然创建好了蛇,我们当然是要将其显示出来,因此我们创建一个函数,这个函数的功能就是打印出整条蛇,蛇的打印,相当于链表的遍历,这里我们为了可视性,将蛇头用宽字符方块表示,蛇身用宽字符圆点表示:如下所示:

void ShowSnack(pSnack ps)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur == ps->_SnackHead)
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", HEAD);
		}
		else
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", BODY);
		}
		cur = cur->next;
	}
}

2.1.6.食物的创建和初始化

食物的本身也是一个蛇结点,所以我们仍然可以使用BuySnackNode来创建食物,只不过我们食物的坐标需要随机生成,这个时候我们就要想起随机数的生成方法:

srand利用时间戳在每时每刻获得不同的unsigned int类型的值,然后用rand函数就可以生成随机数,这里不要忘了包含头文件time.h和stdlib.h,

srand的调用如下:

//调用srand函数,便于生成随机数
srand((unsigned int)time(NULL));

有了随机数的生成,我们就能随机生成食物的坐标,但这里有两个需要注意的地方:

1.食物坐标的x要是2的倍数,否则无法贪吃蛇的头结点无法与食物重合,也就无法吃到食物;

2.食物的坐标不能和蛇身重合,当生成的随机数与蛇身有重合,应使用goto语句再次生成,直到符合条件为止;

再创造完食物后可将食物给显示出来,食物我们用宽字符五角星来表示;

如下所示:

void CreateFood(pSnack ps)
{
	int x = 0, y = 0;
	again:
	//食物的坐标要在地图范围内,同时x要是2的倍数
	do
	{
		x = 2 + rand() % (52 - 2 + 1);
		y = 1 + rand() % (25 - 1 + 1);
	} while (x % 2 != 0);

	//坐标不能与蛇身重合
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == x && cur->coord.y == y)
			goto again;
		cur = cur->next;
	}

	NodeCoord FoodPos = { x,y };
	ps->_food = BuySnackNode(FoodPos);

	//显示出食物
	SetPos(ps->_food->coord.x, ps->_food->coord.y);
	wprintf(L"%lc", FOOD);
}

再将贪吃蛇中未初始化的几个变量初始化一下,那整个GameStart函数就算实现好了;

//初始化蛇自身的部分
ps->_state = OK;//状态默认OK
ps->_dire = RIGHT;//方向默认向右
ps->_sleeptime = 200;//每走一步中间的间隔时间默认为200ms;
ps->_SnackHead = NULL;
//初始化食物的其余数据
ps->_foodweight = 10;
ps->_score = 0;

GameStart整体效果展示:

2.2.GameRun

游戏运行,这个功能的实现就比较直接了,简单来说,这个函数要做的就是通过按键,来改变贪吃蛇中的各个变量,再根据各个变量的改变来进行对应功能的实现:

“↑、↓、←、→分别控制蛇的上下左右移动”

“空格(space)表示暂停”

“Esc表示正常退出游戏”

这是我们的玩法说明,怎么判断我们是否按了这几个按键,这就需要使用到我们前面提到的API函数GetAsyncKeyState,该函数中传入对应按键的虚拟按键值,当按下这个按键之后,该函数的二进制最低位会返回1,否则返回0,以此我们就可以来定义一个宏KET_PRESS(VK),来判断我们是否按下过这个按键,用GetAsyncKeyState的返回值按位与(&)上一个1之后,得到的就是他的二进制最低位的数:

//定义一个宏,用于确定按键是否被按,按后则返回1,否则返回0;
#define KEY_PRESS(VK) (GetAsyncKeyState(VK) & 0x1)

那GameRun函数的执行,就是在一个do......while循环中,通过判断某个按键来改变蛇的状态,每循环一次蛇就会往指定方向走一步,每次循环都有间隔,间隔时间为蛇每走一步的休眠时间,那么do...while循环的结束条件是什么呢?在前面我们定义蛇的时候就知道,蛇有四个状态,用一个枚举变量表示出来,如下所示:

//蛇的状态
enum STATE
{
	OK,
	END_NORMAL,
	HIT_BYSELF,
	HIT_BYWALL
};

正常、正常结束、撞到自己和撞到墙,当我们蛇的状态是正常的时候,它就能持续运动,否则就会退出循环,结束游戏:

当按到上下左右按键时,对应的运动方向发生改变,按到空格时,就需要暂停(暂停的方法是使其休眠时间变为无穷),按到Esc时,状态变为自动结束,F3或者F4时,休眠的时间变长或者变短,对应一个食物的分数增多或者减少;

因此该函数的大体框架就如下所示:

void GameRun(pSnack ps)
{

	do
	{
		//显示得分情况
		ShowScore(ps);

		if (KEY_PRESS(VK_UP) && ps->_dire != DOWN)
			ps->_dire = UP;
		else if (KEY_PRESS(VK_DOWN) && ps->_dire != UP)
			ps->_dire = DOWN;
		else if (KEY_PRESS(VK_LEFT) && ps->_dire != RIGHT)
			ps->_dire = LEFT;
		else if (KEY_PRESS(VK_RIGHT) && ps->_dire != LEFT)
			ps->_dire = RIGHT;
		else if (KEY_PRESS(VK_SPACE))
			SleepForever();
		else if (KEY_PRESS(VK_ESCAPE))
			ps->_state = END_NORMAL;
		else if (KEY_PRESS(VK_F3))
		{
			//加速,每走一步休眠时间变短,得分更高
			if (ps->_sleeptime > 50)
			{
				ps->_sleeptime -= 30;//不能一直加速,最多加速5次
				ps->_foodweight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//加速,每走一步休眠时间变短,得分更高
			if (ps->_sleeptime < 320)
			{
				ps->_sleeptime += 30;//不能一直加速,最多加速4次
				ps->_foodweight -= 2;
			}
		}

		SnackMove(ps);
		ShowSnack(ps);
		Sleep(ps->_sleeptime);

	} while (ps->_state == OK);

}

然后就通过按键所改变的状态,决定是暂停。退出、或者是向哪边移动;

2.2.1.暂停

暂停函数,SleepForever,就是使其休眠时间无线延长,我们可以用一个死循环来表示,当然,再按一次空格,我们就退出暂停状态,因此这个循环中还需要存在是否按到空格这么一个宏

void SleepForever()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			//再按一次,继续运动
			break;
		}
	}
}

2.2.2.移动

void SnackMove(pSnack ps)

该函数的实现需要考虑到很多因素,大概思路如下所示,通过蛇的移动方向找到下一个结点,然后通过这下一个节点判断蛇是吃到食物、撞到自己还是撞墙、当这三者都不发生,蛇就是正常移动,我们先来找到下一个节点:

2.2.2.1.NextNode

根据对应状态,调整坐标,根据调整后的坐标申请节点,采用头插法使这个下一个结点成为新的头结点:

pSnackNode NextNode(pSnack ps)
{
	pSnackNode nextnode = NULL;
	switch (ps->_dire)
	{
	case UP:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y - 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case DOWN:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y + 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case RIGHT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x + 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	case LEFT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x - 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	}

	return nextnode;
}
2.2.2.2.判断是否撞墙或者撞到自己

当撞墙或者撞到自己,就改变贪吃蛇的状态,本次循环走完后就会结束do...while循环,意味着本次游戏的结束,当下一个结点的坐标和墙或者自己重合的时候,意味着撞墙或者撞到了自己,撞墙,即x为0或者54,y为0或26,撞到自己需要以此遍历自身结点,观察下一个结点的坐标是否与自身的某个结点重合,如下所示:

bool IsBeHitedByWall(pSnackNode nextnode)
{
	return nextnode->coord.x == 0 || nextnode->coord.x == 54 ||
		nextnode->coord.y == 0 || nextnode->coord.y == 26;
}


bool IsBeHitedBySelf(pSnack ps, pSnackNode nextnode)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == nextnode->coord.x && cur->coord.y == nextnode->coord.y)
			return true;
		cur = cur->next;
	}

	return false;
}
2.2.2.3.下一个节点是不是食物
bool IsFood(pSnack ps, pSnackNode nextnode)

这是整个贪吃蛇游戏的一个比较核心的环节,贪吃蛇只有吃掉食物,自身长度才能增长,才能获得更高的得分,那怎么样才能吃到食物呢,首先,我们需要判断下一个结点是不是食物,即下一个结点的坐标和食物的坐标是否相同,若相同,则返回true,否则返回false,如下:

bool IsFood(pSnack ps, pSnackNode nextnode)
{
	return ps->_food->coord.x == nextnode->coord.x &&
		   ps->_food->coord.y == nextnode->coord.y;
}
2.2.2.4.吃掉食物
void EatFood(pSnack ps)

如果判断好下一个结点就是食物(IsFood的返回值为true),那这个时候我们就应该吃掉食物,吃食物的方法很简单,我们只掉,食物本身也是贪吃蛇的一个节点,那这个时候,我们只需要让这个食物成为贪吃蛇这个链表的头结点,还是采用简单的头插数据就可以实现,但不要忘了,吃掉食物之后一定记得再生成一个新的食物,如下所示:

void EatFood(pSnack ps)
{
	//食物成为新一个头结点,蛇的结点增加
	ps->_food->next = ps->_SnackHead;
	ps->_SnackHead = ps->_food;

	//加分
	ps->_score += ps->_foodweight;

	//创造一个新食物
	CreateFood(ps);
}
2.2.2.5.正常移动Move

如果说贪吃蛇既没有吃到食物,也没有撞墙或者是撞到自己,那么它就需要正常移动,将下一个结点采用头插法插入到贪吃蛇的链表中,同时需要尾删贪吃蛇的最后一个蛇结点,因为贪吃蛇的正常移动,它的蛇身长度是不会改变的,只是它的位置改变了,删除节点我们需要使用到free函数

但不要忘了,再释放最后一个结点之前,应该将最后一个节点对应坐标处用两个空格覆盖掉,要不然控制台所展示出来的就不会是正常移动的图像,而是一条越拉越长的蛇,该函数的实现如下:

void Move(pSnack ps, pSnackNode nextnode)
{
	nextnode->next = ps->_SnackHead;
	ps->_SnackHead = nextnode;
	pSnackNode cur = ps->_SnackHead;
	pSnackNode prev = NULL;
	while (cur->next != NULL)
	{
		prev = cur;
		cur = cur->next;
	}
	SetPos(cur->coord.x, cur->coord.y);
	printf("  ");
	free(cur);
	prev->next = NULL;
}

在实现完以上功能后,我们SnackMove的整体框架和逻辑就出现了,如下:

void SnackMove(pSnack ps)
{
	//下一个头结点
	pSnackNode nextnode = NextNode(ps);

	//判断下一个头结点是否是食物
	if (IsFood(ps, nextnode))
	{
		//吃掉食物
		EatFood(ps);
	}
	//判断是否撞墙
	else if (IsBeHitedByWall(nextnode))
	{
		ps->_state = HIT_BYWALL;
	}
	//判断是否撞到自己
	else if (IsBeHitedBySelf(ps, nextnode))
	{
		ps->_state = HIT_BYSELF;
	}
	else
	{
		//正常移动
		Move(ps, nextnode);
	}
}

那当我们移动一步后,我们当然就需要把整条贪吃蛇再一次显示在控制台上,也就是再调用一次ShowSnack函数,这样来看,贪吃蛇移动一步就算是已经实现好了,我们只需要在每次移动的间隙中利用Sleep命令停顿上我们所设置好的休眠时间,就可以实现贪吃蛇游戏的运行:

2.3.GameOver

//游戏结束
GameOver(&snack);

游戏结束这个函数功能中主要由两点要实现的功能:

1.游戏是如何结束的声明,是主动结束还是撞到墙,亦或是撞到自己;

2.贪吃蛇是基于链表实现的,而链表的每个结点,包括食物,都是利用动态内存开辟malloc在堆上开辟的空间,因此我们需要释放掉这部分空间,避免造成内存泄露;整体的实现也比较简单,只需遍历整个链表,释放当前结点前,储存好下一个结点的指针,再依次向后跑,如下所示:

void GameOver(pSnack ps)
{
	SetPos(40, 12);
	if (ps->_state == HIT_BYWALL)
		printf("您已撞墙\n");
	else if (ps->_state == HIT_BYSELF)
		printf("您撞到了自己\n");
	else if (ps->_state == END_NORMAL)
		printf("您主动结束了游戏\n");

	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		pSnackNode next = cur->next;
		free(cur);
		cur = next;
	}

	free(ps->_food);
	ps->_food = NULL;

}

游戏到这那我们的贪吃蛇游戏基本就算完全实现好了,我们就可以在主函数中依次调用这三个函数,来进行贪吃蛇游戏,如果我们想要在一局游戏结束后,通过某一个条件来判断我们是否要重新再开一把游戏,那这个时候我们就可以发挥自己的想象,用一个do...while循环来包含这三个函数,通过某一条件来确定循环是否继续执行,这里也就不再过多介绍,参考代码会放在整篇文章的最后;

3.结语:

整体来说,贪吃蛇小游戏这个项目,虽然说并不是很复杂,但是它完美结合了很多C语言的知识,在我们实现贪吃蛇的过程中,也算是对我们以往知识的一个复习与巩固,我们所实现的只不过是一个很简单的贪吃蛇基层逻辑,这个游戏还有很多可以优化的地方,这可以锻炼我们的思维,在学习的同时,我们一定要着手写出代码,只有多写才会有进步,对代码才会更加熟练;

那这篇文章就介绍到这里,感谢大家观看!

4.参考代码:

4.1.snack.h

#pragma once

//贪吃蛇小游戏的完全实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <Windows.h>
#include <locale.h>


//定义的一个结点的坐标
typedef struct CoordinateOfSnackNode
{
	short x;
	short y;
}NodeCoord;

//定义一个蛇的结点————由链表实现
typedef struct SnackNode
{
	NodeCoord coord;
	struct SnakcNode* next;
}SnackNode, * pSnackNode;

//蛇的移动方向
enum DIRECTION
{
	UP,
	DOWN,
	LEFT,
	RIGHT
};

//蛇的状态
enum STATE
{
	OK,
	END_NORMAL,
	HIT_BYSELF,
	HIT_BYWALL
};

//定义一整条蛇————包含如下信息
typedef struct Snack
{
	//蛇头
	pSnackNode _SnackHead;
	//蛇的移动方向
	enum DIRECTION _dire;
	//每走一步的休眠时间
	int _sleeptime;
	//蛇的状态
	enum STATE _state;
	//食物
	pSnackNode _food;
	//一个食物的分数
	int _foodweight;
	//当前获得的总分数
	int _score;
}Snack, * pSnack;


//游戏开始
void GameStart(pSnack ps);

//设置光标位置
void SetPos(short x, short y);

//隐藏光标
void HideCursorInfo();

//调整好控制台的大小和名称
void AdjustConsole();

//打印欢迎界面
void Welcome();

//打印游戏说明界面
void PrintHelp();

//创造游戏地图
void CreateMap(pSnack ps);

//蛇头的初位置
#define POS_X 24
#define POS_Y 5

//初始化蛇身
void SnackInit(pSnack ps);

//创建蛇的一个结点
pSnackNode BuySnackNode(NodeCoord coord);

//打印蛇
//蛇头
#define HEAD L'■'
//蛇身
#define BODY L'●'
void ShowSnack(pSnack ps);

//创造一个食物
#define FOOD L'★'
void CreateFood(pSnack ps);

//游戏运行
void GameRun(pSnack ps);

//定义一个宏,用于确定按键是否被按,按后则返回1,否则返回0;
#define KEY_PRESS(VK) (GetAsyncKeyState(VK) & 0x1)

//显示得分情况
void ShowScore(pSnack ps);

//空格space暂停
void SleepForever();

//移动一步
void SnackMove(pSnack ps);

//下一个头结点
pSnackNode NextNode(pSnack ps);

//判断下一个结点是否是食物
bool IsFood(pSnack ps, pSnackNode nextnode);

//吃掉食物
void EatFood(pSnack ps);

//判断是否撞墙
bool IsBeHitedByWall(pSnackNode nextnode);

//判断是否撞到自己
bool IsBeHitedBySelf(pSnack ps, pSnackNode nextnode);

//正常移动
void Move(pSnack ps, pSnackNode nextnode);

//游戏结束
void GameOver(pSnack ps);

4.2.snack.c

#define  _CRT_SECURE_NO_WARNINGS 1

#include "snack.h"

//设置光标位置
void SetPos(short x, short y)
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(Houtput, pos);
}


void HideCursorInfo()
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo = { 0 };
	GetConsoleCursorInfo(Houtput, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(Houtput, &CursorInfo);
}


void AdjustConsole()
{
	//调整控制台的大小
	system("mode con cols=100 lines=30");
	//修改控制台的名称
	system("title 贪吃蛇");
}


void Welcome()
{
	SetPos(38, 13);
	wprintf(L"欢迎来到贪吃蛇小游戏");
	SetPos(38, 20);
	system("pause");
}


void PrintHelp()
{
	SetPos(30, 10);
	wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
	SetPos(30, 11);
	wprintf(L"空格(space)表示暂停,Esc表示正常退出游戏");
	SetPos(30, 13);
	wprintf(L"争取获得更高的分数");
	SetPos(30, 22);
	system("pause");
}


void CreateMap(pSnack ps)
{
	//以56*27的大小来创建地图
	//上
	SetPos(0, 0);
	for (int i = 0; i < 56; i += 2)
	{
		wprintf(L"□");
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 56; i += 2)
	{
		wprintf(L"□");
	}
	//左
	for (int i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"□");
	}
	//右
	for (int i = 1; i < 26; i++)
	{
		SetPos(54, i);
		wprintf(L"□");
	}
	//打印出帮助信息
	SetPos(60, 19);
	wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
	SetPos(60, 21);
	wprintf(L"空格(space)表示暂停");
	SetPos(60, 23);
	wprintf(L"Esc表示正常退出游戏");
}


pSnackNode BuySnackNode(NodeCoord coord)
{
	pSnackNode newnode = (pSnackNode)malloc(sizeof(SnackNode));
	if (newnode == NULL)
	{
		perror("BuySnackNode()::malloc");
		exit(-1);
	}
	newnode->coord = coord;
	newnode->next = NULL;

	return newnode;
}


void SnackInit(pSnack ps)
{
	//初始蛇有5个结点,假设蛇头的初位置为24,6;
	for (int i = 0; i < 5; i++)
	{
		NodeCoord coord = { POS_X + i * 2, POS_Y };
		if (ps->_SnackHead == NULL)
		{
			ps->_SnackHead = BuySnackNode(coord);
		}
		else
		{
			//头插数据
			pSnackNode newnode = BuySnackNode(coord);
			newnode->next = ps->_SnackHead;
			ps->_SnackHead = newnode;
		}
	}
}


void ShowSnack(pSnack ps)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur == ps->_SnackHead)
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", HEAD);
		}
		else
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", BODY);
		}
		cur = cur->next;
	}
}


void CreateFood(pSnack ps)
{
	int x = 0, y = 0;
	again:
	//食物的坐标要在地图范围内,同时x要是2的倍数
	do
	{
		x = 2 + rand() % (52 - 2 + 1);
		y = 1 + rand() % (25 - 1 + 1);
	} while (x % 2 != 0);

	//坐标不能与蛇身重合
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == x && cur->coord.y == y)
			goto again;
		cur = cur->next;
	}

	NodeCoord FoodPos = { x,y };
	ps->_food = BuySnackNode(FoodPos);

	//显示出食物
	SetPos(ps->_food->coord.x, ps->_food->coord.y);
	wprintf(L"%lc", FOOD);
}


void GameStart(pSnack ps)
{
	//隐藏光标
	HideCursorInfo();
	//调整好控制台的大小和名称
	AdjustConsole();

	//初始化蛇自身的部分
	ps->_state = OK;//状态默认OK
	ps->_dire = RIGHT;//方向默认向右
	ps->_sleeptime = 200;//每走一步中间的间隔时间默认为200ms;
	ps->_SnackHead = NULL;

	//打印欢迎界面
	Welcome();
	system("cls");
	//打印游戏说明界面
	PrintHelp();
	system("cls");
	//创造游戏地图
	CreateMap(ps);

	//初始化蛇身
	SnackInit(ps);
	//打印蛇
	ShowSnack(ps);

	//创造一个食物
	CreateFood(ps);

	//初始化食物的其余数据
	ps->_foodweight = 10;
	ps->_score = 0;
}


void ShowScore(pSnack ps)
{
	SetPos(60, 10);
	wprintf(L"一个食物的分数为%2d", ps->_foodweight);
	SetPos(60, 11);
	wprintf(L"目前总的分数为%2d", ps->_score);

}


void SleepForever()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			//再按一次,继续运动
			break;
		}
	}
}


pSnackNode NextNode(pSnack ps)
{
	pSnackNode nextnode = NULL;
	switch (ps->_dire)
	{
	case UP:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y - 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case DOWN:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y + 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case RIGHT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x + 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	case LEFT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x - 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	}

	return nextnode;
}


bool IsFood(pSnack ps, pSnackNode nextnode)
{
	return ps->_food->coord.x == nextnode->coord.x &&
		   ps->_food->coord.y == nextnode->coord.y;
}


void EatFood(pSnack ps)
{
	//食物成为新一个头结点,蛇的结点增加
	ps->_food->next = ps->_SnackHead;
	ps->_SnackHead = ps->_food;

	//加分
	ps->_score += ps->_foodweight;

	//创造一个新食物
	CreateFood(ps);
}


bool IsBeHitedByWall(pSnackNode nextnode)
{
	return nextnode->coord.x == 0 || nextnode->coord.x == 54 ||
		nextnode->coord.y == 0 || nextnode->coord.y == 26;
}


bool IsBeHitedBySelf(pSnack ps, pSnackNode nextnode)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == nextnode->coord.x && cur->coord.y == nextnode->coord.y)
			return true;
		cur = cur->next;
	}

	return false;
}


void Move(pSnack ps, pSnackNode nextnode)
{
	nextnode->next = ps->_SnackHead;
	ps->_SnackHead = nextnode;
	pSnackNode cur = ps->_SnackHead;
	pSnackNode prev = NULL;
	while (cur->next != NULL)
	{
		prev = cur;
		cur = cur->next;
	}
	SetPos(cur->coord.x, cur->coord.y);
	printf("  ");
	free(cur);
	prev->next = NULL;
}


void SnackMove(pSnack ps)
{
	//下一个头结点
	pSnackNode nextnode = NextNode(ps);

	//判断下一个头结点是否是食物
	if (IsFood(ps, nextnode))
	{
		//吃掉食物
		EatFood(ps);
	}
	//判断是否撞墙
	else if (IsBeHitedByWall(nextnode))
	{
		ps->_state = HIT_BYWALL;
	}
	//判断是否撞到自己
	else if (IsBeHitedBySelf(ps, nextnode))
	{
		ps->_state = HIT_BYSELF;
	}
	else
	{
		//正常移动
		Move(ps, nextnode);
	}
}

void GameRun(pSnack ps)
{

	do
	{
		//显示得分情况
		ShowScore(ps);

		if (KEY_PRESS(VK_UP) && ps->_dire != DOWN)
			ps->_dire = UP;
		else if (KEY_PRESS(VK_DOWN) && ps->_dire != UP)
			ps->_dire = DOWN;
		else if (KEY_PRESS(VK_LEFT) && ps->_dire != RIGHT)
			ps->_dire = LEFT;
		else if (KEY_PRESS(VK_RIGHT) && ps->_dire != LEFT)
			ps->_dire = RIGHT;
		else if (KEY_PRESS(VK_SPACE))
			SleepForever();
		else if (KEY_PRESS(VK_ESCAPE))
			ps->_state = END_NORMAL;
		else if (KEY_PRESS(VK_F3))
		{
			//加速,每走一步休眠时间变短,得分更高
			if (ps->_sleeptime > 50)
			{
				ps->_sleeptime -= 30;//不能一直加速,最多加速5次
				ps->_foodweight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//加速,每走一步休眠时间变短,得分更高
			if (ps->_sleeptime < 320)
			{
				ps->_sleeptime += 30;//不能一直加速,最多加速4次
				ps->_foodweight -= 2;
			}
		}

		SnackMove(ps);
		ShowSnack(ps);
		Sleep(ps->_sleeptime);

	} while (ps->_state == OK);

}


void GameOver(pSnack ps)
{
	SetPos(40, 12);
	if (ps->_state == HIT_BYWALL)
		printf("您已撞墙\n");
	else if (ps->_state == HIT_BYSELF)
		printf("您撞到了自己\n");
	else if (ps->_state == END_NORMAL)
		printf("您主动结束了游戏\n");

	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		pSnackNode next = cur->next;
		free(cur);
		cur = next;
	}

	free(ps->_food);
	ps->_food = NULL;

}

4.3.test.c

#define  _CRT_SECURE_NO_WARNINGS 1

#include "snack.h"

int main()
{
	//将环境改为本地环境,方便宽字符的使用
	setlocale(LC_ALL, "");
	//调用srand函数,便于生成随机数
	srand((unsigned int)time(NULL));
	int ch = 0;
	do
	{
		system("cls");
		//定义一条蛇
		Snack snack;

		//游戏开始——准备工作
		GameStart(&snack);

		//游戏运行
		GameRun(&snack);

		//游戏结束
		GameOver(&snack);

		SetPos(40, 13);
		wprintf(L"需要再来一局吗?(Y/N)");
		scanf("%c", &ch);
		while (getchar() != '\n')
			;
	} while (ch == 'Y' || ch == 'y');

	SetPos(0, 27);

	return 0;
}

贪吃蛇c++程序(A*算法自动追踪功能) 目录 一、摘要 二、概述 三、方法论证和比较 1、方案一:深度学习算法 2、方案二:广度优先搜索算法 3、方案三:A*算法 四、理论分析即代码 1、贪吃蛇的设计流程图 2、初始化地图算法 3、利用键盘控制蛇的移动 4、食物的生成 6、贪吃蛇的自动追踪算法 一、摘要 贪吃蛇是一款简单益智类的大众小游戏,自诞生以来,深受广大玩家喜爱。此次设计传统的贪吃蛇小游戏,将应用所学的理论知识解决一些复杂的工程问题:通过利用键盘的方向键控制蛇的移动,实现简单的贪吃蛇基本功能。... 阅读详情

相关推荐

贪吃蛇(C语言超详细版)

如果你通过该文章完成了一次,其实你会发现,也没那么难,无非就是根据游戏的进程一步一步去实现每个最微小的步骤。其实所有的难题,都是聚沙成塔的,只要你一步一个脚印去实现,真的没有难题!API帮助了我们实现了贪吃蛇,我们可能不知道其内部的实现,但是我们只需要去学会使用它即可。如果你可以独立完成一次,那么恭喜你,你已经彻底学会了C语言,接下来就可以往更高的地点出发。

weixin_64132124的博客 5316

C语言实现贪吃蛇(详细版)

以上就是贪吃蛇的基本内容啦,完结撒花~下面有一些想说的话:这篇文章的完成时间是2023.5.12,我现在是一名大一下的计科学生,其实C语言在去年十月份的中旬就看完了翁恺老师的网课,那个时候其实就有能力完成这个贪吃蛇的编写了,但是知道现在才写完,其实是走了很多弯路,耽误了不少时间北哥,在加入北哥的知识星球”编程指北“后才回正了自己的学习方向,也希望大家都能在正确的道路上越走越远~

m0_73906588的博客 4万+

C语言——贪吃蛇小游戏

基于Ncurse图形库的C语言小游戏

weixin_54859557的博客 7569

C语言实现贪吃蛇小游戏全解析

1. 初始化游戏界面和蛇的状态:使用C语言的图形库(如graphics.h,在Windows平台较为常用)来绘制游戏界面,设置窗口大小、背景颜色等。同时,初始化蛇的位置和身体长度,蛇的身体可以用一个数组来表示每个节点的坐标。贪吃蛇游戏的基本规则是:蛇在一个矩形区域内移动,初始时蛇有一定长度,每吃到一个食物,蛇的长度就会增加。同时,更新蛇的位置,将蛇头移动到新的位置,蛇身跟随蛇头移动。5. 绘制游戏元素:在游戏界面上绘制蛇和食物,使用图形库的绘图函数来绘制矩形表示蛇和食物。// 检查是否碰到自己的身体。

2501_91652765的博客 1086

在linux下用C语言编写贪吃蛇小游戏

利用链表和结构体来编写贪吃蛇小游戏,这边还会用点线程,分别有以下模块: 1、地图规划 2、蛇身体的初始化 3、蛇的方向移动 4、食物的初始化和随机出现 5、蛇撞墙和咬自己的尾巴 地图规划 我们在设计地图的时候,需要用到ncurses,头文件 #include <curses.h> ncurses(new curses)是一个程序库,它提供了API,可以允许程序员编写独立于终端的基...

weixin_44263604的博客 4006

用C语言实现经典游戏——贪吃蛇

蛇对象实现方式原理是坐标,而设置X,Y分别表示横坐标与纵坐标,身体使用数组方式表示可以body[0],表示蛇头,其他表示蛇身,蛇的大小会根据吃的食物个数而增加。int x;int Y;// 蛇身。body[0] -- 蛇头int size;// 蛇的大小}snake;

莫浅子的博客 1万+

C 语言实现贪吃蛇小游戏:从设计到代码全解析

基础功能地图绘制:游戏界面包含墙体、蛇身和食物。墙体构成游戏的边界,蛇身由一系列节点组成,食物则随机出现在地图的空白位置。方向控制:玩家可以使用上下左右键来控制蛇的移动方向,改变蛇的前进路径。碰撞检测:当蛇撞到墙体或自身时,游戏结束。这需要我们在代码中实现相应的碰撞检测逻辑,确保游戏的合理性。得分系统:每当蛇吃到食物时,得分增加,同时蛇身增长一节,激励玩家不断挑战更高的分数。进阶功能加速 / 减速:玩家可以通过按下 F3 和 F4 键来调整蛇的移动速度,增加游戏的趣味性和挑战性。暂停功能。

2403_87794049的博客 1131

C语言程序设计实战:贪吃蛇小游戏

C语言作为编程语言的基石,自1972年由贝尔实验室的Dennis Ritchie发明以来,一直是软件开发中的核心语言之一。它的设计哲学强调简洁、效率,使得程序员能够以接近硬件的方式控制计算机,非常适合系统软件开发。游戏逻辑指的是游戏内的规则、角色行为、环境变化等一系列由程序控制的逻辑关系。它们共同作用,创造出游戏的玩法和体验。游戏逻辑需要开发者以一种既合理又引人入胜的方式去设计,以保持玩家的兴趣和挑战性。

weixin_30653091的博客 1002

C 语言实现经典贪吃蛇小游戏 | 课程设计全解析

C 语言小游戏课设的核心不在于代码量,而在于逻辑的清晰性和问题的解决能力。贪吃蛇的实现过程,是从 “面向过程” 编程思维到 “模块化设计” 思维的一次实践。希望本文能为你的 C 语言课设提供参考,也欢迎大家交流优化思路~

C语言实战大全 1134

Jetson编译安装pycuda

可使用nvcc --version查看是否有信息输出,若没有,且安装cuda的情况下,需要手动将CUDA 的路径添加到环境变量中。编译生成_pvt_struct.cpython-38-aarch64-linux-gnu.so文件。基于jetson orin nano jetpack512模块上安装pycuda。4、获取pycuda源码(版本根据实际情况获取)2、进入conda环境并安装cuda工具包。9、若未检测出来需要清除并强制编译。7、编译成功后安装pycuda。5、配置编译并设置环境变量。

qq_48175141的博客 972

Android dex, odex, oat, vdex, art文件介绍

下图可以简化将Java源转换为OAT的过程: .dex Dalvik虚拟机字节码文件 .vdex 其中包含APK的未压缩DEX代码,以及一些旨在加快验证速度的元数据 .odex .oat // JIT,Just-in-time,即时编译,边运行边编译; // AOT,Ahead Of Time,提前编译,指运行前编译。 其中包含APK中已经过AOT编译的方法代码。 原始DEX文件(例如classes.dex)被转换为另一个包含本机代码的文件。这个新文件通常具有.odex。.oat扩展名,并由ELF格式

he980725866的博客 3620

基于C语言和EsayX实现贪吃蛇小游戏

本代码可实现贪吃蛇小游戏以及将对话框转换为自定义界面。

qq_58981016的博客 470

STM32+A4988控制步进电机

基于A4988芯片驱动42步进电机,在STM32F1上进行测试,最高可以实现16细分。

个人博客 4万+

C语言贪吃蛇小游戏,贪吃蛇C语言代码实现大全

设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言。贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏,也就是实现贪吃蛇的基本功能,比如在地图中,用“↑↓←→”控制移动蛇的方向,吃掉食物之后,蛇身体会变长等等。。。。首先我们得分析,游戏中我们会碰见的一些情况。

chengxuyuanlaow的博客 3万+

Dell R730安装Windows Server 2008 R2

一、RAID驱动准备  在安装Windows server2008 R2操作系统时,如果没有发现硬盘,请将下面的阵列卡驱动先解压(在下载的.exe文件,双击点下一步会自动解压),解压后把那文件夹复制到U盘中。阵列卡 PERC9 H330 - H730 - H730P - H830 Controllers for Windows Server 2008 R2二、系统安装方法方法一:光盘安装  ①请您...

MarshLin 2万+

FPGA AXI DDR3读写时序调试与FIFO缓存实战解析

本文深入解析FPGA中AXI协议与DDR3的读写时序调试,重点探讨FIFO缓存在数据流控制中的关键作用。通过实战案例详细讲解突发传输中的地址对齐、数据分片等常见问题,并提供状态机设计、仿真波形分析和性能优化技巧,帮助开发者高效解决高速数据存储场景下的时序同步与数据一致性问题。

blockchain9miner的博客 1118

小游戏贪吃蛇的实现之C语言版

贪吃蛇游戏的实现

我要学编程的博客 4836

如何实现一个用STM32F103C8T6作为主控芯片,加上AD8232、MAX30102 、MPU6050模块的用于运动中监测心率变异性??

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1212

C语言小游戏--贪吃蛇实现

C语言小游戏--贪吃蛇实现

2301_79761834的博客 3337

基于Simulink的PR(比例谐振)控制器在单相整流中的应用

本文详细介绍了基于Simulink的单相整流系统中PR(比例谐振)控制器的设计与实现方法。首先阐述了PR控制器相较于传统PI控制器的优势,能够有效消除交流信号跟踪中的稳态误差。接着系统讲解了PR控制器的传递函数原理、实用型设计(带带宽)及参数整定方法。通过Simulink建模步骤,包括主功率电路搭建、电压外环PI设计、电流指令生成、PR控制器实现(推荐MATLAB Function方法)和SPWM调制等关键环节。仿真对比显示PR控制在电流跟踪精度(THD仅1.9%)、功率因数(0.998)和动态响应(突变恢

amy_mhd的博客 689

PyTorch深度学习实战(36)——Pix2Pix详解与实现

Pix2Pix 是基于生成对抗网络的图像转换框架,能够将输入图像转换为与之对应的输出图像,能够广泛用于图像到图像转换的任务,如风格转换、图像修复、语义标签到图像的转换等。Pix2Pix 的核心思想是通过对抗训练将输入图像和目标输出图像进行配对,使生成网络可以学习到输入图像到输出图像的映射关系。在本节中,将学习使用 Pix2Pix 根据给定轮廓生成图像。

盼小辉丶的博客 1万+

【C语言】c语言之基于链表实现贪吃蛇小游戏

贪吃蛇作为一个游戏,肯定被大家所熟知,我相信大部分人都玩过这个游戏,所以规则我相信大家都明白,在这我就不过多讲述了。然后今天我们就要基于C语言链表这个内容去实现这个小游戏,下面我们先来看一下这个我们将会实现的几个画面。这就是我们这个游戏的几个画面,下面我们就要基于这几个画面去实现我们的贪吃蛇小游戏。实现过程使用了WIN32的一些API,这里简单介绍一下这些API的功能。本篇博客我们介绍了关于贪吃蛇小游戏的创作,希望对大家有所帮助!

bhbcdxb123的博客 1767

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

C语言实现贪吃蛇

本文旨在使用C语言和基础数据结构链表来实现贪吃蛇经典小游戏更多精彩 点击个人主页:酷酷学!!!代码仓库Gitee:爱马仕正文开始贪吃蛇是久负盛名的游戏, 它和俄罗斯方块, 扫雷等游戏位列经典游戏的行列.在编程语言的学习中, 以贪吃蛇为例, 来提高编程能力和逻辑能力.以上是一个简单的贪吃蛇游戏的代码总结,具体的实现方式可能会有所不同,但是核心的思路是相同的。!!

酷酷学!!!的博客 3988

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【C语言小游戏】贪吃蛇

C语言贪吃蛇

m0_52602233的博客 2490

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

用c 语言实现贪吃蛇小游戏

贪吃蛇每行动一格就要考虑当前方向、下一步的方向、下一步是否吃到食物(如果吃到食物则下一步变成蛇的头其他不变;当按下的按键方向与原运动方向相反时,贪吃蛇方向不改变;只有当按下的按键方向与原运动方向不相反时,贪吃蛇方向变成按键方向。首先要想在窗口中随意构图,便要先获取终端设备的句柄(⽤来标识不同设备的数值,使⽤它可以操作设备)。当蛇头碰到了墙、蛇头碰到了蛇身、按下“ESCAPE”正常退出、按下空格。

2301_80129952的博客 2114
上一篇: 链表的应用——链表实现通讯录
下一篇: 链表的环的问题
鼓浪屿Estelle
博客等级 码龄3年 307粉丝 11原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值