一、实现思路
1、布置雷
定义二维字符数组mine,用以存放雷,以‘1’表示雷,‘0’表示无雷;
定义二维字符数组show,用以保存扫雷需要的信息;
2、排雷
以输入坐标的形式排雷,可能出现四种结果:
坐标非法;
踩到雷,游戏结束;
踩到处周边有雷,显示周边雷的数量;
踩到处周边无雷,将周边无雷的区域展开;
3、检查雷是否被排完
排完则游戏成功,否则游戏继续。
二、函数代码实现
1、“雷”数组的初始化以及打印“雷区”
初始化:
将数组中的元素全部初始化为set;
//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
arr[i][j] = set;
}
}
}
打印雷区:
利用分割线分开每一次排雷,打印行标与列标以便选择位置。
//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,
//因此不予打印,只打印中间9行。
void Display(char arr[ROWS][COLS], int row, int col)
{
printf("------- 扫雷 ------\n");
printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标
for (int i = 1; i < row + 1; i++)
{
printf("%d ", i);//打印行标
for (int j = 1; j < col + 1; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("------- 扫雷 ------\n");
}
2、布置雷与扫雷
布置雷
以count表示游戏难度,即布置雷的数量,通过传参保证函数脱离全局变量后依旧可用;
//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
while (count)
{
//mine为11×11大小,首行末行与首列末列均不布置雷
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
扫雷,根据出现的情况进行分类
在判断输赢时采用了循环检查雷是否排查完成,效率较低;
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
// B、未踩雷->a、周边有雷->显示雷的数量
// b、周边无雷->大面积展开
// C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0, y = 0;
while (1)
{
printf("请输入坐标:\n");
scanf("%d %d", &x, &y);
if (x < 1 || x > row || y < 1 || y > col)
{
printf("坐标非法,请重新输入:\n");
}
else if (mine[x][y] == '1')
{
printf("此处有雷,游戏结束\n");
Display(mine, row, col);
break;
}
else if (show[x][y] != '*')
{
printf("该位置已被扫过,请重新输入:\n");
}
else
{
//周边无雷
if (get_mine(mine, x, y) == 0)
{
unfold(mine, show, x, y);
Display(show, row, col);
}
//周边有雷
else
{
show[x][y] = get_mine(mine, x, y) + '0';
Display(show, row, col);
}
}
//判断是否完成
int win = 0;
for (int i = 1; i < row + 1; i++)
{
for (int j = 1; j < col + 1; j++)
{
if (show[i][j] == '*')
win++;
}
}
if (win == EASY)
{
printf("扫雷成功!\n");
Display(mine, row, col);
break;
}
}
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
递归展开无雷处
采用递归,依次判断周边的八个位置,达到展开效果;
void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)
return;
if (show[x][y] == '*')
{
int ret = get_mine(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
unfold(mine, show, x -1, y -1);
unfold(mine, show, x - 1, y);
unfold(mine, show, x - 1, y + 1);
unfold(mine, show, x, y - 1);
unfold(mine, show, x, y + 1);
unfold(mine, show, x + 1, y - 1);
unfold(mine, show, x + 1, y);
unfold(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = ret + '0';
}
}
}
获取周边雷的数量
检查周边8个位置的雷个数,利用两层嵌套循环检查;
//获取周边的雷的个数
int get_mine(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = -1; i < 2; i++)
{
for(int j = -1; j < 2; j++)
if (mine[x + i][y + j] == '1')
{
count++;
}
}
return count;
}
三、小tips
定义二维数组存放雷时,将数组定义大两个,9×9的棋盘采用11×11的数组,如此,即便是判断边缘格子时也可以采用一样的函数,不用担心越界;
定义游戏难度时,可将相关内容都放在头文件中,以#define定义的方式,方便以后修改;
定义函数时,可将只会在本.c文件中用到的函数前加上static,防止其他文件调用,同时也让在其他.c文件中定义函数时更为方便;
书写代码时,须首先严谨思考,考虑到各种结果,先思考再敲代码,会更有效率。
四、代码汇总
头文件game.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 9
#define COL 9#define ROWS 11
#define COLS 11//游戏难度
#define EASY 10//初始化数组,初始arr数组中的值为set
void Initarr(char arr[ROWS][COLS], int row, int col, char set);//打印“雷区”
void Display(char arr[ROWS][COLS], int row, int col);//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count);//扫雷
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);函数文件game.c#define _CRT_SECURE_NO_WARNINGS 1#include"game.h"
//具体函数的实现//选择菜单
void menu()
{printf("******************\n");printf("***** 1、开始 ****\n");printf("***** 0、退出 ****\n");printf("******************\n");
}//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){arr[i][j] = set;}}
}//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,因此不予打印,只打印中间9行
void Display(char arr[ROWS][COLS], int row, int col)
{printf("------- 扫雷 ------\n");printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标for (int i = 1; i < row + 1; i++){printf("%d ", i);//打印行标for (int j = 1; j < col + 1; j++){printf("%c ", arr[i][j]);}printf("\n");}printf("------- 扫雷 ------\n");
}//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{int x = 0;int y = 0;while (count){//mine为11×11大小,首行末行与首列末列均不布置雷x = rand() % row + 1;y = rand() % col + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count--;}}
}//获取周边的雷的个数
static int get_mine(char mine[ROWS][COLS], int x, int y)
{int count = 0;for (int i = -1; i < 2; i++){for(int j = -1; j < 2; j++)if (mine[x + i][y + j] == '1'){count++;}}return count;
}//展开函数
static void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)return;if (show[x][y] == '*'){int ret = get_mine(mine, x, y);if (ret == 0){show[x][y] = ' ';unfold(mine, show, x -1, y -1);unfold(mine, show, x - 1, y);unfold(mine, show, x - 1, y + 1);unfold(mine, show, x, y - 1);unfold(mine, show, x, y + 1);unfold(mine, show, x + 1, y - 1);unfold(mine, show, x + 1, y);unfold(mine, show, x + 1, y + 1);}else{show[x][y] = ret + '0';}}
}
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
// B、未踩雷->a、周边有雷->显示雷的数量
// b、周边无雷->大面积展开
// C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{int x = 0, y = 0;while (1){printf("请输入坐标:\n");scanf("%d %d", &x, &y);if (x < 1 || x > row || y < 1 || y > col){printf("坐标非法,请重新输入:\n");}else if (mine[x][y] == '1'){printf("此处有雷,游戏结束\n");Display(mine, row, col);break;}else if (show[x][y] != '*'){printf("该位置已被扫过,请重新输入:\n");}else{//周边无雷if (get_mine(mine, x, y) == 0){unfold(mine, show, x, y);Display(show, row, col);}//周边有雷else{show[x][y] = get_mine(mine, x, y) + '0';Display(show, row, col);}}//判断是否完成int win = 0;for (int i = 1; i < row + 1; i++){for (int j = 1; j < col + 1; j++){if (show[i][j] == '*')win++;}}if (win == EASY){printf("扫雷成功!\n");Display(mine, row, col);break;}}
}void game()
{// mine数组用以存放雷char mine[ROWS][COLS] = { 0 };// show数组用以存放扫出的信息char show[ROWS][COLS] = { 0 };//初始化mine数组为字符0,show数组为字符'*'Initarr(mine, ROWS, COLS, '0');//Display(mine, ROW, COL);//检测初始化效果Initarr(show, ROWS, COLS, '*');//Display(show, ROW, COL);//检测初始化效果Setmine(mine, ROW, COL, EASY);//Display(mine, ROW, COL);//查看雷的布置位置Display(show, ROW, COL);Sweep(mine, show, ROW, COL, EASY);
}
测试文件test.h
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int main()
{
int x = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &x);
switch (x)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("游戏退出\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (x);
}
最后,如果还想了解更多知识,请关注我,感谢啦。