理解扫雷原理
9*9 棋盘
上面布置10个雷
排查雷
1.如果这个位置不是雷,就计算这个位置的周围8个坐标有几个雷,并显示雷的个数
2.如果这个位置是雷,就炸死了,游戏结束了
3.如果把不是雷的位置都找出来了,那游戏也结束
梳理扫雷过程
9*9 棋盘
初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set) { int i = 0; int j = 0; for ( i = 0; i < rows; i++) { for ( j = 0; j < cols; j++) { board[i][j] = set; } } }
通过初始化函数
初始化mine这个棋盘来安置随机的地雷的位置(先放入‘0’//后面随机放入‘1’标记地雷的位置)
初始化show这个棋盘来显示扫雷游戏的过程图(先放入‘*’//后面根据坐标逐渐揭开以及周围地雷情况的数字)
char mine[11][11] char show[11][11]
//初始化棋盘 InitBoard(mine, ROWS, COLS,'0'); InitBoard(show, ROWS, COLS, '*');
显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("----------扫雷---------\n"); for ( i = 0; i <= col; 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"); }
如图所示 通过打印行数和列数方便定位
通过----------扫雷---------来划分每个图
布置雷
要随机布置10个雷
利用rand 和 srand 函数来生成随机数去随机生成地雷的坐标
注:
rand函数是C/C++中的随机数生成函数,用于生成一个范围在0到RAND_MAX之间的随机整数。它的原型为:
int rand();
srand函数用于设置rand函数的随机数种子,以便每次生成的随机数序列不同。它的原型为:
void srand(unsigned int seed);
在使用rand函数之前,通常会先使用srand函数设置一个种子,可以使用时间戳等随机值作为种子来保证每次运行程序时生成不同的随机数序列。
rand函数生成的随机数并不是真正的随机数,而是伪随机数,它是根据某种算法生成的,每次程序运行时生成的随机数序列是相同的。因此,为了获取更好的随机性,需要使用srand函数设置不同的种子值。
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] == '0') { mine[x][y] = '1'; count--; } } }
srand一般在main函数里写
srand((unsigned int)time(NULL));
排查雷
统计x,y坐标周围有几个雷
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'); }
如图所示 判断周围8个位置的地雷情况
使用递归函数来实现周围没地雷时展开多个
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值) if (count == 0) { show[x][y] = ' '; int i = 0, j = 0; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { //连续排除时限制范围在棋盘范围内 if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*') { spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除 } } } } else { show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示 } }
如果周围的地雷数是0 直接将这个位置改为空格 ——逐渐化简图像
递归实现周围如果都没地雷连续排除
判断成功排除后剩下的方格数是否等于地雷数
int IsWin(char show[ROWS][COLS], int row, int col) { int num = 0; //排除一个地雷时便进行累加 for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (show[i][j] == '*') num++; } } return num; }
当最后就剩下10个‘ * ’的时候就成功了
排查函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; 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); break; } else { spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格 DisplayBoard(show, ROW, COL); win++; } } else { printf("坐标非法,重新输入\n"); } int ret = IsWin(show, row, col); if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除 { printf("恭喜你,通关成功!\n"); DisplayBoard(mine, ROW, COL); break; } } }
梳理编写代码思路
头文件game.h
库函数所需要头文件
#include <stdio.h> #include <stdlib.h> #include <time.h>
stdio.h
rand(头文件:stdlib.h)
time(头文件:time.h)
需要rand(头文件:stdlib.h)和time(头文件:time.h),结合使用可以使电脑随机布置雷
宏定义
扫雷标准是9行9列,但是由于考虑到靠边的坐标计算周围的地雷数时,还需要不包括越界的地方
所以将两个数组都扩大一圈,就不会有这方面的困扰了,即变为了11行11列的二维数组
#pragma once #define COL 9 #define ROW 9 #define ROWS ROW + 2 #define COLS COL + 2 #define EASY_COUNT 10
使用宏定义的方式分别定义ROW9,COL9,ROWS11,COLS11,以及设置的地雷数EASY_COUNT10
注:需要在定义之前加#pragma once
#pragma once是C++中的预处理指令,用于确保头文件只被编译一次。当多个源文件包含同一个头文件时,如果没有#pragma once指令,会导致头文件被重复包含,从而引发编译错误。使用#pragma once可以避免这种情况的发生,提高编译效率。
函数声明
//初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //显示棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int colt); //布置雷 void SetMine(char mine[ROWS][COLS], int row, int col); //排查雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //统计x,y坐标周围有几个雷 int GetMineCount(char mine[ROWS][COLS], int x, int y); //判断成功排除后剩下的方格数是否等于地雷数 int IsWin(char show[ROWS][COLS], int row, int col); //使用递归函数来实现周围没地雷时展开多个 void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
源文件game.c
包含所有有关的游戏的函数
具体代码见下
源代码 test.c
菜单
void menu() { printf("*****************************\n"); printf("********** 1.PLAY **********\n"); printf("********** 2.EXIT **********\n"); printf("*****************************\n"); }
丰富游戏细节
游戏
void game() { char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 InitBoard(mine, ROWS, COLS,'0'); InitBoard(show, ROWS, COLS, '*'); //显示show棋盘 DisplayBoard(mine, ROW, COL); //布置雷 //显示mine棋盘 SetMine(mine, ROW, COL); DisplayBoard(show, ROW, COL); //排查雷 FindMine(mine, show, ROW, COL); }
相当于将实现游戏的代码单独列出来
main函数
int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); default: printf("选择错误,重新选择\n"); break; } } while (1); return 0; }
写循环来实现在游戏结束后,继续打印选择的菜单,由玩家决定是否继续进行游戏
完整代码
game.h
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <time.h> #pragma once #define COL 9 #define ROW 9 #define ROWS ROW + 2 #define COLS COL + 2 #define EASY_COUNT 10 //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //显示棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int colt); //布置雷 void SetMine(char mine[ROWS][COLS], int row, int col); //排查雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //统计x,y坐标周围有几个雷 int GetMineCount(char mine[ROWS][COLS], int x, int y); //判断成功排除后剩下的方格数是否等于地雷数 int IsWin(char show[ROWS][COLS], int row, int col); //使用递归函数来实现周围没地雷时展开多个 void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
game.c
#include "game.h" #define _CRT_SECURE_NO_WARNINGS 1 void InitBoard(char board[ROWS][COLS], int rows, int cols,char set) { int i = 0; int j = 0; for ( i = 0; i < rows; i++) { for ( j = 0; j < cols; j++) { board[i][j] = set; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("----------扫雷---------\n"); for ( i = 0; i <= col; 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] == '0') { mine[x][y] = '1'; count--; } } } 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'); } int IsWin(char show[ROWS][COLS], int row, int col) { int num = 0; //排除一个地雷时便进行累加 for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (show[i][j] == '*') num++; } } return num; } void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值) if (count == 0) { show[x][y] = ' '; int i = 0, j = 0; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { //连续排除时限制范围在棋盘范围内 if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*') { spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除 } } } } else { show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示 } } void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; 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); break; } else { spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格 DisplayBoard(show, ROW, COL); win++; } } else { printf("坐标非法,重新输入\n"); } int ret = IsWin(show, row, col); if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除 { printf("恭喜你,通关成功!\n"); DisplayBoard(mine, ROW, COL); break; } } }
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void menu() { printf("*****************************\n"); printf("********** 1.PLAY **********\n"); printf("********** 2.EXIT **********\n"); printf("*****************************\n"); } void game() { char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 InitBoard(mine, ROWS, COLS,'0'); InitBoard(show, ROWS, COLS, '*'); //显示show棋盘 DisplayBoard(mine, ROW, COL); //布置雷 //显示mine棋盘 SetMine(mine, ROW, COL); DisplayBoard(show, ROW, COL); //排查雷 FindMine(mine, show, ROW, COL); } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); default: printf("选择错误,重新选择\n"); break; } } while (1); return 0; }
运行结果
部分运行截图