C从零开始实现贪吃蛇大作战

 

 个人主页:星纭-CSDN博客

系列文章专栏 : C语言

踏上取经路,比抵达灵山更重要!一起努力一起进步!

有关Win32API的知识点在上一篇文章:

目录

一.地图

1.控制台基本介绍

2.宽字符

1.本地化

2.类项

3.setlocale函数

4.打印宽字符 

3.地图坐标

 二.游戏的结构设计

1.基础结构

2.游戏流程(未完成)

3.核心逻辑实现

1.游戏开始

1.控制台设置 

2.欢迎界面 

3.创建地图

 4.初始化蛇

5.创建食物

4.游戏运行

 1.打印信息

2.判断按键 

 3.蛇的移动

5.游戏结束 


一.地图

这个游戏中,我们是通过控制台来完成的。首先就是需要完成这个地图。

1.控制台基本介绍

接下来介绍有关控制台窗口的一些知识点,当运行程序,需要在控制台上输出信息,打印的时候,第一个字符是在控制台最左上角打印的。我们将这个位置的坐标当作(0,0).

横向的坐标轴称为x轴,从左向右依次增长,纵向的坐标轴是y轴,从上到下依次增长。

2.宽字符

 普通的字符是占一个字节的,宽字符是占用两个字节的。

打印字符,不难发现,两个英文字符的宽度是等于一个中文字符的宽度的。 而且一个英文字符的高大概是其宽的两倍。

1.<locale.h>本地化

<locale.h>提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分。

在标准中,以来地区的部分有以下几项:

  1. 数字量的格式
  2. 货币量的格式
  3. 字符集
  4. 日期和时间的表示形式

2.类项

通过修改地区,程序可以改变它的⾏为来适应世界的不同区域。但地区的改变可能会影响库的许多部 分,其中⼀部分可能是我们不希望修改的。所以C语⾔⽀持针对不同的类项进⾏修改,下⾯的⼀个宏, 指定⼀个类项:

  1. • LC_COLLATE:影响字符串⽐较函数 strcoll() 和 strxfrm() 。
  2. • LC_CTYPE:影响字符处理函数的⾏为。
  3. • LC_MONETARY:影响货币格式。
  4. • LC_NUMERIC:影响 printf() 的数字格式。
  5. • LC_TIME:影响时间格式 strftime() 和 wcsftime() 。
  6. • LC_ALL-针对所有类项修改,将以上所有类别设置为给定的语⾔环境。

参考:setlocale,_wsetlocale | Microsoft Learn 

3.setlocale函数

char *setlocale(
   int category,
   const char *locale
);

这个函数用于修改当前地区,可针对一个类项修改,也可以针对所有类型。

第一个参数是前面说明的类项目中的一个,那么每次都只会影响一个类项,如果第一个参数是LC_ALL就会影响所有的类项。

C标准给第二个参数仅仅定义了两种取值:“C”正常模式和“”本地模式

在任意程序开始执行之前,都会隐藏执行调用:

    setlocale(LC_ALL, "C");

 当地区设置为"C"时,库函数按正常⽅式执⾏,⼩数点是⼀个点。 当程序运⾏起来后想改变地区,就只能显⽰调⽤setlocale函数。⽤""作为第2个参数,调⽤setlocale 函数就可以切换到本地模式,这种模式下程序会适应本地环境。 ⽐如:切换到我们的本地模式后就⽀持宽字符(汉字)的输出等。

setlocale(LC_ALL, " ");//切换到本地环境

4.打印宽字符 

如果想要在屏幕上打印宽字符,那么该如何打印呢?

为了区分宽字符与普通字符,宽字符的字面量前面必须加上前缀 L ,否则C语言会把字面量当作窄字符类型来处理。

同时还需要另一个函数wprintf函数来打印宽字符,用法与printf一样。宽字符的占位符为%lc,宽字符串的占位符为%ls.

	setlocale(LC_ALL, "");
	wprintf(L"%lc\n", L'□');
	wprintf(L"%lc\n", L'●');
	wprintf(L"%lc\n", L'★');

打印一个宽字符选哟占用两个字符的位置,那么我们在贪吃蛇中使用宽字符,就需要处理好地图上坐标的位置的计算。

3.地图坐标

我们假设实现一个三十行六十列的地图,在围绕地图画出墙。

