贪吃蛇游戏(C语言实现)

news2024/11/27 1:34:11

目录

  • 游戏效果展示
  • 文件
  • 代码的展示
    • test.c
    • Snake.c
    • Snake.h
  • 下一个坐标不是食物

游戏效果展示

QQ录屏20240507163633

文件

在这里插入图片描述

代码的展示

test.c

#define _CRT_SECURE_NO_WARNINGS

#include<locale.h>
//设置本地化
#include"Snake.h"


//游戏的测试逻辑
void test()
{
	int ch = 0;
	do 
	{
		system("cls");
		//每次上来清理一下屏幕的信息
		//创建贪吃蛇
		snake snake = {0};
		//初始化游戏
		//1.打印环境界面
		//2.功能介绍
		//3.绘制地图
		//4. 创建蛇
		//5.创建食物
		//6.设置游戏的相关信息
		//开始游戏
		GameStart(&snake);
		//运行游戏
		GameRun(&snake);
		//结束游戏
		GameEnd(&snake);
		SetPos(20,15);
		printf("再来一局吗(Y/N):");
		ch = getchar();

		while (getchar() != '\n');
		//防止输入太多的字符而出现的bug

	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 27);
}
int main()
{
	//设置本地化环境
	setlocale(LC_ALL,"");
	//生成随机数
	srand((unsigned int)time(NULL));
	//测试的逻辑
	test();

	return 0;
}

Snake.c

#define _CRT_SECURE_NO_WARNINGS

#include"Snake.h"

//坐标的定位
//定位光标的位置
void SetPos(short x, short y)
{
	//获取标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置
	COORD pos = { x, y };
	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 CreatMap()
{
	//上
	int i = 0;
	for (i = 0; i < 29; i++)
	{
		wprintf(L"%lc",WALL);
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

//初始化蛇身
void InitSnake(psnake ps)
{
	int i = 0;
	psnakenode cur = NULL;

	for (i = 0; i < 5; i++)
	{
		cur = (psnakenode)malloc(sizeof(snakenode));
		if (cur == NULL)
		{
			perror("InitSnake:malloc");
			return;
		}
		//开辟新节点成功
		cur->next = NULL;
		cur->x = POS_X + 2*i;
		cur->y = POS_Y;

		//头插法插入节点
		if (ps->_phead == NULL)//空链表
		{
			ps->_phead = cur;
		}
		else//非空链表
		{
			cur->next = ps->_phead;
			ps->_phead = cur;
		}
	}

	//打印蛇身
	cur = ps->_phead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//设置蛇的属性
	ps->_dir = RIGHT;//默认蛇的方向是向右的
	ps->_food_weight = 10;//每个食物的分数
	ps->_score = 0;//开始总分为0
	ps->_status = OK;//蛇的状态是正常的
	ps->_time_sleep = 200;//睡眠时间为200ms
}

//创建食物
void CreatFood(psnake ps)
{
	int x = 0;
	int y = 0;
	 
	//生成的x是2的倍数
	// x - 2~54
	// y - 1~25

again:
	do
	{
		//创建的食物的节点不能和蛇身重叠
	    x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);
    
    psnakenode cur = ps->_phead;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}

	//创建食物的节点
	psnakenode pfood = (psnakenode)malloc(sizeof(snakenode));
	if (pfood == NULL)
	{
		//开辟失败
		perror("CreatFood::malloc");
		return;
	}
	//开辟成功
	pfood->x = x;
	pfood->y = y;
	pfood->next = NULL;

	SetPos(x, y);
    //定位坐标,打印节点
	wprintf(L"%lc", FOOD);

	ps->_food = pfood;
	//食物的信息记录到food(贪吃蛇)里面去
}

//游戏的初始化
void GameStart(psnake ps)
{
	//1.先设置窗口的大小,再隐藏光标
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	//设置窗口的名称
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//得到屏幕的句柄
	//隐藏光标操作
	CONSOLE_CURSOR_INFO cursoninfo;
	//控制光标信息的结构体
	GetConsoleCursorInfo(houtput, &cursoninfo);
	//得到控制台的光标信息
	cursoninfo.bVisible = false;
	//将光标设置为不可见(隐藏控制台光标)
	SetConsoleCursorInfo(houtput, &cursoninfo);
	//设置控制台光标的状态(光标为隐藏)

	//2.打印环境界面和功能介绍
	WelComeToGame();
	//3.创建地图
	CreatMap();
	//4.创建蛇
	InitSnake(ps);
	//5.创建食物
	CreatFood(ps);
}

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

	SetPos(64, 19);
	wprintf(L"%ls", L"英雄不问出处制作");
}

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1) ? 1 : 0)
//获取按键的情况,按了是真,没按是假
//?在:之前

