首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【C语言】贪吃蛇(c语言含源码)

  • 25-02-19 03:00
  • 3381
  • 8960
blog.csdn.net

目录

  • 一、简述
  • 二、先调整环境
    • 2.1 首先我们要将我们的vs终端改为Windows控制台主机。步骤如下
  • 三、本地化
    • 3.1 setlocale函数
    • 3.2 宽字符的打印
  • 四、创建蛇,用链表实现
    • 4.1 封装节点
    • 4.2 封装蛇
  • 五、游戏初始化
    • 5.1 光标设置函数
    • 5.2 打印环境界面和功能介绍函数
    • 5.3 绘制地图函数
    • 5.4 创建蛇
    • 55 创建食物
  • 六、游戏运行
    • 6.1 打印帮助信息函数
    • 6.2 暂停函数
    • 6.3 蛇移动函数
      • 6.3.1 下一个是食物
      • 6.3.2 下一个不是食物
  • 七、游戏结束
  • 八、源码

在这里插入图片描述

一、简述

我们要实现一个在控制台上的贪吃蛇,成品是下图这种。
在这里插入图片描述

二、先调整环境

我们可以看到我在控制台上有贪吃蛇名字,如何实现呢?
在这里插入图片描述

2.1 首先我们要将我们的vs终端改为Windows控制台主机。步骤如下

1)先鼠标右击上图终端的白色边框,当出现选项时。2)左击属性
3)点终端在有下面选择Windows控制台主机。最后如下图在这里插入图片描述
后续就通过函数来调整环境,在后续一步一步讲解

三、本地化

C语言为适应国际化就提供了本地模式和标准模式

3.1 setlocale函数

用此函数修改模式
改为本地模式

setlocale(LC_ALL," ");
  • 1

改为标准模式

setlocale(LC_ALL,"C");
  • 1

3.2 宽字符的打印

使用wprintf函数,该函数打印的字符占两个空间,它和printf使用区别在于要在双引号前加上L。

//打印一个字符c
wprintf(L"%c",L'c');
  • 1
  • 2

但在我写的代码中就算使用了wprintf还是只占一个空间,因为我的控制台环境没改。
在这里插入图片描述
将此处改为宋体就好。

四、创建蛇,用链表实现

当我们要实现这样一个贪吃蛇时,我们先要考虑用什么数据结构来实现,在这我们使用链表来搞。
可以看到我们蛇是一个一个节点连出来的。所以我们也将蛇的节点封装起来。

4.1 封装节点

那在节点中我们存储什么东西呢?在我们上述的终端中我们是可以用坐标把每一个地方表示出来的。所以我们就在节点中存储每一个节点的坐标和下一个节点的地址。因此代码实现就如下

//蛇的节点、坐标,指针
typedef struct SnakeNode
{
	int x;
	int y;//坐标信息
	struct SnakeNode* next;//下一个节点
}SnakeNode;
typedef SnakeNode* pSnakeNode;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.2 封装蛇

那我们要实现一个蛇,我们就要考虑里面要包含哪些成员。首先我们肯定要有1.找到蛇身的蛇头指针,2.一个蛇运动的方向也需要,3.蛇每移动一下的状态也会有不同,4.从最后的原型图可以看出我们还有总分,5.一个食物的分,7,食物的分又是根据速度来的,速度我们就用当前代码休眠时间来表示,休眠时间约大速度越慢,8.食物我们也储存在里面。
蛇的方向和蛇的状态有很多种,我们就用enum枚举出来。

//方向上下左右
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};
//蛇的状态,正常,撞墙死,撞自己死,正常退出
enum GAME_STATUS
{
	NORMAL,
	KILL_BY_WALL,
	KILL_BY_SELF,
	EXIT_NORMAL
};
//贪吃蛇对象
typedef struct Snake
{
	pSnakeNode psnake;//蛇头
	pSnakeNode pfood;//食物
	enum DIRECTION direction;//蛇的方向
	enum GAME_STATUS status;//蛇的状态
	int food_wight;//一个食物分数
	int sorce;//总分数
	int sleep_time;//休息时间
}Snake;
typedef Snake* pSnake;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

五、游戏初始化

初始化游戏函数

//初始化游戏
void GameStart(pSnake ps);
  • 1
  • 2

