贪吃蛇的C语言实现

目录

一、游戏流程设计

二、游戏实现原理

2.1如何创建并管理数据

2.2如何实现蛇身移动

2.3如何实现食物随机放置

2.4如何检测按键与调整光标位置

三、源代码

3.1 test.c

3.2 snake.h

3.3 snake.c


一、游戏流程设计

  • GameStart
  1. WelcomeToGame:打印欢迎界面
  2. CreateMap:打印地图
  3. InitSnake:初始化蛇身
  4. InitFood:初始化食物
  • GameRun
  1. SnakeMove:实现蛇身移动
  2. EatFood:判断是否吃到食物并对吃到食物进行处理
  3. NoFood:判断是否吃到食物并对没有吃到食物进行处理
  4. DropByWall:判断是否撞墙
  5. DropBySelf:判断是否撞到自己
  • GameEnd
    判断游戏终止的条件,并销毁中途创建的数据

二、游戏实现原理

2.1如何创建并管理数据

  • 贪吃蛇在吃到食物后节点会加长,为了便于管理数据,简化操作方式,采用链表的形式进行操作。
  • 将游戏运行时的状态位全部保存在结构体中管理。
    typedef struct SnakeNode
    {
    	int x;
    	int y;
    	struct SnakeNode* next;
    }SnakeNode,*pSnakeNode;
    
    enum DIRECTION
    {
    	UP=1,
    	DOWN,
    	RIGHT,
    	LEFT,
    };
    
    enum STATUS
    {
    	OK,
    	NORMAL,
    	DROP_BY_WALL,
    	DROP_BY_SELF,
    	ESC,	
    };
    
    
    typedef struct Snake
    {
    	pSnakeNode _pSnake;  //指向蛇头的指针
    	pSnakeNode _pFood;	//指向食物的指针
    	enum DIRECTION _dir;
    	enum STATUS _status;
    	int _food_weight;	//食物权重由速度决定
    	int _score;			//总分数
    	int _sleeptime;		//休眠时间
    
    }snakestruct,*psnake;

2.2如何实现蛇身移动

先移动头节点,打印后续所有节点。因为之前打印了尾节点,所以二次打印时要在原先尾节点的位置打印空格

2.3如何实现食物随机放置

采用rand函数,详见博主另一篇博客:http://t.csdnimg.cn/YJLFO

2.4如何检测按键与调整光标位置

涉及到控制台相关知识,详见博主另一篇博客:http://t.csdnimg.cn/Vyyo1

三、源代码

3.1 test.c

#include"snake.h"
#include<locale.h>

void test()
{
	system("cls");
	//创建贪吃蛇
	snakestruct snake = { 0 };
	//初始化游戏
	GameStart(&snake);
	//运行游戏
	GameRun(&snake);
	//结束游戏 - 善后工作
	GameEnd(&snake);
	SetPos(0, 27);
}

int main()
{
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	test();
	
	return 0;
}

3.2 snake.h

#pragma once
#include<windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<string.h>

#define POS_X 24
#define POS_Y 5

//蛇身/食物 链表
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
//方向枚举
enum DIRECTION
{
	UP=1,
	DOWN,
	RIGHT,
	LEFT,
};
//状态位枚举
enum STATUS
{
	OK,
	NORMAL,
	DROP_BY_WALL,
	DROP_BY_SELF,
	ESC,	
};
//总数据管理
typedef struct Snake
{
	pSnakeNode _pSnake;  //指向蛇头的指针
	pSnakeNode _pFood;	//指向食物的指针
	enum DIRECTION _dir;
	enum STATUS _status;
	int _food_weight;	//食物权重由速度决定
	int _score;			//总分数
	int _sleeptime;		//休眠时间

}snakestruct,*psnake;

//游戏开始
void GameStart(psnake ps);

void WelcomeToGame();
void CreateMap();
void InitSnake(psnake ps);
void InitFood(psnake ps);

//游戏运行
void GameRun(psnake ps);

void SnakeMove(psnake ps);
void EatFood(psnake ps, pSnakeNode pn);
void NoFood(psnake ps, pSnakeNode pn);
void DropByWall(psnake ps);
void DropBySelf(psnake ps);

//游戏结束
void GameEnd(psnake ps);

3.3 snake.c