#define WALL L'□'   //墙
#define BODY L'●'   //蛇身
#define FOOD L'★'  //食物

需要打印的东西总共有三个分别是墙,蛇身,食物。为了方便,我们define。

1.蛇身与食物

初始化状态,假设蛇的其实长度是5,蛇的每一个节点都是BODY.并且在一个固定的位置开始生成。

从该图不难发现,蛇与食物的每一个节点的坐标都应该是偶数,不能是技术,否则就就不对其了,而且会出现身体一半在墙体之内,一半在墙体之外的现象。

 二.游戏的结构设计

1.基础结构

在游戏运行过程中,蛇每次吃一个食物,蛇的身体就会变长一节,如果我们使用链表来储存蛇的信息,那么蛇的每一个节点其实就是链表的每一个节点,每一个节点就需要记录好蛇身节点的位置,以及下一个节点的位置。

typedef struct SnakeNode {
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

为了方便管理这个游戏,我们在封装一个Snake的结构来维护整个贪吃蛇。

//蛇的运动方向:上下左右
enum DIRECTION { UP = 1, DOWN, RIGHT, LEFT };
//游戏运行状态:正常运行,撞墙,撞自己,非正常结束。
//非正常结束:比如按Esc退出游戏。
enum GAME_STATUS { OK, KILL_BY_WALL, KILL_BY_SELF, END_NORMAL };

typedef struct GreedySnake {
	pSnakeNode _pSnake;
	pSnakeNode _pFood;
	enum DIRECTION _Dir;
	enum GAME_STATUS _Status;
	int _Score;
	int _FoodWeight;
	int _SleepTime;//可以理解为蛇的运行速度。
}GSnake,*pGSnake;

2.游戏流程(未完成)

3.核心逻辑实现

程序开始就设置程序⽀持本地模式,然后进⼊游戏的主逻辑。 主逻辑分为3个过程:

  1.  • 游戏开始(GameStart)完成游戏的初始化
  2. • 游戏运⾏(GameRun)完成游戏运⾏逻辑的实现
  3.  • 游戏结束(GameEnd)完成游戏结束的说明,实现资源释放

1.游戏开始

 这个模块需要完成游戏的初始化任务。

  1. 控制台的设置
  2. 欢迎界面

1.控制台设置 
	//一.设置控制台以及光标
	
	//设置控制台窗口的大小
	system("mode con cols=100 lines=30");
	//设置控制台窗口的名字
	system("title 贪吃蛇");
	//获得标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义一个光标信息的结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取和houput句柄相关的控制台上的光标的信息,存放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);
	//修改光标信息
	cursor_info.bVisible = false;//可见度
	//设置和houtput句柄相关的控制台上的光标的信息
	SetConsoleCursorInfo(houtput, &cursor_info);

在这里需要更改控制台窗口的名字,以及将光标不可见。

2.欢迎界面 

1.首先需要完成坐标定位函数,这样方便我们在特定的位置打印信息

void SetPos(int x, int y) {

	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	COORD pos = { x, y };
	SetConsoleCursorPosition(houtput, pos);

}

2.然后就是欢迎界面以及游戏规则的讲解

void WelcomeToGame() {
	SetPos(36,13);
	printf("欢迎来到星纭的贪吃蛇小游戏");
	SetPos(40,25);
	system("pause");
	system("cls");
	SetPos(15,10);
	printf("游戏规则介绍:");
	SetPos(24,13);
	wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速\n");
	SetPos(36,16);
	printf("加速可以获得更更高的分数");
	SetPos(40,25);
	system("pause");
	system("cls");
}

 

3.创建地图

 创建地图就是将地图打印出来,因为是宽字符打印,所以使用wprintf函数进行打印。

void CreateMap() {
	
	int i = 0;
	//上面的墙
	for (i = 0; i < 60; i += 2) {
		wprintf(L"%lc",WALL);
	}
	//左边的墙
	for (i = 1; i <= 30; i++) {
		SetPos(0,i);
		wprintf(L"%lc", WALL);
	}
	//右边的墙
	for (i = 1; i <= 30; i++) {
		SetPos(58,i);
		wprintf(L"%lc", WALL);
	}
	//下面的墙
	for (i = 2; i < 57; i += 2) {
		SetPos(i,30);
		wprintf(L"%lc", WALL);
	}

	SetPos(0, 33);
}
 4.初始化蛇

蛇最开始⻓度为5节,每节对应链表的⼀个节点,蛇⾝的每⼀个节点都有⾃⼰的坐标。 创建5个节点,然后将每个节点存放在链表中进⾏管理。创建完蛇⾝后,将蛇的每⼀节打印在屏幕上。

  1.  • 蛇的初始位置从(10,5)开始。 再设置当前游戏的状态,蛇移动的速度,默认的⽅向,初始成绩,每个⻝物的分数。
  2. • 游戏状态是:OK
  3. • 蛇的移动速度:200毫秒
  4.  • 蛇的默认⽅向:RIGHT
  5.  • 初始成绩:0
  6. • 每个⻝物的分数:10
void InitSnake(pGSnake ps) {

	//创建五个蛇节点,并完成头插
	pSnakeNode cur = NULL;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		cur->next = NULL;
		cur->x = POS_X - i * 2;
		cur->y = POS_Y;
		if (ps->_pSnake == NULL) {
			ps->_pSnake = cur;
		}
		else {
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}
	//打印蛇
	cur = ps->_pSnake;
	while (cur) {
		SetPos(cur->x,cur->y);
		wprintf(L"%lc",BODY);
		cur = cur->next;
	}
	//初始化游戏
	ps->_SleepTime = 200;
	ps->_Score = 0;
	ps->_Dir = RIGHT;
	ps->_Status = OK;
	ps->_FoodWeight = 10;
}
5.创建食物
void CreateFood(pGSnake ps) {
	int x = 0;
	int y = 0;
	again:
	do {
		x = rand() % 55 + 2;
		y = rand() % 29 + 1;
	} while (x % 2 != 0);
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		if (cur->x == x && cur->y == y) {
			goto again;
		}
		cur = cur->next;
	}
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	pFood->x = x;
	pFood->y = y;
	SetPos(x, y);
	wprintf(L"%lc",FOOD);
	ps->_pFood = pFood;
};

