目录
引言
扫雷的灵感来源于上世纪60年代的计算机编程游戏,其现代版本由微软的Robert Donner和Curt Johnson在1989年左右开发。从Windows 3.1开始,它和“纸牌”、“空当接龙”一起成为Windows系统的内置游戏,目的是为了让用户练习使用鼠标(尤其是左右键点击和双击),这极大地推广了它的普及。对于许多80后、90后来说,扫雷是在学校、办公室电脑上“摸鱼”的经典回忆,是一款真正的“时代记忆”游戏。至今仍有庞大的玩家社群和世界级的竞速比赛,顶尖玩家完成高级棋盘的时间通常在50秒以内,世界纪录更是达到了惊人的30秒级别。
1 游戏规则
一个棋盘上,有若干个雷,当我们点击非雷区时,会展开一片没有雷的区域,这片区域的边界会显示其周围8格雷的数量,我们的目标就是找到所有的雷或者展开所有无雷的区域。

2 代码实现
2.1 基础功能分析
(1)自定义棋盘大小。
(2)自定义雷的数量。
(3)程序能够自动初始化棋盘,并随机往棋盘中安排雷。
(4)玩家输入坐标显示周围8格雷的数量,踩到雷则游戏结束。
2.2 功能实现
通过分析可以看到我们的代码要实现不少功能,所以我们考虑将每一个功能模块分配给一个个函数来实现,并且为了逻辑清晰,我们可以将一些宏定义和函数在头文件game.h里进行申明,在game.c文件进行函数实现,test.c文件进行主程序实现。如下图:

那么理论可行,我们正式开始实践。
(1)自定义棋盘大小。
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。 假设我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数组来存放信息。
所以我们可以在game.h文件中将棋盘的长宽定义为常量,后续数组的创建我们都用定义的字母代替,这样我们更改棋盘大小就只用改头文件的数字就行了。
#define ROW 9//设置盘面长度
#define COL 9//设置盘面宽度
(2)自定义雷的数量。
同理,在game.h中将雷的数量定义为常量
#define minecount 10//设置雷数
(3)程序能够自动初始化棋盘,并随机往棋盘中安排雷。
在这个数组中,如果这个位置布置雷,我们就存放1,没有布置雷就存放0。
另外,假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数是1 假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数时,最下⾯的三 个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的9*9的坐 标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11 是⽐较合适。
#define ROWS ROW+2
#define COLS COL+2

这时候我们还要思考一个问题,程序排布雷的棋盘和展现给我们玩家的棋盘应该是分开的。我们玩家可见的棋盘一开始可以全部用*填充,表示未知,当我们选择坐标时,再显示坐标周围雷的数量。