#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);
}
//打印欢迎界面
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");
}
//创建地图
void CreateMap()
{
	SetPos(0, 0);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"□");
	}
	
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"□");
	}
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"□");
	}
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"□");
	}
	SetPos(0, 30);
}
//初始化蛇身
void InitSnake(psnake ps)
{
	pSnakeNode cur = NULL;
	//创建虚拟节点:避免了空指针的判断
	pSnakeNode dummynode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (dummynode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	dummynode->next = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("malloc");
			exit(1);
		}
		cur->next = NULL;
		cur->x = POS_X + 2 * i;
		cur->y = POS_Y;
		//头插法
		cur->next = dummynode->next;
		dummynode->next = cur;
	}
	ps->_pSnake = dummynode->next;
	//打印
	while (ps->_pSnake)
	{
		SetPos(ps->_pSnake->x, ps->_pSnake->y);
		wprintf(L"●");
		ps->_pSnake = ps->_pSnake->next;
	}
	ps->_pSnake = dummynode->next;
	//设置属性
	ps->_dir = RIGHT;
	ps->_score = 0;
	ps->_food_weight = 10;
	ps->_sleeptime = 200;  //单位为毫秒
	ps->_status = OK;
}
//初始化食物
void InitFood(psnake ps)
{
	int food_x, food_y = 0;
again:
	do
	{
		food_x = (rand() % 53) + 2;    //2~54
		food_y = (rand() % 25) + 1;		  //0~24
	} while (food_x % 2 != 0);

	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (food_x == cur->x && food_y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//创建节点
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("malloc");
		exit(1);
	}
	food->x = food_x;
	food->y = food_y;
	food->next = NULL;
	SetPos(food_x, food_y);
	wprintf(L"★");
	ps->_pFood = food;
}
//游戏开始总函数
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; //隐藏控制台光标
	SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态
	//打印环境界面
	WelcomeToGame();
	//绘制地图
	CreateMap();
	//创建蛇
	InitSnake(ps);
	//创建食物
	InitFood(ps);
}


//打印帮助界面
void HelpPrint()
{
	SetPos(64, 14);
	wprintf(L"%ls", L"不能穿墙,不能咬到自己");
	SetPos(64, 15);
	wprintf(L"%ls", L"用 ↑. ↓ . ← . → 来控制蛇的移动");
	SetPos(64, 16);
	wprintf(L"%ls", L"按F3加速,F4减速");
	SetPos(64, 17);
	wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");

	SetPos(64, 18);
	wprintf(L"%ls", L"CSDN@都市隸人");
}
//判断下一节点是不是食物
int NextIsFood(psnake pf, pSnakeNode ps)
{
	return (pf->_pFood->x == ps->x && pf->_pFood->y == ps->y);
}
//吃食物操作
void EatFood(psnake ps, pSnakeNode pn)
{
	//头插法
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;

	//释放下一个节点
	free(pn);
	pn = NULL;

	//打印
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"●");
		cur = cur->next;
	}
	ps->_score += ps->_food_weight;

	//重新创建食物
	InitFood(ps);
}
//不是食物操作
void NoFood(psnake ps, pSnakeNode pn)
{
	//头插法
	pn->next = ps->_pSnake;
	ps->_pSnake = pn;
	pSnakeNode cur = ps->_pSnake;
	//打印
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"●");
		cur = cur->next;
	}
	
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放节点
	free(cur->next);
	cur->next = NULL;
}
//撞墙操作
void DropByWall(psnake ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||
		ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->_status = DROP_BY_WALL;
	}
}
//撞到自己操作
void DropBySelf(psnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
		{
			ps->_status = DROP_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}
//蛇身移动主函数
void SnakeMove(psnake ps)
{
	pSnakeNode pnextpos = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pnextpos == NULL)
	{
		perror("malloc");
		exit(1);
	}
	switch (ps->_dir)
	{
	case UP:
		pnextpos->x = ps->_pSnake->x;
		pnextpos->y = ps->_pSnake->y-1;
		break;
	case DOWN:
		pnextpos->x = ps->_pSnake->x;
		pnextpos->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		pnextpos->x = ps->_pSnake->x-2;
		pnextpos->y = ps->_pSnake->y;
		break;
	case RIGHT:
		pnextpos->x = ps->_pSnake->x + 2;
		pnextpos->y = ps->_pSnake->y;
		break;
	}

	//食物检测
	if (NextIsFood(ps, pnextpos))
	{
		EatFood(ps, pnextpos);
	}
	else
	{
		NoFood(ps, pnextpos);
	}
	//撞墙检测
	DropByWall(ps);
	//撞自己检测
	DropBySelf(ps);
}

