【C语言小游戏】贪吃蛇

文章目录

  • 1.引言
  • 2.运行图
  • 2.涉及知识
  • 3 Windows API
    • 3.1 控制台
    • 3.2 控制台屏幕坐标
    • 3.3 操作句柄
    • 3.4 控制台屏幕光标
    • 3.5 监视按键
  • 4. 设计说明
  • 5. 完整代码

1.引言

  使⽤C语⾔在Windows环境的控制台中模拟实现经典⼩游戏贪吃蛇
实现基本的功能:

  1. 贪吃蛇地图绘制
  2. 蛇吃⻝物的功能 (上、下、左、右⽅向键控制蛇的动作)
  3. 蛇撞墙死亡
  4. 蛇撞⾃⾝死亡
  5. 计算得分
  6. 蛇⾝加速、减速
  7. 暂停游戏

2.运行图

游戏指引页面
在这里插入图片描述
在这里插入图片描述
游戏页面
在这里插入图片描述

2.涉及知识

  1. 指针;
  2. 动态内存;
  3. 结构体;
  4. locale本地化字符格式;
  5. #define宏;
  6. srand()、time()、rand();
  7. 枚举;
  8. 单链表;
  9. Windows API。

3 Windows API

  Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程式达到开启视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(Application), 所以便称之为 Application Programming Interface,简称 API 函数。WIN32 API也就是Microsoft Windows 32位平台的应⽤程序编程接⼝。

3.1 控制台

  平常运⾏起来的⿊框程序其实就是控制台程序,可以使⽤cmd命令来设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩。

mode con cols=100 lines=30

通过命令设置控制台窗⼝的名字:

title 贪吃蛇

3.2 控制台屏幕坐标

COORD 是Windows API中定义的⼀种结构,表⽰⼀个字符在控制台屏幕上的坐标。

// 原型
typedef struct _COORD {
	SHORT X;
	SHORT Y;
} COORD, * PCOORD;

控制台窗⼝的坐标如下所⽰,横向的是X轴,从左向右依次增⻓,纵向是Y轴,从上到下依次增⻓。

3.3 操作句柄

  GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。

// 原型
HANDLE GetStdHandle(DWORD nStdHandle);

例:

// 获取标准输出的句柄(⽤来标识不同设备的数值)
HANDLE stdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);

官方文档:GetStdHandle 函数

3.4 控制台屏幕光标

可以指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息。