4.游戏运行

游戏运行期间,右侧打印帮助信息,提示玩家,坐标开始位置(64,15) 

根据游戏状态检测游戏是否继续,如果状态时OK,游戏继续,其他状态游戏结束。

并且根据游戏的过程中,按键的情况来确定蛇的下一步方向,或者是否加速减速,暂停或者退出游戏。

  1. • 上:VK_UP
  2.  • 下:VK_DOWN
  3. • 左:VK_LEFT
  4. • 右:VK_RIGHT
  5. • 空格:VK_SPACE
  6. • ESC:VK_ESCAPE
  7. • F3:VK_F3
  8. • F4:VK_F4

这是所需的虚拟按键

 1.打印信息
void PrintHelpInfo()
{
	SetPos(60,10);
	wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动方向");
	SetPos(70,12);
	wprintf(L"按F3加速,F4减速");
	SetPos(66,14);
	printf("加速可以获得更更高的分数");
}

在控制台窗口中,打印游戏规则以及分数。

2.判断按键 
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1) ? 1 : 0)
 3.蛇的移动

先创建下⼀个节点,根据移动⽅向和蛇头的坐标,蛇移动到下⼀个位置的坐标。 确定了下⼀个位置后,看下⼀个位置是否是⻝物(NextIsFood),是⻝物就做吃⻝物处理 (EatFood),如果不是⻝物则做前进⼀步的处理(NoFood)。 蛇⾝移动后,判断此次移动是否会造成撞墙(KillByWall)或者撞上⾃⼰蛇⾝(KillBySelf),从⽽影 响游戏的状态。

int NextisFood(pSnakeNode pNextNode, pGSnake ps) {
	if (ps->_pFood->x == pNextNode->x && ps->_pFood->y == pNextNode->y) {
		return 1;
	}
	return 0;
}
void EatFood(pSnakeNode pNextNode, pGSnake ps) {
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;
	free(pNextNode);
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}
	ps->_Score += ps->_FoodWeight;
	CreateFood(ps);

}
void NoFood(pSnakeNode pNextNode, pGSnake ps) {
	pNextNode->next = ps->_pSnake;
	ps->_pSnake = pNextNode;
	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;
}

