C语言贪吃蛇(有详细注释)

news2024/9/23 23:34:01

这个贪吃蛇是在比特特训营里学到的,同时我还写了用EasyX图形库实现的图形化贪吃蛇,含有每个函数的实现以及游戏中各种细节的讲解,感兴趣的可以去看一看。
贪吃蛇小游戏

实现效果
在这里插入图片描述

以下就是源码,感兴趣的小伙伴可以cv自己玩一玩改造改造,每个函数都有相应功能细节的注释,有用的话欢迎大家点赞
snake.h

#pragma once
#include <locale.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#define wall L'¤'
#define body L'◎'
#define food L'★'
#define POS_X 24
#define POS_Y 5

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) &0x1)?1:0)//今天继续写,写完删除该行注释
enum DIRECTION//四个方向
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};
enum GAME_STATUS//游戏状态:正常,退出,撞墙,吃到自己
{
	OK=1,
	END_NOMAL,//正常退出
	KILL_BY_WALL,
	KILL_BY_SELF
};
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,* pSnakeNode;
//相当于
//typedef struct SnakeNode* pSnakeNode;//结构体指针的重命名

//描述蛇的结构体
typedef struct snake
{
	pSnakeNode _psnake;//指向贪吃蛇头结点的指针。
	pSnakeNode _fFood;//假设食物也是蛇节点的指针,吃掉时改变其状态即可。
	int _Score;//分数,到时候要打印
	int _Foodweight;
	int SleepTime;//每走一步休息的时间,时间越短,速度越快
	enum DIRECTION _Dir;//方向,用枚举常量给出
	enum GAME_STATUS _status;
}Snake,*psnake;

//游戏开始
void GameStart(psnake);
//欢迎界面
WecomeGame();//打印游戏界面
//创建地图
void CreatMap();
void InitSnake(psnake ps);
void CreateFood(psnake ps);

//游戏的正常运行
void GameRun(psnake ps);
//打印帮助信息
void SetPos(short x, short y);

int KillBySelf(psnake ps);

void GameOver(psnake ps);

===============================
snake.c

#define _CRT_SECURE_NO_WARNINGS
#include "snake.h"
//设置光标位置
void SetPos(short x, short y)
{
	COORD pos = { x,y };
	HANDLE hOutput = NULL;
	//获取标准输出的句柄
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置标准输出上光标的位置为pos
	SetConsoleCursorPosition(hOutput, pos);
}
WecomeGame()//打印游戏界面
{
	//定位光标,打印欢迎语句
	SetPos(40,15);
	printf("欢迎来到贪吃蛇游戏");
	
	SetPos(37, 27);
	//printf("按任意键继续");
	system("pause");//暂停程序,库函数的暂停命令
	//清空屏幕
	system("cls");
	SetPos(20, 15);
	printf("上下左右为↑↓←→,F3为加速,F4为减速  ");
	system("pause");//暂停程序,库函数的暂停命令
	system("cls");
	CreatMap();
}
void CreatMap()
{
	int i = 0;
	//通过创建的终端大小打印地图
	SetPos(0, 0);
	for (i = 0; i <= 56; i+=2)
	{
		wprintf(L"%c",wall);
	}
	SetPos(0, 26);
	for (i = 0; i <= 56; i += 2)
	{
		wprintf(L"%c", wall);
	}
	for (i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%c", wall);	
	}
	for (i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%c", wall);
	}
}

