C语言实现贪吃蛇

前言:今天给大家详细介绍一下小游戏贪吃蛇的代码。

目录

一 .贪吃蛇实现的功能 

二.贪吃蛇游戏设计与分析 

1.贪吃蛇以及贪吃蛇所需要维护的数据

(1)贪吃蛇蛇体

(2)数据维护 

2.地图设计

(1)标题以及行高和列宽的修改。

​编辑 (2)光标的隐藏以及光标位置的定位

(2)游戏开始第一界面 

(3)游戏开始第二界面

(4)游戏开始第三界面 

 3.蛇身以及食物的生成

(1)蛇身 

 (2)食物的生成

三.游戏运行

1.游戏的规则分数以及食物权重的打印

2.检测按键是否被按过

3.贪吃蛇的移动

(1)下一步移动位置是食物

(2)下一步移动位置不是食物 

4.检测是否撞墙或咬到自己

四.游戏结束后的善后

五.完整代码的实现

1.snake.h

2.snake.c

3.tets.c


一 .贪吃蛇实现的功能 

使⽤C语⾔在Windows环境的控制台中模拟实现经典⼩游戏贪吃蛇
实现基本的功能:
贪吃蛇地图绘制
蛇吃⻝物的功能 (上、下、左、右⽅向键控制蛇的动作)
蛇撞墙死亡
蛇撞⾃⾝死亡
计算得分
蛇⾝加速、减速
暂停游戏

我们贪吃蛇所需要用到的知识有:

C语⾔函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。

二.贪吃蛇游戏设计与分析 

1.贪吃蛇以及贪吃蛇所需要维护的数据

(1)贪吃蛇蛇体

蛇体我们使用链表的方式来操作

搜先我们定义一个结构体通过光标信息的x,y操作来定位蛇的位置以及后续的移动

typedef struct SnakeNode {
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

(2)数据维护 

在玩游戏的过程中我们需要将游戏的运行状态,蛇身的状态,方向,分数,食物,食物权重,速度(休眠时间),游戏运行状态进行维护起来,那么这时我们就需要创建一个存放各个数据的结构体:

//贪吃蛇各个数据维护
typedef struct Snake
{
	pSnakeNode pSnake;//蛇的信息
	pSnakeNode pFood;//食物信息
	enum DIR dir;//蛇运动的方向
	enum STATUE statue;//游戏运行的状态
	int Score;//分数
	int FoodWeight;//食物权重
	int SleepTime;//速度(休眠时间)
}Snake,*pSnake;

同样为了方便起见我们对蛇运动方向以及游戏状态用枚举来更简单的表达出来:

//贪吃蛇移动方向
enum DIR {
	UP=1,
	DOWN,
	LEFT,
	RIGHT,
};
//游戏进行的状态
enum STATUE {
	OK=1,
	ESC,
	KILL_BY_WALL,
	KILL_BY_SELF,
};

2.地图设计

首先这是我们刚进入游戏所需要的准备工作:

我们可以看到控制台的标题以及光标输入文字的定位信息都有一定的不一样,并且可以看到这边并没有光标的闪烁。

这些就是我们上一篇文章所写的内容我们可以使用cmd控制台来改变行号和列宽并且标题也与我们平常编译不一样。

(1)标题以及行高和列宽的修改。

//设置控制台的行和列
	system("mode con cols=100 lines=30");
	//设置控制台额名称
	system("title 贪吃蛇");

 (2)光标的隐藏以及光标位置的定位

	//隐藏光标
	//获取设备句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//获取光标信息
	CONSOLE_CURSOR_INFO CurInfor;
	GetConsoleCursorInfo(handle, &CurInfor);
	//修改光标信息
	CurInfor.bVisible = false;
	SetConsoleCursorInfo(handle, &CurInfor);
	//游戏开始界面的设计

 我们可以看到在加上getchar()的情况下控制台界面上仍然没有光标的显示。

(2)游戏开始第一界面 

平常注意点我们可以发现每次打印信息都是在输入框的左上角的起点开始,但是我们的第一界面的输入信息并没有在左上角的起点而是在控制台的中间。这时我们就可以用到上篇用到我们的COORD的坐标信息来定位。我们可以将其包装成一个函数:

void Setpos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x, y };
	SetConsoleCursorPosition(handle, pos);
}

