【C/C++ 11】贪吃蛇游戏

一、题目

贪吃蛇游戏机制是通过控制蛇上下左右移动并吃到食物得分。

蛇头碰到墙壁或者碰到蛇身就游戏结束。

食物随机生成,蛇吃到食物之后蛇身变长,蛇速加快。

二、算法

  1. 初始化游戏地图并打印,地图的边缘是墙,地图的每个坐标都有属性(EMPTY、WALL、FOOD、HEAD、BODY),通过<Window.h>库里面的函数控制光标跳转和颜色。

  2. 初始化蛇,蛇是一个单独的类,类里面的属性有蛇头、蛇身、长度、速度,蛇头一个SnakeNode节点,蛇身是一个SnakeNode指针,每个SnakeNode都是一个x、y坐标,用于表示蛇在地图上的位置。

  3. 随机生成食物,蛇移动的下一步如果是食物则得分,若下一步是墙壁或蛇身则游戏失败。

  4. 通过键盘输入控制方向,若键盘没有输入则保持方向不变。

三、代码

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#pragma warning (disable:4996)
#include <iostream>
#include <Windows.h>
#include <conio.h>
#include <ctime>
#include <vector>
using namespace std;

#define ROW 22
#define COL 42

#define EMPTY 0
#define WALL  1
#define FOOD  2
#define HEAD  3
#define BODY  4

#define COL_WALL  6
#define COL_FOOD  12
#define COL_SNAKE 10

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define SPACE 32
#define ESC 27
#define ENTER 13

int g_map[ROW][COL] = { 0 };
int g_grade = 0;

void CursorJump(int x, int y)
{
	COORD pos;    //定义光标位置的结构体变量
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);        //设置光标位置
}

void Color(int x)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);    //设置颜色
	// 6------土黄色    7------白色    10------绿色    12------红色
}

void SysInit()
{
	srand((unsigned int)time(NULL));
	system("title 贪吃蛇");
	system("mode con cols=84 lines=23");    //设置终端窗口大小
	CONSOLE_CURSOR_INFO curInfo;    //光标信息结构体变量
	curInfo.dwSize = 1;
	curInfo.bVisible = FALSE;        //光标光标隐藏不可见
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo);    //设置光标信息
}

void MapInit()
{
	for (int i = 0; i < ROW; ++i)
	{
		for (int j = 0; j < COL; ++j)
		{
			CursorJump(2 * j, i);
			if (i == 0 || i == ROW - 1 || j == 0 || j == COL - 1)
			{
				Color(COL_WALL);
				g_map[i][j] = WALL;
				cout << "■";
			}
			else
			{
				g_map[i][j] = EMPTY;
				cout << "  ";
			}
		}
	}
	Color(7);
	CursorJump(0, ROW);
	cout << "当前得分是:" << g_grade;
}

void RandFood()
{
	int row, col;
	do
	{
		row = rand() % ROW;
		col = rand() % COL;
	} while (g_map[row][col] != EMPTY);
	g_map[row][col] = FOOD;
	Color(COL_FOOD);
	CursorJump(2 * col, row);
	cout << "●";
}

class Snack
{
public:
	Snack()
	{
		len = 2;
		rate = 3000;

		head.x = COL / 2;
		head.y = ROW / 2;
		g_map[head.y][head.x] = HEAD;

		body.resize(ROW * COL, Pos(0, 0));
		for (int i = 0; i < len; ++i)
		{
			body[i].x = head.x - i - 1;
			body[i].y = head.y;
			g_map[body[i].y][body[i].x] == BODY;
		}
	}

	void PrintSnake(int flag)
	{
		if (flag)
		{
			// 打印蛇
			Color(COL_SNAKE);
			CursorJump(2 * head.x, head.y);
			cout << "◆";
			for (int i = 0; i < len; ++i)
			{
				CursorJump(2 * body[i].x, body[i].y);
				cout << "◇";
			}
		}
		else
		{
			// 覆盖蛇
			if (body[len - 1].x != 0)
			{
				CursorJump(2 * body[len - 1].x, body[len - 1].y);
				cout << "  ";
			}
		}
	}