void GameStart(psnake ps)
{
	//初始化控制台窗体及初始化信息

	system("mode con cols=100 lines=30");//长宽
	system("title 贪吃蛇");
	//光标隐藏掉
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//得到控制台光标信息
	CursorInfo.bVisible = false;//将状态设置为fasle,隐藏
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
	//打印欢迎界面,提示
	WecomeGame();//打印游戏界面
	//初始化贪吃蛇
	InitSnake(ps);
	//创建食物
	CreateFood(ps);
}
void CreateFood(psnake ps)//创建食物
{
	//坐标范围内随机生成,且不可以生成在蛇身上。
	int x = 0;
	int y = 0;
	again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);//横坐标为2的倍数,因为宽字符的原因
	//坐标不可以和蛇的节点坐标冲突
	pSnakeNode cur = ps->_psnake;
	while (cur)
	{
		//比较坐标
		if (cur->x == x && cur->y == y)
		{
			goto again;//也可以利用循环解决
		}
		cur = cur->next;
	}
	pSnakeNode pfood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pfood == NULL)
	{
		perror("malloc fail");
		return;
	}
	pfood->x = x;
	pfood->y = y;
	ps->_fFood = pfood;
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", food);
	//getchar();随时阻塞,判断效果
}
void InitSnake(psnake ps)//初始化蛇
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		pSnakeNode snk = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (snk == NULL)//问题检查
		{
			perror("malloc fail");
			return;
		}
		snk->x = POS_X + 2*i;//节点位置不同
		snk->y = POS_Y;
		snk->next = NULL;
		if (ps->_psnake == NULL)
		{
			ps->_psnake = snk;
		}
		else//此时_psnake修饰的就是蛇节点的头结点
		{
			snk->next = ps->_psnake;
			ps->_psnake = snk;
		}
	}
	//打印蛇的身体
	pSnakeNode cur = ps->_psnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"% c", body);
		cur = cur->next;
	}
	//蛇的相关数据
	ps->_status = OK;
	ps->_Score = 0;
	ps->_Foodweight = 10;
	ps->_fFood = NULL;
	ps->SleepTime = 200;//休眠时间关乎蛇移动的速度
	ps->_Dir = RIGHT;
}
void PrintInform()
{
	SetPos(60, 15);
	printf("1:不能穿墙,不能咬到自己");
	SetPos(60, 17);
	printf("2:上下左右为↑↓←→,F3为加速,F4为减速");
	SetPos(60, 19);
	printf("3:F3加速,F4减速,Esc退出,空格暂停");

	//getchar();
}

void Pause()//暂停游戏或者继续游戏
{
	while (1)
	{
		Sleep(100);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

//判断是否吃掉食物
int NextIsFood(psnake ps,pSnakeNode pnext)
{
	if (ps->_fFood->x == pnext->x && ps->_fFood->y == pnext->y)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

void EatFood(psnake ps, pSnakeNode pnext)//吃掉
{
	pnext->next = ps->_psnake;
	ps->_psnake = pnext;//把蛇头换一换
	//打印蛇
	pSnakeNode cur = ps->_psnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;

	}
	//清理食物节点(食物节点是malloc出来的,所以要清理,不然会造成内存浪费),加分
	free(ps->_fFood);
	ps->_Score += ps->_Foodweight;
	//继续创建食物
	CreateFood(ps);
}

void NotEatFood(psnake ps, pSnakeNode pnext)//没有吃掉
{
	//如果不是食物,生成新的节点,删掉最后一个节点
	//头插
	pnext->next = ps->_psnake;
	ps->_psnake = pnext;//把蛇头换一换
	//打印社蛇的身体
	pSnakeNode cur = ps->_psnake;
	while (cur->next->next)//生成了新的头节点,只打印五个
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", body);
		cur = cur->next;
	}
	//清理最后一节点
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur ->next = NULL;
}

//蛇是否撞墙
int KillByWall(psnake ps)
{
	if (ps->_psnake->x == 0 || ps->_psnake->x == 56 || ps->_psnake->y == 0 || ps->_psnake->y == 26)
	{
		ps->_status = KILL_BY_WALL;
		return 1;
	}
	return 0;
}

//是否吃到自己
int KillBySelf(psnake ps)
{
	pSnakeNode cur = ps->_psnake->next;
	while (cur)
	{
		if (ps->_psnake->x == cur->x && ps->_psnake->y == cur->y)
		{
			ps->_status = KILL_BY_SELF;//改变状态
			return 1;
		}
		cur = cur->next;
	}
	return 0;
}

//值得学习的地方,修改整个数组
void SnakeMove(psnake ps)
{
	pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pnext == NULL)
	{
		perror("malloc fail");
		return;
	}
	//pnext->next = NULL;
	switch (ps->_Dir)
	{
	case UP:
		//根据蛇头计算更新之后的坐标
		pnext->x = ps->_psnake->x;
		pnext->y = ps->_psnake->y-1;

		break;
	case DOWN:
		pnext->x = ps->_psnake->x;
		pnext->y = ps->_psnake->y + 1;
		break;
	case RIGHT:
		pnext->x = ps->_psnake->x+2;//减2,因为宽度为2.
		pnext->y = ps->_psnake->y;
		break;
	case LEFT:
		pnext->x = ps->_psnake->x-2;
		pnext->y = ps->_psnake->y;
		break;
	}
	//判断是否吃掉食物,如果吃掉食物,最后一个节点不清理,如果没有吃掉食物,就将其清理、
	//蛇头坐标和食物坐标
	if (NextIsFood(ps, pnext))
	{
		//吃掉食物
		EatFood(ps,pnext);
	}
	else
	{
		//没吃食物
		NotEatFood(ps,pnext);
	}
	//蛇是否撞墙
	KillByWall(ps);

	//是否吃到自己
	KillBySelf(ps);
}

