扫雷游戏(c语言超详细解析)

139 阅读6分钟

扫雷游戏的实现分为:初始化游戏界面; 打印游戏界面;,布置雷,排查雷随便判断游戏胜负.

实现游戏的基础设置

为了方便我们后面修改游戏难度,我们应该把雷的个数(MINE_COUNT),游戏界面的大小在头文件表示出来 `

#define MINE_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

ROW代表行,COl代表列

为什么会有ROWS和COLS呢?

在这里插入图片描述 我们在遇到这种在边角的情况时,如果我们使用9行9列作为数组的大小时,我们发现在遍历数字1的九宫格会发生数组越界. 解决方法 我们把数组的行和列分别加2,数组就会覆盖整个界面,不会发生这种情况* 在这里插入图片描述

为什么要用两个二维数组

关于游戏的设置,我想使用两个二维数组.就图形来看,我们貌似只需要一个,但是大家有没有想过.如果我们只设置一个二维数组.这个数组要实现的功能有: 1.要有雷存放的信息 2.玩家选定坐标后,如果该位置没有雷,要判断该坐标周围的九宫格有多少雷 3.还需要一些类似游戏中的一个个盖子来制造神秘感. 综上,我们使用两个二维数组,一个用来存放雷的信息,另外一个给玩家展示,也就是我们的游戏界面.在这里插入图片描述

初始化棋盘

void menu() {
	printf("**********************************\n");
	printf("***********  1. play  ************\n");
	printf("***********  0. exit  ************\n");
	printf("**********************************\n");

}
int main() {
	int input = 0;
	do {
		srand((unsigned int)time(NULL));//设置rand的起点
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

*首先,我们要打印一个菜单,判断玩家要不要玩游戏.*这里我们使用input接收,用switch语句判断. 在这里插入图片描述 设置两个二维数组. 注意我设置的是字符数组

雷(Mine)----数组

我们要认识一点,数组传参传的是数组首元素的地址,所以我们要把数组的行和列(大小)也进行传参. 在这个数组里面我想用1来表示有雷,0表示安全,所以把字符 ' 0 ' 传过去

游戏界面(Show)----数组

在游戏刚刚开始的时候,我们什么数字也看不到,所以要先用' * '来制造神秘. 在这里插入图片描述 记得要在头文件里面声明

初始化的代码实现

//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set) {
	int i = 0, j = 0;
	for (i = 0; i < rows; i++) {
		for (j = 0; j < cols; j++) {
			board[i][j] = set;
		}
	}
}

将两个数组全部进行初始化. 发现了没有,上面把字符' 0 '或者' * '也传过去是为了多次调用Initboard这个函数.使我们的代码更加简洁

展示游戏界面

初始化之后要打印出来看看我们写的是否正确,同时将我们的游戏界面展现出来 在这里插入图片描述 我们打印的是9*9的,所以把ROW和COL传过去就可以. 在这里插入图片描述 上面圈出来的代码是为了实现行号和列号的打印 在这里插入图片描述

布置雷

我们上面设置的MINE_COUNT的值为10.所以写一个循环,让它把这10个雷给布置完就行

注意

1.rand()函数的使用要应用2个头文件,并用srand()为rand()设置起点在这里插入图片描述 在这里插入图片描述

2.这里的循环不止10次,因为两次产生的随机数可能相同 3.玩家输入的坐标是由1到ROW的,所以要进行+1操作

void Setmine(char board[ROWS][COLS], int row, int col) {
	int cnt = MINE_COUNT;
	while(cnt>0) {
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			cnt--;
		}
	}
}

排雷

排雷是这个游戏的精髓,也是这个游戏最难实现的地方,因为它还嵌套了许多函数让我们一个个来分析.

模拟一次排雷的情况

在这里插入图片描述 1.玩家先输入坐标,我们要先判断这个坐标是否在我们数组的范围内 2.然后再去判断Mine数组该坐是否有雷(是否为'1'),如果有雷,那游戏结束.打印雷的分布让玩家死得明白在这里插入图片描述 3.如果非雷,我们要对雷区进行清除,提高玩家的游戏体验 在这里插入图片描述 点击一个坐标,其他地方也会暴露出来 我们发现展现的地方遇到边角或者数字就会停留下来.而数字代表的是在那个九宫格里面有雷. 为了方便我们后面的操作,我们先写一个函数来计算一个坐标的九宫格有几个雷 在这里插入图片描述 我们对九宫格里面所有字符进行叠加.9个字符进行叠加,字符的ASCLL码叠加,再减去八个字符零,就能得到一圈雷的个数(字符形式) 比如' 0 '的ASCLL值为48,假设一圈都没有雷,九个字符0就是948, 948-848 = 48---' 0 '因为我们刚开始创建的字符数组. 在这里插入图片描述

排雷一下子灭掉一片的实现

在这里插入图片描述 灭掉一片的前提条件是: Countmine传回来的是'0',也就是该位置以及周围一圈都没有雷, 在这里插入图片描述 用空格取代' * '的原因 像上面所画的一样,我们对第一个红色矩形所围住进行遍历时,发现它符合条件,所以进入递归.进入递归后又会进入刚才红色矩形,也就是红圈圈所画位置 通俗一点来说就是会不断套娃,永无止境,程序就会崩溃 递归是要有终止条件的,不然回不了家.空格相当于标记,在下一次递归发现该坐标已经被标记过就不会再进入----这个跟我们在迷路时对该地方做标记是一样的

在这里插入图片描述 上面代码函数Optimize的返回类型是void,不是int

实现多次排雷

在这里插入图片描述

注意:游戏的结束有两种情况,所以在打印游戏胜利时要加判断条件

游戏总代码的实现

头文件 game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define MINE_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);
//展示棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
//布雷
void Setmine(char board[ROWS][COLS], int row, int col);
//排雷
void GuessMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//数周围一圈雷的个数
char Countmine(char Mine[ROWS][COLS], int x, int y);
//一下子灭掉一大片
void Optimize(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y);

主函数 test.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void game() {
	char Mine[ROWS][COLS];
	char Show[ROWS][COLS];
	//初始化棋盘
	Initboard(Mine, ROWS, COLS, '0');
	Initboard(Show, ROWS, COLS, '*');
	//展示棋盘
	Displayboard(Show, ROW, COL);
	//设置雷
	Setmine(Mine, ROW, COL);
	//Displayboard(Mine, ROW, COL);

	//排雷
	GuessMine(Mine, Show, ROW , COL);
}
void menu() {
	printf("**********************************\n");
	printf("***********  1. play  ************\n");
	printf("***********  0. exit  ************\n");
	printf("**********************************\n");

}
int main() {
	int input = 0;
	do {
		srand((unsigned int)time(NULL));
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

游戏函数的实现 game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set) {
	int i = 0, j = 0;
	for (i = 0; i < rows; i++) {
		for (j = 0; j < cols; j++) {
			board[i][j] = set;
		}
	}
}
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col) {
	printf("-------扫雷---------\n");
	int i = 0, j = 0;
	for (j = 0; j <= row; j++) {
		printf("%d ", j);
	}
	printf("\n");
	for (i = 1; i <= row; i++) {
		printf("%d ", i);
		for (j = 1; j <= col; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-------扫雷---------\n");
}
//布雷
void Setmine(char board[ROWS][COLS], int row, int col) {
	int cnt = MINE_COUNT;
	while(cnt>0) {
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			cnt--;
		}
	}
}
//数周围一圈雷的个数
char Countmine(char Mine[ROWS][COLS], int x, int y) {
	int i = 0;
	int j = 0;
	int sum = 0;
	char cnt = 0;
	for (i = x - 1; i <= x + 1; i++) {
		for (j = y - 1; j <= y + 1; j++) {
			sum += Mine[i][j];
		}
	}
	cnt = sum - 8 * '0';
	return cnt;
}
//一下子灭掉一大片
void Optimize(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y) {
	int i = 0;
	int j = 0;
	char cnt = Countmine(Mine, x, y);
	if (cnt == '0'&& Show[x][y] != ' ') {
		Show[x][y] = ' ';
		for (i = x - 1; i <= x + 1; i++) {
			for (j = y - 1; j <= y + 1; j++) {
				if (i >= 0 && j >= 0 && i <= 9 && j <= 9) {
					Optimize(Mine, Show, i, j);
				}
			}
		}
	}
	if (cnt != '0') {
		Show[x][y] = cnt;
	}
}

//排雷
void GuessMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int leftover = row * col - MINE_COUNT;
	while (leftover>0) {
		printf("请输入坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9) {
			if (Mine[x][y] == '1') {
				printf("你被炸死了,游戏结束\n");
				Displayboard(Mine, ROW, COL);
				break;
			}
			else {
				Optimize(Mine, Show, x, y);
				Displayboard(Show, ROW, COL);
				leftover--;
				
			}
		}
		else {
			printf("坐标非法,请重新输入\n");
		}
	}
	if (leftover == 0) {
		printf("游戏胜利!\n");
		Displayboard(Mine, ROW, COL);
	}
}

image.png