[C]项目--扫雷游戏

扫雷

  • 楔子:
    扫雷游戏是我们小时候无聊时消磨时间的小玩意,虽然更新到Win10系统后经典的扫雷游戏不再了,不过它现在仍以一种抓虫子的游戏形式存在于Windows这个系统平台,不禁感慨游戏还是那个游戏,不过人已经不是那些人了啊.
  • 其实扫雷游戏的实现也主要运用了数组函数封装与调用的知识,具体请看程序.

以下为程序主体:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

//定义方格大小
#define MAX_ROW 10
#define MAX_COL 10
//定义方格雷阵中的地雷数
#define DEFAULT_MINE_COUNT 10

//制作图形化游戏界面函数(菜单)
int Menu() {
	printf("=============================\n");
	printf("||        <扫雷游戏>       ||\n");
	printf("||  1.开始游戏 0.退出游戏  ||\n");
	printf("=============================\n");
	int choice = 0;
	while (1) {
		scanf("%d", &choice);
		if (choice != 0 && choice != 1) {
			printf("您的输入有误,请重新输入!\n");
			continue;
		}
		return choice;
	}
}

//清空方格函数 这里地雷表示为 * ,安全无地雷表示为 0
void Init(char show_map[MAX_ROW + 2][MAX_COL + 2],
	char mine_map[MAX_ROW + 2][MAX_COL + 2]) {
	for (int row = 0; row < MAX_ROW + 2; ++row) {
		for (int col = 0; col < MAX_COL + 2; ++col) {
			show_map[row][col] = '*';
		}
	}
	for (int row = 0; row < MAX_ROW + 2; ++row) {
		for (int col = 0; col < MAX_COL + 2; ++col) {
			mine_map[row][col] = '0';
		}
	}
	//能够随机的构造出 N 个数,放在雷阵中
	int mine_count = DEFAULT_MINE_COUNT;
	while (mine_count > 0) {
		//随机布置地雷
		int row = rand() % MAX_ROW + 1;
		int col = rand() % MAX_COL + 1;
		if (mine_map[row][col] == '1') {
			continue;
		}
		mine_map[row][col] = '1';
		--mine_count;
	}
}

void DisplayMap(char map[MAX_ROW + 2][MAX_COL + 2]) {
	printf("   ");
	//打印坐标横行
	for (int row = 1; row <= MAX_ROW; ++row) {
		printf("%d ", row);
	}
	printf("\n");
	//打印上边框
	for (int row = 1; row <= MAX_ROW; ++row) {
		printf("---");
	}
	printf("\n");
	for (int row = 1; row <= MAX_ROW; ++row) {
		printf("%02d|", row);
		for (int col = 1; col <= MAX_COL; ++col) {
			printf("%c ", map[row][col]);
		}
		printf("\n");

	}
	printf("\n");
	printf("\n");
	printf("\n");

}

void UpdateShowMap(char show_map[MAX_ROW + 2][MAX_COL + 2],
	char mine_map[MAX_ROW + 2][MAX_COL + 2], int row, int col) {
	//这个函数要根据mine_map来计算row,col位置上周围是有几个地雷
	//把结果写到对应的show_map位置上
	//===========此处这8个位置对应的下标不会越界======
	//===========因为引入了一圈边框===================
	//row和col取值是[1,MAX_ROW]
	//数组下标取值为[0,MAX_ROW + 1]

	int mine_count = 0;
	for (int i = row - 1; i <= row + 1; ++i) {
		for (int j = col - 1; j <= col + 1; ++j) {
			mine_count =
				mine_map[i - 1][j - 1] - '0' +
				mine_map[i - 1][j] - '0' +
				mine_map[i - 1][j + 1] - '0' +
				mine_map[i][j - 1] - '0' +
				mine_map[i][j + 1] - '0' +
				mine_map[i + 1][j - 1] - '0' +
				mine_map[i + 1][j] - '0' +
				mine_map[i + 1][j + 1] - '0';

			if (mine_map[i][j] == '1') {
				show_map[i][j] = '*';
			}
			else {
				show_map[i][j] = '0' + mine_count;
			}
		}
	}
}


