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

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

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

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-11 18:08:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-11 18:08:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-11 18:08:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-11 18:08:03       20 阅读

热门阅读

  1. docker创建自定义网络

    2024-03-11 18:08:03       22 阅读
  2. python协程理解笔记,

    2024-03-11 18:08:03       20 阅读
  3. 递归在解决链表问题中的应用

    2024-03-11 18:08:03       20 阅读
  4. 前端环境配置Nodejs,NRM,YARN

    2024-03-11 18:08:03       21 阅读
  5. vue,elementUI多表单同时提交,表单校验解决方案

    2024-03-11 18:08:03       22 阅读
  6. 高级优化理论与方法(三)

    2024-03-11 18:08:03       18 阅读
  7. List数据去重的4种有效方法

    2024-03-11 18:08:03       25 阅读
  8. linux服务器升级tomcat步骤

    2024-03-11 18:08:03       20 阅读