C语言实现扫雷(可自动显示无雷区)

今天我们用c语言写一个简单版本的扫雷,等以后,我写的游戏多了,我们再去写一个游戏大厅,可以供玩家选择自己想玩的游戏

分析:

(1)我们用两个二维数组分别存储给玩家展示的面板和储存地雷的

(2)地雷用字符‘1’存储,不是地雷用‘0’存储,这样方便我们判断出玩家选的地方是否是雷,如果不是雷,也方便我们知道周围雷的个数

(3)可自动显示无雷区的思想:
如果周围雷的个数为0的话,就可以通过递归,把周围都展开,展开后如果还出现‘0’此(‘0’为周围没有雷),将再通过递归,一直展示出非‘0’数字(非‘0’数字指的是有0处)或到达边界位置

给大家举个例:

这是雷的分布图,就是上面说用一个二维数组存储出来的,‘0’表示不是雷,‘1’表示雷,显然可以看见如果玩家输入一个3 3,那么就会显示为‘0’,表示周围格子都不是雷,那样玩家就要又输入8个坐标,那样会大大的打击玩家的耐心。
C语言实现扫雷(可自动显示无雷区)
当我们开辟了可自动显示无雷区的功能后,再输入(3 3)这个坐标时,就可以,展示所有显然已知周围不是雷的地方,如下图,输入3 3坐标后
C语言实现扫雷(可自动显示无雷区)

(4)就给玩家展示了这么多,是不是很方便,这是通过一个递归实现的,那我们如何判断玩家是否胜利了呢?

写一个方法,统计出我们所用的10*10的格子内有多少个
‘*’,返回值为int类型,返回‘*’的个数。然后我们可以
在一个循环外,定义一个count,来接受该方法,类似如下

C语言实现扫雷(可自动显示无雷区)
如何判断玩家最后是否,是选中炸弹或者除了炸弹全部打开了,可看上面那张图片中,有一个break,可以提前结束循环,如果是自动跳出循环,那么count肯定大于雷的个数,那么就说明踩到炸弹了,失败,如果count小于等于雷的个数,那么就说明自己通过判断条件,退出循环的,那么说明剩下的格子全是雷了,那说明玩家胜利了,代码可如下图
C语言实现扫雷(可自动显示无雷区)

这是选择是否进去游戏的一个界面
C语言实现扫雷(可自动显示无雷区)

这是进去后,给的图
C语言实现扫雷(可自动显示无雷区)
下面是点到炸弹后,输了界面,还会给玩家显示地雷分布图,然后再让玩家选择是否再来一把
C语言实现扫雷(可自动显示无雷区)
这就是一个大部分的分析,然后后面是源代码,我注释写的很清楚,每一部分是干嘛的,我都写了,希望对大家有帮助

MineSweep.h

#ifndef _MINESWEEP_H_
#define _MINESWEEP_H_

#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<time.h>
#pragma warning(disable:4996)

#define ROW 12	//行
#define LIE 12	//列
#define MINE 20	//地雷个数

int determine(char board[][LIE], int row, int lie);	//判断还剩下多少*位置,就可以知道玩家什么时候胜利,知道剩下格子数减去炸弹数,但是我们有两行两列,是不用的,所以我们只统计[1-(row-2)]之间的'*'
void voluntarily(char board[][LIE], char mineBoard[][LIE], int row, int lie);	//可自动展示无雷区,然后把如果点到'0',周围都打开,并且,如果周围也有'0',周围也自动打开
void boardInit(char mineBoard[][LIE], int row, int lie, char ch);	//给面板初始化	最后一个形参为把面板初始化为什么‘字符’
char isNunmber(char mineBoard[][LIE], int row, int lie);	//确定mineBoard[row][lie]不是炸弹,返回他周围炸弹的个数,但是是字符。
int judgeMine(char mineBoard[][LIE], int row, int lie);	//判断玩家选的位置是否是炸弹,返回1,则是炸弹,返回0则不是炸弹
void printBoard(char mineBoard[][LIE], int row, int lie);	//显示面板
void landMines(char mineBoard[][LIE],int row,int lie);	//给存放地雷的面板,随机存放20颗地雷,字符1代表是地雷,字符0代表无地雷
void mineSweepGame();	//扫雷游戏

