[C语言]超详细讲解扫雷游戏(递归+标记),附思维导图
Posted SuchABigBug
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C语言]超详细讲解扫雷游戏(递归+标记),附思维导图相关的知识,希望对你有一定的参考价值。
I have a dream today!
---Martin Luther King
无论是90后还是00后,相信大家都在xp系统上玩过扫雷游戏,我们初中电脑老师神采飞扬的炫耀过自己初级9x9棋盘扫雷成绩,只耗时2秒,问有没有来挑战的,现在想想给你80个雷,你要不猜一下试试吧~
思维导图
结构分析+图片案例
1. game.h内容分析
我们设计一个9x9的扫雷棋盘,需要考虑一个问题,当边界区需要排查周围8个区域是否会越界?答案是显然的,因此我们需要在9x9的周围加一个护城河防止数组在判断周围雷区的时候越界,即初始化一个11x11的棋盘。见下图:
2. test.c内容分析
首先根据用户的选择进行do…while判断,用户根据菜单进行判断,其他符号都满足while的循环条件,重新进行选择。
接着对棋盘进行初始化:
如果此区域是雷 ---- 用字符‘0’表示
如果此区域非雷 ---- 用字符‘1’表示
但有个问题是我们如果在排雷的过程中发现,此区域周围的八个区域中只有一个雷,那程序如何区分这个‘1’是雷?还是排查后统计出来总的雷的个数呢?
为了解决这个问题我们用两个二维数组进行判断:
- 第一个二维数组,布置雷,把生成出来的雷存放到这个数组里,用字符‘1’表示雷,字符’0’表示非雷
- 第二个二维数组,用来排查雷,记录当前区域周围雷的个数
问题解决后就可以对数组进行初始化了:
mine棋盘用于存放雷的信息,全部为‘0’
show棋盘用于显示当前所选区域周围雷的个数,全部初始化为’1’
初始化后开始布置雷,这里我们9x9的棋盘,默认雷的个数为10个,随机产生x,y雷的坐标(范围是1~9),放入show棋盘。
我们可以打印出mine,和show棋盘看一下,当然玩家在玩的时候mine棋盘是不能显示的。
Mine棋盘:
Show棋盘:
最后玩家进行雷的排查,具体函数实现见game.c
3. game.c内容分析
- InitBoard()函数,这里棋盘ROWS和COLS都为11,rows和cols都为11,set为自定义初始化字符
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
-
DisplayBoard()函数,注意的棋盘并不是打印11x11的棋盘,玩家只需要看到9x9的,因此我们从第二行第二列开始打印,具体先打印列号,其次打印行号和初始化符号。
-
SetMine()函数,我们引用头文件
#include <time.h>
产生随机数srand((unsigned int)time(NULL));
, 雷的x,y坐标为int x=rand()%row+1 and int y=rand()%col+1
布置雷前判断一下此区域是否为字符’0’,是则设为’1’ -
getMineCount()函数,判断此坐标附近把个坐标的位置是否为雷,值得注意的是二维数组内都是用char类型来存储,那么如果是雷怎么转换成数字类型来进行相加呢? 我们知道 0+‘0’ = ‘0’,因为字符’0’被替换成了48,那么四周的八个区域字符进行相加得到的值减去8*'0’就可以得到有几个雷了。
-
no_mine_spread()函数,如果此区域是’0’,我们就分别去此区域周围的八个区域也进行一次相应的判断,看是否为雷区,如果不是雷,递归下去,直到周围是雷为止,并把当前周围雷的数量show[x][y] = mine进行记录。
-
game_win()函数,此函数为判断玩家是否胜利,遍历二维数组,判断标记符号或者剩余未踩的区域的数量,如果count和雷的数量相等,恭喜玩家,游戏结束,否则游戏结束
-
mark_Mine()函数,玩家如果想标记此区域可能是雷,将此区域标记为符号’!’
-
FIndMine()函数
- 输入即将排查雷的坐标
- 检查坐标是不是雷,如果是雷,gameover,否则调用getMineCount()函数,来判断周围雷的个数,存储到show数组
- 询问是否要进行疑似雷的标记
代码实现
game.h
//
// game.h
// MineSweeper_Game
//
// Created by henry on 2021/5/5.
//
#ifndef game_h
#define game_h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MINE_NUM 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char board[ROWS][COLS], int row, int col, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char mine[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#endif /* game_h */
test.c
//
// main.c
// MineSweeper_Game
//
// Created by henry on 2021/5/5.
//
#include "game.h"
void callMenu(){
printf("*****************************************\\n");
printf("************ 1. Play ************\\n");
printf("************ 0. Exit ************\\n");
printf("*****************************************\\n");
}
void enterGame(){
printf("MineSweeper\\n");
char mine[ROWS][COLS] = {0}; //存放布置好的雷的信息
char show[ROWS][COLS] = {0}; //存放排查出的雷的信息
//初始化mine棋盘,全部放字符型的'0'
//初始化show棋盘,全部放字符型的'*'
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//开始布置雷
SetMine(mine, ROW,COL);
//打印棋盘
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main(int argc, const char * argv[]) {
int input = 0;
srand((unsigned int)time(NULL));
do{
callMenu();
printf("Please Choose :> ");
scanf("%d", &input);
switch (input) {
case 0:
printf("Exit Game\\n");
break;
case 1:
enterGame();
break;
default:
printf("Cannot recognise it, Please retry~ \\n");
break;
}
}while(input);
return 0;
}
game.c
//
// game.c
// MineSweeper_Game
//
// Created by henry on 2021/5/5.
//
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set){
for(int i=0; i<rows; ++i){
for(int j=0; j<cols; ++j){
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col){
//这里是从第二行第二列开始打印棋盘的
printf("===========GAME==========\\n");
//打印列号
for(int i=0; i<=col; ++i){
printf("%d ", i);
}
printf("\\n");
for(int i=1; i<=row; ++i){
printf("%d ", i);
for(int j=1; j<=col; ++j){
printf("%c ", board[i][j]);
}
printf("\\n");
}
printf("===========GAME==========\\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col){
int count = MINE_NUM;
while(count){
//生成x,y坐标
int x = rand()%row+1;
int y = rand()%col+1;
//如果是非雷,布置雷
if(mine[x][y]=='0'){
mine[x][y] = '1';
count--;
}
}
}
/*
利用static只能在自己所在的源文件内使用
1. 修饰局部变量
2. 修饰全局变量
3. 修饰函数
*/
static int getMineCount(char mine[ROWS][COLS], int x, int y){
return mine[x-1][y]+
mine[x-1][y-1]+
mine[x][y-1]+
mine[x+1][y-1]+
mine[x+1][y]+
mine[x+1][y+1]+
mine[x][y+1]+
mine[x-1][y+1] - 8 * '0'; //周围每个值相加 - 八个'0'得到ASCII码字符的十进制
// for(int i=-1; i<=1; i++){
// for(int j=-1; j<=1; j++){
// count+=mine[x+i][y+i];
// }
// }
// count = count-8*0;
}
static void not_mine_spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y){
//如果不是雷,统计x,y周围有几个雷
int count = getMineCount(mine, x, y);
if(count == 0){
show[x][y] = '0';
if((x-1)>0 && (y>0) && show[x-1][y] == '*') not_mine_spread(mine, show, x-1, y);
if((x-1)>0 && (y-1)>0 && show[x-1][y-1] == '*') not_mine_spread(mine, show, x-1, y-1);
if((x>0) && (y-1)>0 && show[x][y-1] == '*') not_mine_spread(mine, show, x, y-1);
if((x+1>0) && (y-1)>0 && show[x+1][y-1] == '*') not_mine_spread(mine, show, x+1, y-1);
if((x+1)>0 && (y>0) && show[x+1][y] == '*') not_mine_spread(mine, show, x+1, y);
if((x+1)>0 && (y+1)>0 && show[x+1][y+1] == '*') not_mine_spread(mine, show, x+1, y+1);
if((x>0) && (y+1)>0 && show[x][y+1] == '*') not_mine_spread(mine, show, x, y+1);
if((x-1>0) && (y+1)>0 && show[x-1][y+1] == '*') not_mine_spread(mine, show, x-1, y+1);
}else{
show[x][y] = count + '0'; //转换成字符类型数字
}
}
int game_win(char show[ROWS][COLS], int row, int col){
int mine = 0;
for(int i=1; i<=row; ++i){
for(int j=1; j<=col; j++){
if(show[i][j] == '*' || show[i][j] == '!'){
mine++;
}
}
}
return mine;
}
static void mark_Mine(char show[ROWS][COLS], int row, int col){
int input = 0;
int x = 0, y = 0;
do{
printf("Give Mine mark ? Type 1 \\n");
printf("Not Mine mark ? Type 0 \\n");
scanf("%d",&input);
printf("\\n");
switch (input) {
case 0:
break;
case 1:
printf("Select x y coordinate:>");
scanf("%d %d", &x, &y);
if(x>=1 && x<= row && y>=1 && y<=col){
show[x][y] = '!';
DisplayBoard(show, row, col);
}
default:
break;
}
}while(input);
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col){
/*
1. 输入即将排查雷的坐标
2. 检查坐标是不是雷
1. 如果是雷,游戏结束
2. 反之,统计此坐标周围雷的个数,并存储到show数组,游戏继续
*/
int x = 0, y = 0;
//判断是否非雷已排查完
int win = 0;
while(1){
printf("Please insert x,y coordinate:>");
scanf("%d%d", &x, &y); //x和y的取值范围为1~9
if( x >= 1 && x <= row && y >= 1 && y <= col ){
if(mine[x][y] == '1'){
printf("Sorry, this is mine you have been killed\\n");
DisplayBoard(mine, row, col);
break;
}else{
//如果自身附近没有雷,进行递归
not_mine_spread(mine, show, x, y);
//显示排查出的信息
DisplayBoard(show, row, col);
win = game_win(show, row, col);
if(win == MINE_NUM){
printf("Congratulation!!, Mission Success !! \\n");
DisplayBoard(mine, row, col);
break;
}
}
}else{
printf("x y coordinate is not validate, Please retry~\\n");
}
mark_Mine(show, row, col);
}
如果此文章对你有帮助的话,给博主一键三连,Thank You ~ 😃
以上是关于[C语言]超详细讲解扫雷游戏(递归+标记),附思维导图的主要内容,如果未能解决你的问题,请参考以下文章
C语言实现小游戏篇我接触的第一款电脑游戏,你可以永远相信 “ 扫雷 ” 。[ C语言实现 ] [ 超详细,超清楚 ] [ 有代码 ]