C语言实现扫雷(扩展)

声明

本次撰写代码使用多文件,test.h包含头文件,main.c包含主函数和菜单,test.c包含所有自定义函数。若有需要代码的直接将后续代码粘贴复制到对应文件中即可。

难点解析

提及扫雷,相信大多数人都不陌生,其规则就是字面意思,将所有雷区排查出来(展开所有非雷区域)。那么在大家玩扫雷游戏中其实暗含了,两个规则,一:点开非雷区域可得到附近8个区域的雷区个数;二:若附近8个区域中都没有雷则会自动展开直至有个数的区域。那么接下来我们来分析一下,这两个难点。

探索八区

其实探索附近区域共有一下三种情况:

在这里插入图片描述
如果我们按三种情况分别去编写代码是不是就会非常麻烦?为了简便,我们可以给外侧再加一层即给二维数组行列分别加二,并且把外层全部设置为非雷区域,就可以解决这一问题。

递归展开

探索八区解决了探索附近八个区域的雷区个数,八区中有雷,则显示雷的个数。若没有雷(即雷的个数为0),则需要展开。
在这里插入图片描述

代码实现

test.h

#ifndef _GAME_H_
#define _GAME_H_

#include<stdio.h>
#include<string.h>
#include<time.h>

#define ROW 12//设置10*10的区域,则需要行列各增加两层,以达到外部扩充一层。
#define COL 12
#define MINENUM 20//布雷个数为20

void setmine(char mineboard[][COL]);
void show(char board[][COL]);
int minecount(char mineboard[][COL], int x, int y);
void game();
void open(char mineboard[][COL], char sweepboard[][COL], int x, int y);
int judge(char sweepboard[][COL]);

#endif

main.c

#include "test.h"

void meau()
{
	printf("##########################################\n");
	printf("###           欢迎来到扫雷世界         ###\n");
	printf("##########################################\n");
	printf("###      1.play               2.exit   ###\n");
	printf("##########################################\n");
	printf("请选择:");
}

int main()
{
	int quit = 1;//做标记
	while (quit){
		int k = 0;
		meau();//展示菜单
		scanf("%d", &k);//玩家输入选择
		switch (k){
		case 1://如果是1,开始游戏
			game();
			break;
		case 2://如果是2,退出游戏
			quit = 0;
			printf("客观再来玩哦~\n");
			break;
		default:
			printf("输入有误,请重新输入:\n");
			break;
		}
	}
}

test.c

test.c复制的时候不要忘了 #include “test.h”

game()

void game()
{
	char sweepboard[ROW][COL];//第一个二维数组,扫描棋盘,用于给用户展示。为方便计算
	  // 定义数组为12 * 12,实际用到的是10 * 10的棋盘,后续棋盘均指10 * 10棋盘。
	char mineboard[ROW][COL];//第二个二维数组,埋雷棋盘,用于埋雷。
	memset(sweepboard, '*',sizeof(sweepboard));//扫描棋盘初始化均为‘*’
	memset(mineboard, '0', sizeof(mineboard));//埋雷棋盘初始化均为‘0’
	setmine(mineboard);//埋雷
	while (1){
		system("cls");//清屏
		show(sweepboard);//展示棋盘
		printf("请输入坐标:");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x < 1 || x > 10 || y < 1 || y > 10){
			printf("输入坐标有误,请重新输入!\n");
			Sleep(1000);
			continue;
		}
		if (mineboard[x][y] == '1'){
			printf("你被雷炸死了!\n");
			show(mineboard);
			break;
		}
		if (sweepboard[x][y] != '*'){
			printf("重复输入,请重新输入:\n");
		}
		open(mineboard, sweepboard, x, y);
		if (judge(sweepboard) == 1){
			printf("欧呦~你咋就赢了呢,太厉害了\n");
			break;
		}
	}

}

setmine()

setmine函数用来布置雷区,这里有一个注意点,不要忘了我们只给内层布置雷区,所以范围是ROW和COL分别减2

void setmine(char mineboard[][COL])
{
	int num = MINENUM;
	srand((unsigned long)time(NULL));
	while (num){
		int x = rand() % (ROW - 2) + 1;
		int y = rand() % (COL - 2) + 1;
		if (mineboard[x][y] != '1'){
			mineboard[x][y] = '1';
			num--;
		}
	}
}

show()

show函数用来展示当前游戏面板,我们在编写代码的时候也可以按自己构思的来,美观即可。

void show(char board[][COL])
{
	int i = 1;
	int j = 0;
	printf("    ");
	for (; i < 11; i++) {
		printf("%d   ", i);
	}
	printf("\n");
	for (i = 1; i < 11; i++) {
		printf("   ----------------------------------------\n");
		printf("%2d|", i);//输出行标。
		for (j = 1; j < 11; j++) {
			printf(" %c |", board[i][j]);
		}
		printf("\n");
	}
	printf("   ----------------------------------------\n");
}

minecount()

minecount函数即为探索八区

int minecount(char mineboard[][COL], int x, int y)
{
	int num = mineboard[x - 1][y - 1] + mineboard[x - 1][y] + mineboard[x - 1][y + 1] + \
		mineboard[x][y - 1] + mineboard[x][y + 1] + mineboard[x + 1][y - 1] + \
		mineboard[x + 1][y] + mineboard[x + 1][y + 1] - 7 * '0';
	//棋盘是字符型的,把该坐标周围8个坐标里的字符加起来
	 //再减去8 * '0',计算时用的是acsii值,所得结果就是雷的个数。
	return num;
}

open()

open函数实现展开。

void open(char mineboard[][COL], char sweepboard[][COL], int x, int y)//open函数,判断是否需要递归式展开。
{
	if (minecount(mineboard, x, y) == '0'){//判断周围雷的个数,若为0,则需要展开
		sweepboard[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++){
			for (j = y - 1; j <= y + 1; j++){
				if (sweepboard[i][j] == '*' && i > 0 && i < 11 && j > 0 && j < 11){
					//如果该位置未被扫过且在棋盘范围内则继续递归调用open函数
					open(mineboard, sweepboard, i, j);
				}
			}
		}
	}
	else{
		sweepboard[x][y] = minecount(mineboard, x, y);
		//不需要展开则显示附近雷的个数
	}
}

judge()

判断玩家是否扫雷成功

int judge(char sweepboard[][COL])//判断玩家是否扫雷成功
{
	int i;
	int j;
	int count = 0;
	for (i = 1; i <= ROW - 2; i++){
		for (j = 1; j <= COL - 2; j++){
			if (sweepboard[i][j] == '*'){
				count++;
			}
			if (count > MINENUM){
				return 0;
			}
		}
	}
	return 1;
}

知识补充

在这里给大家补充一下memset
其函数原型是:void *memset(void *s,int ch,size_t n);
函数功能是:将是所指向的某一块内存中的前n个字节的内容全部设置为ch指定的ASCII值,第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作它是对较大的结构体或数组进行清零操作的一种最快方法
头文件是:<memory.h>或<string.h>

总结

不论我们写什么代码,我个人认为都应该先认真思考,再着手操作,这样会使我们编写代码更顺畅,思路更清晰。这次扫雷的代码编写和我的上一篇博客三子棋有很多相似之处,例如从菜单到主函数编写,或者说很多编写的思路有相似之处,如果现在看我博客的你,对三子棋也感兴趣,欢迎来看!创作不易,希望大家给我个赞!谢谢您嘞~

猜你喜欢

转载自blog.csdn.net/zSoaring/article/details/105956757
今日推荐