// 原型
typedef struct _CONSOLE_CURSOR_INFO {
	DWORD dwSize;
	BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
// 原型
BOOL WINAPI GetConsoleCursorInfo(
	HANDLE hConsoleOutput,
	PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

官方文档:GetConsoleCursorInfo 函数

// 原型
BOOL WINAPI SetConsoleCursorInfo(
	HANDLE hConsoleOutput,
	const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

官方文档:SetConsoleCursorInfo 函数

例:

HANDLE stdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO consoleCursorInfo;
GetConsoleCursorInfo(stdOutputHandle , &consoleCursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; // 隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo); //设置控制台光标状态

===================================================================

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。

// 原型
BOOL WINAPI SetConsoleCursorPosition(
	HANDLE hConsoleOutput,
	COORD pos
);

官方文档:SetConsoleCursorPosition 函数

例:

COORD pos = { 10, 5 };
//获取标准输出的句柄(⽤来标识不同设备的数值)
HANDLE stdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(stdOutputHandle , pos);

3.5 监视按键

获取按键情况,GetAsyncKeyState的函数原型如下:

SHORT GetAsyncKeyState(
	int vKey
);

官方文档:getAsyncKeyState 函数 (winuser.h))

键值表:虚拟键代码

将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果
返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。如果要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.

例:

// 用宏比较方便
#define KEY_PRESSED(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

使用这个宏,先到虚拟键代码表去找对应键值,比如 ↑(上箭头 UP ARROW key),在表中说明常量为VK_UP,值是0x26,则这么使用这个宏就可以监测到上箭头是否被按下:

KEY_PRESSED(VK_UP); 或者 KEY_PRESSED(0x26);

4. 设计说明

在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇身体使⽤宽字符●,打印蛇头使用宽字符■,打印⻝物使⽤宽字符★,宽字符即占两个字节大小的字符,在控制台屏幕占的位置也是比正常键盘敲的字符大一倍。

由于宽字符引入,就不得不设置本地化字符。过去C语⾔并不适合⾮英语国家(地区)使⽤,后来为了使C语⾔适应国家化,C语⾔的标准中不断加⼊了国际化的⽀持。⽐如:加⼊和宽字符的类型wchar_t 和宽字符的输⼊和输出函数,加⼊和<locale.h>头⽂件,其中提供了允许程序员针对特定地区(通常是国家或者说某种特定语⾔的地理区域)调整程序⾏为的函数。

<locale.h>提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。
在标准可以中,依赖地区的部分有以下⼏项:
• 数字量的格式
• 货币量的格式
• 字符集
• ⽇期和时间的表⽰形式

setlocale函数

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

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

setlocale 函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项。第⼀个参数可以是前⾯说明的类项中的⼀个,那么每次只会影响⼀个类项,如果第⼀个参数是LC_ALL,就会影响所有的类项。C标准给第⼆个参数仅定义了2种可能取值:“C"和” "。
在任意程序执⾏开始,都会隐藏式执⾏调⽤。

当地区设置为"C"时,库函数按正常⽅式执⾏:

setlocale(LC_ALL, "C");

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

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

宽字符打印:

wchar_t ch1 = L'●';
wchar_t ch2 = L'★';
wprintf(L"%c\n", ch1);
wprintf(L"%c\n", ch2);

5. 完整代码

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "snake.h"

void game() {
	// 1.创建一条贪吃蛇
	Snake snake = { NULL };
	// 2.游戏开始(初始化)
	Init(&snake);
	// 3.游戏进行
	Play(&snake);
	// 4.游戏结束
	GameOver(&snake);
}

int main() {
	// 1.本地化字符格式
	setlocale(LC_ALL, "");
	// 2.游戏逻辑
	game();
	return 0;
}

snake.h

#pragma once

#include <stdio.h> // 标准输入输出
#include <locale.h> // 本地化格式
#include <stdlib.h> // 动态内存、system()
#include <Windows.h> // Windows API
#include <stdbool.h> // 布尔值
#include <time.h> // 时间戳

#define WALL_WIDTH 80 // 墙宽
#define WALL_HEIGHT 32 // 墙高
#define BRICK L'□' // 墙身

#define SNAKE_BODY L'●' // 蛇身
#define SNAKE_HEAD L'■' // 蛇头
#define FOOD L'★' // 食物

#define POS_X 34 // 蛇头起始X位置
#define POS_Y 15 // 蛇头起始Y位置

// 蛇初始长度
#define DEFAULT_LENGTH 4
// 蛇走一步休息的间隔时间(毫秒),影响蛇移动速度
#define PACE_INTERVAL_SLOW 250 
#define PACE_INTERVAL_MODERATE 200
#define PACE_INTERVAL_QUICK 150 
#define PACE_INTERVAL_EXTREME 100
#define PACE_INTERVAL_GREEDY 50
// 食物的分数(受移动速度影响)
#define SLOW_SCORE 10 
#define MODERATE_SCORE (SLOW_SCORE*1.5) 
#define QUICK_SCORE (SLOW_SCORE*2)
#define EXTREME_SCORE (SLOW_SCORE*2.5)
#define GREEDY_SCORE (SLOW_SCORE*3)


// 按键是否被按下
#define KEY_PRESSED(VK) ((GetAsyncKeyState(VK) & 0x1) ? true : false) 

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

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

// 游戏状态
typedef enum GAME_STATE {
	OK, // 游戏正常进行
	EXIT, // 正常退出
	KILL_BY_WALL, // 蛇头撞墙
	KILL_BY_SELF // 吃到尾巴
} GAME_STATE;

// 蛇状态
typedef struct Snake {
	pSnakeNode _pSnakeHead; // 蛇头指针维护蛇链表
	pSnakeNode _pFood; // 维护食物的指针
	double _totalScore; // 总分数
	int _paceInterval; // 移动速度
	int _length; // 蛇的长度
	double _foodScore; // 吃一个食物能得到的分数值(加速减速有影响)
	DIRECTION _direction; // 移动方向
	GAME_STATE _gameState; // 游戏状态
} Snake, * pSnake;

// 设置光标位置
void SetCursorPosition(short x, short y);

// 游戏开始(初始化)
void Init(pSnake pSnake);
// 主界面
void Welcome();
// 地图
void CreateMap();
// 初始化蛇
void InitSnake(pSnake ps);
// 打印蛇
void ShowSnake(pSnakeNode psHead);
// 食物
void CreateFood(pSnake ps);

// 游戏进行
void Play(pSnake ps);
// 暂停
void Pause();
// 蛇移动
void Move(pSnake ps);
// 移动的位置是否有食物
bool IsFood(pSnakeNode pNext, pSnakeNode pFood);
// 吃食物
void EatFood(pSnake ps, pSnakeNode pNext);
// 不吃食物
void NoFood(pSnake ps, pSnakeNode pNext);
// 移动约束
void CheckMove(pSnake ps);

// 游戏结束(释放资源等)
void GameOver(pSnake ps);

snake.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "snake.h"

// 游戏开始(初始化)
void Init(pSnake ps)
{
	// 1.控制台窗口大小、游戏名
	system("title 贪吃蛇"); // 失效,不知原因
	system("mode con cols=128 lines=32");
	// 2.隐藏光标
	HANDLE stdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursorInfo;
	GetConsoleCursorInfo(stdOutput, &cursorInfo);
	cursorInfo.bVisible = false;
	SetConsoleCursorInfo(stdOutput, &cursorInfo);
	// 3.主界面
	Welcome();
	// 4.地图
	CreateMap();
	// 5.蛇
	InitSnake(ps);
	// 6.食物
	srand((unsigned int)time(NULL));
	CreateFood(ps);
}

// 设置光标位置
void SetCursorPosition(short x, short y) {
	COORD pos = { x, y };
	HANDLE stdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(stdOutput, pos);
}

// 主界面
void Welcome()
{
	SetCursorPosition(50, 10);
	printf("WELCOME TO GREEDY SNAKE\n");
	SetCursorPosition(52, 18);
	system("pause");
	system("cls");
	SetCursorPosition(52, 8);
	printf("移动:WASD 或↑↓←→");
	SetCursorPosition(52, 10);
	printf("加速:J 或 1");
	SetCursorPosition(52, 12);
	printf("减速:K 或 2");
	SetCursorPosition(52, 14);
	printf("退出游戏:ESC");
	SetCursorPosition(52, 16);
	printf("暂停游戏:空格");
	SetCursorPosition(52, 20);
	system("pause");
	system("cls");

	// 避免造成一进游戏就暂停游戏
	KEY_PRESSED(VK_SPACE); // space 
	// 避免一进游戏就加速了
	KEY_PRESSED(0x4A); // J 
	KEY_PRESSED(0x31); // 1 
	// 避免一进游戏就已经被改变了移动方向(默认向右)
	KEY_PRESSED(0x57); // W
	KEY_PRESSED(0x53); // S
	KEY_PRESSED(VK_UP); // ↑
	KEY_PRESSED(VK_DOWN); // ↓
}

// 地图界面
void CreateMap() {
	SetCursorPosition(0, 0);
	for (int i = 0; i < WALL_WIDTH; i+=2)
	{
		wprintf(L"%lc", BRICK);
	}
	SetCursorPosition(0, WALL_HEIGHT-1);
	for (int i = 0; i < WALL_WIDTH; i+=2)
	{
		wprintf(L"%lc", BRICK);
	}
	for (int i = 1; i <= WALL_HEIGHT-2; i++)
	{
		SetCursorPosition(0, i);
		wprintf(L"%lc", BRICK);
	}
	for (int i = 1; i <= WALL_HEIGHT-2; i++)
	{
		SetCursorPosition(WALL_WIDTH-2, i);
		wprintf(L"%lc", BRICK);
	}
	// 提示信息
	SetCursorPosition(94, 16);
	printf("移动:WASD 或↑↓←→");
	SetCursorPosition(94, 18);
	printf("加速:J 或 1");
	SetCursorPosition(94, 20);
	printf("减速:K 或 2");
	SetCursorPosition(94, 22);
	printf("退出游戏:ESC");
	SetCursorPosition(94, 24);
	printf("暂停游戏:空格");
}

// 初始化蛇
void InitSnake(pSnake ps) {
	// 链表形式,蛇初始长度为4
	pSnakeNode cur = NULL;
	for (int i = 0; i < DEFAULT_LENGTH; i++)
	{
		cur =  (SnakeNode*)calloc(1, sizeof(SnakeNode));
		if (cur == NULL) {
			perror("InitSnake(pSnake): malloc");
			return;
		}
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		// 形成蛇身
		if (ps->_pSnakeHead == NULL) {
			ps->_pSnakeHead = cur;
		}
		else {
			cur->next = ps->_pSnakeHead;
			ps->_pSnakeHead = cur;
		}
	}
	// 打印蛇头和蛇身
	ShowSnake(ps->_pSnakeHead);
	// 蛇状态
	ps->_totalScore = 0;
	ps->_direction = RIGHT;
	ps->_gameState = OK;
	ps->_paceInterval = PACE_INTERVAL_SLOW;
	ps->_pFood = NULL;
	ps->_foodScore = SLOW_SCORE;
	ps->_length = DEFAULT_LENGTH;
}

// 打印蛇
void ShowSnake(pSnakeNode psHead) {
	// 蛇头
	SetCursorPosition(psHead->x, psHead->y);
	wprintf(L"%lc", SNAKE_HEAD);
	pSnakeNode cur = psHead->next;
	// 蛇尾
	while (cur) {
		SetCursorPosition(cur->x, cur->y);
		wprintf(L"%lc", SNAKE_BODY);
		cur = cur->next;
	}
}

// 食物
void CreateFood(pSnake ps) {
	// 随机产生食物
	short foodPosX = 0;
	short foodPosY = 0;
generatePosAgain:
	do {
		// 食物坐标必须在墙内:x>=2&&x<=76 y>=1&&y<=31
		foodPosX = rand() % (WALL_WIDTH - 5) + 2;
		foodPosY = rand() % (WALL_HEIGHT - 2) + 1;
		// 食物x坐标必须是2的倍数
	} while (foodPosX % 2 != 0);
	// 食物坐标不能与蛇身坐标冲突
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur) {
		if (foodPosX == cur->x && foodPosY == cur->y) {
			goto generatePosAgain;
		}
		cur = cur->next;
	}

	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL) {
		perror("CreateFood: malloc() error");
		return;
	}
	else {
		pFood->x = foodPosX;
		pFood->y = foodPosY;
		pFood->next = NULL;
		ps->_pFood = pFood;
		SetCursorPosition(foodPosX, foodPosY);
		wprintf(L"%c", FOOD);
	}
}


// 游戏运行
void Play(pSnake ps) {
	do {
		// 分数更新提示
		SetCursorPosition(94, 8);
		printf("当前分数:%.1lf", ps->_totalScore);
		// 长度更新提示
		SetCursorPosition(94, 10);
		printf("当前长度:%d", ps->_length);
		// 速度更新提示
		SetCursorPosition(94, 12);
		char* speed = NULL;
		if (ps->_paceInterval == PACE_INTERVAL_GREEDY) {
			speed = "疯狂";
		}
		else if (ps->_paceInterval == PACE_INTERVAL_EXTREME) {
			speed = "极速";
		} else if (ps->_paceInterval == PACE_INTERVAL_QUICK) {
			speed = "快速";
		}
		else if (ps->_paceInterval == PACE_INTERVAL_MODERATE) {
			speed = "中速";
		}
		else {
			speed = "慢速";
		}
		printf("当前速度:%s", speed);

		// 按键监视
		if ((KEY_PRESSED(VK_UP) || KEY_PRESSED(0x57)) && ps->_direction != DOWN) { // 上
			ps->_direction = UP;
		}
		else if ((KEY_PRESSED(VK_DOWN) || KEY_PRESSED(0x53)) && ps->_direction != UP) { // 下
			ps->_direction = DOWN;
		}
		else if ((KEY_PRESSED(VK_LEFT) || KEY_PRESSED(0x41)) && ps->_direction != RIGHT) { // 左
			ps->_direction = LEFT;
		}
		else if ((KEY_PRESSED(VK_RIGHT) || KEY_PRESSED(0x44)) && ps->_direction != LEFT) { // 右
			ps->_direction = RIGHT;
		}
		else if (KEY_PRESSED(VK_SPACE)) { // 暂停
			Pause();
		}
		else if (KEY_PRESSED(VK_ESCAPE)) { // 退出
			ps->_gameState = EXIT;
			break;
		}
		else if (KEY_PRESSED(0x4A) || KEY_PRESSED(0x31)) { // 加速
			if (ps->_paceInterval >= PACE_INTERVAL_EXTREME) {
				ps->_paceInterval -= 50;
			}
		}
		else if (KEY_PRESSED(0x4B) || KEY_PRESSED(0x32)) { // 减速
			if (ps->_paceInterval <= PACE_INTERVAL_MODERATE) {
				ps->_paceInterval += 50;
			}
		}

		// 不同速度下食物的分数
		if (ps->_paceInterval == PACE_INTERVAL_GREEDY) {
			ps->_foodScore = GREEDY_SCORE;
		}
		else if (ps->_paceInterval == PACE_INTERVAL_EXTREME) {
			ps->_foodScore = EXTREME_SCORE;
		}
		else if (ps->_paceInterval == PACE_INTERVAL_QUICK) {
			ps->_foodScore = QUICK_SCORE;
		}
		else if (ps->_paceInterval == PACE_INTERVAL_MODERATE) {
			ps->_foodScore = MODERATE_SCORE;
		}
		else {
			ps->_foodScore = SLOW_SCORE;
		}
		// 休眠间隔
		Sleep(ps->_paceInterval);
		// 蛇移动
		Move(ps);
		// 移动约束:撞墙、或吃到自己
		CheckMove(ps);
	} while (ps->_gameState == OK);
}

// 暂停游戏
void Pause() {
	while (true) {
		if (KEY_PRESSED(VK_SPACE)) {
			break;
		}
		Sleep(100);
	}
}

// 蛇移动
void Move(pSnake ps) {
	pSnakeNode pNext = (pSnakeNode)calloc(1, sizeof(SnakeNode));
	if (pNext == NULL) {
		perror("Move(pSnake) malloc error");
		return;
	}
	// 四个方向
	switch (ps->_direction)
	{
	case UP:
		pNext->x = ps->_pSnakeHead->x;
		pNext->y = ps->_pSnakeHead->y - 1;
		break;
	case DOWN:
		pNext->x = ps->_pSnakeHead->x;
		pNext->y = ps->_pSnakeHead->y + 1;
		break;
	case LEFT:
		pNext->x = ps->_pSnakeHead->x - 2;
		pNext->y = ps->_pSnakeHead->y;
		break;
	case RIGHT:
		pNext->x = ps->_pSnakeHead->x + 2;
		pNext->y = ps->_pSnakeHead->y;
		break;
	default:
		break;
	}

	// 是否吃到食物
	if (IsFood(pNext, ps->_pFood)) {
		EatFood(ps, pNext);
		// 产生新的食物
		CreateFood(ps);
	}
	else {
		NoFood(ps, pNext);
	}
	ShowSnake(ps->_pSnakeHead);
}

// 是否有食物
bool IsFood(pSnakeNode pNext, pSnakeNode pFood) {
	if (pNext->x == pFood->x && pNext->y == pFood->y) {
		return true;
	}
	return false;
}

// 吃食物
void EatFood(pSnake ps, pSnakeNode pNext) {
	// 头插
	pNext->next = ps->_pSnakeHead;
	ps->_pSnakeHead = pNext;
	// 删除食物节点
	free(ps->_pFood);
	ps->_pFood = NULL;
	// 增加分数
	ps->_totalScore += ps->_foodScore;
	// 增加长度
	ps->_length += 1;
}

// 不吃食物
void NoFood(pSnake ps, pSnakeNode pNext) {
	// 头插
	pNext->next = ps->_pSnakeHead;
	ps->_pSnakeHead = pNext;
	// 尾删
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur->next->next) {
		cur = cur->next;
	}
	SetCursorPosition(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
}

// 移动约束
void CheckMove(pSnake ps) {
	short headX = ps->_pSnakeHead->x;
	short headY = ps->_pSnakeHead->y;
	// 是否撞墙
	if (headX < 2 || headX > WALL_WIDTH - 4 || headY < 1 || headY > WALL_HEIGHT - 1) {
		ps->_gameState = KILL_BY_WALL;
	}
	// 是否吃到自己
	pSnakeNode snakeBody = ps->_pSnakeHead->next;
	while (snakeBody) {
		short bodyX = snakeBody->x;
		short bodyY = snakeBody->y;
		if ((headX == bodyX && headY == bodyY)) {
			ps->_gameState = KILL_BY_SELF;
		}
		snakeBody = snakeBody->next;
	}
}


// 游戏结束(释放资源等)
void GameOver(pSnake ps) {
	switch (ps->_gameState)
	{
	case EXIT:
		SetCursorPosition(32, 15);
		printf("退出游戏...");
		Sleep(1000);
		system("cls");
		Sleep(1000);
		printf("已退出游戏.");
		break;
	case KILL_BY_WALL:
		SetCursorPosition(32, 12);
		printf("你被墙杀死了...");
		break;
	case KILL_BY_SELF:
		SetCursorPosition(32, 12);
		printf("你被自己杀死了...");
		break;
	default:
		break;
	}
	// 释放内存
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur) {
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
}

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

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

相关文章

基于SpringBoot的洗衣店管理系统

基于SpringBoot的洗衣店管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 可视化展示 用户界面 管理员界面 摘要 洗衣店管理系统基于Spring Boot框…

LeetCode 38 外观数列

题目描述 外观数列 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列&#xff1a; countAndSay(1) "1…

09Bean的生命周期/作用域不同管理方式不同/自己new的对象纳入Spring容器管理

Spring其实就是一个管理Bean对象的工厂。它负责对象的创建&#xff0c;对象的销毁等。 所谓的生命周期就是&#xff1a;对象从创建开始到最终销毁的整个过程。 Bean的生命周期之5步 ● 第一步&#xff1a;实例化Bean(无参构造方法执行) ● 第二步&#xff1a;Bean属性赋值(注…

Swin Transformer 学习笔记(附代码)

论文地址&#xff1a;https://arxiv.org/pdf/2103.14030.pdf 代码地址&#xff1a; GitHub - microsoft/Swin-Transformer: This is an official implementation for "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows". 1.是什么&#x…

Unity——VContainer的依赖注入

一、IOC控制反转和DI依赖倒置 1、IOC框架核心原理是依赖倒置原则 C#设计模式的六大原则 使用这种思想方式&#xff0c;可以让我们无需关心对象的生成方式&#xff0c;只需要告诉容器我需要的对象即可&#xff0c;而告诉容器我需要对象的方式就叫做DI&#xff08;依赖注入&…

SqlAlchemy使用教程(一) 原理与环境搭建

一、SqlAlchemy 原理及环境搭建 SqlAlchemy是1个支持连接各种不同数据库的Python库&#xff0c;提供DBAPI与ORM&#xff08;object relation mapper&#xff09;两种方式使用数据库。 DBAPI方式&#xff0c;即使用SQL方式访问数据库 ORM, 对象关系模型&#xff0c;是用 Python…

js 实现拖动按钮添加布局

效果&#xff1a; h布局生成左右布局&#xff0c; v布局生成上下布局 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, ini…

Python笔记08-面向对象

文章目录 类和对象构造方法内置方法封装继承类型注解多态 类只是一种程序内的“设计图纸”&#xff0c;需要基于图纸生产实体&#xff08;对象&#xff09;&#xff0c;才能正常工作 这种套路&#xff0c;称之为&#xff1a;面向对象编程 类和对象 定义类的语法如下&#xff…

vue v-for循环拖拽排序,实现数组选中的数据拖拽后对应的子数据也进行重新排序

如下图所有&#xff0c;有个需求更新&#xff0c; 实现拖拽。 1&#xff0c;当新增了测点类型的时候每个对应的回路子数据都会新增对应的测点类型。 2&#xff0c;当拖动测点类型结束的时候对应的回路里面的内容也会跟着测点类型的排序自动排序 其实很简单&#xff0c;只要会了…

el-tree多个树进行节点同步联动(完整版)

2024.1.11今天我学习了如何对多个el-tree树进行相同节点的联动效果&#xff0c;如图&#xff1a; 这边有两棵树&#xff0c;我们发现第一个树和第二个树之间会有重复的指标&#xff0c;当我们选中第一个树的指标&#xff0c;我们希望第二个树如果也有重复的指标也能进行勾选上&…

鸿蒙实战基础(ArkTS)-窗口管理

基于窗口能力&#xff0c;实现验证码登录的场景&#xff0c;主要完成以下功能&#xff1a; 登录页面主窗口实现沉浸式。输入用户名和密码后&#xff0c;拉起验证码校验子窗口。验证码校验成功后&#xff0c;主窗口跳转到应用首页。 登录界面实现沉浸式 完成登录界面布局的编…

全面解析微服务

导读 微服务是企业应用及数据变革升级的利器&#xff0c;也是数字化转型及运营不可或缺的助产工具&#xff0c;企业云原生更离不开微服务&#xff0c;同时云原生的既要最大化发挥微服务的价值&#xff0c;也要最大化弥补微服务的缺陷。本文梳理了微服务基础设施组件、服务网格、…

企业数据中台整体介绍及建设方案:文件全文51页,附下载

关键词&#xff1a;数据中台解决方案&#xff0c;数据治理&#xff0c;数据中台技术架构&#xff0c;数据中台建设内容&#xff0c;数据中台核心价值 一、什么是数据中台&#xff1f; 数据中台是指通过数据技术&#xff0c;对海量数据进行采集、计算、存储、加工&#xff0c;…

富唯智能新研发的复合机器人,轻松破解汽车底盘零配件生产中的难题

随着汽车工业的快速发展&#xff0c;对于底盘零配件的需求也日益增长。为了满足市场需求&#xff0c;智能物流解决方案在汽车底盘零配件生产中扮演着越来越重要的角色。如何实现高效、准确的生产和物流管理&#xff0c;以满足市场快速变化的需求&#xff0c;成为了汽车生产商亟…

在加载第三方库过程中,无法加载到库的问题(使用readelf, patchelf命令)

无法加载到库问题 问题及分析过程readelf 命令patchelf命令 问题及分析过程 在开发一个程序过程中&#xff0c;需要加载第三方库iTapTradeAPI, 在CMakeList.txt中已经设置了CMAKE_INSTALL_RPATH&#xff0c;但是发布到生产之后由于目录问题无法加载到libiTapTradeAPI库了 下面…

Vue过滤器详解

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介基本用法多个过滤器的串联过滤器在指令中的应用全局过滤器 ⭐ 本期推荐 ⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vue 技能树参考资料专栏&#xff01;创建这个专栏的初衷是为了帮助大家更好地应对 Vue.js 技能树的学习。每…

活动回顾∣“全邻友好,艺术大咖交流会”——员村街开展社区微型养老博览会长者文艺汇演活动

为进一步营造邻里守望&#xff0c;共建美好社区的氛围&#xff0c;促进社区长者参与社区服务&#xff0c;展示社区长者健康、积极向上的精神风貌&#xff0c;2024年1月10日&#xff0c;员村街开展“全邻友好&#xff0c;艺术大咖交流会”——微型养老博览会活动&#xff0c;让长…

电影《艾里甫与赛乃姆》简介

电影《艾里甫与赛乃姆》由天山电影制片厂于1981年摄制&#xff0c;该片由傅杰执导&#xff0c;由买买提祖农司马依、布维古丽、阿布里米提沙迪克、努力曼阿不力孜、买买提依不拉音江、阿不都热合曼艾力等主演。 该片改编自维吾尔族民间爱情叙事长诗《艾里甫与赛乃姆》&#xf…

ModuleNotFoundError: No module named ‘simple_knn‘

【报错】使用 AutoDL 复现 GaussianEditor 时引用 3D Gaussian Splatting 调用simple_knn 时遇到 ModuleNotFoundError: No module named ‘simple_knn‘ 报错&#xff1a; 【原因】 一开始以为是版本问题&#xff0c;于是将所有可能的版本都尝试了 (from versions: 0.1, 0.2…

一套成熟的Spring Cloud智慧工地平台源码,自主版权,支持二次开发!

智慧工地源码&#xff0c;java语言开发的智慧工地源码 智慧工地利用移动互联、物联网、云计算、大数据等新一代信息技术&#xff0c;彻底改变传统施工现场各参建方的交互方式、工作方式和管理模式&#xff0c;为建设集团、施工企业、监理单位、设计单位、政府监管部门等提供一揽…