这时我吗们就可以开始对页面信息的编写:

	//第一界面
	Setpos(40, 12);
	printf("欢迎来到贪吃蛇小游戏!");
	
	Setpos(40, 25);
	system("pause");
	system("cls");

 效果就是这样的。

(3)游戏开始第二界面

第二界面也就是游戏的操作方式以及得分规则(与第一界面方式相同)

	//第二界面
	Setpos(30, 12);
	printf("请使用↑ ↓← →来控制蛇的方向。");
	Setpos(30, 13);
	printf("使用F3,F4来实现加速和减速,加速能得到更高的分数。");
	
	Setpos(30, 25);
	system("pause");
	system("cls");

使用同样的方式来进行编写。

(4)游戏开始第三界面 

我们这第三界面也就是我们地图的打印以及分数和一些规则提示,我们先将地图打印剩下分数由于涉及到分数的变化以及食物权重的问题在游戏开始界面时另行设计。

我们假设实现⼀个棋盘27⾏,58列的棋盘(⾏和列可以根据⾃⼰的情况修改),再围绕地图画出墙, 如下:

 地图我们也是可以用到我们的循环以及光标位置的设计,实现出来也不是特别的困难。

值得注意的是我们需要将地图的大小来设计清楚。搜先我们墙体是中文符号'□',也就是我们所说的宽字符,占x坐标上的2个单元格也就是一个'□'会占x轴的两个坐标。

为了方便我们使用宏的方法来定义墙体符号,可以便于后期的更改:

#define WALL L'□'

下面是具体实现过程

	//打印地图
	int i = 0;
	// 上
	for (i = 0; i < 57; i += 2)
	{
		Setpos(i, 0);
		wprintf(L"%lc", WALL);
	}
	// 下
	for (i = 0; i < 57; i += 2)
	{
		Setpos(i, 25);
		wprintf(L"%lc", WALL);
	}
	//左
	for (i = 0; i < 25; i += 1)
	{
		Setpos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (i = 0; i < 25; i += 1)
	{
		Setpos(56, i);
		wprintf(L"%lc", WALL);
	}

 3.蛇身以及食物的生成

(1)蛇身 

初始化状态,假设蛇的⻓度是5,蛇⾝的每个节点是●,在固定的⼀个坐标处,⽐如(24, 5)处开始出现 蛇,连续5个节点。注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有⼀半 ⼉出现在墙体中,另外⼀般在墙外的现象,坐标不好对⻬。
	int i = 0;
	pSnakeNode pcur = NULL;
	for (i = 0; i < 5; i++)
	{
		pcur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pcur == NULL)
		{
			perror("InitSnake():malloc()");
			return;
		}
		pcur->x = POS_X + 2 * i;
		pcur->y = POS_Y;
		pcur->next = NULL;
    }

首先我们先完成蛇身的创建,接下来就是我们如何将蛇身去链接起来。

为了方便起见我们可以将蛇头设定为链表的头将蛇的尾巴设定为链表的尾 ,也就是说我们需要从蛇尾进行创建。那么当我们的Psnake为空时我们将pcur的信息赋予给我们的Psnake,我们在传参时将我们的蛇数据的维护为Psnake设定为ps。

并且运用头插的方式进行依次插入:

	int i = 0;
	pSnakeNode pcur = NULL;
	for (i = 0; i < 5; i++)
	{
		pcur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pcur == NULL)
		{
			perror("InitSnake():malloc()");
			return;
		}
		pcur->x = POS_X + 2 * i;
		pcur->y = POS_Y;
		pcur->next = NULL;
		if (ps->pSnake == NULL)
		{
			ps->pSnake = pcur;
		}
		else
		{
			pcur->next = ps->pSnake;
			ps->pSnake = pcur;
		}
	}

 以及将蛇的起始位置定位到自己像定位的位置进行打印(依自己的喜好来定):

	//打印蛇身
	pcur = ps->pSnake;
	while (pcur)
	{
		Setpos(pcur->x, pcur->y);
		wprintf(L" % lc",BODY);
		pcur = pcur->next;
	}

 接下来就是将ps内容的其他数据进行初始化:

//设置贪吃蛇的其他内容
	ps->dir = RIGHT;
	ps->FoodWeight = 10;
	ps->pFood = NULL;
	ps->Score = 0;
	ps->SleepTime = 200;
	ps->statue = OK;

 (2)食物的生成

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

 先随机⽣成⻝物的坐标

x坐标必须是2的倍数
⻝物的坐标不能和蛇⾝每个节点的坐标重复
创建⻝物节点,打印⻝物

为了方便我们同样可以用宏来进行定义:

#define FOOD L'★'
	int x = 0;
	int y = 0;
	again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);
	pSnakeNode pcur = ps->pSnake;
	while (pcur)
	{
		if (x==pcur->x && y==pcur->y)
		{
			goto again;
		}
		pcur = pcur->next;
	}
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreatFood::malloc");
		return;
	}
	
	food->x = x;
	food->y = y;
	ps->pFood = food;

创建好食物信息之后就是进行食物的打印:

	//打印食物
	Setpos(x,y );
	wprintf(L"%lc", FOOD);

三.游戏运行

1.游戏的规则分数以及食物权重的打印

在上面我们打印地图时没有对分数以及规则的打印,现在我们来进行对这一部分的打印。

在玩游戏时我们的分数以及食物的权重是会变化的,但是规则内容不会变则我们要将规则放到循环外面,分数以及食物权重放到循环内部。

循环我们可以设定一下什么情况下结束游戏什么情况下游戏正常运行,并且我们必须要进入函数一回才能正常运行游戏。所以我们可以使用do while函数。

首先完成对规则的打印:

	Setpos(62, 15);
	printf("1.使用↑ ↓← →来控制蛇的方向。");
	Setpos(62, 16);
	printf("2.F3,F4来实现加速和减速。");
	Setpos(62, 17);
	printf("3.加速会获得更高的分数。");
	Setpos(62, 18);
	printf("4.不能穿墙,不能碰到自己。");
	Setpos(62, 19);
	printf("版权来自@xxx");

将分数打印到循环的内部: 

    Setpos(62, 14);
    printf("得分:%05d   食物权重:%02d", ps->Score, ps->FoodWeight);

2.检测按键是否被按过

上一篇文章有讲到过按键的检测,为了方便使用我们用宏定义这个函数:

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

并且我们可以根据每个按键的虚拟键码来检测是否按过。

我们需要用到的按键分别有↑ ,↓,←, →,F3 , F4, " ",ESC。 

当按下加速或者减速键时根据自己喜好调节速度加减,减速以及分数增加的多少与分数减少多少。

并且蛇转向的方向不能是蛇身移动的对立方向。

代码如下:

		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_ESCAPE))
		{
			ps->statue = ESC;
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			GamePause();
		}
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime >= 80)
			{
				ps->SleepTime -= 30;
				ps->FoodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodWeight > 2)
			{
				ps->SleepTime += 30;
				ps->FoodWeight -= 2;
			}
		}

3.贪吃蛇的移动

我们在移动时无非也就是这4种情况。下面我们来分别对这几种情况进行编写:

switch (ps->dir)
	{
	case UP:
		NEXT->x = ps->pSnake->x;
		NEXT->y = ps->pSnake->y - 1;
		break;
	case DOWN:
		NEXT->x = ps->pSnake->x;
		NEXT->y = ps->pSnake->y + 1;
		break;
	case LEFT:
		NEXT->x = ps->pSnake->x-2;
		NEXT->y = ps->pSnake->y;
		break;
	case RIGHT:
		NEXT->x = ps->pSnake->x + 2;
		NEXT->y = ps->pSnake->y;
		break;

	}

我们将可能发生的几种情况下一步位置的信息储存到一个NEXT指针中,方便后面进行的头插与打印:

	pSnakeNode NEXT = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (NEXT == NULL)
	{
		perror("SnakeMove::malloc");
		return;
	}
	NEXT->next = NULL;

我们贪吃在移动时可能会面临两种情况:

