本次撰写代码使用多文件,test.h包含头文件,main.c包含主函数和菜单,test.c包含所有自定义函数。若有需要代码的直接将后续代码粘贴复制到对应文件中即可。
提及扫雷,相信大多数人都不陌生,其规则就是字面意思,将所有雷区排查出来(展开所有非雷区域)。那么在大家玩扫雷游戏中其实暗含了,两个规则,一:点开非雷区域,可得到附近8个区域的雷区个数;二:若附近8个区域中都没有雷,则会自动展开,直至有个数的区域。那么接下来我们来分析一下,这两个难点。
其实探索附近区域共有一下三种情况:
如果我们按三种情况分别去编写代码是不是就会非常麻烦?为了简便,我们可以给外侧再加一层,即给二维数组行列分别加二,并且把外层全部设置为非雷区域,就可以解决这一问题。
探索八区解决了探索附近八个区域的雷区个数,八区中有雷,则显示雷的个数。若没有雷(即雷的个数为0),则需要展开。
#ifndef _GAME_H_
#define _GAME_H_
#include
#include
#include
#define ROW 12//设置10*10的区域,则需要行列各增加两层,以达到外部扩充一层。
#define COL 12
#define MINENUM 20//布雷个数为20
void setmine(char mineboard[][COL]);
void show(char board[][COL]);
int minecount(char mineboard[][COL], int x, int y);
void game();
void open(char mineboard[][COL], char sweepboard[][COL], int x, int y);
int judge(char sweepboard[][COL]);
#endif
#include "test.h"
void meau()
{
printf("##########################################\n");
printf("### 欢迎来到扫雷世界 ###\n");
printf("##########################################\n");
printf("### 1.play 2.exit ###\n");
printf("##########################################\n");
printf("请选择:");
}
int main()
{
int quit = 1;//做标记
while (quit){
int k = 0;
meau();//展示菜单
scanf("%d", &k);//玩家输入选择
switch (k){
case 1://如果是1,开始游戏
game();
break;
case 2://如果是2,退出游戏
quit = 0;
printf("客观再来玩哦~\n");
break;
default:
printf("输入有误,请重新输入:\n");
break;
}
}
}
test.c复制的时候不要忘了 #include “test.h”
void game()
{
char sweepboard[ROW][COL];//第一个二维数组,扫描棋盘,用于给用户展示。为方便计算
// 定义数组为12 * 12,实际用到的是10 * 10的棋盘,后续棋盘均指10 * 10棋盘。
char mineboard[ROW][COL];//第二个二维数组,埋雷棋盘,用于埋雷。
memset(sweepboard, '*',sizeof(sweepboard));//扫描棋盘初始化均为‘*’
memset(mineboard, '0', sizeof(mineboard));//埋雷棋盘初始化均为‘0’
setmine(mineboard);//埋雷
while (1){
system("cls");//清屏
show(sweepboard);//展示棋盘
printf("请输入坐标:");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
if (x < 1 || x > 10 || y < 1 || y > 10){
printf("输入坐标有误,请重新输入!\n");
Sleep(1000);
continue;
}
if (mineboard[x][y] == '1'){
printf("你被雷炸死了!\n");
show(mineboard);
break;
}
if (sweepboard[x][y] != '*'){
printf("重复输入,请重新输入:\n");
}
open(mineboard, sweepboard, x, y);
if (judge(sweepboard) == 1){
printf("欧呦~你咋就赢了呢,太厉害了\n");
break;
}
}
}
setmine函数用来布置雷区,这里有一个注意点,不要忘了我们只给内层布置雷区,所以范围是ROW和COL分别减2。
void setmine(char mineboard[][COL])
{
int num = MINENUM;
srand((unsigned long)time(NULL));
while (num){
int x = rand() % (ROW - 2) + 1;
int y = rand() % (COL - 2) + 1;
if (mineboard[x][y] != '1'){
mineboard[x][y] = '1';
num--;
}
}
}
show函数用来展示当前游戏面板,我们在编写代码的时候也可以按自己构思的来,美观即可。
void show(char board[][COL])
{
int i = 1;
int j = 0;
printf(" ");
for (; i < 11; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i < 11; i++) {
printf(" ----------------------------------------\n");
printf("%2d|", i);//输出行标。
for (j = 1; j < 11; j++) {
printf(" %c |", board[i][j]);
}
printf("\n");
}
printf(" ----------------------------------------\n");
}
minecount函数即为探索八区。
int minecount(char mineboard[][COL], int x, int y)
{
int num = mineboard[x - 1][y - 1] + mineboard[x - 1][y] + mineboard[x - 1][y + 1] + \
mineboard[x][y - 1] + mineboard[x][y + 1] + mineboard[x + 1][y - 1] + \
mineboard[x + 1][y] + mineboard[x + 1][y + 1] - 7 * '0';
//棋盘是字符型的,把该坐标周围8个坐标里的字符加起来
//再减去8 * '0',计算时用的是acsii值,所得结果就是雷的个数。
return num;
}
open函数实现展开。
void open(char mineboard[][COL], char sweepboard[][COL], int x, int y)//open函数,判断是否需要递归式展开。
{
if (minecount(mineboard, x, y) == '0'){
//判断周围雷的个数,若为0,则需要展开
sweepboard[x][y] = ' ';
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++){
for (j = y - 1; j <= y + 1; j++){
if (sweepboard[i][j] == '*' && i > 0 && i < 11 && j > 0 && j < 11){
//如果该位置未被扫过且在棋盘范围内则继续递归调用open函数
open(mineboard, sweepboard, i, j);
}
}
}
}
else{
sweepboard[x][y] = minecount(mineboard, x, y);
//不需要展开则显示附近雷的个数
}
}
判断玩家是否扫雷成功
int judge(char sweepboard[][COL])//判断玩家是否扫雷成功
{
int i;
int j;
int count = 0;
for (i = 1; i <= ROW - 2; i++){
for (j = 1; j <= COL - 2; j++){
if (sweepboard[i][j] == '*'){
count++;
}
if (count > MINENUM){
return 0;
}
}
}
return 1;
}
在这里给大家补充一下memset
其函数原型是:void *memset(void *s,int ch,size_t n);
函数功能是:将是所指向的某一块内存中的前n个字节的内容全部设置为ch指定的ASCII值,第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,它是对较大的结构体或数组进行清零操作的一种最快方法。
头文件是:
不论我们写什么代码,我个人认为都应该先认真思考,再着手操作,这样会使我们编写代码更顺畅,思路更清晰。这次扫雷的代码编写和我的上一篇博客三子棋有很多相似之处,例如从菜单到主函数编写,或者说很多编写的思路有相似之处,如果现在看我博客的你,对三子棋也感兴趣,欢迎来看!创作不易,希望大家给我个赞!谢谢您嘞~