//游戏主逻辑,入口
void Game() {
	//具体的一局扫雷游戏
	//两个二维数组来表示地图
	//第一个数组表示给玩家展示的地图
	char show_map[MAX_ROW + 2][MAX_COL + 2];
	//此处加上一圈边框(防止数组下标越界)
	//对于show_map:里面元素有2种情况:
	//1.这个位置没有被掀开,用 * 表示
	//2.这个位置已经被掀开了,用一个具体的数字来表示(例如字符'2'来表示)
	char mine_map[MAX_ROW + 2][MAX_COL + 2];
	//第二个数组表示雷阵
	//对于mine_map,里面的元素以下情况:
	//1.这个位置是地雷,使用字符'1'表示
	//2.这个位置不是地雷,使用字符'0'表示
	int blank_count = 0;//空格数 不包含地雷,被掀开的格子数
	//1.对这两个数组进行初始化
	//2.打印初始地图
	Init(show_map, mine_map);
	DisplayMap(show_map);
	while (1) {
		//3.让玩家输入坐标,判定是否合法
		int row, col;
		printf("请输入坐标:\n");
		scanf("%d %d", &row, &col);
		if (row <= 0 || row > MAX_ROW
			|| col <= 0 || col > MAX_COL) {
			printf("您的输入不合法,重新输入!\n");
			continue;
		}
		//4.判断玩家是否踩雷,如果是,游戏结束
		if (mine_map[row][col] == '1') {
			printf("游戏结束!\n");
			printf("扫雷失败!\n");
			DisplayMap(mine_map);
			break;
		}
		//5,如果没踩雷,判定是否掀开了全部格子(玩家胜利)
		//6.就统计该位置周围有几个雷,并把这个数字更新到地图上
		UpdateShowMap(show_map, mine_map, row, col);
		DisplayMap(show_map);
		int blank_cnt = 0;
		for (row = 1; row <= MAX_ROW; ++row)
		{
			for (col = 1; col <= MAX_COL; ++col)
			{
				if (show_map[row][col] != '*')
				{
					++blank_cnt;
					if (blank_cnt == MAX_ROW * MAX_COL - DEFAULT_MINE_COUNT)
					{
						DisplayMap(mine_map);
						printf("扫雷成功,游戏结束\n");
						break;
					}
				}
			}
		}
		printf("\n");
	}
}

void Start() {
	//这是游戏入口函数,游戏菜单
	while (1) {
		int choice = Menu();
		if (choice == 0) {
			break;
		}
		Game();
	}
}


//主函数
int main() {
	Start();//由开始函数引导游戏开始
	system("pause");
	return 0;
}

需要注意的是:

  1. 大佬可以通过更改程序首部的宏定义来改变方格大小,扩充界面大小以提高难度.
  2. 整个游戏逻辑必须明晰:
    打印方格
    随机布置雷阵
    玩家输入掀开方格坐标
    判断是否踩雷(若是,游戏结束.否则显示出周围八个数的地雷数)
    若未踩雷判断是否除了地雷外掀开了所有方格(若是,扫雷成功.否则继续游戏)
    更新表示地图
    循环步骤,返回至用户输入环节,继续游戏,直至游戏截止.
  • 总结:
    程序内部需要有许多的注意点,比如引入一圈边框,他的作用就是为了确保更新地图时数组下标不会越界,是一种保护机制,但是这种机制的构建可能会在程序其他地方的完善工作带来不小的工程量,但是有这么一道设定,总比程序直接报错或者运行一段时间由玩家反馈这个bug来的划算/值得一些,所以尽可能在开发环节做到极致,以免折腾后面修改代码缺陷的自己.

猜你喜欢

转载自blog.csdn.net/qq_42351880/article/details/84933871