1.下一步移动位置是食物。

2.下一步移动位置不是食物。

(1)下一步移动位置是食物

如果下一步是食物那么我们就不用对尾部的链表进行删除,只需要将我们的NEXT插入到我们蛇的头部并且顺带将蛇身也进行打印以及分数的增加也就是这样:

NEXT->next = ps->pSnake;
	ps->pSnake = NEXT;
	pSnakeNode pcur = ps->pSnake;
	while (pcur)
	{
		Setpos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	ps->Score += ps->FoodWeight;

下面就是对食物的释放以及重新放置新的食物:

	//释放旧的食物
	free(ps->pFood);
	//创建新的食物
	CreadFood(ps);

(2)下一步移动位置不是食物 

 这个情况相当与上面的情况比较复杂一点,涉及到了尾部链表的释放,其他都与上面没有什么区别(注意:将释放掉的空间打印为空格):

NEXT->next = ps->pSnake;
	ps->pSnake = NEXT;
	pSnakeNode pcur = ps->pSnake;
	while (pcur->next->next)
	{
		
		Setpos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	Setpos(pcur->next->x, pcur->next->y);
	printf("  ");
	free(pcur->next);
	pcur->next = NULL;

4.检测是否撞墙或咬到自己

这两种就比较简单了只需要遍历并观察是否与墙的坐标重合,以及身体有没有与头部坐标重合即可。

检测是否撞墙:

	if (ps->pSnake->x == 0 ||
		ps->pSnake->x == 56 ||
		ps->pSnake->y == 0 ||
		ps->pSnake->y == 25)
	{
		ps->statue = KILL_BY_WALL;
	}

检测是否咬到自己:

pSnakeNode pcur = ps->pSnake->next;

	while (pcur)
	{
		if (pcur->x == ps->pSnake->x && pcur->y == ps->pSnake->y)
		{
			ps->statue = KILL_BY_SELF;
			return;
		}
		pcur = pcur->next;
	}

四.游戏结束后的善后

无非也就是将空间回收,以及游戏状态的处理方式,以及一些提示语:

	Setpos(15, 22);
	switch (ps->statue)
	{
	case OK:
		break;
	case ESC:
		printf("退出成功\n");
		break;
	case KILL_BY_SELF:
		printf("由于你的操作失误小蛇将自己咬死了\n");
		break;
	case KILL_BY_WALL:
		printf("由于你的操作失误小蛇被撞死了\n");
		break;
	}
	//释放贪吃蛇的资源
	pSnakeNode pcur = ps->pSnake;
	pSnakeNode del = NULL;
	while (pcur)
	{
		del = pcur;
		pcur = pcur->next;
		free(del);
		del = NULL;
	}

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

五.完整代码的实现

1.snake.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>

#define WALL L'□'
#define BODY L'●' //★○●◇◆□■
#define FOOD L'★' //★○●◇◆□■

#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
//初始蛇的位置
#define POS_X 24
#define POS_Y 5
//贪吃蛇移动方向
enum DIR {
	UP=1,
	DOWN,
	LEFT,
	RIGHT,
};
//游戏进行的状态
enum STATUE {
	OK=1,
	ESC,
	KILL_BY_WALL,
	KILL_BY_SELF,
};
//创建一个蛇的链表
typedef struct SnakeNode {
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

//贪吃蛇各个数据维护
typedef struct Snake
{
	pSnakeNode pSnake;//蛇的信息
	pSnakeNode pFood;//食物信息
	enum DIR dir;//蛇运动的方向
	enum STATUE statue;//游戏运行的状态
	int Score;//分数
	int FoodWeight;//食物权重
	int SleepTime;//速度(休眠时间)
}Snake,*pSnake;

//游戏开始准备工作
void BeforeGame(pSnake ps);
//游戏开始时的界面打印
void GameShow();
//初始化蛇
void InitSnake(pSnake ps);
//创建食物
void CreadFood(pSnake ps);
//游戏中的内容
void Gameing(pSnake ps);
//游戏旁的操作内容
void GameWarn();
//游戏暂停
void GamePause();
//贪吃蛇的移动
void SnakeMove(pSnake ps);
//移动的目的地有食物
void EatFoof(pSnake ps,pSnakeNode NEXT);
//判断蛇头移动的位置是不是食物
int ISfoog(pSnake ps,pSnakeNode NEXT);
//移动的目的地不是食物
void NotEatFood(pSnake ps,pSnakeNode NEXT);
//检测是否撞墙
void IFWall(pSnake ps);
//检测是否咬到自己
void IFSelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);
void Setpos(int x, int y);

2.snake.c

#include"snake.h"
//玩游戏前的准备
void Setpos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x, y };
	SetConsoleCursorPosition(handle, pos);
}
void GameShow()
{
	//第一界面
	Setpos(40, 12);
	printf("欢迎来到贪吃蛇小游戏!");
	
	Setpos(40, 25);
	system("pause");
	system("cls");
	
	//第二界面
	Setpos(30, 12);
	printf("请使用↑ ↓← →来控制蛇的方向。");
	Setpos(30, 13);
	printf("使用F3,F4来实现加速和减速,加速能得到更高的分数。");
	
	Setpos(30, 25);
	system("pause");
	system("cls");

	//打印地图
	int i = 0;
	// 上
	for (i = 0; i < 57; i += 2)
	{
		Setpos(i, 0);
		wprintf(L"%lc", WALL);
	}
	// 下
	for (i = 0; i < 57; i += 2)
	{
		Setpos(i, 25);
		wprintf(L"%lc", WALL);
	}
	//左
	for (i = 0; i < 25; i += 1)
	{
		Setpos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (i = 0; i < 25; i += 1)
	{
		Setpos(56, i);
		wprintf(L"%lc", WALL);
	}

}
//初始化蛇
void InitSnake(pSnake ps)
{
	int i = 0;
	pSnakeNode pcur = NULL;
	for (i = 0; i < 5; i++)
	{
		pcur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pcur == NULL)
		{
			perror("InitSnake():malloc()");
			return;
		}
		pcur->x = POS_X + 2 * i;
		pcur->y = POS_Y;
		pcur->next = NULL;
		if (ps->pSnake == NULL)
		{
			ps->pSnake = pcur;
		}
		else
		{
			pcur->next = ps->pSnake;
			ps->pSnake = pcur;
		}
	}
	//打印蛇身
	pcur = ps->pSnake;
	while (pcur)
	{
		Setpos(pcur->x, pcur->y);
		wprintf(L" % lc",BODY);
		pcur = pcur->next;
	}

	//设置贪吃蛇的其他内容
	ps->dir = RIGHT;
	ps->FoodWeight = 10;
	ps->pFood = NULL;
	ps->Score = 0;
	ps->SleepTime = 200;
	ps->statue = OK;

}
//创建食物