	void Judge(int x, int y)
	{
		if (g_map[head.y + y][head.x + x] == FOOD)
		{
			// 得分
			g_grade += 10;

			len++;
			if (rate > 1000)
				rate -= 50;
			Color(7);
			CursorJump(0, ROW);
			cout << "当前得分是:" << g_grade;
			RandFood();
		}
		else if (g_map[head.y + y][head.x + x] == WALL
			|| g_map[head.y + y][head.x + x] == BODY)
		{
			// 失败
			Sleep(2000);
			Color(7);
			system("cls");
			cout << "           GAME OVER!          " << endl;
			cout << "            游戏失败!          " << endl;
			exit(0);
		}
	}

	void Move(int x, int y)
	{
		Judge(x, y);
		PrintSnake(0);
		int tail = len - 1;
		g_map[body[tail].y][body[tail].x] = EMPTY;
		while (tail > 0)
		{
			body[tail].x = body[tail - 1].x;
			body[tail].y = body[tail - 1].y;
			--tail;
		}
		body[0].x = head.x;
		body[0].y = head.y;
		g_map[body[0].y][body[0].x] = BODY;
		head.x += x;
		head.y += y;
		g_map[head.y][head.x] = HEAD;
		PrintSnake(1);
	}

	void Run(int x, int y)
	{
		int t = 0;
		while (1)
		{
			if (t == 0)
				t = rate;

			while (--t)
			{
				if (kbhit() != 0)
					break;
			}

			if (t == 0)
				Move(x, y);
			else
				break;
		}
	}

	void Play()
	{
		int dir = RIGHT;
		int old = dir;
		while (1)
		{
			switch (dir)
			{
			case 'w':
			case 'W':
			case UP:
				Run(0, -1);
				old = dir;
				break;
			case 's':
			case 'S':
			case DOWN:
				Run(0, 1);
				old = dir;
				break;
			case 'a':
			case 'A':
			case LEFT:
				Run(-1, 0);
				old = dir;
				break;
			case 'd':
			case 'D':
			case RIGHT:
				Run(1, 0);
				old = dir;
				break;
			case SPACE:
				system("pause>nul");
				break;
			case ESC:
				system("cls");
				cout << "   ESC 退出游戏" << endl;
				exit(0);
			}

			dir = getch();
			switch (dir)
			{
			case 'w':
			case 'W':
			case UP:
			case 's':
			case 'S':
			case DOWN:
				if (old == UP || old == DOWN)
					dir = old;
				break;
			case 'a':
			case 'A':
			case LEFT:
			case 'd':
			case 'D':
			case RIGHT:
				if (old == LEFT || old == RIGHT)
					dir = old;
				break;
			case SPACE:
			case ESC:
				break;
			default:
				dir = old;
			}
		}
	}

private:
	struct Pos
	{
		int x, y;

		Pos() {}
		Pos(int x1, int y1)
			: x(x1), y(y1)
		{}
	};

	Pos head;
	vector<Pos> body;
	int len;
	int rate;
};

int main()
{
	SysInit();
	MapInit();
	RandFood();

	Snack s;
	s.Play();

	return 0;
}

四、测试

相关推荐
Code_流苏42 分钟前
C++课设:智能优惠快餐点餐系统
开发语言·c++·课设·期末大作业·快餐点餐系统·智能优惠算法
越城1 小时前
深入解析C++引用:从别名机制到函数特性实践
c++
qwertyuiop_i2 小时前
pe文件结构(TLS)
c++·tls·pe文件结构
岁忧2 小时前
(nice!!!)(LeetCode每日一题)2434. 使用机器人打印字典序最小的字符串(贪心+栈)
java·c++·算法·leetcode·职场和发展·go
无敌的小笼包3 小时前
第四讲:类和对象(下)
数据结构·c++
鑫鑫向栄3 小时前
[蓝桥杯]解谜游戏
数据结构·c++·算法·职场和发展·蓝桥杯
闻缺陷则喜何志丹4 小时前
【分治法 容斥原理 矩阵快速幂】P6692 出生点|普及+
c++·线性代数·数学·洛谷·容斥原理·分治法·矩阵快速幂
鑫鑫向栄4 小时前
[蓝桥杯]整理玩具
数据结构·c++·算法·蓝桥杯·动态规划
南郁14 小时前
007-nlohmann/json 项目应用-C++开源库108杰
c++·开源·json·nlohmann·现代c++·d2school·108杰
菠萝0116 小时前
共识算法Raft系列(1)——什么是Raft?
c++·后端·算法·区块链·共识算法