#endif

MineSweep.c

#include "MineSweep.h"


//确定mineBoard[row][lie]不是炸弹,返回他周围炸弹的个数,但是是字符。
char isNunmber(char mineBoard[][LIE], int row, int lie){
	if (row >=1 && row <= 10 && lie >=1 && lie <= 10){
		char ch = mineBoard[row - 1][lie - 1] + mineBoard[row - 1][lie] + mineBoard[row - 1][lie + 1] + mineBoard[row][lie - 1] + mineBoard[row][lie + 1] + mineBoard[row + 1][lie - 1] + mineBoard[row + 1][lie] + mineBoard[row + 1][lie + 1] - 7 * '0';
		return ch;
	}
}

//判断玩家选的位置是否是炸弹,返回1,则是炸弹,返回0则不是炸弹
int judgeMine(char mineBoard[][LIE], int row, int lie){
	if (mineBoard[row][lie] == '1'){
		return 1;
	}
	else{
		return 0;
	}
}

//显示面板
void printBoard(char mineBoard[][LIE], int row, int lie){
	printf("   ");
	for (int i = 1; i <= lie - 2; i++){
		if (i != lie - 2){
			printf(" | %2d", i);
		}
		else{
			printf(" | %2d |", i);
		}
	}
	printf("\n");
	printf("-------------------------------------------------------\n");

	for (int i = 1; i <=lie-2; i++){
		if (i != 10){
			printf("%2d ", i);
		}
		else{
			printf("%2d ", i);
		}
		for (int j = 1; j <= row-2; j++){
			if (j != row - 2){
				printf(" | %2c", mineBoard[i][j]);
			}
			else{
				printf(" | %2c |", mineBoard[i][j]);
			}
		}
		printf("\n");
		printf("-------------------------------------------------------\n");
	}
}

//给面板初始化	最后一个形参为把面板初始化为什么‘字符’
void boardInit(char mineBoard[][LIE], int row, int lie,char ch)	{
	for (int i = 0; i < row; i++){
		for (int j = 0; j < lie; j++){
			mineBoard[i][j] = ch;
		}
	}
}

//给存放地雷的面板,随机存放20颗地雷,字符1代表是地雷,字符0代表无地雷
void landMines(char mineBoard[][LIE], int row, int lie){

	//产生[1,10]之间的随机数字,因为只用有最外面一圈,我们不用
	//k=rand()%(Y-X+1)+X;	这是一个公式,可产生[x,y]之间的随机数
	int count = MINE;
	while (count>0){
		row = rand() % (10 - 1 + 1) + 1;
		lie = rand() % (10 - 1 + 1) + 1;
		//printf("%d %d\n", row,lie);
		if (mineBoard[row][lie] == '0'){
			mineBoard[row][lie] = '1';
			count--;
		}
		else{
			continue;
		}
	}
}	

//可自动展示无雷区,然后把如果点到'0',周围都打开,并且,如果周围也有'0',周围也自动打开
void voluntarily(char board[][LIE], char mineBoard[][LIE], int row, int lie){
	//先判断row和lie是否越界
	if (row >= 1 && row <= 10 && lie >= 1 && lie <= 10){
		if (isNunmber(mineBoard, row, lie) == '0'){
			if (board[row][lie] == '*'){

				board[row][lie] = '0';	//先给当前位置赋值为'0',把周围八个位置全部递归,递归出口:(1)越界(2)周围不再有无雷区

				voluntarily(board, mineBoard, (row - 1), (lie - 1));
				voluntarily(board, mineBoard, (row - 1), (lie));
				voluntarily(board, mineBoard, (row - 1), (lie + 1));
				voluntarily(board, mineBoard, (row), (lie - 1));
				voluntarily(board, mineBoard, (row), (lie + 1));
				voluntarily(board, mineBoard, (row + 1), (lie - 1));
				voluntarily(board, mineBoard, (row + 1), (lie));
				voluntarily(board, mineBoard, (row + 1), (lie + 1));

			}
		}
		else{
			board[row][lie] = isNunmber(mineBoard, row, lie);
		}
	}
}