//判断按键是否被按下
#define KEY_PRESS(vk)  ((GetAsyncKeyState(vk)&1)?1:0)
//游戏运行主函数
void GameRun(psnake ps)
{
	HelpPrint();
	do
	{
		//刷新打印
		SetPos(64, 10);
		printf("总分数:%d\n", ps->_score);
		SetPos(64, 11);
		printf("当前食物的分数:%2d\n", ps->_food_weight);

		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_SPACE))
		{
			while (1)
			{
				Sleep(200);
				if (KEY_PRESS(VK_SPACE))
				{
					break;
				}
			}
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出游戏
			ps->_status = NORMAL;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleeptime > 80)
			{
				ps->_sleeptime -= 30;   
				ps->_food_weight += 2;   //分数权重增加
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_food_weight > 2)
			{
				ps->_sleeptime += 30;
				ps->_food_weight -= 2;	//分数权重减少
			}
		}

		SnakeMove(ps);//蛇走一步的过程

		Sleep(ps->_sleeptime);

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


//游戏结束主函数
void GameEnd(psnake ps)
{
	SetPos(24, 12);
	switch (ps->_status)
	{
	case NORMAL:
		wprintf(L"您主动结束游戏\n");
		break;
	case DROP_BY_WALL:
		wprintf(L"您撞到墙上,游戏结束\n");
		break;
	case DROP_BY_SELF:
		wprintf(L"您撞到了自己,游戏结束\n");
		break;
	}
	//销毁链表
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
}

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

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

相关文章

架构师系列-消息中间件(九)- RocketMQ 进阶(三)-消费端消息保障

5.2 消费端保障 5.2.1 注意幂等性 应用程序在使用RocketMQ进行消息消费时必须支持幂等消费&#xff0c;即同一个消息被消费多次和消费一次的结果一样&#xff0c;这一点在使用RoketMQ或者分析RocketMQ源代码之前再怎么强调也不为过。 “至少一次送达”的消息交付策略&#xff…

开启医疗数据新纪元:山海鲸可视化智慧医疗解决方案

在数字化浪潮席卷而来的今天&#xff0c;智慧医疗作为医疗行业的创新力量&#xff0c;正以其独特的技术优势&#xff0c;推动着医疗服务的升级和变革。而在这场变革中&#xff0c;山海鲸可视化以其出色的数据可视化能力&#xff0c;为智慧医疗提供了强大的技术支持&#xff0c;…

用Python和Pygame实现简单贪吃蛇游戏

1.pip安装pygame pygam插件安装 pip install 插件名字 # 安装 pip uninstall 插件名字 # 卸载 pip install 插件名字 -i 指定下载的镜像网址 pip show 插件名字 # 查看插件名字 pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple pip show p…

【网络编程】网络编程概念 | TCP和UDP的区别 | UDP数据报套接字编程 | Socket

文章目录 网络编程一、什么是网络编程1.TCP和UDP的区别 二、UDP数据报套接字编程DatagramSocketDatagramPacket回显服务器&#xff08;echo server&#xff09; 网络编程 一、什么是网络编程 通过网络&#xff0c;让两个主机之间能够进行通信。基于通信来完成一定的功能。 ​…

MacOS 下gif 文件的几种压缩方法

categories: Tips tags: Tips GIF 写在前面 最近想转换几个 tg 的 tgs 文件到 gif, 然后上传到微信, 所以又涉及到了 gif 的操作了. 工具介绍 安装 brew install imagemagick gifsicleimagemagick 是专业的图像处理工具, gifsicle 是专门处理 gif 的小工具 ,都是开源的. …

C++之AVL树的使用以及原理详解

1.AVL树 1.1AVL树的概念 1.2AVL树的定义 1.3AVL树的插入 1.4AVL树的旋转 1. 右单旋 2. 左单旋 3. 左右双旋 4. 右左双旋 1.5AVL树的验证 1.6AVL的实现 在之前对map/multimap/set/multiset进行了简单的介绍&#xff08;C之map_set的使用-CSDN博客&#xff09;&#xff0c;…

说说2024年暑期三下乡社会实践工作新闻投稿经验

作为一名在校大学生,我有幸自去年起参与学院组织的暑期大学生三下乡社会实践团活动。这项活动不仅是我们深入基层、服务社会的重要平台,也是展现当代大学生风采、传递青春正能量的有效途径。然而,如何将这些生动鲜活的实践故事、感人至深的瞬间传播出去,让更多人了解并受到启发…

火绒安全的应用介绍

火绒安全软件是一款集成了杀毒、防御和管控功能的安全软件&#xff0c;旨在为用户提供全面的计算机安全保障。以下是火绒安全软件的一些详细介绍&#xff1a; 系统兼容性强&#xff1a;该软件支持多种操作系统&#xff0c;包括Windows 11、Windows 10、Windows 8、Windows 7、…

xgp加速器免费 微软商店xgp用什么加速器

