C语言递归实现扫雷小游戏代码解析

目录

一、功能展示:

二、代码详解

 main()函数

InitBoard函数

DisplayBoard函数

SetMine函数

GetMineCount函数

ShowCount函数

FindMine函数

game函数

三、代码展示


一、功能展示:

在最开始有一个小的菜单:

c3f63afac4294029a87083a01aa8066d.png

 在输入排查的坐标之后能够展开当前格子并且自动展开附近的空白格,直到遇到数字格或雷格为止(中间显示地雷的格子是为了方便实验各种操作)。

578bd5fca08e4cc4bfb6db6bfe29c329.png

  在把除地雷格以外的格子都打开了之后,系统判定玩家赢得了游戏。

65ecb816c9c343bc9e4e38a893348b34.png

若玩家输入的格子为地雷格,则判断游戏失败。 

bfe22cc6d7854c74a47cc3723c96fa97.png

二、代码详解

void menu() {
	printf("********************\n");
	printf("*****0.开始游戏*****\n");
	printf("*****1.结束游戏*****\n");
	printf("********************\n");
}
int main() {
	int input;
	menu();
	printf("请输入:");
	scanf("%d", &input);
	switch (input) {
	case 0:
		game();
		break;
	case 1:
		break;
	}
	return 0;
}

 main()函数

函数最先通过switch_case语句判断进行什么操作。

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

InitBoard函数

函数通过for循环初始化数组,根据传入的set函数为数组初始化赋值。

void DisplayBoard(char board[ROWS][COLS], int row, int col) { //输出扫雷盘
	int i, j;
	for (i = 0; i <= row; i++) {
		printf("%d ", i);
	}
	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");
}

DisplayBoard函数

函数首先通过for循环输出第一排数字记录列数,再在每一行前输出数字记录行数并同时输出数组。 这样玩家在游玩过程中能够通过辅助的行列数方便地定位格子。

void SetMine(char mine[ROWS][COLS], int row, int col) { //布置雷 
	int count = easy_count;
	while (count) {
		int x = rand()%row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] != '1') {
			mine[x][y] = '1';
			count--;
		}
	}
}

SetMine函数

函数通过2个rand函数随机为地雷的x、y坐标赋值,通过while循环控制地雷的数量。当count减为0时,说明布置的地雷数已经满足。

int GetMineCount(char mine[ROWS][COLS], int x, int y) { //查找坐标周围雷的个数
	int i, j, t = 0;
	for (i = x - 1; i <= x + 1; i++) {
		for (j = y - 1; j <= y + 1; j++) {
			t = t + (mine[i][j] - '0');
		}
	}
	return t;
}

GetMineCount函数

函数将(x-1)~(x+1)列,(y-1)~(y+1)行的mine数组内的值相加来求得当前坐标周围雷的个数。由于在mine数组内字符'0'为没雷,字符'1'为有雷,所以仅需要将mine[i][j] - '0'即可将字符转换为数字进行加减法('0'-'0'=0,'1'-'0'=1)。最后算出来的结果为传入坐标周围雷的个数。

void ShowCount(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col) {
	if (x < 1 || x > row || y < 1 || y > col || show[x][y] != '*') {
		// 如果坐标越界或已经展开过,直接返回
		return;
	}
	// 统计坐标周围的雷
	int t = GetMineCount(mine, x, y);
	show[x][y] = t + '0';

	if (t == 0) {
		// 如果周围没有雷,递归展开周围的空白格
		for (int i = -1; i <= 1; i++) {
			for (int j = -1; j <= 1; j++) {
				ShowCount(mine, show, x + i, y + j, row, col);
			}
		}
	}
}

ShowCount函数

函数先通过if函数判断传入坐标是否合法。这里不仅仅判断了坐标是否越界,还通过格子字符是否为'*'判断了坐标格子是否被访问过。这样在遍历的时候即可避免陷入死循环

再调用GetMineCount函数求周围格子内是否有雷,并将排查的坐标格子字符更换成附件的地雷数。

如果排查的格子周围地雷数为0,则递归周围8个格子。

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { //排查雷
	int x, y;
	int win = 0;
	while (1) {
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1') {
				printf("你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				exit(0);
			}
			else {  //不是雷,则统计坐标周围的雷
				ShowCount(mine, show, x, y, row, col);
				DisplayBoard(show, ROW, COL);
				int t = 0;
				for (int i = 1; i <= row; i++) {
					for (int j = 1; j <= col; j++) {
						if (show[i][j] == '*') t++;
					}
				}
				if (t == easy_count) break;
			}
		}
		else {
			printf("坐标非法,请重新输入\n");
		}
	}
	printf("恭喜你赢了!");
}