//判断还剩下多少*位置,就可以知道玩家什么时候胜利,知道剩下格子数减去炸弹数,但是我们有两行两列,是不用的,所以我们只统计[1-(row-2)]之间的'*'
int determine(char board[][LIE], int row, int lie){
	int count = 0;
	for (int i = 1; i <= row-2; i++){
		for (int j = 1; j <= lie-2; j++){
			if (board[i][j] == '*'){
				count++;
			}
		}
	}
	return count;
}

void mineSweepGame(){
	/*先定义两个相同大小的二维数组,行列都定义为12
	定义为12×12,但是我们只用10×10,因为扫雷边角的那些,
	都不需要判断周围8个那么多,但是我们行列都多定义了两行就可以,把用的每个都判断周围8个
	*/
	char board[ROW][LIE];	//给玩家展示的面板
	char mineBoard[ROW][LIE];	//存放地雷的面板
	
	boardInit(board,ROW,LIE,'*');	//给玩家展示的面板初始化
	boardInit(mineBoard, ROW, LIE, '0');	//给玩家展示的面板初始化

	srand((unsigned int)(time(NULL)));	//种下随机数的种子
	landMines(mineBoard,ROW,LIE);	//给存放地雷的面板,随机存放地雷

	int count = determine(board, ROW, LIE);	//记录还剩下多少个'*'
	
	while (count>MINE){
		system("cls");
		printBoard(board, ROW, LIE);	//显示面板
		//printBoard(mineBoard, ROW, LIE);	//显示地雷面板
		int row = 0;
		int lie = 0;
		printf("请输入一个位置的坐标=>:");
		scanf("%d %d",&row,&lie);

		//判断输入位置是否正确
		if (row >= 1 && row <= 10 && lie >= 1 && lie <= 10&&board[row][lie]=='*'){
			int judge = judgeMine(mineBoard, row, lie);	//判断玩家输入的位置是否为炸弹,如果是炸弹返回1,如果不是返回0;
			if (judge){
				break;
			}
			else{
				voluntarily(board, mineBoard, row, lie);	//递归实现可自动显示无雷区
			}
		}
		else{
			printf("您输入坐标有误,请重新输入!!\n");
			Sleep(1000);
		}

		count = determine(board, ROW, LIE);	//更新'*'的数量
	}

	if (count>MINE){
		printf("########################################\n");
		printf("##            您被炸死了:<            ##\n");
		printf("########################################\n");
		printBoard(mineBoard, ROW, LIE);	//显示面板
	}
	else{
		printf("########################################\n");
		printf("##          恭喜你,胜利了:>          ##\n");
		printf("########################################\n");
		printBoard(mineBoard, ROW, LIE);	//显示面板
	}
}

main.c

#include "MineSweep.h"

void menu(){
	printf("########################################\n");
	printf("############欢迎来到扫雷游戏############\n");
	printf("########################################\n");
	printf("## 1.Play #################### 2.Exit ##\n");
	printf("########################################\n");
	printf("Please Player Choice=>:");

}
int main(){
	int selet=0;	//控制while循环,
	
	while (!selet){
		menu();
		int choose=0;	//供玩家选择进入游戏或者退出
		scanf("%d", &choose);
		setbuf(stdin, NULL);	//如果玩家输入字母,也不会出错
		switch (choose){
		case 1:
			mineSweepGame();
			printf("请问是否再来一把?\n");
			Sleep(500);
			break;
		case 2:
			printf("欢迎下次再来玩\n");
			selet = 1;
			break;
		default:
			printf("输入有误,请重新输入!!!\n");
			Sleep(500);
			break;
		}

		
	}

	system("pause");
}
上一篇:大数据之Hive:其他常用查询函数之列转行


下一篇:洛谷P2024 [NOI2001]食物链 题解 并查集