void KillByWall(pGSnake ps) {
	if (
		(ps->_pSnake->x == 0) ||
		(ps->_pSnake->x == 58) ||
		(ps->_pSnake->y == 0) ||
		(ps->_pSnake->y == 29)
		) {
		ps->_Status = KILL_BY_WALL;
		return 1;
	}
	return 0;
}
void KillBySelf(pGSnake ps) {
	pSnakeNode cur = ps->_pSnake->next;
	while (cur) {
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y) {
			ps->_pSnake = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}

}
void SnakeMove(pGSnake ps) {
	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	switch (ps->_Dir) {
	case UP:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y - 1;

		break;
	case DOWN:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_pSnake->x - 2;
		pNextNode->y = ps->_pSnake->y;

		break;
	case RIGHT:
		pNextNode->x = ps->_pSnake->x + 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	}
	if (NextisFood(pNextNode,ps)) {
		EatFood(pNextNode, ps);
	}
	else 
	{
		NoFood(pNextNode, ps);
	}
	KillByWall(ps);
	KillBySelf(ps);
}

5.游戏结束 

游戏状态不再是OK(游戏继续)的时候,要告知游戏结束的原因,并且释放蛇⾝节点。

void GameEnd(pGSnake ps) {
	system("cls");
	SetPos(32, 12);
	switch (ps->_Status) {
	case END_NORMAL:
		printf("游戏已结束。");
		break;
	case KILL_BY_WALL:
		printf("蛇撞墙!游戏结束。");
		break;
	case KILL_BY_SELF:
		printf("蛇撞到自己!游戏结束。");
		break;
	}

	printf("总得分:%d", ps->_Score);
	//
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	SetPos(0, 26);

}

三.代码

greedysnake.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<stdbool.h>
#include<locale.h>
#include<time.h>
#define WALL L'□'   //墙
#define BODY L'●'   //蛇
#define FOOD L'★'  //食物


typedef struct SnakeNode {
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, * pSnakeNode;
//蛇的运动方向:上下左右
enum DIRECTION { UP = 1, DOWN, RIGHT, LEFT };
//游戏运行状态:正常运行,撞墙,撞自己,非正常结束。
//非正常结束:比如按Esc退出游戏。
enum GAME_STATUS { OK, KILL_BY_WALL, KILL_BY_SELF, END_NORMAL };

typedef struct GreedySnake {
	pSnakeNode _pSnake;
	pSnakeNode _pFood;
	enum DIRECTION _Dir;
	enum GAME_STATUS _Status;
	int _Score;
	int _FoodWeight;
	int _SleepTime;//可以理解为蛇的运行速度。
}GSnake,*pGSnake;

#define POS_X 10 //起始位置x
#define POS_Y 5  //起始位置y

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

void WelcomeToGame();
void CreateMap();
void InitSnake(pGSnake ps);
void CreateFood(pGSnake ps);

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

void PrintHelpInfo();
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1) ? 1 : 0)
void SnakeMove(pGSnake ps);
int NextisFood(pSnakeNode pNextNode, pGSnake ps);
void EatFood(pSnakeNode pNextNode, pGSnake ps);
void NoFood(pSnakeNode pNextNode, pGSnake ps);
void KillByWall(pGSnake ps);
void KillBySelf(pGSnake ps);

void GameEnd(pGSnake ps);

greedysnake.c

#define _CRT_SECURE_NO_WARNINGS 
#include"greedysnake.h"