void CreadFood(pSnake ps)
{
	int x = 0;
	int y = 0;
	again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);
	pSnakeNode pcur = ps->pSnake;
	while (pcur)
	{
		if (x==pcur->x && y==pcur->y)
		{
			goto again;
		}
		pcur = pcur->next;
	}
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreatFood::malloc");
		return;
	}
	
	food->x = x;
	food->y = y;
	ps->pFood = food;
	//打印食物
	Setpos(x,y );
	wprintf(L"%lc", FOOD);

}

void BeforeGame(pSnake ps)
{

	//设置控制台的行和列
	system("mode con cols=100 lines=30");
	//设置控制台额名称
	system("title 贪吃蛇");
	//隐藏光标
	//获取设备句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//获取光标信息
	CONSOLE_CURSOR_INFO CurInfor;
	GetConsoleCursorInfo(handle, &CurInfor);
	//修改光标信息
	CurInfor.bVisible = false;
	SetConsoleCursorInfo(handle, &CurInfor);
	//游戏开始界面的设计
	GameShow();
	//初始化蛇
	InitSnake(ps);
	//创建食物
	CreadFood(ps);
}
//游戏旁的操作内容

void GameWarn()
{
	Setpos(62, 15);
	printf("1.使用↑ ↓← →来控制蛇的方向。");
	Setpos(62, 16);
	printf("2.F3,F4来实现加速和减速。");
	Setpos(62, 17);
	printf("3.加速会获得更高的分数。");
	Setpos(62, 18);
	printf("4.不能穿墙,不能碰到自己。");
	Setpos(62, 19);
	printf("版权来自@xxx");
	
}
//游戏暂停
void GamePause()
{
	while (1)
	{
		Sleep(100);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}
//判断蛇头移动的位置是不是食物
int ISfoog(pSnake ps, pSnakeNode NEXT)
{
	if (ps->pFood->x == NEXT->x && ps->pFood->y == NEXT->y)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//移动的目的地有食物
void EatFoof(pSnake ps, pSnakeNode NEXT)
{
	NEXT->next = ps->pSnake;
	ps->pSnake = NEXT;
	pSnakeNode pcur = ps->pSnake;
	while (pcur)
	{
		Setpos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	ps->Score += ps->FoodWeight;
	//释放旧的食物
	free(ps->pFood);
	//创建新的食物
	CreadFood(ps);
}
void NotEatFood(pSnake ps, pSnakeNode NEXT)
{
	NEXT->next = ps->pSnake;
	ps->pSnake = NEXT;
	pSnakeNode pcur = ps->pSnake;
	while (pcur->next->next)
	{
		
		Setpos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	Setpos(pcur->next->x, pcur->next->y);
	printf("  ");
	free(pcur->next);
	pcur->next = NULL;
}
//检测是否撞墙
void IFWall(pSnake ps)
{
	if (ps->pSnake->x == 0 ||
		ps->pSnake->x == 56 ||
		ps->pSnake->y == 0 ||
		ps->pSnake->y == 25)
	{
		ps->statue = KILL_BY_WALL;
	}
	
}
//检测是否咬到自己
void IFSelf(pSnake ps)
{
	pSnakeNode pcur = ps->pSnake->next;

	while (pcur)
	{
		if (pcur->x == ps->pSnake->x && pcur->y == ps->pSnake->y)
		{
			ps->statue = KILL_BY_SELF;
			return;
		}
		pcur = pcur->next;
	}
}
//贪吃蛇的移动
void SnakeMove(pSnake ps)
{
	pSnakeNode NEXT = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (NEXT == NULL)
	{
		perror("SnakeMove::malloc");
		return;
	}
	NEXT->next = NULL;
	switch (ps->dir)
	{
	case UP:
		NEXT->x = ps->pSnake->x;
		NEXT->y = ps->pSnake->y - 1;
		break;
	case DOWN:
		NEXT->x = ps->pSnake->x;
		NEXT->y = ps->pSnake->y + 1;
		break;
	case LEFT:
		NEXT->x = ps->pSnake->x-2;
		NEXT->y = ps->pSnake->y;
		break;
	case RIGHT:
		NEXT->x = ps->pSnake->x + 2;
		NEXT->y = ps->pSnake->y;
		break;

	}
	//判断蛇头移动的位置是不是食物
	if (ISfoog(ps, NEXT))
	{
		//移动的目的地有食物
		EatFoof(ps, NEXT);
	}
	else
	{
		//移动的目的地不是食物
		NotEatFood(ps, NEXT);
	}
	//检测是否撞墙
	IFWall(ps);
	//检测是否咬到自己
	IFSelf(ps);
}

//游戏开始的内容
void Gameing(pSnake ps)
{
	//游戏旁的操作内容
	GameWarn();
	do 
	{
		// 当前的得分以及食物的权重
		Setpos(62, 14);
		printf("得分:%05d   食物权重:%02d", ps->Score, ps->FoodWeight);
	   //检测按键是否被按过
		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_ESCAPE))
		{
			ps->statue = ESC;
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			GamePause();
		}
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime >= 80)
			{
				ps->SleepTime -= 30;
				ps->FoodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodWeight > 2)
			{
				ps->SleepTime += 30;
				ps->FoodWeight -= 2;
			}
		}
		//贪吃蛇的移动
		SnakeMove(ps);
		//休眠一下
		Sleep(ps->SleepTime);
	
	} while (ps->statue==OK);
	
}
//游戏结束
void GameEnd(pSnake ps)
{
	Setpos(15, 22);
	switch (ps->statue)
	{
	case OK:
		break;
	case ESC:
		printf("退出成功\n");
		break;
	case KILL_BY_SELF:
		printf("由于你的操作失误小蛇将自己咬死了\n");
		break;
	case KILL_BY_WALL:
		printf("由于你的操作失误小蛇被撞死了\n");
		break;
	}
	//释放贪吃蛇的资源
	pSnakeNode pcur = ps->pSnake;
	pSnakeNode del = NULL;
	while (pcur)
	{
		del = pcur;
		pcur = pcur->next;
		free(del);
		del = NULL;
	}

	free(ps->pFood);
	ps->pFood = NULL;
}