这样我们就需要初始化两个棋盘。先在game.h中申明函数
void creatboard(char arr[ROWS][COLS], int rows, int cols, char m);
在game.c中实现函数
void creatboard(char arr[ROWS][COLS], int row, int col,char m) {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
arr[i][j] = m;
}
}
}
我们把初始化棋盘函数放在game()函数中调用。game.h中申明。
void game();
game.c中定义
void game()
{
char show[ROWS][COLS];
char mine[ROWS][COLS];
creatboard(mine, ROWS, COLS, '0');//放置布置雷的信息,先全部初始化为0
creatboard(show, ROWS, COLS, '*');//显示排查雷的信息,初始化为‘*’
}
初始化完成后,我们还需要定义一个函数来布置雷,为了实现随机性,我们要引用<stdlib.h>库,里面提供的rand()函数可以生成随机数。
void setmine(char arr[ROWS][COLS], int row, int col);
void setmine(char arr[ROWS][COLS], int row, int col) {
srand((unsigned int)time(NULL));//rand()实际上是伪随机,需要设置初始种子,
//我们将当前时间戳返回当作种子
int count = minecount;
while (count) {
int x = rand() % 9 + 1;//随机数生成范围限定在1到9
int y = rand() % 9 + 1;
if (arr[x][y] != '1') {
arr[x][y] = '1';
count--;
}
}
}
布置完雷后还需要一个函数将排雷信息输出给玩家。
void displayboard(char arr[ROWS][COLS],int row ,int col);
void displayboard(char arr[ROWS][COLS], int row, int col) {
printf("----------------分界线-----------------\n");
for (int i = 0; i <= col; i++) printf("%d ", i);
printf("\n");
for (int i = 1; i <= row; i++) {
printf("%d ", i );
for (int j = 1; j <= col; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
这些都定义完后,game()函数就变成了这样。
void game()
{
char show[ROWS][COLS];
char mine[ROWS][COLS];
creatboard(mine, ROWS, COLS, '0');
creatboard(show, ROWS, COLS, '*');
displayboard(show, ROW, COL);
setmine(mine, ROW, COL);
}
(4)玩家输入坐标显示周围8格雷的数量,踩到雷则游戏结束。
game.h中申明函数
void findmine(char sh[ROWS][COLS],char mi[ROWS][COLS], int row, int col);
int countmine(char mi[ROWS][COLS], int x, int y);
game.c中实现函数
void findmine(char sh[ROWS][COLS], char mi[ROWS][COLS], int row, int col) {
int x, y;
int count = ROW * COL - minecount;//无雷区域个数,全部排查完后游戏结束。
while (count) {
printf("请输入坐标-->\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW&& y >= 1 && y <= COL) {
if (mi[x][y] != '1') {
sh[x][y] = countmine(mi, x, y) + '0';
count--;
displayboard(sh, ROW, COL);
}
else { displayboard(mi,ROW,COL);
printf("很遗憾,你踩到雷了,是否重新开始\n");
break;
}
}
else printf("请重新输入");
if (count == 0) {
displayboard(mi, ROW, COL);
printf("恭喜你,排雷成功,是否再玩一次\n");
}
}
}
int countmine(char mi[ROWS][COLS], int x, int y) {//实现统计周围8格雷的数量
int count=0;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y+1; j++) {
count += mi[i][j] - '0';
}
}
return count;
}
现在的game()函数就是这样。
void game()
{
char show[ROWS][COLS];
char mine[ROWS][COLS];
creatboard(mine, ROWS, COLS, '0');
creatboard(show, ROWS, COLS, '*');
displayboard(show, ROW, COL);
setmine(mine, ROW, COL);
//isplayboard(mine, ROW, COL);测试用,最终程序中要注释掉
findmine(show, mine, ROW, COL);
}
现在各种功能完成了!
我们再把test()中的主函数一写
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
int main() {
int enter = 0;
do {
menu();
scanf("%d", &enter);
switch (enter) {
case 1: {
game();
break;
}
case 0: {
printf("游戏结束\n");
break;
}
default:printf("请输入0或1\n");
}
} while (enter);
return 0;
}
2.3 运行效果


3 附源码
game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9//设置盘面长度
#define COL 9//设置盘面宽度
#define ROWS ROW+2
#define COLS COL+2
#define minecount 10//设置雷数
void menu();
void game();
void creatboard(char arr[ROWS][COLS], int rows, int cols, char m);
void displayboard(char arr[ROWS][COLS],int row ,int col);
void setmine(char arr[ROWS][COLS], int row, int col);
void findmine(char sh[ROWS][COLS],char mi[ROWS][COLS], int row, int col);
int countmine(char mi[ROWS][COLS], int x, int y);
game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu() {
printf("***********************\n");
printf("******* 1--play *******\n");
printf("******* 0--exit *******\n");
printf("***********************\n");
printf("请选择-->\n");
}
void creatboard(char arr[ROWS][COLS], int row, int col,char m) {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
arr[i][j] = m;
}
}
}
void displayboard(char arr[ROWS][COLS], int row, int col) {
printf("----------------分界线-----------------\n");
for (int i = 0; i <= col; i++) printf("%d ", i);
printf("\n");
for (int i = 1; i <= row; i++) {
printf("%d ", i );
for (int j = 1; j <= col; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void setmine(char arr[ROWS][COLS], int row, int col) {
srand((unsigned int)time(NULL));
int count = minecount;
while (count) {
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (arr[x][y] != '1') {
arr[x][y] = '1';
count--;
}
}
}
void findmine(char sh[ROWS][COLS], char mi[ROWS][COLS], int row, int col) {
int x, y;
int count = ROW * COL - minecount;
while (count) {
printf("请输入坐标-->\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW&& y >= 1 && y <= COL) {
if (mi[x][y] != '1') {
sh[x][y] = countmine(mi, x, y) + '0';
count--;
displayboard(sh, ROW, COL);
}
else { displayboard(mi,ROW,COL);
printf("很遗憾,你踩到雷了,是否重新开始\n");
break;
}
}
else printf("请重新输入");
if (count == 0) {
displayboard(mi, ROW, COL);
printf("恭喜你,排雷成功,是否再玩一次\n");
}
}
}
int countmine(char mi[ROWS][COLS], int x, int y) {
int count=0;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y+1; j++) {
count += mi[i][j] - '0';
}
}
return count;
}
void game()
{
char show[ROWS][COLS];
char mine[ROWS][COLS];
creatboard(mine, ROWS, COLS, '0');
creatboard(show, ROWS, COLS, '*');
displayboard(show, ROW, COL);
setmine(mine, ROW, COL);
/*displayboard(mine, ROW, COL);*/
findmine(show, mine, ROW, COL);
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
int main() {
int enter = 0;
do {
menu();
scanf("%d", &enter);
switch (enter) {
case 1: {
game();
break;
}
case 0: {
printf("游戏结束\n");
break;
}
default:printf("请输入0或1\n");
}
} while (enter);
return 0;
}

1103

被折叠的 条评论
为什么被折叠?



