C语言实现 扫雷小游戏

文章目录

  • 一、游戏规则
  • 二、设计思路
  • 三、程序设计+分段解析
    • 游戏基本流程+功能函数
      • 1.创建地图并初始化 init函数
        • 两个二维数组 ShowMap&MineMap
      • 2.打印地图 Print函数
      • 3.玩家确认要排查的位置
      • 4.判断是否踩雷
      • 5.更新排查后的地图 Update
      • 6.判定输赢
  • 四、完整代码
  • 五、补充

一、游戏规则

把所有非地雷的地图格子排查掉,即胜利。踩到地雷格子即失败!

二、设计思路

C语言实现 扫雷小游戏_第1张图片

三、程序设计+分段解析

游戏基本流程+功能函数

C语言实现 扫雷小游戏_第2张图片

1.创建地图并初始化 init函数

init 初始化函数
数组创建好后,我们要对两个数组进行初始化,用双重循环有些许麻烦,这里使用memset实现,memset功能是把一段内存上的每个字节都设置成一个具体的值, memset(第一段内存起始的地址,具体想要转换成什么样的值,内存的长度)

//1.初始化地图
void init(char ShowMap[MAX_ROW][MAX_COL], char MineMap[MAX_ROW][MAX_COL]) {
     
	//for (int row = 0; row < MAX_ROW; row++) {
     
	//	for (int col = 0; col < MAX_COL; col++) {
     
	//		ShowMap[row][col] = '*';
	//		MineMap[row][col] = '0';
	//	}
	//}
	memset(ShowMap, '*', MAX_ROW * MAX_COL);
	//此处计算内存的长度不能用sizeof,会隐式转化成指针!因此用sizeof求的是一个指针的长度,并非数组的长度
	memset(MineMap, '0', MAX_ROW * MAX_COL);
	srand((unsigned int)time(0));//随机种子
	int MineCount = 0;
	while (MineCount<MINE_COUNT) {
     
		int row = rand() % MAX_ROW;//随即生成10个位置 存放地雷
		int col = rand() % MAX_COL;
		if (MineMap[row][col] == '1') {
     
			continue;
		}
		MineMap[row][col] = '1';
		MineCount++;
	}
}

C语言实现 扫雷小游戏_第3张图片

两个二维数组 ShowMap&MineMap

第一个数组存放地图的排查状态,面向玩家创建,展示给玩家,称之为ShowMap,如下所示:

char ShowMap[MAX_ROW][MAX_COL] = {
      0 };
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *

第二个数组存放地雷的分布信息,面向游戏设计者创建,称之为MineMap,如下所示:

char MineMAP[MAX_ROW][MAX_COL] = {
      0 };
1  0  0  0  1  0  0  0  0 
0  0  0  0  0  0  0  1  0 
0  1  0  0  0  0  0  0  0 
0  0  0  0  1  0  0  0  0 
0  0  0  0  0  0  0  0  1 
0  0  0  0  0  0  1  0  0 
0  1  0  0  0  0  0  0  0 
0  0  0  0  0  1  0  0  0 
0  0  0  0  0  0  0  0  1

2.打印地图 Print函数

此处形参命名为theMap是希望这个函数同时具备打印两张地图的功能,只叫map也不太合适,因为C++标准库中有一个 std::map

使用双for循环实现地图的打印

void Print(char theMap[MAX_ROW][MAX_COL]) {
     
	for (int row = 0; row < MAX_ROW; row++) {
     
		for (int col = 0; col < MAX_COL; col++) {
     
			printf("%c ", theMap[row][col]);
		}
		printf("\n");
	}
}

3.玩家确认要排查的位置

玩家确定排查位置:在主函数中写一个while循环即可实现!
玩家输入要排查的坐标时,一定不要忘了合法性判定!
1.越界判定
2.重复排查判定