3.tets.c

#include"snake.h"

void test1()
{
	srand((unsigned int)time(NULL));
	int ch=0;
	do 
	{
		Snake snake = { 0 };
		//游戏的准备工作
		BeforeGame(&snake);
		//游戏内容
		Gameing(&snake);
		//游戏结束
		GameEnd(&snake);
		Setpos(20, 15);
		printf("是否再来一次(Y/N)");

		ch = getchar();
		getchar();
	} while (ch == 32 || ch == 32+32);
	
}
int main()
{
	//将类项改为当前地区
	setlocale(LC_ALL, "");
	test1();
	return 0;
}

好了兄弟们这就是贪吃蛇的完整内容了,喜欢的朋友们一键三连支持一下。

毕竟你们的支持才是博主的动力嘛!也请大佬们指点。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

详解7道经典指针运算笔试题!

目录 ​编辑 1. 题目一 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 2. 题目二 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 3. 题目三 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 4. 题目四 &#xff08;…

鸿蒙开发为什么这么火,现在入行鸿蒙是否来的及?

鸿蒙开发是当前备受关注的技术领域之一&#xff0c;对于想要入门学习鸿蒙开发的初学者来说&#xff0c;需要掌握一定的基础知识和技能。鸿蒙开发又是否能为程序员们带来一片光明的未来呢&#xff1f;让我们一同探讨这些问题。 对于初学者来说&#xff0c;鸿蒙开发是否易于上手呢…