void GameRun(psnake ps)
{
	PrintInform();
	do
	{
		SetPos(64, 10);
		printf("得分:%0.5d", ps->_Score);
		SetPos(64, 12);
		printf("每个食物10分");
		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->_status = END_NOMAL;//正常退出
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停函数
			Pause();//封装一个函数,按下一次暂停,再按就继续
		}
		else if (KEY_PRESS(VK_F3))//加速
		{
			//ps->SleepTime = 100;//如果一直按着,那就一直二倍加速
			//ps->_Foodweight = 15;
			if (ps->SleepTime >= 80)//也可以逐渐加速
			{
				ps->SleepTime -= 30;
				ps->_Foodweight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))//减速
		{
			if (ps->SleepTime <= 300)//逐渐减速
			{
				ps->SleepTime += 30;
				ps->_Foodweight -= 2;
			}
		}

		Sleep(ps->SleepTime);
		//蛇的移动
		SnakeMove(ps);//继续封装成函数

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

}

//善后处理,打印分数,清理贪吃蛇
void GameOver(psnake ps)
{
	SetPos(20, 12);
	switch (ps->_status)
	{
	case END_NOMAL:
		printf("您主动退出游戏");
		break;
	case KILL_BY_SELF:
		printf("自杀成功");
		break;
	case KILL_BY_WALL:
		printf("撞墙啦");
		break;
	}
	SetPos(0, 27);
	//释放蛇的节点
	pSnakeNode cur = ps->_psnake;
	while (cur)//循环全部释放
	{
		pSnakeNode Next = cur->next;
		free(cur);
		cur = Next;
	}
	ps->_psnake = NULL;
}

=====================================
test.c

#define _CRT_SECURE_NO_WARNINGS
#include "snake.h"
//Win32,API,Windows 32位接口。

//宽字符类型,一个汉字占用两个字符。
// 一个字母宽一个字符,一个汉字占两个字符
//wchar_t宽字符类型□☆★¤◎㊣
//setlocale(LC_ALL,"");//适应中文环境
//宽字符的打印,前缀加上L
//int main()
//{
//	SetPos(10, 10);
//	setlocale(LC_ALL, "");
//	wchar_t ch1 = L'●';
//	wprintf(L"%lc\n", ch1);//打印时printf前边加w,打印时前边大写L,类型为lc=
//	return 0;
//}