while (1) {
     
		Print(ShowMap);
		int row = 0;
		int col = 0;
		printf("请输入要排查的坐标(row,col):");
		scanf("%d %d", &row, &col);
		//合法性判定
		//1.越界判定
		if (row<0 || row>=MAX_ROW || col<0 || col>=MAX_COL) {
     
			printf("您输入的坐标已越界,请重新输入:\n");
			continue;
		}
		//2.重复排查判定
		if (ShowMap[row][col] != '*') {
     
			printf("您输入的坐标位置已被排查,请重新输入:\n");
				continue;
		}

4.判断是否踩雷

玩家输入坐标排查地图格子,我们初始化的时候,

while (1) {
     
		int row = 0;
		int col = 0;
		printf("请输入要排查的坐标(row,col):");
		scanf("%d %d", &row, &col);
		if (MineMap[row][col] == '1') {
     
			printf("您踩雷了!游戏结束!!\n");
			break;
		}
	}

5.更新排查后的地图 Update

Update函数 即实现未踩雷,进行已排查地图格子周围格子地雷的个数统计
计算中间位置周围格子的地雷数count,边角位置和中间位置不参与计算,使用双for循环实现count的计数
周围格子的表示方式如下图:

C语言实现 扫雷小游戏_第4张图片

void Update(char ShowMap[MAX_ROW][MAX_COL], char MineMap[MAX_ROW][MAX_COL], int row, int col) {
     
	int count = 0;
	for (int r = row - 1; r <= row + 1; r++) {
     
		for (int c = col - 1; c <= col + 1; c++) {
     
			if (r < 0 || r >= MAX_ROW || c < 0 || c >= MAX_COL) {
      //超出地图范围,直接跳出进入下次循环
				continue;
			}
			if (r == row && c == col) {
      //中间位置不参与计算 直接进入下次循环
				continue;
			}
			if (MineMap[r][c] == '1') {
     
				count++;
			}
		}
	}
	ShowMap[row][col] = '0' + count;//类似这种转换 仅限C中使用
	//不能直接给赋值,因为 数值是int类型,ShowMap是char型,
	//ShowMap[row][col] =2  此时row ,col的位置元素就设置成了ASCII值为2的字符 而不是字符'2'
}

6.判定输赢

进行扫雷,如果玩家踩雷就被炸死,未踩雷,系统报告周围雷的个数。直到所有非地雷地图格子都被排查 即获胜!

int main(){
     
	char ShowMap[MAX_ROW][MAX_COL] = {
      0 };
	char MineMap[MAX_ROW][MAX_COL] = {
      0 };
	init(ShowMap, MineMap);
	int openedCount = 0;
	while (1) {
     
		Print(ShowMap);
		int row = 0;
		int col = 0;
		printf("请输入要排查的坐标(row,col):");
		scanf("%d %d", &row, &col);
		//合法性判定
		//1.越界判定
		if (row<0 || row>=MAX_ROW || col<0 || col>=MAX_COL) {
     
			printf("您输入的坐标已越界,请重新输入:\n");
			continue;
		}
		//2.重复排查判定
		if (ShowMap[row][col] != '*') {
     
			printf("您输入的坐标位置已被排查,请重新输入:\n");
				continue;
		}
		if (MineMap[row][col] == '1') {
     
			printf("您踩雷了!游戏结束!!\n");
			break;
		}
		system("cls");
		//更新ShowMap 
		Update(ShowMap,MineMap,row,col);
		openedCount++;
		if (openedCount == MAX_COL * MAX_COL - MINE_COUNT) {
     
			printf("恭喜您!赢得胜利!\n");
		}
	}
	system("pause");
	return 0;
}

四、完整代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define MAX_ROW 9
#define MAX_COL 9
#define MINE_COUNT 10

//1.初始化地图
void init(char ShowMap[MAX_ROW][MAX_COL], char MineMap[MAX_ROW][MAX_COL]) {
     
	//for (int row = 0; row < MAX_ROW; row++) {
     
	//	for (int col = 0; col < MAX_COL; col++) {
     
	//		ShowMap[row][col] = '*';
	//		MineMap[row][col] = '0';
	//	}
	//}
	memset(ShowMap, '*', MAX_ROW * MAX_COL);//(第一段内存起始的地址,具体想要转换成什么样的值,内存的长度)
	//此处计算内存的长度不能用sizeof,会隐式转化成指针!因此用sizeof求的是一个指针的长度,并非数组的长度
	//memset功能是把 一段内存上的每个字节都设置成一个具体的值
	memset(MineMap, '0', MAX_ROW * MAX_COL);//memset 虽然在string头文件里,但与字符串没关系,只是方式放错了,现在改不了,因为“向前兼容!”
	srand((unsigned int)time(0));
	int MineCount = 0;
	while (MineCount<MINE_COUNT) {
     
		int row = rand() % MAX_ROW;//随即生成10个位置 存放地雷
		int col = rand() % MAX_COL;//这样生成随机数可能会重复,
		if (MineMap[row][col] == '1') {
     
			continue;
		}
		MineMap[row][col] = '1';
		MineCount++;
	}
}
void Print(char theMap[MAX_ROW][MAX_COL]) {
     
	for (int row = 0; row < MAX_ROW; row++) {
     
		for (int col = 0; col < MAX_COL; col++) {
     
			printf("%c ", theMap[row][col]);
		}
		printf("\n");
	}
}
void Update(char ShowMap[MAX_ROW][MAX_COL], char MineMap[MAX_ROW][MAX_COL], int row, int col) {
     
	int count = 0;
	//if (MineMap[row - 1][col - 1] == '1') {
     
		//count++;
	//}
	for (int r = row - 1; r <= row + 1; r++) {
     
		for (int c = col - 1; c <= col + 1; c++) {
     
			if (r < 0 || r >= MAX_ROW || c < 0 || c >= MAX_COL) {
      //超出地图范围,直接跳出进入下次循环
				continue;
			}
			if (r == row && c == col) {
      //中间位置不参与计算 直接进入下次循环
				continue;
			}
			if (MineMap[r][c] == '1') {
     
				count++;
			}
		}
	}
	ShowMap[row][col] = '0' + count;//类似这种转换 仅限C中使用
	//不能直接给赋值,因为 数值是int类型,ShowMap是char型,
	//ShowMap[row][col] =2  此时row ,col的位置元素就设置成了ASCII值为2的字符 而不是字符'2'
}
int main(){
     
	//Menu();
	char ShowMap[MAX_ROW][MAX_COL] = {
      0 };
	char MineMap[MAX_ROW][MAX_COL] = {
      0 };
	init(ShowMap, MineMap);
	int openedCount = 0;
	while (1) {
     
		Print(ShowMap);
		int row = 0;
		int col = 0;
		printf("请输入要排查的坐标(row,col):");
		scanf("%d %d", &row, &col);
		//合法性判定
		//1.越界判定
		if (row<0 || row>=MAX_ROW || col<0 || col>=MAX_COL) {
     
			printf("您输入的坐标已越界,请重新输入:\n");
			continue;
		}
		//2.重复排查判定
		if (ShowMap[row][col] != '*') {
     
			printf("您输入的坐标位置已被排查,请重新输入:\n");
				continue;
		}
		if (MineMap[row][col] == '1') {
     
			printf("您踩雷了!游戏结束!!\n");
			break;
		}
		system("cls");
		//更新ShowMap 
		Update(ShowMap,MineMap,row,col);
		openedCount++;
		if (openedCount == MAX_COL * MAX_COL - MINE_COUNT) {
     
			printf("恭喜您!赢得胜利!\n");
		}
	}
	system("pause");
	return 0;
}

五、补充

#define MAX_ROW 9 //地图行宽
#define MAX_COL 9 //地图列宽
#define MINE_COUNT 10

在创建地图时,建立了两个二维数组,ShowMap[9][9] 和 MineMap[9][9],代码中没有直接写9,是用宏给一个定义,避免这些魔幻数字的出现!大大增加代码的可读性~~

srand((unsigned int)time(0));//随机种子
int row = rand() % MAX_ROW;//随即生成10个位置 存放地雷
int col = rand() % MAX_COL;

rand() 用于生成随机数,但不是生成真正的随机数
srand()用于设置供rand使用的随机数种子
第一次调用rand之前没有调用srand,那么系统会为你自动调用srand,自动调用传参值为1,这样的话,采用相同的种子则每次生成的随机数便会重复,故我们常常使用“时间戳”来初始化~包含在头文件
这里不做详细的解释,后序会出 rand&srand 篇!

你可能感兴趣的:(C,c语言)