void SetPos(int x, int y) {

	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	COORD pos = { x, y };
	SetConsoleCursorPosition(houtput, pos);

}
void WelcomeToGame() {
	SetPos(36, 13);
	printf("欢迎来到星纭的贪吃蛇小游戏");
	SetPos(40, 25);
	system("pause");
	system("cls");
	SetPos(15, 10);
	printf("游戏规则介绍:");
	SetPos(24, 13);
	wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动,按F3加速,F4减速\n");
	SetPos(36, 16);
	printf("加速可以获得更更高的分数");
	SetPos(40, 25);
	system("pause");
	system("cls");
}
void CreateMap() {

	int i = 0;
	//上面的墙
	for (i = 0; i < 60; i += 2) {
		wprintf(L"%lc", WALL);
	}
	//左边的墙
	for (i = 1; i <= 30; i++) {
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右边的墙
	for (i = 1; i <= 30; i++) {
		SetPos(58, i);
		wprintf(L"%lc", WALL);
	}
	//下面的墙
	for (i = 2; i < 57; i += 2) {
		SetPos(i, 30);
		wprintf(L"%lc", WALL);
	}


}
void InitSnake(pGSnake ps) {

	//创建五个蛇节点,并完成头插
	pSnakeNode cur = NULL;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		if (ps->_pSnake == NULL) {
			ps->_pSnake = cur;
		}
		else {
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}
	//打印蛇
	cur = ps->_pSnake;
	while (cur) {
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//初始化游戏
	ps->_SleepTime = 200;
	ps->_Score = 0;
	ps->_Dir = RIGHT;
	ps->_Status = OK;
	ps->_FoodWeight = 10;
}
void CreateFood(pGSnake ps) {
	int x = 0;
	int y = 0;
again:
	do {
		x = rand() % 47 + 6;
		y = rand() % 25 + 3;
	} while (x % 2 != 0);
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		if (cur->x == x && cur->y == y) {
			goto again;
		}
		cur = cur->next;
	}
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	pFood->x = x;
	pFood->y = y;
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	ps->_pFood = pFood;
};

void GameStart(pGSnake ps) {
	//一.设置控制台以及光标

	//设置控制台窗口的大小
	system("mode con cols=100 lines=36");
	//设置控制台窗口的名字
	system("title 贪吃蛇");
	//获得标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义一个光标信息的结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取和houput句柄相关的控制台上的光标的信息,存放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);
	//修改光标信息
	cursor_info.bVisible = false;//可见度
	//设置和houtput句柄相关的控制台上的光标的信息
	SetConsoleCursorInfo(houtput, &cursor_info);

	//二.打印欢迎界面
	WelcomeToGame();

	//三.创建地图
	CreateMap();

	//四.初始化蛇
	InitSnake(ps);

	//五.随机生成食物
	CreateFood(ps);

};

void PrintHelpInfo()
{
	SetPos(60, 10);
	wprintf(L"用 ↑. ↓ . ← . → 来控制蛇的移动方向");
	SetPos(70, 12);
	wprintf(L"按F3加速,F4减速");
	SetPos(66, 14);
	printf("加速可以获得更更高的分数");
}
void pause() {
	while (1) {
		Sleep(1);
		if (KEY_PRESS(VK_SPACE)) {
			break;
		}
	}

}
int NextisFood(pSnakeNode pNextNode, pGSnake ps) {
	if (ps->_pFood->x == pNextNode->x && ps->_pFood->y == pNextNode->y) {
		return 1;
	}
	return 0;
}
void EatFood(pSnakeNode pNextNode, pGSnake ps) {
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;
	free(pNextNode);
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", L'●');
		cur = cur->next;
	}
	ps->_Score += ps->_FoodWeight;
	CreateFood(ps);

}
void NoFood(pSnakeNode pNextNode, pGSnake ps) {
	pNextNode->next = ps->_pSnake;
	ps->_pSnake = pNextNode;
	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;
}

void KillByWall(pGSnake ps) {
	if (
		(ps->_pSnake->x == 0) ||
		(ps->_pSnake->x == 58) ||
		(ps->_pSnake->y == 0) ||
		(ps->_pSnake->y == 29)
		) {
		ps->_Status = KILL_BY_WALL;
		return 1;
	}
	return 0;
}
void KillBySelf(pGSnake ps) {
	pSnakeNode cur = ps->_pSnake->next;
	while (cur) {
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y) {
			ps->_Status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}

}
void SnakeMove(pGSnake ps) {
	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	switch (ps->_Dir) {
	case UP:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y - 1;

		break;
	case DOWN:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_pSnake->x - 2;
		pNextNode->y = ps->_pSnake->y;

		break;
	case RIGHT:
		pNextNode->x = ps->_pSnake->x + 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	}
	if (NextisFood(pNextNode, ps)) {
		EatFood(pNextNode, ps);
	}
	else
	{
		NoFood(pNextNode, ps);
	}
	KillByWall(ps);
	KillBySelf(ps);
}
void GameRun(pGSnake ps) {
	//打印帮助信息
	PrintHelpInfo();

	//循环
	do {
		SetPos(66, 8);
		printf("游戏得分:%4d", ps->_Score);
		printf("食物重量:%4d", 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_SPACE)) {
			pause();
		}
		else if (KEY_PRESS(VK_ESCAPE)) {
			ps->_Status = END_NORMAL;
			break;
		}
		else if (KEY_PRESS(VK_F3)) {
			if (ps->_SleepTime > 80) {
				ps->_SleepTime -= 30;
				ps->_FoodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4)) {
			if (ps->_SleepTime < 320) {
				ps->_SleepTime += 30;
				ps->_FoodWeight -= 2;
			}
		}
		Sleep(ps->_SleepTime);
		SnakeMove(ps);
	} while (ps->_Status == OK);

}