构建空间场景轻应用,Mapmost Alpha来啦【文末赠书(10本)--第一期】

文章目录&#xff1a; 一、Mapmost Alpha 介绍二、Mapmost Alpha应对数字孪生业务痛点解决之道2.1 Mapmost Alpha 提供海量城市底板2.2 Mapmost Alpha 提供便捷的配置管理工具2.3 Mapmost Alpha 提供一键式部署发布和分享 三、沉浸式体验Mapmost Alpha3.1 创建应用3.2 新手指导…

树莓派安装Nginx服务搭建web网站结合内网穿透实现公网访问本地站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

Everything:文件查找工具,一搜即得

名人说&#xff1a;东边日出西边雨&#xff0c;道是无晴却有晴。——刘禹锡 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍①Everything②核心功能③原理 二、下载安装①下载②安装 三、使用方法①文…

如何利用音频转换器将多个MP3音频转换为OGG格式

现在&#xff0c;我们接触到的很多音频文件一般都是MP3格式的。但是我们偶尔也需要用到ogg格式的音频文件&#xff0c;所以就需要我们将MP3格式音频文件转换为ogg格式了&#xff0c;那么&#xff0c;如果想要将MP3格式文件转换为OGG格式该如何操作呢&#xff1f;相信很多朋友心…

