C语言实现贪吃蛇

news2024/10/5 16:32:26

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

目录

一 .贪吃蛇实现的功能 

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

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

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

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

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

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

相关文章

【三十】springboot项目上高并发解决示例

互相交流入口地址 整体目录&#xff1a; 【一】springboot整合swagger 【二】springboot整合自定义swagger 【三】springboot整合token 【四】springboot整合mybatis-plus 【五】springboot整合mybatis-plus 【六】springboot整合redis 【七】springboot整合AOP实现日志操作 【…

【Python】一文详细介绍plt.rcParams 在 Matplotlib 中的原理、作用、注意事项

【Python】一文详细介绍plt.rcParams 在 Matplotlib 中的原理、作用、注意事项 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x…

导出谷歌浏览器收藏的网页,并查看网页保存的登录密码

导出谷歌浏览器&#xff08;Chrome&#xff09;收藏的网页&#xff08;书签&#xff09;&#xff1a; 打开谷歌浏览器。在浏览器右上角找到并点击三个垂直排列的小点&#xff08;或称汉堡菜单&#xff09;以打开主菜单。在下拉菜单中选择“书签” > “书签管理器”。在书签…

NodeJS实现线性查找算法

NodeJS实现线性查找算法 以下是使用 Node.js 实现线性搜索算法的示例代码&#xff1a; function linearSearch(arr, target) {for (let i 0; i < arr.length; i) {if (arr[i] target) {return i; // 如果找到目标&#xff0c;返回索引}}return -1; // 如果未找到目标&am…

Android随手记

activity的生命周期 创建时 onCreate() - onStart() - onResume() - onPause() - onStop() - onDestroy() 切换时 a切换到b a.onCreate() - a.onStart() - a.onResume - a.onPause - b.onCreate() - b.onStart() - b.onResume() - a.onStop() b切换回a b.onPause() - a.onR…

uniapp:小程序数字键盘功能样式实现

代码如下&#xff1a; <template><view><view><view class"money-input"><view class"input-container" click"toggleBox"><view class"input-wrapper"><view class"input-iconone"…

信息系统项目管理师--成本管理

项⽬成本管理重点关注完成项⽬活动所需资源的成本&#xff0c;但同时也考虑项⽬决策对项⽬产品、服务或成果的使⽤成本、维护成本和⽀持成本的影响。不同的⼲系⼈会在不同的时间&#xff0c;⽤不同的⽅法 测算项⽬成本。 就某些项⽬&#xff0c;特别是⼩项⽬⽽⾔&#xff0c;成…

鸿蒙操作系统 HarmonyOS 3.2 API 9 Stage模型通过ArkTS接入高德地图

用鸿蒙ArkTS语言开发地图APP应用时&#xff0c;很多地图厂商只接入了鸿蒙Java&#xff0c;ArkTS版本陆续接入中&#xff0c;等一段时间才能面世&#xff0c;当前使用地图只能通过鸿蒙的Web组件&#xff0c;将HTML页面嵌入到鸿蒙APP中。具体方法如下&#xff1a;编写HTML <!…

【Linux】Shell及Linux权限

Shell Shell的定义 Shell最简单的定义是&#xff1a;命令行解释器。 Shell的主要任务&#xff1a;1. 将使用者的命令翻译给核心进行处理。2.将核心的处理结果翻译给使用者 为什么要有Shell? 使用者和内核的关系就相当于两个完全陌生的外国人之间的关系&#xff0c;他们要进…

酒店客房管理系统|基于Springboot的酒店客房管理系统设计与实现(源码+数据库+文档)

酒店客房管理系统目录 目录 基于Springboot的酒店客房管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、 用户信息管理 2、会员信息管理 3、 客房信息管理 4、收藏客房管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机…

【Vue】.sync 修饰符作用

文章目录 基本用法 基本用法 官方文档是这样介绍的&#xff1a;.sync 修饰符 简单来说就是实现父子组件数据之间的双向绑定&#xff0c;当子组件修改了一个 props 的值时&#xff0c;也会同步到父组件中&#xff0c;实现子组件同步修改父组件&#xff0c;与v-model类似。类别在…

Redis分段锁,如何设计?

问题场景&#xff1a;热点库存扣减问题 秒杀场景&#xff0c;有一个难度的问题&#xff1a;热点库存扣减问题。 既要保证不发生超卖 又要保证高并发 如果解决这个高难度的问题呢&#xff1f; 答案就是使用redis 分段锁。 什么是分布式锁&#xff1f; 一个分布式系统中&am…

SSM整合项目(使用Vue3 + Element-Plus创建项目基础页面)

1.配置Vue启动端口 1.修改vue.config.js const {defineConfig} require(vue/cli-service) module.exports defineConfig({transpileDependencies: true }) module.exports {devServer: {port: 9999 //启动端口} }2.启动 2.安装Element Plus 命令行输入 npm install eleme…

老阳推荐的视频号带货蓝海项目靠谱吗?

近年来&#xff0c;随着短视频平台的崛起&#xff0c;视频号带货逐渐成为了一个热门的新兴行业。在这个领域里&#xff0c;不少专家和达人纷纷涌现&#xff0c;其中老阳就是备受关注的一位。他推荐的视频号带货蓝海项目吸引了众多眼球&#xff0c;但这样的项目究竟靠不靠谱呢?…

【小黑送书—第十二期】>>一本书讲透Elasticsearch:原理、进阶与工程实践(文末送书)

Elasticsearch 是一种强大的搜索和分析引擎&#xff0c;被广泛用于各种应用中&#xff0c;以其强大的全文搜索能力而著称。 不过&#xff0c;在日常管理 Elasticsearch 时&#xff0c;我们经常需要对索引进行保护&#xff0c;以防止数据被意外修改或删除&#xff0c;特别是在进…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《含海上风电制氢的综合能源系统分布鲁棒低碳优化运行》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

【网络】数据在同网段和跨网段通信流程

情景一&#xff1a;同一广播域内&#xff0c;两台主机通信过程&#xff1a; 当NO要和N1通信时&#xff0c;假如N0知道N1的IP但却不知道它的MAC地址&#xff0c;那NO就会发送一个ARP的广播请求<1>&#xff08;里面源IP是NO 目标IP是N1 源MAC是N0 目标MAC是12个F&#xff0…

Python 分析— 使用 LeuvenMapMatching 包进行地图匹配用于道路导航

在道路导航中,我们有了街道网络地图。轨迹/GPS 数据必须与街道相匹配才能进行导航,因为 GPS 读数提供纯粹的纬度和经度坐标,但我们想知道车辆行驶的具体道路。 我首先尝试了一种简单的方法来匹配点,将每个点独立地匹配到最近的路段。如果没有道路,只需扩大缓冲距离…

耐腐蚀特氟龙塑料材质PFA烧杯超纯试剂反应杯

PFA烧杯在实验过程中可作为储酸容器或涉及强酸强碱类实验的反应容器&#xff0c;用于盛放样品、试剂&#xff0c;也可搭配电热板加热、蒸煮、赶酸用。 外壁均有凸起刻度&#xff0c;直筒设计&#xff0c;带翻边&#xff0c;便于夹持和移动&#xff0c;边沿有嘴&#xff0c;便于…

amv是什么文件格式?如何播放amv视频?

AMV文件格式源自于中国公司Actions Semiconductor&#xff0c;最初作为其MP4播放器中使用的专有视频格式。产生于数码媒体发展的需求下&#xff0c;AMV格式为小屏幕便携设备提供了一种高度压缩的视频存储方案。 AMV文件格式的主要特性与使用场景 AMV格式以其独特的特性在小尺寸…