FindMine函数

函数先判断输入坐标是否合法,再判断输入坐标是否为雷。若为雷则游戏结束,并且输出地雷坐标图,让你寄的明明白白。若不是雷,则调用ShowCount函数。

此时使用for循环判断游戏是否结束,如果当前场上的格子为'*',则t数目+1。如果t的数目与当前游戏内的地雷数量相同,则游戏结束。(如果格子为'*',则代表当前格子并没有被排查过。如果'*'的数量与地雷数相同,则代表除了地雷格之外所有的格子都被排查过

如果t的数目与当前游戏内的地雷数量相同,则通过break跳出while(1)的循环,赢得游戏

void game() {
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	InitBoard(show, ROWS, COLS, '*');	
	InitBoard(mine, ROWS, COLS, '0');
	//布置好棋盘
	DisplayBoard(show, ROW, COL);
	SetMine(mine, ROW, COL);	//布置好雷
	DisplayBoard(mine, ROW, COL);
	FindMine(mine, show, ROW, COL);
}

game函数

函数创建二维数组时长宽为11*11,这样不仅仅再输入输出时可以直接用1~9来代表第一个到第九个,还可以在调用GetMineCount函数时即使是在计算边框处格子周围的地雷数时省去多余的限制和判断条件。

三、代码展示

最后附上头文件和主函数

#pragma once

#define ROW 9
#define COL 9
#define easy_count 2

#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 game();
void menu();
void SetMine(char mine[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetMineCount(char mine[ROWS][COLS], int x, int y);
void ShowCount(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col);
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"game1.h"
void menu() {
	printf("********************\n");
	printf("*****0.开始游戏*****\n");
	printf("*****1.结束游戏*****\n");
	printf("********************\n");
}
int main() {
	int input;
	menu();
	printf("请输入:");
	scanf("%d", &input);
	switch (input) {
	case 0:
		game();
		break;
	case 1:
		break;
	}
	return 0;
}
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { //初始化数组
	int i, j;
	for (i = 0; i < rows; i++) {
		for (j = 0; j < cols; j++) {
			board[i][j] = set;
		}
	}
}
void game() {
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	InitBoard(show, ROWS, COLS, '*');	
	InitBoard(mine, ROWS, COLS, '0');
	//布置好棋盘
	DisplayBoard(show, ROW, COL);
	SetMine(mine, ROW, COL);	//布置好雷
	DisplayBoard(mine, ROW, COL);
	FindMine(mine, show, ROW, COL);
}
void DisplayBoard(char board[ROWS][COLS], int row, int col) { //输出扫雷盘
	int i, j;
	for (i = 0; i <= row; i++) {
		printf("%d ", i);
	}
	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 mine[ROWS][COLS], int row, int col) { //布置雷 
	int count = easy_count;
	while (count) {
		int x = rand()%row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] != '1') {
			mine[x][y] = '1';
			count--;
		}
	}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { //排查雷
	int x, y;
	int win = 0;
	while (1) {
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1') {
				printf("你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				exit(0);
			}
			else {  //不是雷,则统计坐标周围的雷
				ShowCount(mine, show, x, y, row, col);
				DisplayBoard(show, ROW, COL);
				int t = 0;
				for (int i = 1; i <= row; i++) {
					for (int j = 1; j <= col; j++) {
						if (show[i][j] == '*') t++;
					}
				}
				if (t == easy_count) break;
			}
		}
		else {
			printf("坐标非法,请重新输入\n");
		}
	}
	printf("恭喜你赢了!");
}


int GetMineCount(char mine[ROWS][COLS], int x, int y) { //查找坐标周围雷的个数
	int i, j, t = 0;
	for (i = x - 1; i <= x + 1; i++) {
		for (j = y - 1; j <= y + 1; j++) {
			t = t + (mine[i][j] - '0');
		}
	}
	return t;
}
void ShowCount(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col) {
	if (x < 1 || x > row || y < 1 || y > col || show[x][y] != '*') {
		// 如果坐标越界或已经展开过,直接返回
		return;
	}
	// 统计坐标周围的雷
	int t = GetMineCount(mine, x, y);
	show[x][y] = t + '0';

	if (t == 0) {
		// 如果周围没有雷,递归展开周围的空白格
		for (int i = -1; i <= 1; i++) {
			for (int j = -1; j <= 1; j++) {
				ShowCount(mine, show, x + i, y + j, row, col);
			}
		}
	}
}

78e1b59d69b643e2933dfc2aaed154c3.jpeg

 

在下只是一名小白,如有什么错误欢迎指出!

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值