在这个函数中我们需要将1.控制台界面大小安排好,2.光标隐藏起来,3. 打印环境界面和功能介绍,4. 绘制地图,5. 创建蛇,6. 创建食物。
对界面的操作详见我上一篇关于Win32API的文章

Win32API的文章

具体实现如下:

void GameStart(pSnake ps)
{
	//1.修改界面大小
	system("mode con cols=150 lines=30");
	system("title 贪吃蛇");
	//2.隐藏光标
	HANDLE hOutPut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo ;
	GetConsoleCursorInfo(hOutPut, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(hOutPut, &CursorInfo);//隐藏控制台光标信息

	
	//3. 打印环境界面和功能介绍
	WelcomeToGame();
	//4. 绘制地图
	CreateMap();
	//5. 创建蛇
	IniteSnake(ps);
	//6. 创建食物
	IniteFood(ps);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

5.1 光标设置函数

我们封装一个函数来设置光标坐标,以便后续使用。

void SetPos(short x, short y)
{
	//获取一个句柄
	HANDLE hOutPut = GetStdHandle(STD_OUTPUT_HANDLE);
	//坐标
	COORD pos = { x,y };
	//设置光标坐标
	SetConsoleCursorPosition(hOutPut, pos);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.2 打印环境界面和功能介绍函数

我们在这个函数中打印欢迎界面和游戏玩法。

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

	SetPos(42, 20);
	system("pause");
	system("cls");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5.3 绘制地图函数

我们绘制一个80行27列的地图。我们先将上下两行边界绘制出来,在绘制左右两个边界,注意左右时我们要从1到25最前面的已经绘制好了。还有每次绘制的其实光标位置一定要计算好根据自己设置的地图大小。

void CreateMap()
{
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i < 80; i++)
	{
		wprintf(L"%lc", WALL);
	}

	//下
	SetPos(0, 26);
	for (i = 0; i < 80; i++)
	{
		wprintf(L"%lc", WALL);
	}
	for ( i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for ( i = 1; i <= 25; i++)
	{
		SetPos(79, i);
		wprintf(L"%lc", WALL);
	}
	system("pause");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

5.4 创建蛇

在这个函数中我们初始蛇有5个节点,用malloc申请空间,然后用头插法将节点联系起来,然后打印。在将蛇的状态等成员设置。在创建蛇时因为我是字符只占一个空间,我就随便可以创建,但是有占两个时要在设置x坐标是下一个是加2。

void IniteSnake(pSnake ps)
{
	pSnakeNode node = NULL;
	//初始蛇身有5个节点
	for (int i = 0; i < 5; i++)
	{
		node = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (node == NULL)
		{
			perror("IniteSnake malloc");
			exit(1);
		}
		//设置坐标
		node->x = POS_X + i;
		node->y = POS_Y;
		node->next = NULL;

		//头插
		if (ps->psnake == NULL)
			ps->psnake = node;
		else
		{
			node->next = ps->psnake;
			ps->psnake = node;
		}
	}
	//打印蛇身
	node = ps->psnake;
	
	while (node)
	{
		SetPos(node->x, node->y);
		wprintf(L"%c",BODY);
		node = node->next;
	}
	//设置蛇状态
	ps->direction = RIGHT;
	ps->sleep_time = 300;
	ps->status = NORMAL;
	ps->sorce = 0;
	ps->food_wight = 30;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

55 创建食物

我们创建一个食物节点,通过随机数生成坐标,但是要该坐标不能是墙,不能是蛇身。最后打印出来。在创建食物时因为我是字符只占一个空间,我就不用考虑蛇是否可以吃到食物,但是有占两个时要在设置食物x坐标要和蛇节点吻合,可以让蛇蛇吃到。

void IniteFood(pSnake ps)
{
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("IniteFood malloc");
		exit(1);
	}
	//随机生成食物坐标,判断与蛇是否重合
	int x = 0;
	int y = 0;
again:
	x = rand() % 78 + 1;
	y = rand() % 23 + 1;
		pSnakeNode cur = ps->psnake;
		while (cur)
		{
			if (x == cur->x && y == cur->y)
			{
				goto again;
			}
			cur = cur->next;
		}
	food->x = x;
	food->y = y;
	food->next = NULL;

	ps->pfood = food;
	//打印食物
	SetPos(food->x, food->y);
	wprintf(L"%c", FOOD);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

六、游戏运行

void GameRun(pSnake ps)
{
	do {
		SetPos(90, 10);
		//右侧打印帮助信息
		PrintInfo(ps);
		//获取键盘信息
		//移动方向
		if (KEY_PRESS(VK_UP) && ps->direction != DOWN)
			ps->direction = UP;
		else if (KEY_PRESS(VK_DOWN) && ps->direction != UP)
			ps->direction = DOWN;
		else if (KEY_PRESS(VK_RIGHT) && ps->direction != LEFT)
			ps->direction = RIGHT;
		else if (KEY_PRESS(VK_LEFT) && ps->direction != RIGHT)
			ps->direction = LEFT;
		//加减速
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->food_wight <= 50 && ps->food_wight >= 10)
			{
				ps->sleep_time -= 10;
				ps->food_wight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->food_wight <= 50 && ps->food_wight >= 10)
			{
				ps->sleep_time += 10;
				ps->food_wight -= 2;
			}
		}
		//暂停
		else if (KEY_PRESS(VK_SPACE))
			pause();
		//退出
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = EXIT_NORMAL;
			break;
		}
		//判断分数超出限制调回
		if (ps->food_wight > 50)
		{
			ps->food_wight = 50;
		}
		else if (ps->food_wight < 10)
		{
			ps->food_wight = 10;
		}
		//蛇移动
		Sleep(ps->sleep_time);
 		SnakeMove(ps);
	} while (ps->status == NORMAL);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

6.1 打印帮助信息函数

我们在游戏界面右边打印帮助信息。打印完帮助信息后我们在通过获取键盘信息来判断下一步应该怎么做。

void PrintInfo(pSnake ps)
{
	printf("当前总分:%d", ps->sorce);
	SetPos(90, 11);
	printf("当前一个食物分数: %2d", ps->food_wight);
	SetPos(90, 12);
	printf("不能穿墙,不能咬到自己");
	SetPos(90, 13);
	printf("空格键暂停,esc键退出");
	SetPos(90, 14);
	printf("用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速");
	SetPos(90, 15);
	printf("加速减速最多10次,加速一次食物分数加2,减速一次食物分数减2");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6.2 暂停函数

当我们接收键盘信息为空格暂停时,我们调用该函数,该函数本质就是死循环睡眠。再次接收到空格时我们就恢复。

void pause()
{
	while (1)
	{
		Sleep(300);
		if (KEY_PRESS(VK_SPACE))
			break;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6.3 蛇移动函数

我们根据蛇移动方向来判断下一步在哪,其实就是将下一个位置处创建结点当头结点。但是我们需要判断移动后头结点位置是食物,还是墙,还是正常位置。然后看是否需要释放最后一个节点。

void SnakeMove(pSnake ps)
{
	//创建一个节点
	pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (node == NULL)
	{
		perror("SnakeMove malloc");
		exit(1);
	}
	node->next = NULL;
	//通过switch case来将下一个节点坐标赋值
	switch (ps->direction)
	{
	case UP:
		node->x = ps->psnake->x;
		node->y = ps->psnake->y - 1;
		break;
	case DOWN:
		node->x = ps->psnake->x;
		node->y = ps->psnake->y + 1;
		break;
	case RIGHT:
		node->x = ps->psnake->x + 1;
		node->y = ps->psnake->y;
		break;
	case LEFT:
		node->x = ps->psnake->x - 1;
		node->y = ps->psnake->y;
		break;
	}
	pSnakeNode tempNode = node;
	//下一个节点是食物
	if (node->x == ps->pfood->x && node->y == ps->pfood->y)
	{
		EatFood(ps, node);
	}

	//不是食物
	else
	{
		NoFood(ps,node);
	}
	//判断移动后是否死来
	if (NextIsSelf (ps,tempNode)== 1)
	{
		ps->status = KILL_BY_SELF;
	}
	else if (tempNode->x == 0
		|| tempNode->x == 79
		|| tempNode->y == 0
		|| tempNode->y == 27)
	{
		ps->status = KILL_BY_WALL;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

6.3.1 下一个是食物

当下个是食物我们就不用对最后一个节点释放。吃掉之后释放掉在创建下一个食物。

void EatFood(pSnake ps,pSnakeNode node)
{
	//头插
	node->next = ps->psnake;
	ps->psnake = node;

	//打印蛇
	pSnakeNode cur = ps->psnake;
	ps->sorce += ps->food_wight;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%c", BODY);
		cur = cur->next;
	}
	//释放食物
	free(ps->pfood);
	IniteFood(ps);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

6.3.2 下一个不是食物

下一个不是食物蛇往下一步走后需要释放掉最后一个节点。

void NoFood(pSnake ps, pSnakeNode node)
{
	//头插
	node->next = ps->psnake;
	ps->psnake = node;

	//打印蛇
	pSnakeNode cur = ps->psnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%c", BODY);
		cur = cur->next;
	}
	cur = ps->psnake;
	while (cur->next->next)
	{
		cur = cur->next;
	}
	pSnakeNode temp = cur->next;
	SetPos(temp->x, temp->y);
	printf(" ");
	cur->next = NULL;
	free(temp);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

七、游戏结束

在游戏结束后通过蛇状态来打印死因,然后释放掉所有空间。

void GameOver(pSnake ps)
{
	SetPos(40, 15);
	if (ps->status == KILL_BY_SELF)
		printf("你撞到自己死了");
	else if (ps->status == KILL_BY_WALL)
		printf("你撞到墙死了");
	else if (ps->status == EXIT_NORMAL)
		printf("你正常退出了");
	pSnakeNode cur = ps->psnake;
	while (cur)
	{
		ps->psnake = cur->next;
		free(cur);
		cur = ps->psnake;
	}
	free(ps->pfood);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

八、源码

snake.h

#pragma once
#include 
#include 
#include 
#include 
#include
#include
#define WALL L'█'
#define POS_X 25
#define POS_Y 5
#define BODY L'○'
#define FOOD L'♥'
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&1)?1:0)
//方向上下左右
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};
//蛇的状态,正常,撞墙死,撞自己死,正常退出
enum GAME_STATUS
{
	NORMAL,
	KILL_BY_WALL,
	KILL_BY_SELF,
	EXIT_NORMAL
};
//蛇的节点、坐标,指针
typedef struct SnakeNode
{
	int x;
	int y;//坐标信息
	struct SnakeNode* next;//下一个节点
}SnakeNode;
typedef SnakeNode* pSnakeNode;
//贪吃蛇对象
typedef struct Snake
{
	pSnakeNode psnake;//蛇头
	pSnakeNode pfood;//食物
	enum DIRECTION direction;//蛇的方向
	enum GAME_STATUS status;//蛇的状态
	int food_wight;//一个食物分数
	int sorce;//总分数
	int sleep_time;//休息时间
}Snake;
typedef Snake* pSnake;

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

//初始化游戏
void GameStart(pSnake ps);
//打印欢迎界面
void WelcomeToGame();
//绘制地图x 80, y 27
void CreateMap();
//5. 创建蛇
void IniteSnake(pSnake ps);
//6. 创建食物
void IniteFood(pSnake ps);

//游戏进行页面
void GameRun(pSnake ps);
//不是食物
void NoFood(pSnake ps, pSnakeNode node);
//吃食物
void EatFood(pSnake ps, pSnakeNode node);
//蛇移动
void SnakeMove(pSnake ps);

//游戏结束
void GameOver(pSnake ps);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

snake.c

# define _CRT_SECURE_NO_WARNINGS 1;
#include"snake.h"

//设置光标坐标
void SetPos(short x, short y)
{
	//获取一个句柄
	HANDLE hOutPut = GetStdHandle(STD_OUTPUT_HANDLE);
	//坐标
	COORD pos = { x,y };
	//设置光标坐标
	SetConsoleCursorPosition(hOutPut, pos);
}

//3. 打印环境界面和功能介绍
void WelcomeToGame()
{
	SetPos(40, 14);
	wprintf(L"欢迎来到贪吃蛇小游戏\n");
	SetPos(42, 20);
	system("pause");
	system("cls");
	SetPos(25, 14);
	wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速\n");
	SetPos(25, 15);
	wprintf(L"加速能够得到更高的分数\n");

	SetPos(42, 20);
	system("pause");
	system("cls");
}
//4. 绘制地图
void CreateMap()
{
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i < 80; i++)
	{
		wprintf(L"%lc", WALL);
	}

	//下
	SetPos(0, 26);
	for (i = 0; i < 80; i++)
	{
		wprintf(L"%lc", WALL);
	}
	for ( i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for ( i = 1; i <= 25; i++)
	{
		SetPos(79, i);
		wprintf(L"%lc", WALL);
	}
	system("pause");
}
//5. 创建蛇
void IniteSnake(pSnake ps)
{
	pSnakeNode node = NULL;
	//初始蛇身有5个节点
	for (int i = 0; i < 5; i++)
	{
		node = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (node == NULL)
		{
			perror("IniteSnake malloc");
			exit(1);
		}
		//设置坐标
		node->x = POS_X + i;
		node->y = POS_Y;
		node->next = NULL;

		//头插
		if (ps->psnake == NULL)
			ps->psnake = node;
		else
		{
			node->next = ps->psnake;
			ps->psnake = node;
		}
	}
	//打印蛇身
	node = ps->psnake;
	
	while (node)
	{
		SetPos(node->x, node->y);
		wprintf(L"%c",BODY);
		node = node->next;
	}
	//设置蛇状态
	ps->direction = RIGHT;
	ps->sleep_time = 300;
	ps->status = NORMAL;
	ps->sorce = 0;
	ps->food_wight = 30;
}
//6. 创建食物
void IniteFood(pSnake ps)
{
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("IniteFood malloc");
		exit(1);
	}
	//随机生成食物坐标,判断与蛇是否重合
	int x = 0;
	int y = 0;
again:
	x = rand() % 78 + 1;
	y = rand() % 23 + 1;
		pSnakeNode cur = ps->psnake;
		while (cur)
		{
			if (x == cur->x && y == cur->y)
			{
				goto again;
			}
			cur = cur->next;
		}
	food->x = x;
	food->y = y;
	food->next = NULL;

	ps->pfood = food;
	//打印食物
	SetPos(food->x, food->y);
	wprintf(L"%c", FOOD);
}
//初始化游戏
void GameStart(pSnake ps)
{
	//1.修改界面大小
	system("mode con cols=150 lines=30");
	system("title 贪吃蛇");
	//2.隐藏光标
	HANDLE hOutPut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo ;
	GetConsoleCursorInfo(hOutPut, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(hOutPut, &CursorInfo);//隐藏控制台光标信息

	
	//3. 打印环境界面和功能介绍
	WelcomeToGame();
	//4. 绘制地图
	CreateMap();
	//5. 创建蛇
	IniteSnake(ps);
	//6. 创建食物
	IniteFood(ps);
}


//打印帮助信息
void PrintInfo(pSnake ps)
{
	printf("当前总分:%d", ps->sorce);
	SetPos(90, 11);
	printf("当前一个食物分数: %2d", ps->food_wight);
	SetPos(90, 12);
	printf("不能穿墙,不能咬到自己");
	SetPos(90, 13);
	printf("空格键暂停,esc键退出");
	SetPos(90, 14);
	printf("用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速");
	SetPos(90, 15);
	printf("加速减速最多10次,加速一次食物分数加2,减速一次食物分数减2");
}
//暂停
void pause()
{
	while (1)
	{
		Sleep(300);
		if (KEY_PRESS(VK_SPACE))
			break;
	}
}
//判断下一个是不是自己
int NextIsSelf(pSnake ps, pSnakeNode node)
{
	pSnakeNode cur = ps->psnake->next;
	while (cur)
	{
		if (cur->x == node->x && cur->y == node->y)
			return 1;
		cur = cur->next;
	}
	return 0;
}
//吃食物
void EatFood(pSnake ps,pSnakeNode node)
{
	//头插
	node->next = ps->psnake;
	ps->psnake = node;

	//打印蛇
	pSnakeNode cur = ps->psnake;
	ps->sorce += ps->food_wight;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%c", BODY);
		cur = cur->next;
	}
	//释放食物
	free(ps->pfood);
	IniteFood(ps);
}
//不是食物
void NoFood(pSnake ps, pSnakeNode node)
{
	//头插
	node->next = ps->psnake;
	ps->psnake = node;

	//打印蛇
	pSnakeNode cur = ps->psnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%c", BODY);
		cur = cur->next;
	}
	cur = ps->psnake;
	while (cur->next->next)
	{
		cur = cur->next;
	}
	pSnakeNode temp = cur->next;
	SetPos(temp->x, temp->y);
	printf(" ");
	cur->next = NULL;
	free(temp);
}
//蛇移动
void SnakeMove(pSnake ps)
{
	//创建一个节点
	pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (node == NULL)
	{
		perror("SnakeMove malloc");
		exit(1);
	}
	node->next = NULL;
	//通过switch case来将下一个节点坐标赋值
	switch (ps->direction)
	{
	case UP:
		node->x = ps->psnake->x;
		node->y = ps->psnake->y - 1;
		break;
	case DOWN:
		node->x = ps->psnake->x;
		node->y = ps->psnake->y + 1;
		break;
	case RIGHT:
		node->x = ps->psnake->x + 1;
		node->y = ps->psnake->y;
		break;
	case LEFT:
		node->x = ps->psnake->x - 1;
		node->y = ps->psnake->y;
		break;
	}
	pSnakeNode tempNode = node;
	//下一个节点是食物
	if (node->x == ps->pfood->x && node->y == ps->pfood->y)
	{
		EatFood(ps, node);
	}

	//不是食物
	else
	{
		NoFood(ps,node);
	}
	//判断移动后是否死来
	if (NextIsSelf (ps,tempNode)== 1)
	{
		ps->status = KILL_BY_SELF;
	}
	else if (tempNode->x == 0
		|| tempNode->x == 79
		|| tempNode->y == 0
		|| tempNode->y == 27)
	{
		ps->status = KILL_BY_WALL;
	}
}
//游戏进行页面
void GameRun(pSnake ps)
{
	do {
		SetPos(90, 10);
		//右侧打印帮助信息
		PrintInfo(ps);
		//获取键盘信息
		//移动方向
		if (KEY_PRESS(VK_UP) && ps->direction != DOWN)
			ps->direction = UP;
		else if (KEY_PRESS(VK_DOWN) && ps->direction != UP)
			ps->direction = DOWN;
		else if (KEY_PRESS(VK_RIGHT) && ps->direction != LEFT)
			ps->direction = RIGHT;
		else if (KEY_PRESS(VK_LEFT) && ps->direction != RIGHT)
			ps->direction = LEFT;
		//加减速
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->food_wight <= 50 && ps->food_wight >= 10)
			{
				ps->sleep_time -= 10;
				ps->food_wight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->food_wight <= 50 && ps->food_wight >= 10)
			{
				ps->sleep_time += 10;
				ps->food_wight -= 2;
			}
		}
		//暂停
		else if (KEY_PRESS(VK_SPACE))
			pause();
		//退出
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = EXIT_NORMAL;
			break;
		}
		//判断分数超出限制调回
		if (ps->food_wight > 50)
		{
			ps->food_wight = 50;
		}
		else if (ps->food_wight < 10)
		{
			ps->food_wight = 10;
		}
		//蛇移动
		Sleep(ps->sleep_time);
 		SnakeMove(ps);
	} while (ps->status == NORMAL);
}
//游戏结束
void GameOver(pSnake ps)
{
	SetPos(40, 15);
	if (ps->status == KILL_BY_SELF)
		printf("你撞到自己死了");
	else if (ps->status == KILL_BY_WALL)
		printf("你撞到墙死了");
	else if (ps->status == EXIT_NORMAL)
		printf("你正常退出了");
	pSnakeNode cur = ps->psnake;
	while (cur)
	{
		ps->psnake = cur->next;
		free(cur);
		cur = ps->psnake;
	}
	free(ps->pfood);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377

test.c


# define _CRT_SECURE_NO_WARNINGS 1;
#include"snake.h"
void test()
{
	int n = 4;
	srand((unsigned int)time(NULL));
	do {
		//创建贪吃蛇
		Snake snake = { 0 };
		//初始化游戏
		GameStart(&snake);
		//运行游戏
		GameRun(&snake);
		//结束游戏-善后工作
		GameOver(&snake);
		SetPos(10, 10);
		//system("cls");
		printf("是否再来一局Y/N: ");
		n = getchar();
		while (getchar() != '\n');
		system("cls");
	} while (n == 'Y' || n == 'y');
	SetPos(0, 29);
}
int main()
{
	//调节为本地状态
	setlocale(LC_ALL, "");
	test();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
注:本文转载自blog.csdn.net的鸽鸽程序猿的文章"https://blog.csdn.net/yj20040627/article/details/137978518"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top