[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. 第一个二维数组,布置雷,把生成出来的雷存放到这个数组里,用字符‘1’表示雷,字符’0’表示非雷
  2. 第二个二维数组,用来排查雷,记录当前区域周围雷的个数

问题解决后就可以对数组进行初始化了:
mine棋盘用于存放雷的信息,全部为‘0’
show棋盘用于显示当前所选区域周围雷的个数,全部初始化为’1’
初始化后开始布置雷,这里我们9x9的棋盘,默认雷的个数为10个,随机产生x,y雷的坐标(范围是1~9),放入show棋盘。
我们可以打印出mine,和show棋盘看一下,当然玩家在玩的时候mine棋盘是不能显示的。

Mine棋盘:
在这里插入图片描述
Show棋盘:
在这里插入图片描述
最后玩家进行雷的排查,具体函数实现见game.c

3. game.c内容分析

  1. InitBoard()函数,这里棋盘ROWS和COLS都为11,rows和cols都为11,set为自定义初始化字符
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
  1. DisplayBoard()函数,注意的棋盘并不是打印11x11的棋盘,玩家只需要看到9x9的,因此我们从第二行第二列开始打印,具体先打印列号,其次打印行号和初始化符号。

  2. SetMine()函数,我们引用头文件#include <time.h>
    产生随机数srand((unsigned int)time(NULL));, 雷的x,y坐标为int x=rand()%row+1 and int y=rand()%col+1
    布置雷前判断一下此区域是否为字符’0’,是则设为’1’

  3. getMineCount()函数,判断此坐标附近把个坐标的位置是否为雷,值得注意的是二维数组内都是用char类型来存储,那么如果是雷怎么转换成数字类型来进行相加呢? 我们知道 0+‘0’ = ‘0’,因为字符’0’被替换成了48,那么四周的八个区域字符进行相加得到的值减去8*'0’就可以得到有几个雷了。

  4. no_mine_spread()函数,如果此区域是’0’,我们就分别去此区域周围的八个区域也进行一次相应的判断,看是否为雷区,如果不是雷,递归下去,直到周围是雷为止,并把当前周围雷的数量show[x][y] = mine进行记录。

  5. game_win()函数,此函数为判断玩家是否胜利,遍历二维数组,判断标记符号或者剩余未踩的区域的数量,如果count和雷的数量相等,恭喜玩家,游戏结束,否则游戏结束

  6. mark_Mine()函数,玩家如果想标记此区域可能是雷,将此区域标记为符号’!’

  7. FIndMine()函数

    1. 输入即将排查雷的坐标
    2. 检查坐标是不是雷,如果是雷,gameover,否则调用getMineCount()函数,来判断周围雷的个数,存储到show数组
    3. 询问是否要进行疑似雷的标记

代码实现

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语言实现小游戏篇我接触的第一款电脑游戏,你可以永远相信 “ 扫雷 ” 。[ C语言实现 ] [ 超详细,超清楚 ] [ 有代码 ]

扫雷游戏C语言实现完美版非常详细的讲解,看完必会

C语言游戏超详解扫雷游戏完整版,细节满满!!

C语言游戏超详解扫雷游戏完整版,细节满满!!

扫雷小游戏最详细讲解C语言