Linux内核之报错-Werror,-Wunused-variable等通用解决方案(二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

企业数据备份体系化方法论的七大原则之二:区分环境数据与业务数据

在之前讨论的分布式存储系统七大原则的第一原则中&#xff0c;我们了解了容灾切换和数据备份的差异。现在&#xff0c;我们继续探索第二原则&#xff1a;区分环境数据与业务数据。这一原则强调了两种类型数据在变化频率、价值以及数据一致性获取难度方面的根本区别&#xff0c;…

Python中starmap有什么用的?

目录 前言 starmap函数的作用 starmap函数的用法 starmap函数的示例 1. 对每个元组元素进行求和 2. 对每个元组元素进行乘积 实际应用场景 1. 批量处理函数参数 2. 并行处理任务 3. 批量更新数据库 总结 前言 在Python中&#xff0c; starmap 是一个非常有用的函数&…

Vue3 + antv/x6 实现流程图

新建流程图 // AddDag.vue <template><div class"content-main"><div class"tool-container"><div click"undo" class"command" title"后退"><Icon icon"ant-design:undo-outlined" /…

用信号的方式回收僵尸进程

当子进程退出后&#xff0c;会给父进程发送一个17号SIGCHLD信号&#xff0c;父进程接收到17号信号后&#xff0c;进入信号处理函数调用waitpid函数回收僵尸进程若多个子进程同时退出后&#xff0c;这是切回到父进程&#xff0c;此时父进程只会处理一个17号信号&#xff0c;其他…

pycharm手动安装常用插件

下载插件 &#xff08;1&#xff09;下载地址&#xff1a;JetBrains Marketplace 这里以语言包为例子 2、中文语言包 进入pycharm中的设置&#xff0c;点击plugins,选从磁盘中安装插件

2024.3.11 训练记录(13)

继续补题 文章目录 ICPC 2018青岛I Soldier GameICPC 2018青岛K Airdrop ICPC 2018青岛I Soldier Game 题目链接 线段树 果然稍微复杂一点的线段树就很难实现啊&#xff0c;不看题解根本没反应过来是线段树 struct Node {int l, r, lb, rb, nb, b; } tr[N * 4];其中&#x…

帮管客 CRM jiliyu SQL注入漏洞复现

0x01 产品简介 帮管客CRM是一款集客户档案、销售记录、业务往来等功能于一体的客户管理系统。帮管客CRM客户管理系统,客户管理,从未如此简单,一个平台满足企业全方位的销售跟进、智能化服务管理、高效的沟通协同、图表化数据分析帮管客颠覆传统,重新定义企业管理系统。 …

不知道吧,腾讯云轻量应用服务器使用有一些限制!

腾讯云轻量应用服务器相对于云服务器CVM是有一些限制的&#xff0c;比如轻量服务器不支持更换内网IP地址&#xff0c;不支持自定义私有网络VPC&#xff0c;内网连通性方面也有限制&#xff0c;轻量不支持CPU内存、带宽或系统盘单独升级&#xff0c;只能整个套餐整体升级&#x…

使用Git拉取代码时候出现error file write error no space left on device问题如何解决

1. 检查磁盘空间 首先&#xff0c;确认是哪个分区或驱动器满了。你可以使用以下命令来查看各分区的使用情况&#xff1a; 对于 Linux 或 macOS&#xff1a; df -h对于 Windows&#xff0c;你可以在“我的电脑”中查看各驱动器的剩余空间&#xff0c;或者在命令提示符&#x…

基于遗传算法改进的RBF神经网络流量控制,基于GA-RBF的流量预测

目录 完整代码和数据下载链接:基于遗传算法改进的RBF神经网络流量控制,基于GA-RBF的流量预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88937452 RBF的详细原理 RBF的定义 RBF理论 易错及常见问题 RBF应用实例,基于遗传算法改…

力扣刷题Days15第二题-137. 只出现一次的数字 II(js)

目录 1&#xff0c;题目 2&#xff0c;代码 2.1哈希表 2.2 依次确定每一个二进制位 3&#xff0c;学习与总结 1. 左移运算符 (<<) 2. 有符号右移运算符 (>>) 3. 无符号右移运算符 (>>>) 1&#xff0c;题目 给你一个整数数组 nums &#xff0c;除…

不要在代码中随便使用try...catch了

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 背景 js中的try...catch try...catch运行机制 js的事件循环机制 try...c…

PMP没过能考下一次吗?PMP·考试不过下次费用多少?

每年都有许多人参加PMP考试&#xff0c;然而&#xff0c;并不是每个人都能一次性通过PMP考试&#xff0c;许多人可能会面临失败。带你了解PMP补考费用以及补考流程 01PMP补考费用是多少 考生若未能成功通过考试&#xff0c;还可以在一年PMI有效期内提交补考申请&#xff0c;若…