void GameEnd(pGSnake ps) {
	system("cls");
	SetPos(32, 12);
	switch (ps->_Status) {
	case END_NORMAL:
		printf("游戏已结束。");
		break;
	case KILL_BY_WALL:
		printf("蛇撞墙!游戏结束。");
		break;
	case KILL_BY_SELF:
		printf("蛇撞到自己!游戏结束。");
		break;
	}

	printf("总得分:%d", ps->_Score);
	
	pSnakeNode cur = ps->_pSnake;
	while (cur) {
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	SetPos(0, 26);

}

源.c

#define _CRT_SECURE_NO_WARNINGS 
#include"greedysnake.h"

int main()
{
	srand((unsigned int)time(NULL));
	setlocale(LC_ALL, "");

	GSnake snake = { 0 };
	//游戏初始化
	GameStart(&snake);

	//游戏运行中
	GameRun(&snake);

	GameEnd(&snake);
	SetPos(0, 33);
	system("pause");
	return 0;
}

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

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

相关文章

推荐10款优秀的组件库(一)

1.Ant Desgin UI 网址&#xff1a; https://ant-design-mobile.antgroup.com/zh Ant Design - 一套企业级 UI 设计语言和 React 组件库 "Ant Design Mobile"是一个在线的移动端Web体验平台&#xff0c;让你探索移动端Web的体验极限。 添加图片注释&#xff0c;不…

5月26(信息差)

&#x1f30d; 珠峰登顶“堵车”后冰架断裂 5人坠崖 2人没爬上来&#xff01; 珠峰登顶“堵车”后冰架断裂 5人坠崖 2人没爬上来&#xff01; &#x1f384; Windows 11 Beta 22635.3646 预览版发布&#xff1a;中国大陆地区新增“微软电脑管家”应用 ✨ 成都限购解除即将满…

5.23.12 计算机视觉的 Inception 架构

1. 介绍 分类性能的提升往往会转化为各种应用领域中显着的质量提升&#xff0c;深度卷积架构的架构改进可用于提高大多数其他计算机视觉任务的性能&#xff0c;这些任务越来越依赖于高质量的学习视觉特征。在 AlexNet 功能无法与手工设计、制作的解决方案竞争的情况下&#xf…

能找伴侣的相亲婚恋平台有哪些?6款值得信赖的恋爱交友软件体验测评

在这个超快节奏的社会里&#xff0c;好多人都忙着搞事业和搞钱&#xff0c;却把终身大事给忽略了。但是随着年龄越来越大&#xff0c;来自长辈和社会的压力也越来越大&#xff0c;因此网络上的相亲交友软件&#xff0c;就成了大多数单身贵族的脱单首选了。下面就来给大家讲讲我…

Day06:Flex 布局

目标&#xff1a;熟练使用 Flex 完成结构化布局 一、标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如&#xff1a;块元素独占一行&#xff0c;行内元素可以一行显示多个。 二、浮动 1、基本使用 作用&#xff1a;让块元素水平排列。 …

【C++题解】1698. 请输出带有特殊尾数的数

问题&#xff1a;1698. 请输出带有特殊尾数的数 类型&#xff1a; 题目描述&#xff1a; 请输出1∼n 中所有个位为 1、3、5、7中任意一个数的整数&#xff0c;每行 1 个。( n<1000 ) 比如&#xff0c;假设从键盘读入 20&#xff0c;输出结果如下&#xff1a; 1 3 5 7 11 1…

树莓派4B 有电但无法启动

试过多个SD卡&#xff0c;反复烧系统镜像都无法启动。接HDMI显示器没有信号输出&#xff0c;上电后PWR红灯长亮&#xff0c;ACT绿灯闪一下就不亮了&#xff0c;GPIO几个电源脚有电&#xff0c;芯片会发热&#xff0c;测量多个TP点电压好像都正常。 ……

N进制计数器【01】

N进制计数器 前面介绍过二进制计数器和十进制计数器&#xff0c;但是在很多时候需要到其他进制的计数器&#xff0c;我们把这些任意进制的计数器简称为 N 进制计数器 设计 N 进制计数器的方法有两种&#xff1a; 用时钟触发器和门电路设计&#xff08;前面常用的方法&#xf…

【Telemac】Telemac相关报错记录

文章目录 1.下载BlueKenue后缀为man解决办法2.运行Telemac项目提示Fortran报错解决办法1.下载BlueKenue后缀为man BlueKenue官方下载链接: 可以看到下载器请求时出现了问题,下载BlueKenue后缀为man. 解决办法 修改下载后的文件后缀为msi即可 2.运行Telemac项目提示Fortr…

Git时光机、Git标签、Git分支、GitHub协作

Git时光机&#xff08;切换版本&#xff09; 1.查看提交历史 HEAD指针指向这次分支的最后一次提交 版本信息一行显示【git log --prettyoneline】 2.引用日志【git reflog】 &#xff08;只在自己的工作区中存在&#xff09; 非常重要&#xff1a;当HEAD指针进行切换之后&…

el-switch自动触发更新事件

比如有这样一个列表&#xff0c;允许修改单条数据的状态。希望在更改el-switch状态时能够有个弹框做二次确认&#xff0c;没问题&#xff0c;el-switch已经帮我们想到了&#xff0c;所以它提供了beforeChange&#xff0c;根据beforeChange的结果来决定是否修改状态。一般确认修…

qt-C++笔记之使用QtConcurrent异步地执行槽函数中的内容,使其不阻塞主界面

qt-C笔记之使用QtConcurrent异步地执行槽函数中的内容&#xff0c;使其不阻塞主界面 code review! 文章目录 qt-C笔记之使用QtConcurrent异步地执行槽函数中的内容&#xff0c;使其不阻塞主界面1.QtConcurrent::run基本用法基本用法启动一个全局函数或静态成员函数使用 Lambda…

C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

脚注:书籍的小秘密,躲藏在脚注间

脚注&#xff1a;书籍的小秘密&#xff0c;躲藏在脚注间 脚注是一种在文本中提供补充信息、引用出处或注解的方式&#xff0c;有助于读者更全面地理解文中内容&#xff0c;并为进一步研究提供参考和跳转点。 在一书本中&#xff0c;脚注是额外提供给读者的文字信息&#xff0…

SpringCloud系列(31)--使用Hystrix进行服务降级

前言&#xff1a;在上一章节中我们创建了服务消费者模块&#xff0c;而本节内容则是使用Hystrix对服务进行服务降级处理。 1、首先我们先对服务提供者的服务进行服务降级处理 (1)修改cloud-provider-hystrix-payment8001子模块的PaymentServiceImpl类 注&#xff1a;HystrixP…

Stream流的使用

目录 一&#xff0c;Stream流 1.1 概述 1.2 Stream代码示例 二&#xff0c;Stream流的使用 2.1 数据准备 2.2 创建流对象 2.3 中间操作 filter map distinct sorted limit skip flatMap 2.4 终结操作 foreach count max&min collect 2.5 查找与匹配 a…

秒级达百万高并发框架Disruptor

1、起源 Disruptor最初由lmax.com开发&#xff0c;2010年在Qcon公开发表&#xff0c;并于2011年开源&#xff0c;企业应用软件专家Martin Fowler专门撰写长文介绍&#xff0c;同年它还获得了Oracle官方的Duke大奖。其官网定义为&#xff1a;“High Performance Inter-Thread M…

2022年CSP-J入门级第一轮初赛真题

一、单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1b;每题有且仅有一个正确选项&#xff09; 第 1 题 在内存储器中每个存储单元都被赋予一个唯一的序号&#xff0c;称为&#xff08;&#xff09;。 A. 地址B. 序号C. 下标D. 编号 第 2 题 编…

Spring MVC+mybatis 项目入门:旅游网(三)用户注册——控制反转以及Hibernate Validator数据验证

个人博客&#xff1a;Spring MVCmybatis 项目入门:旅游网&#xff08;三&#xff09;用户注册 | iwtss blog 先看这个&#xff01; 这是18年的文章&#xff0c;回收站里恢复的&#xff0c;现阶段看基本是没有参考意义的&#xff0c;技术老旧脱离时代&#xff08;2024年辣铁铁&…

Leetcode 剑指 Offer II 079.子集

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返…