排查雷🍊
我们在一开始就说过,我们会在mine数组根据玩家输入的坐标排查周围的雷,然后把雷的数量返回到show数组,所以我们在传参的时候两个数组都应该传。
void game() { //定义二维数组 char mine[ROWS][COLS]; char show[ROWS][COLS]; //初始化二维数组 init_arr(mine, ROWS, COLS, '0'); init_arr(show, ROWS, COLS, '*'); //打印二维数组 display (show, ROW, COL); //埋雷 set_mine (mine, ROW, COL); //display(mine, ROW, COL); //排查雷 find_mine(mine, show, ROW, COL); }
这就是我们整个游戏的过程了。
int mine_count(char mine[ROWS][COLS], int x, int y) { return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + + mine[x + 1][y + 1] - 8 * '0'; /*for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { 也可以用这种方式 } }*/ }
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; int sign = EASY; int input = 0; printf("请选择排查(1)或者标记(2)O(∩_∩)O\n"); scanf("%d", &input); switch (input) { case 1: while (win < row * col - EASY) { printf("排查\n"); printf("请输入你要排查的坐标\n"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { //判断合法 if (mine[x][y] == '1') { //如果是雷 printf("很遗憾,艺术就是派大星\n"); display(mine, ROW, COL); break; } else //不是雷 { show[x][y] = mine_count(mine, x, y) + '0'; display(show, ROW, COL); win++; } } else { printf("坐标不合法,请重新输入\n"); } } system("cls"); if (win == row * col - EASY) { printf("恭喜通关了\n"); display(show, ROW, COL); } break; case 2: if (sign != 0) { printf("标记\n"); printf("请输入你要标记的坐标\n"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { //判断合法 show[x][y] = '#'; sign--; display(show, ROW, COL); break; } else { printf("坐标不合法,请重新输入\n"); } } else { printf("标记已经用完了\n"); break; } } }
现在来看看这个查找雷的函数,
我写了两个功能,一个是标记,一个是排查(排查里面有TNT式展开,我们下一个标题讲,大家就当没看到 ^ _ ^ )
我们先讲排查:我先定义了一个sign表示可标记的数量,因为只有10个雷,所以你只能用10个标记,用完了就提示一下,我们也要判断你要标记的坐标是否合法,合法就把这个坐标变成’#'表示被标记了,当然不要忘记再打印一遍show数组。
我们再说最重要的排查,还有要你输入一个坐标,然后判断是否合法,合法之后就判断你这个位置的坐标是不是雷,是雷就提示你爆炸了,表示就在show数组的对应坐标上显示雷的数量。
但是我们会有一个排查的上限,我们81个坐标有10个雷也就是说我们最多只能排查71个位置,所以用了一个int win 来记录已经被排查的非雷的区域,当win到达71了就说明你排完了所有雷。
再来仔细说说怎么查找那周围8个坐标的雷的个数,当然我们还是要用函数来实现,int mine_count(char mine[ROWS][COLS], int x, int y), 这个函数就是看有几个雷,那就用8个位置的雷数减去8个位置都没有雷,结果就是有几个雷,也就是-8*‘0’的含义
大家看这个函数的返回值,我们想要的是显示字符数字但是这个函数返回的是整形那怎么办呢?
show[x][y] = mine_count(mine, x, y) + ‘0’;这就是转换的方法
当一个整型数值与一个字符型的 ASCII 码值相加时,
会自动将整型数值转换为字符型例如,
如果 mine_count(mine, x, y) 的值为 3,
那么 mine_count(mine, x, y) + ‘0’ 的结果就是字符型的 ‘3’。
TNT式展开🍊
这是我们没有展开的排查雷的一个部分,我在上个标题删去了展开,方便大家理解。
else //不是雷 { show[x][y] = mine_count(mine, x, y) + '0'; display(show, ROW, COL); win++; }
else //不是雷 { expend(mine, show, x, y, &win);//递归展开 /*show[x][y] = mine_count(mine, x, y) + '0';*/ display(show, ROW, COL); win++; }
这个才是我们真正的展开代码。但是想完成这个代码,我们需要仔细研究一下展开的这个过程。再说一句这其实是一个递归的过程,我们要靠向递归模式思考
我们还是简单的分析一下这个过程,我们点击的红色坐标周围如果没有雷,就再从这个坐标周围的八个坐标分别开始递归,然后就再递归······。
我们先简单分析然后我们来继续细致的分析。
思考一🍊
由于画太多影响观感,我就只画了三个。
我们先点击红色区域,然后排查周围八个坐标,我们可以蓝,绿,橙,这三个区域的中心坐标都是用空格表示的,为什么呢?因为在蓝,绿,橙三个区域内没有一个雷,所以中心区域才能变成空格。
然后我们再仔细观察这个过程,我们在检查红色区域的时候会检查到(x-1,y),我们在蓝色区域检测的时候我们还是会检测(x,y),如果是这个的话,当又到(x,y)的时候不是又要检测一次吗?再检测(x,y)就又要检查
到(x-1,y),那这样不就是会一直死循环下去吗?
所以我们要设置一下我们的递归的第一个条件,这个坐标不能为空格,或者说只有没有被排查的坐标才可以被递归,可以想一下,我们如果是空格的坐标说明已经检查过它周围的坐标了,也就是不需要再检测。
思考二🍊
如果我们只是用上面的条件去实现递归是不可能成功的。
为什么?
我就直接讲了,因为还是越界访问(数组)。
还是请大家看图方便理解。
我们大块的绿色区域就是我们9x9的棋盘,我们之前在构思的时候确实解决了,在检查周围8个坐标的越界问题,但是现在是递归了,递归不会管你的二维数组有多大,它会一直递归下下去,大家看蓝色区域,蓝色区域之后还会有很多很多区域这里就不画出来了。
总结,递归要限制在我们的绿色区域内。
递归展开代码
void expend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win) { if (mine_count(mine,x,y) == 0) { show[x][y] = ' '; (*win)++; int i, j; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { if (x + i >= 1 && x + i <= ROW && y + j >= 1 && y + j <= COL && show[x+i][y+j]=='*') { expend(mine, show, x + i, y + j, win); } } } } else { //如果九宫格有雷就算数 show[x][y] = mine_count(mine, x, y) + '0'; } }
解释:首先还是没有雷才开始递归,再把我们选择的坐标赋值为space,再把(*win)++,说明你里胜利又进了一步,然后我们就用两个for循环开始递归, 当然递归的条件是在9x9棋盘内和该坐标没有被排查过。
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() { printf("********************\n"); printf("***** 1.play *****\n"); printf("***** 0.exit *****\n"); printf("********************\n"); } void game() { //定义二维数组 char mine[ROWS][COLS]; char show[ROWS][COLS]; //初始化二维数组 init_arr(mine, ROWS, COLS, '0'); init_arr(show, ROWS, COLS, '*'); //打印二维数组 display (show, ROW, COL); //埋雷 set_mine (mine, ROW, COL); //display(mine, ROW, COL); //排查雷 find_mine(mine, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请输入—>\n"); scanf("%d", &input); switch (input) { case 0: printf("退出游戏\n"); break; case 1: game(); break; default: printf("输入错误请重新输入\n"); break; } } while (input); return 0;
game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //初始化二维数组 void init_arr(char arr[ROWS][COLS], int rows, int cols, char ch) { int i = 0; int j = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { arr[i][j] = ch; } } } void display(char arr[ROWS][COLS], int row, int col) { printf("·················扫雷·················\n"); int i = 0; int j = 0; for (i = 0; i <= row; i++) { printf(" %d ", i); if (i > 0) printf(" "); } printf("\n"); printf("\n"); for (i = 1; i <= row; i++) { printf(" %d ", i); for (j = 1; j <= col; j++) { printf(" %c ", arr[i][j]); if (j <= col -1) printf("|"); } printf("\n"); if (i <= row - 1) { printf(" "); for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } printf("\n"); } } /*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 ", arr[i][j]); } printf("\n"); }*/ printf("·················扫雷··················\n"); } void set_mine(char mine[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count = EASY; while (count) { x = rand() % row + 1; y = rand() % col + 1; if (x > 0 && x <= row && y > 0 && y <= col && mine[x][y]!='1') { mine[x][y] = '1'; count--; } } } int mine_count(char mine[ROWS][COLS], int x, int y) { return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0'; } void expend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win) { if (mine_count(mine,x,y) == 0) { show[x][y] = ' '; (*win)++; int i, j; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { if (x + i >= 1 && x + i <= ROW && y + j >= 1 && y + j <= COL && show[x+i][y+j]=='*') { expend(mine, show, x + i, y + j, win); } } } } else {//如果九宫格有雷就算数 show[x][y] = mine_count(mine, x, y) + '0'; } //当一个整型数值与一个字符型的 ASCII 码值相加时, //会自动将整型数值转换为字符型例如, //如果 mine_count(mine, x, y) 的值为 3, //那么 mine_count(mine, x, y) + '0' 的结果就是字符型的 '3'。 } void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; int sign = EASY; int input = 0; printf("请选择排查(1)或者标记(2)O(∩_∩)O\n"); scanf("%d", &input); switch (input) { case 1: while (win < row * col - EASY) { printf("排查\n"); printf("请输入你要排查的坐标\n"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { //判断合法 if (mine[x][y] == '1') { //如果是雷 printf("很遗憾,艺术就是派大星\n"); display(mine, ROW, COL); break; } else //不是雷 { expend(mine, show, x, y, &win);//递归展开 /*show[x][y] = mine_count(mine, x, y) + '0';*/ display(show, ROW, COL); win++; } } else { printf("坐标不合法,请重新输入\n"); } } system("cls"); if (win == row * col - EASY) { printf("恭喜通关了\n"); display(show, ROW, COL); } break; case 2: if (sign != 0) { printf("标记\n"); printf("请输入你要标记的坐标\n"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { //判断合法 show[x][y] = '#'; sign--; display(show, ROW, COL); break; } else { printf("坐标不合法,请重新输入\n"); } } else { printf("标记已经用完了\n"); break; } } }
game.h
#pragma once #define ROW 9 #define COL 9 #define ROWS 9+2 #define COLS 9+2 #define EASY 10 #include<stdio.h> #include<stdlib.h> #include<time.h> #include<windows.h> //初始化 void init_arr(char arr[ROWS][COLS], int row, int col, char ch); //打印二维数组 void display(char mine[ROWS][COLS], int row, int col); //埋雷 void set_mine( char mine[ROWS][COLS], int row, int col); //排查雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
总结🍊
这篇博客我们系统的介绍了扫雷以及扫雷的TNT展开,我自己感觉我的代码还是有很多不足之处的,如果有兴趣的小伙伴可试一试。
最后如果这篇博客有帮助到你,欢迎点赞关注加收藏
如果本文有任何错误或者有疑点欢迎在评论区评论