void test()
{
	char ch = 0;
	do
	{
		Snake snake = { 0 };//创建贪吃蛇
		//1,游戏开始——初始化游戏
		GameStart(&snake);
		//getchar();//设置光标状态是否成功可以检查一下,用getchar阻塞程序运行
		//2,游戏运行——正常运行
		GameRun(&snake);

		//3,游戏结束——如何结束,释放资源
		GameOver(&snake);
		SetPos(20, 15);
		printf("是否想再来一把?(Y/N):");
		ch = getchar();
		getchar();//清理‘/n’。
	} while (ch == 'Y' || ch == 'y');
}

int main()
{
	srand((unsigned int)time(NULL));
	//设置程序适应本地化
	setlocale(LC_ALL, "");
	test();

	return 0;
}
//地图,长为宽的2倍

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

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

相关文章

vatee万腾的科技征途:Vatee独特探索的数字化力量

在数字化时代的浪潮中&#xff0c;Vatee万腾以其独特的科技征途成为引领者。公司在数字化领域的探索之路不仅是技术的创新&#xff0c;更是一种对未知的勇敢涉足&#xff0c;是对新时代的深刻洞察和积极实践。 Vatee万腾通过独特的探索&#xff0c;展示了在数字化征途上的创新力…

分享11款原型图软件,让你的创意无限发挥!

即时设计 即时设计是一个集设计、原型、开发于一身的一站式在线设计工具&#xff0c;也是一个可以云端编辑、团队写作的在线原型网站。 即时设计 - 可实时协作的专业 UI 设计工具即时设计是一款支持在线协作的专业级 UI 设计工具&#xff0c;支持 Sketch、Figma、XD 格式导入&…

为什么说赤霞珠是葡萄酒中的明星?

种植酿酒葡萄是一项艰难的事业&#xff0c;它的成功取决于许多不同的因素。土壤和气候是最关键的因素&#xff0c;它们决定了哪些酿酒葡萄可以在哪个地区培育。来自云仓酒庄品牌雷盛红酒分享但是有些酿酒葡萄品种适应性很强&#xff0c;几乎可以在任何地方生长&#xff0c;其中…

Interactive Visual Data Analysis

Words&Contents Home | Interactive Visual Data Analysis Book Outline 这本书对视觉、互动和分析方法进行了系统而全面的概述&#xff0c;作为数据可视化方面比较好的读物&#xff1b; 目录 Words&Contents Book Outline &#xff08;一&#xff09;Introduct…

智慧城市运营管理平台解决方案:PPT全文61页,附下载

关键词&#xff1a;智慧城市建设方案&#xff0c;智慧城市解决方案&#xff0c;智慧城市的发展前景和趋势&#xff0c;智慧城市建设内容&#xff0c;智慧城市运营管理平台 一、智慧城市运营平台建设背景 随着城市化进程的加速&#xff0c;城市面临着诸多挑战&#xff0c;如环…

(Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码&#xff1a; 四、本文代码数据说明手册分享 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matalb平台编译&am…

Ubuntu系统安装docker

1.检查是否安装老版本 检查卸载老版本docker ubuntu下自带了docker的库&#xff0c;不需要添加新的源。 但是ubuntu自带的docker版本太低&#xff0c;需要先卸载旧的再安装新的。 apt-get remove docker docker-engine docker.io containerd runc 如果不能正常卸载&#x…

初识Linux(1),看了这篇文章,妈妈再也不用担心我Linux找不到门了。

文章目录 前言1. ls 指令例如&#xff1a;只显示文件名属性ls显示文件详细属性 ls - l 该操作可以简写成ll查看隐藏文件ls -l -a 2.pwd例如&#xff1a;显示当前目录所处的路径类似于windows如下操作: 3.cd 指令例如&#xff1a;改变工作目录相当于windows如下操作 4.whoami 指…

quickapp_快应用_某些css样式不兼容问题

样式问题 引入css样式文件[1] 单位px [2]选择器[3]盒模型[4]样式布局-默认弹性布局且不可取消[5-1]样式切换-类名的动态切换-语法[5-2]样式切换 - 类名的动态切换-目标元素[5-3] 样式切换 - 行内样式动态切换[6]background[7]overflow[8]border-radius[9]盒子阴影[10] 定位erro…

JVM深入理解

JVM深入理解&#xff08;一&#xff09; JVM是什么 JRE、JDK和JVM 的关系 JVM原理 1、JVM是什么&#xff1f; JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组…

前端(HTML + CSS + JS)

文章目录 一、HTML1. 概念&#xff08;1&#xff09;HTML 文件基本结构&#xff08;2&#xff09;HTML代码框架 2. 、HTML常见标签 二、CSS1. CSS基本语法规范2. 用法&#xff08;1&#xff09; 引用方式&#xff08;2&#xff09;选择器&#xff08;3&#xff09;常用元素属性…

【Vue】filter的用法

上一篇&#xff1a; vue的指令 https://blog.csdn.net/m0_67930426/article/details/134599378?spm1001.2014.3001.5502 本篇所使用指令 v-for v-on v-html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"&…

最新Midjourney绘画提示词Prompt教程无需魔法

最新Midjourney绘画提示词Prompt教程无需魔法使用 一、AI绘画工具 SparkAi【无需魔法使用】&#xff1a; SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01;本系统使用NestjsVueTypes…

分享5款经过时间验证的精品软件

​ 今天来给大家推荐5款良心软件,每款都是经过时间检验的精品,用起来让你的工作效率提升飞快&#xff0c;各个都让你觉得相见恨晚&#xff01; 1.文件夹隐藏工具——文件夹隐藏精灵 ​ 文件夹隐藏精灵是一款可以隐藏你的文件夹和文件的工具&#xff0c;它可以让你的隐私和重要…

基于法医调查算法优化概率神经网络PNN的分类预测 - 附代码

基于法医调查算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于法医调查算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于法医调查优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

插件预热 | 且看安全小白如何轻松利用Goby插件快速上分

001 前言 各位师傅们好&#xff0c;首先强调一遍我可没做坏事&#xff0c;我只是想学技术&#xff0c;我有什么坏心思呢 回到正题&#xff0c;作为一个初学者&#xff0c;我想和大家分享一下我是如何利用 Goby 进行刷分的经历。大家都知道&#xff0c;刚开始学习的时候&…

windows远程linux或远程虚拟机连接拒绝问题排查

当我们使用MobaXterm远程连接时&#xff0c;报错如下&#xff1a; 1.首先检查该ubuntu防火墙是否关闭&#xff0c;先将防火墙关闭。 1.检查防火墙状态 sudo ufw status 2.开启防火墙 sudo ufw enable 3.关闭防火墙 sudo ufw disable 2.关闭防火墙后&#xff0c;使用ping命令相…

无线网络下VMWare+CentOS7使用桥接模式无法联通网络问题

因为最近新配了台带无线网卡的主机&#xff0c;所以准备把所有的内容都转移到新电脑上&#xff0c;其中就包括虚拟机 安装好VMWareCentOS7选择桥接模式 然后我们去修改一下网络配置 cd /etc/sysconfig/network-scripts/进入这个ifcfg-ens33文件 我们修改箭头所示内容&#xff…

【数据结构】树与二叉树(廿二):树和森林的遍历——后根遍历(递归算法PostOrder、非递归算法NPO)

文章目录 5.1 树的基本概念5.1.1 树的定义5.1.2 森林的定义5.1.3 树的术语 5.2 二叉树5.3 树5.3.1 树的存储结构1. 理论基础2. 典型实例3. Father链接结构4. 儿子链表链接结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法5.3.3 树和森林的遍历1. 先根遍历&#xff08;递归、非…

项目总结报告(案例模板)

软件项目总结报告模板套用&#xff1a; 项目概要项目工作分析经验与教训改进建议可纳入的项目过程资产 --------进主页获取更多资料-------