//按了空格键的情况
void Pause()
{
	while (1)
	{
		Sleep(200);
		//睡眠200ms
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

//判断下一个坐标是否是食物
//返回值判断真假,是否有食物
int NextIsFood(psnakenode pn, psnake ps)
{
	return (pn->x == ps->_food->x && pn->y == ps->_food->y);
	//如果下一个节点是食物就吃掉食物
}

//下一个坐标是食物就吃掉食物
void EatFood(psnakenode pn, psnake ps)
{
	//把食物的节点头插
	ps->_food->next = ps->_phead;
	ps->_phead = ps->_food;

	//释放下一个节点的地址,因为下一个节点和食物的节点冲突了
	free(pn);
	pn = NULL;

	//把蛇打印出来
	psnakenode cur = ps->_phead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//吃掉一个食物要加分
	ps->_score += ps->_food_weight;

	//一个食物吃掉后重新创建一个食物
	CreatFood(ps);
}

//下一个坐标不是食物
void NoFood(psnakenode pn, psnake ps)
{
	//头插法
	pn->next = ps->_phead;
	ps->_phead = pn;

	psnakenode cur = ps->_phead;
	while (cur->next->next!=NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//把最后一个节点打印成空
	SetPos(cur->next->x, cur->next->y);
	printf("  ");

	//释放最后一个节点
	free(cur->next);

	//把倒数第二个节点的下一个节点的地址置为NULL
	cur->next = NULL;
}

//检测蛇是否撞墙
void KillByWall(psnake ps)
{
	if (ps->_phead->x == 0 || ps->_phead->y == 0 ||
		ps->_phead->x == 56 || ps->_phead->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}

//检测蛇是否撞到自己
void KillByMyself(psnake ps)
{
	psnakenode cur = ps->_phead->next;
	//蛇头的下一个节点和蛇头的节点的坐标是否相同
	while (cur)
	{
		if (cur->x == ps->_phead->x && cur->y == ps->_phead->y)
		{
			ps->_status = KILL_BY_MYSELF;
			break;
			//撞到了就跳出循环
		}
		cur = cur->next;
	}
}

//蛇的移动 -- 走一步
//蛇的移动控制着蛇的主逻辑
void SnakeMove(psnake ps)
{
    //创建一个节点,表示蛇即将到的下一个节点
	psnakenode pNextnode = (psnakenode)malloc(sizeof(snakenode));
	if (pNextnode == NULL)
	{
		perror("SnakeMove::malloc");
		return;
	}

	//创建节点成功
	//蛇的方向
	switch (ps->_dir)
	{
	case UP:
		pNextnode->x = ps->_phead->x;
		pNextnode->y = ps->_phead->y - 1;
		break;
	case DOWN:
		pNextnode->x = ps->_phead->x;
		pNextnode->y = ps->_phead->y + 1;
		break;
	case LEFT:
		pNextnode->x = ps->_phead->x - 2;
		pNextnode->y = ps->_phead->y;
		break;
	case RIGHT:
		pNextnode->x = ps->_phead->x + 2;
		pNextnode->y = ps->_phead->y;
		break;
	}

	//检查下一个节点是否是食物
	if (NextIsFood(pNextnode, ps))
	{
		EatFood(pNextnode, ps);
	}
	else
	{
		NoFood(pNextnode, ps);
	}

	//判断蛇是否撞墙
	KillByWall(ps);
	//判断蛇是否撞到自己
	KillByMyself(ps);

}

//游戏运行的逻辑
void GameRun(psnake ps)
{
	//打印帮助信息
	PrintHelpInfo();
	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))
		{
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出游戏
			ps->_status = EXIT;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_time_sleep > 80)
			{
				ps->_time_sleep -= 30;
				ps->_food_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_food_weight > 2)
			{
				ps->_time_sleep += 30;
				ps->_food_weight -= 2;
			}
		}
		
		//蛇走一步的过程
		SnakeMove(ps);

		Sleep(ps->_time_sleep);
		//蛇走完一步再让蛇睡眠一下,可以达到蛇动态的移动过程	

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

//游戏善后的工作
//游戏的状态是因为什么而结束的
void GameEnd(psnake ps)
{
	SetPos(24, 12);
	//定位到屏幕的正中央然后打印
	switch (ps->_status)
	{
	case KILL_BY_MYSELF:
		wprintf(L"您撞到了自己,游戏结束\n");
		break;
	case KILL_BY_WALL:
		wprintf(L"您撞到墙上,游戏结束\n");
		break;
	case EXIT:
		wprintf(L"您主动结束游戏\n");
		break;
	}

	//释放掉蛇身的节点
	psnakenode cur = ps->_phead;
	while (cur)
	{
		psnakenode Del = cur;
		cur = cur->next;
		free(Del);
	}
}

Snake.h

#define _CRT_SECURE_NO_WARNINGS
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
#include<stdbool.h>

//#define 后面不用打括号

//定位,Y是X的两倍
#define POS_X 24
#define POS_Y 5

#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'


//类型的声明 

//蛇的方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

//蛇的状态
//撞到自己,撞到墙,正常运行,正常退出
enum STATUS
{
	OK,//正常运行
	EXIT,//正常退出
	KILL_BY_WALL,//撞到墙
	KILL_BY_MYSELF//撞到自己
};

//蛇身的节点类型
typedef struct SnakeNode
{
	int x;
	int y;
	//坐标
	struct SnakeNode* next;
	//指向下一个节点的指针
}snakenode,* psnakenode;

//typedef struct SnakeNode* pSnake; == 上面的写法 

//贪吃蛇
typedef struct SnakeList
{
	psnakenode _phead;//指向蛇头节点的指针
	psnakenode _food;//指向食物节点的指针
	enum DIRECTION _dir;//蛇的方向
	enum STATUS _status;//蛇的状态
	int _food_weight;//一个食物的分数
	int _score;//总分数
	int _time_sleep;//休息的时间,时间越短,速度越快,时间越长,速度越慢
}snake,* psnake;


//函数的声明

//坐标的定位
//定位光标的位置
void SetPos(short x, short y);

//游戏的初始化
void GameStart(psnake ps);

//欢迎界面的打印
void WelComeToGame();

//创建地图
void CreatMap();

//初始化蛇身
void InitSnake(psnake ps);

//创建食物
void CreatFood(psnake ps);

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

//蛇的移动 -- 走一步
void SnakeMove(psnake ps);

//判断下一个坐标是否是食物
//pn是下一个节点的指针
int NextIsFood(psnakenode pn, psnake ps);

//下一个坐标是食物就吃掉食物
void EatFood(psnakenode pn, psnake ps);

//下一个坐标不是食物
void NoFood(psnakenode pn, psnake ps);

//检测蛇是否撞墙
void KillByWall(psnake ps);

//检测蛇是否撞到自己
void KillByMyself(psnake ps);

//游戏善后的工作
void GameEnd(psnake ps);

下一个坐标不是食物

在这里插入图片描述
打印前四个节点,打印完第四个节点退出循环,把cur下一个节点打印成空,也就是最后一个节点打印成空释放最后一个节点,把倒数第二个节点的下一个节点的地址置为NULL,倒数第二个节点不用打印,之前打印了前5个节点,节点不会消失,所以倒数第二个节点存在

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

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

相关文章

免单狂欢模式:电商销售的新风潮

免单狂欢模式&#xff0c;是电商行业里一股新兴的销售力量。它以其独特的激励机制、社交属性与合规运营&#xff0c;迅速吸引了众多消费者的目光&#xff0c;成为推动销售业绩飙升的强大动力。 一、合规运营&#xff0c;稳健发展 在免单狂欢模式中&#xff0c;我们严格遵守法律…

搭建Harbor仓库

文章目录 Harbor仓库搭建Harbor仓库安装 docker 服务修改配置文件 Harbor仓库 搭建Harbor仓库 下载 Harbor 仓库 安装 docker 服务 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-m…

NFCP502-W05 电流数据是多少安培?

YOKOGAWA NFCP502-W05 是一款由横河电机&#xff08;Yokogawa Electric Corporation&#xff09;生产的微型断路器&#xff08;Microcircuit Breaker&#xff0c;简称 MCB&#xff09;。 横河电机是一家日本的跨国公司&#xff0c;专注于自动化和控制系统、仪器和其他相关设备…

sh包装脚本

两个脚本,运行的时间间隔分别是一分钟和五分钟,放入到sh文件中,挂在后代,脚本里面的路径最好是绝对路径。 新建sh文件 新建 run_test.sh 文件,使其可以运行两个不同的 Python 脚本,一个每分钟运行一次,另一个每五分钟运行一次。下面是修改后的 run_test.sh 文件的示例:…

学QT的第二天~

小黑子鉴别界面 #include "mywidget.h" void MyWidget::bth1() { if(edit3 ->text()"520cxk"&&edit4 ->text()"1314520") { qDebug()<< "你好&#xff0c;真爱粉"; this->close(); } else { speecher->sa…

EMAIL-PHP功能齐全的发送邮件类可以发送HTML和附件

EMAIL-PHP功能齐全的发送邮件类可以发送HTML和附件 <?php class Email { //---设置全局变量 var $mailTo ""; // 收件人 var $mailCC ""; // 抄送 var $mailBCC ""; // 秘密抄送 var $mailFrom ""; // 发件人 var $mailSubje…

PyQt6--Python桌面开发(1.安装配置环境)

一.PyQt6简介 PyQt&#xff1a;PyQt是一个功能强大且成熟的GUI框架&#xff0c;基于Qt库。它提供了丰富的组件、布局和主题选项&#xff0c;以及强大的功能和灵活性。PyQt的优点是它具有现代化的外观和丰富的功能&#xff0c;适用于复杂的GUI应用程序。然而&#xff0c;由于Py…

Cobalt Strike DNS Beacon使用

0. 前言 1. 实验运行流程 2. 环境搭建 2.1.1 安装虚拟机软件VMware……大家应该都有装吧&#xff0c;这里就不教了 2.1.2 Windows 10虚拟机&#xff08;受害机&#xff09; 2.1.3 Ubuntu虚拟机&#xff08;攻击机&#xff09; 2.1 安装虚拟机 2.2 安装Java 2.3 安装Dock…

通过管理系统进行升级怎么选?

现在通过系统来做办公效率提升的又很多&#xff0c;但怎么选&#xff0c;确实很关键。 我们是在去年年初的时候进行企业系统化的。当时刚摘下口罩&#xff0c;领导也是意识到团队办公的不便&#xff0c;数据管理的混乱&#xff0c;业务流转的低效等原因&#xff0c;开始寻找各…

数据结构——链表专题3

文章目录 一、判断链表是否有环二、返回入环的第一个节点三、随机链表的复制 一、判断链表是否有环 原题链接&#xff1a;判断链表是否有环 这道题可以使用快慢指针&#xff0c;fast一次走两步&#xff0c;slow一次走一步&#xff0c;如果有环&#xff0c;它们在环里面必定会…

SAP-ABAP-视图

1、什么是视图&#xff1f; 当需要查询多个表中的某些字段的数据时&#xff0c;就可以使用视图。视图不影响数据库中的数据&#xff0c;仅作为查询手段或工具。 2、视图类型&#xff1a; 数据库视图和维护视图经常使用。 3、创建视图SE11 3.1、数据库视图 可以直接输入表名…

【半个月我拿下了软考证】软件设计师高频考点--系统化教学-计算机与组成原理

&#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件设计师考点暴击 ⭐&#x1f170;️进入狂砍分⭐ ⭐软件设计师高频考点文档&#xff0c; ⭐软件设计师高频考点专栏 ⭐⭐ &#x1f3b6;&#xff08;1) 考点6&#xff0c;流水线 考点&#xff1a;流水线相关的计算 可…

《QT实用小工具·六十》Qt 多列时间轴控件

1、概述 源码放在文章末尾 Qt 多列时间轴控件。 可与多段字符串格式自由转换&#xff0c;也可手动添加列表项。 专门用来以时间轴作为事件线发展顺序的故事大纲。 特点 时间背包功能&#xff1a;记录所有物品或属性发生的变化&#xff0c;随时回溯 时间可输入任意内容&…

linux学习:音视频编程+alsa声音架构

目录 概念 采样 量化 编码 音频文件wav 格式 标准音频接口 ALSA 录制音频 步骤 api 获取pcm设备句柄 设置 PCM 设备参数 代码 播放音频 步骤 代码 概念 信号都是模拟信号&#xff0c;不管是声音还是光线&#xff0c;这些模拟信号需要被 A/D 转换器转换成数字信…

RK3576芯片规格,以及与RK3588对比

瑞芯微RK3576是一款高性能、低功耗的SoC&#xff08;系统级芯片&#xff09;处理器&#xff0c;适用于基于ARM的PC、边缘计算设备、个人移动互联网设备等多种应用场景。它采用Arm架构的八核心CPU&#xff0c;集成了GPU、MCU、NPU、VPU等多种计算核心&#xff0c;并具有丰富的外…

我的256天之创作纪念日

目录 时光 数据的一些变化 开心的事 憧憬 时光 自上次CSDN的消息推送&#xff0c;又一个128天过去了&#xff0c;整天的工作和生活都在忙忙碌碌中度过&#xff0c;每到能静下来片刻&#xff0c;都倍感珍惜。因为一些原因&#xff0c;能够陪伴家人的时间越来越少&#xff…

【LLM 论文】OpenAI 基于对比学习微调 LLM 得到嵌入模型

论文&#xff1a;Text and Code Embeddings by Contrastive Pre-Training ⭐⭐⭐⭐ OpenAI 一、论文速读 这篇论文基于大型生成式 LLM 通过对比学习来微调得到一个高质量的 text 和 code 的 embedding 模型。 训练数据的格式&#xff1a;是一堆 ( x i , y i ) (x_i, y_i) (x…

前端nginx(windows操作系统)学习配置开发验证

Nginx概述 Nginx 作为负载均衡在 Linux 系统上具备很好的并发性能&#xff0c;并且占用极小的内存。但是在 Windows 系统上并不支撑较高并发&#xff0c;所以在Windows系统上选用Nginx作为负载均衡&#xff0c;需要考虑并发情况。 若并发需求低于 300&#xff0c;部署集群仅以…

Spring的基本应用

概述&#xff1a;Spring是由Rod Johnson组织开发的一个分层的java SE/EE一站式的轻量级开源框架&#xff0c;以IOC(控制反转)和AOP&#xff08;面向切面&#xff09;为核心&#xff0c;的开发模式。 注&#xff1a;喜欢的朋友可以关注公众号“JAVA学习课堂”系统学习相关技术&a…

ElasticSearch知识点汇总

1、ES中的​​​​​​​倒排索引是什么。 倒排索引&#xff0c;是通过分词策略&#xff0c;形成了词和文章的映射关系表&#xff0c;这种词典映射表即为倒排索引 2、ES是如何实现master选举的。 选举过程主要包括以下几个步骤&#xff1a; 心跳检测&#xff1a; 每个节点…