如何一步步实现扫雷
整体思路
大概框架
棋盘的定义以及初始化
放雷环节
扫雷环节(Death or Survive)
完整代码
头文件
game.c源文件
测试源文件
C语言实现扫雷自由
整体思路
对于上面视频所示,我们该如何用C语言来实现呢?这跟之前写过的那个三子棋很相像,或者说两者的本质是相同的,都是在棋盘上下棋,只是有些方式不同罢了。那么,如何具体实现呢?
首先,还是和以往一样,我们需要一个.h的头文件来存放函数声明以及一些库函数头文件的包含。
其次,我们需要两个.c源文件,一个用来存放函数的定义,另一个用来作为测试。这两个环节是必备的,它会使我们的代码看起来条理更加清晰,可读性更加好。
然后,我们需要棋盘,一个进行下棋的棋盘,有了棋盘,我们还要考虑该如何随机放雷,以及放完雷后该怎么进行扫雷,还有游戏的输赢判定。
以上便是我们整体的思路。
大概框架
有了以上思路,便开始入手写代码了:
#include"game.h" //菜单栏 void menu() { printf("----------1、开始游戏-----------\n"); printf("----------2、操作说明-----------\n"); printf("----------0、退出游戏-----------\n"); } //操作说明 void explain() { printf("一共有%d个雷,请输入具体坐标来排除,望好运!\n",MINE); } void game() { //布置好的雷 //初始化0,放雷的时候1 char mine[ROWS][COLS] = { 0 }; //排查出的雷 //初始化为*,排查时用字符数字表示 char show[ROWS][COLS] = { 0 }; //初始化 init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 //dis_play(mine, ROW, COL); dis_play(show, ROW, COL); //放雷 set_mine(mine, ROW, COL); //dis_play(mine, ROW, COL); //排查雷 find_mine(show, mine, ROW, COL); dis_play(show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: break; case 2: explain(); break; default: printf("输入错误!请重新输入!\n"); break; } } while (input); return 0; }
这里为了避免我们把代码写死,不利于以后的修改,我是在头文件#define定义棋盘以及雷的数量的,这样我们日后修改棋盘大小以及雷的数量都比较方便。
有了上面的框架,我们就可以在.c文件里书写定义以及在.h文件里写函数说明了。
棋盘的定义以及初始化
我们都知道,扫雷是在一个33的小格子里进行排雷,如上图所示,这是一个99的棋盘,但假如我们利用二维数组创建一个99棋盘的话,对于边上溢出来的部分,我们不好处理,那我们就索性给它加上两行两列,凑成(9+2)(9+2)的棋盘,但是,我们只利用其中的9*9的棋盘部分,对于边上溢出来的两行两列,我们全都初始化为非雷区域。就不用管它了。
在这里,我们可以创建二维数组,一个用来进行初始化,放雷所用,另一个用来排雷所用,当然,我们最后打印棋盘的时候是要把放雷的那个棋盘给屏蔽掉的,不然就公开雷的位置了。首先,进行初始化两个棋盘:
void init_board(char board[ROWS][COLS], int rows, int cols,char ret) { int i = 0; int j = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { board[i][j] = ret; } } }
在这里,我们规定:0代表非雷,1代表雷。这样是为了方便我们后面进行统计所用。
初始化完后便开始打印棋盘:
//打印棋盘 void dis_play(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; int m = 0; for (m = 0; m <=col; m++) { printf("%d ", m); } printf("\n"); //printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
在这里,这两步是为了打印出棋盘的坐标,方便玩家根据坐标进行扫雷,需要注意的是,这个时候我们是打印出来我们的9*9的棋盘的:大家看一下传的参数:
在头文件#define定义常量里,ROW与COL分别对应9行9列:
这里打印出来就是如图所示:
一个9*9的棋盘,我们可以看到,所有数据全是0,接下来我们便开始放雷了。
放雷环节
void set_mine(char mine[ROWS][COLS], int row, int col) { int cont = MINE; while (cont) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; cont--; } } }
我们在mine棋盘进行放雷,这里用到了rand函数,以及时间戳
srand((unsigned int)time(NULL));
这是用来生成随机数所用,需要包含头文件#include<stdlib.h>以及#include<time.h>,
rand()%一个数再加上,表示的就是随机生成1-这个数。
这里我们屏蔽掉show棋盘,来看一下我们的mine棋盘:
可以看到,已经在棋盘里随机生成了15个1,即15个雷。
扫雷环节(Death or Survive)
接下来便是惊心动魄的扫雷环节了,在这里,往往伴随着死亡与生存。
//排查雷
int get_mine(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'); } void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<ROW*COL-MINE) { printf("请输入排查雷的坐标(行 列):>"); scanf("%d %d", &x,&y); system("cls"); if (show[x][y] == '*') { //踩中雷 if (mine[x][y] == '1') { printf("很遗憾,你被炸死了!\n"); dis_play(mine, ROW, COL); printf("上方为具体雷的位置,死个明白吧。\n"); printf("\n"); break; } //继续排雷 else { if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { int cont = get_mine(mine, x, y); show[x][y] = cont + '0'; dis_play(show, ROW, COL); win++; } else { printf("已出界\n"); dis_play(show, ROW, COL); } } } else { printf("该位置已被排查过!\n"); dis_play(show, ROW, COL); } } if (win == ROW * COL - MINE) { printf("NB!游戏通关!!!"); dis_play(mine, ROW, COL); } }
首先,我们是在show棋盘上对mine的99棋盘里的雷进行排除,所以这里传送的是4个参数
然后在show棋盘上进行扫雷,有一个前提,就是这个棋盘是空的,即还没有被扫,所以加了一个大的前提:if (show[x][y] == ''),否则就说明这里已经排查过了,
然后扫雷途中有可能会被炸死,也有可能继续扫下去,但是扫的前提是在棋盘内,总不能跑棋盘外面去扫,
继续扫雷的话,我们知道,它是在一个3*3的范围内统计附近的雷,就好象这样:
这里的1,就表示以它为中心,周围8个里面只有一个雷,同样,在我们的棋盘里,也需要这样进行统计,那么如何进行统计呢?
这就提到了前面为何要把0设置成非雷,把1设置成雷了,大家仔细想一下,假如我们要扫的地方显示出一个2,就代表周围一圈8个有两个雷,而我们这里规定,0代表非雷,1代表雷,把他们八个加起来,不就是2了吗,这个2就是我们所排查的地方!
所以,我们调用了一个get_mine(mine, x, y)函数,用来统计mine棋盘里有多少个雷,再把结果给cont,cont再从我们的show棋盘里显示出来:
int cont = get_mine(mine, x, y); show[x][y] = cont + '0'; dis_play(show, ROW, COL);
同样,扫雷是一个循环往复的过程,不是说一下子就停止,所以我们把这一整个全都用while进行循环
判定条件我们可以给一个变量win,来统计排查的个数,当win == 所有格子-雷的个数,即win == 所有非雷位置时,就代表我们已经排除完毕,否则一直循环,每排除一个,win++,直到所有排完。
最后我们可以在这里加一个system函数,清理屏幕,有一个完好的体验感。
完整代码
头文件
#pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #include<Windows.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define MINE 15 //初始化棋盘 void init_board(char board[ROWS][COLS], int rows,int cols,char ret); //打印棋盘 void dis_play(char board[ROWS][COLS], int row, int col); //放雷 void set_mine(char mine[ROWS][COLS], int row, int col); //排查雷 void find_mine(char show[ROWS][COLS],char mine[ROWS][COLS], int row,int col);
game.c源文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //棋盘初始化 void init_board(char board[ROWS][COLS], int rows, int cols,char ret) { int i = 0; int j = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { board[i][j] = ret; } } } //打印棋盘 void dis_play(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; int m = 0; for (m = 0; m <=col; m++) { printf("%d ", m); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } } //放置雷 void set_mine(char mine[ROWS][COLS], int row, int col) { int cont = MINE; while (cont) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; cont--; } } } //排查雷 int get_mine(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'); } void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<ROW*COL-MINE) { printf("请输入排查雷的坐标(行 列):>"); scanf("%d %d", &x,&y); system("cls"); if (show[x][y] == '*') { //踩中雷 if (mine[x][y] == '1') { printf("很遗憾,你被炸死了!\n"); dis_play(mine, ROW, COL); printf("上方为具体雷的位置,死个明白吧。\n"); printf("\n"); break; } //继续排雷 else { if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { int cont = get_mine(mine, x, y); show[x][y] = cont + '0'; dis_play(show, ROW, COL); win++; } else { printf("已出界\n"); dis_play(show, ROW, COL); } } } else { printf("该位置已被排查过!\n"); dis_play(show, ROW, COL); } } if (win == ROW * COL - MINE) { printf("NB!游戏通关!!!"); dis_play(mine, ROW, COL); } }
测试源文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //菜单栏 void menu() { printf("----------1、开始游戏-----------\n"); printf("----------2、操作说明-----------\n"); printf("----------0、退出游戏-----------\n"); } //操作说明 void explain() { printf("一共有%d个雷,请输入具体坐标来排除,望好运!\n",MINE); } void game() { //布置好的雷 //初始化0,放雷的时候1 char mine[ROWS][COLS] = { 0 }; //排查出的雷 //初始化为*,排查时用字符数字表示 char show[ROWS][COLS] = { 0 }; //初始化 init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 //dis_play(mine, ROW, COL); printf("\n"); dis_play(show, ROW, COL); //放雷 set_mine(mine, ROW, COL); //dis_play(mine, ROW, COL); // 排查雷 find_mine(show, mine, ROW, COL); dis_play(show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: break; case 2: explain(); break; default: printf("输入错误!请重新输入!\n"); break; } } while (input); return 0; }