2001年11月14日深夜&#xff0c;比尔盖茨亲自来到时代广场&#xff0c;在午夜时分将第一台Xbox交给了来自新泽西的20岁年轻人爱德华格拉克曼&#xff0c;后者在回忆中说&#xff1a;“比尔盖茨就是上帝。”性能超越顶级PC的Xbox让他们趋之若鹜。2000年3月10日&#xff0c;微软宣…

25-代码随想录第454题.四数相加II

25-代码随想录第454题.四数相加II 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) &#xff0c;使得 A[i] B[j] C[k] D[l] 0。 为了使问题简单化&#xff0c;所有的 A, B, C, D 具有相同的长度 N&#xff0c;且 0 ≤ N ≤ 500 。所有整数的范…

python 笔记ast.literal_eval

1 介绍 ast.literal_eval 是 Python 标准库 ast 模块中的一个函数&#xff0c;用于安全地评估表示 Python 字面量或容器&#xff08;如列表、字典、元组、集合&#xff09;的字符串 import ast # 解析并执行一个数字表达式 num ast.literal_eval("3.14") prin…

OpenFeign微服务调用组件!!!

1.Feign是什么 GitHub - OpenFeign/feign: Feign makes writing java http clients easierFeign makes writing java http clients easier. Contribute to OpenFeign/feign development by creating an account on GitHub.https://github.com/OpenFeign/feignFeign是Netflix开…

项目十一:爬取热搜榜(小白实战级)

首先&#xff0c;恭喜各位也恭喜自已学习爬虫基础到达圆满级&#xff0c;今后的自已python爬虫之旅会随着网络发展而不断进步。回想起来&#xff0c;我学过请求库requests模块、解析库re模块、lmxl模块到数据保存的基本应用方法&#xff0c;这一次的学习python爬虫之旅收获很多…

Vu3+QuaggaJs实现web页面识别条形码

一、什么是QuaggaJs QuaggaJS是一个基于JavaScript的开源图像识别库&#xff0c;可用于识别条形码。 QuaggaJs的作用主要体现在以下几个方面&#xff1a; 实时图像处理与识别&#xff1a;QuaggaJs是一款基于JavaScript的开源库&#xff0c;它允许在Web浏览器中实现实时的图像…

ASP.NET Core 3 高级编程(第8版) 学习笔记 03

本篇介绍原书的第 18 章&#xff0c;为 19 章 Restful Service 编写基础代码。本章实现了如下内容&#xff1a; 1&#xff09;使用 Entity Framework Core 操作 Sql Server 数据库 2&#xff09;Entity Framework Core 数据库迁移和使用种子数据的方法 3&#xff09;使用中间件…

Qt Quick centerIn和fill 的用法

1&#xff09;Qt Quick centerIn和fill 的用法&#xff1a; import QtQuick 2.5 Rectangle { width:300; height:200; Rectangle { color: "blue"; anchors.fill: parent; border.width: 6; border.co…

详解工业网关在线探测功能及用途

工业网关专为工业物联网应用设计&#xff0c;可实现包括不同通讯协议之间的兼容和转换&#xff0c;提供软硬件加密保障工业数据安全传输&#xff0c;发挥强大算力实现数据边缘预处理&#xff0c;联动联调工业网络设备实现高效协同等。在线探测功能是佰马工业网关的一项重要功能…

unity学习(89)——unity塞满c盘!--删除editor下的log文件

卸了一个视频后强制续命打开详细信息&#xff1a; 这个再往下找也是没用的&#xff01; 显示隐藏文件夹后&#xff01;执行如下操作&#xff01; 30个g&#xff01; 其中unity占23g editer占了21g 删除C:\Users\王栋林\AppData\Local\Unity\Editor下的log文件 恢复到之前的水…

使用 Flask 和 WTForms 构建一个用户注册表单

在这篇技术博客中&#xff0c;我们将使用 Flask 和 WTForms 库来构建一个用户注册表单。我们将创建一个简单的 Flask 应用&#xff0c;并使用 WTForms 定义一个注册表单&#xff0c;包括用户名、密码、确认密码、邮箱、性别、城市和爱好等字段。我们还将为表单添加验证规则&…

【C 数据结构】图

文章目录 【 1. 基本原理 】1.1 无向图1.2 有向图1.3 基本知识 【 2. 图的存储结构 】2.1 完全图2.2 稀疏图和稠密图2.3 连通图2.3.1 (普通)连通图连通图 - 无向图非连通图 的 连通分量 2.3.2 强连通图强连通图 - 有向图非强连通有向图 的 强连通分量 2.3.3 生成树 - 连通图2.3…