前言:
扫雷,在那个联网困难的年代,是我们电脑课消遣必玩游戏之一,今天就让我们来自己写一个扫雷小游戏,玩耍一下吧。有了三子棋的经验,我想写扫雷应该会容易一些,在完成扫雷代码之前我们分析一下,扫雷的实现都需要那些功能,第一,我们需要有个雷场提供我们布置雷;第二我们布置完雷之后,当有玩家进行排雷的时候,选定一个坐标进行排雷的话,如果是雷的话,游戏结束,如果不是雷的话,需要以选定坐标为中心,将周围雷的个数显示出来,以便给玩家提供线索,进一步排雷。为了方便,我们需要有个跟布置雷场坐标一一对应的雷盘用来显示雷场。为了防止在计算雷个数的时候数组越界,定义显示雷场的数组应该比布置雷场多出一圈。如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/d3a38912c5d7b17fba3e30f7f5b87b4f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c9eaf812d83cfdc442d19c2b16add08.png)
![显示雷场](https://i-blog.csdnimg.cn/blog_migrate/afe5c2c69a3083f75983d5a561202943.png)
目录
一、程序框架
我们像写三子棋一样,将扫雷的程序也分为三个文件编写,分别为test.c用来调用实现不同功能的子程序,game.c用来实现各个子程序的模块,game.h用来放置各个子程序的的声明和定义,让扫雷的代码看起来逻辑清晰,跟我们小时候玩的任何游戏一样 首先进入一个供我们选择的界面,我们选择是否进入游戏,不同的选择对应不同的结果,所以我们要用到case switch结构,游戏不可能只玩一次 所以我们用到循环结构,因为首先进入选择界面 所以我们使用do while 结构 代码如下:
void menu()
{
printf("*********************************************\n");
printf("*************** 1.play **********************\n");
printf("*************** 0.play **********************\n");
printf("*********************************************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请玩家输入->");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("欢迎下次来玩\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
二、游戏函数
初始化函数
当我们选择进入游戏之后,首先我们应该看到雷盘,所以第一步我们要定义雷场并初始化,布置雷场和显示雷场我们使用的是二维数组,二维数组的大小,根据我们个人喜好来定,为了方便修改,我们在头文件中预处理,设置一下雷盘大小,为了方便观看,我们将布置雷盘初始化为 0 将显示雷场初始化为*具体代码如下:
声明和定义
我们前面提到,我们要布置雷初步设定是8个雷,为了方便后期修改 我们 预处理一下,用COUNT代替,而且因为是为了方便统计地雷个数,我们将雷盘大小整体扩大一圈,但是布置雷的的范围只可以在ROW 和COL这个范围内,所以我们定义了两个方便修改的常量,如下面代码所示:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define COUNT 8
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//初始化雷场景
void init_board(char board[ROWS][COLS], char rows,char cols,char set);
实现及调用
为了方便,我们封装了初始化函数,因为不同雷盘初始化样子不一样,所以我们多传了一个参数,简便代码,具体如下:
//初始化雷场景函数
void init_board(char board[ROWS][COLS], char rows, char cols,char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
char mine[ROWS][COLS];//定义一个布置雷的雷场
char show[ROWS][COLS];//定义一个显示雷的雷场
//进行初始化雷场
// 将布置的雷场全部初始化为字符0
// 将显示的雷场景全部初始化为字符*
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
布置雷函数
我们需要将八个雷放置在任意的地方,布置一个减少一个,所以我们用while循环函数,在二维坐标里就是在不越界的情况下,随意生成坐标,所以我们会用到时间戳函数,保证在雷盘内,我们就模一下横、纵坐标之后 加一,另外,在我们布置雷的的时候,需要判断一下,当前坐标有没有已经布置过雷,没有布置的情况下,我们才可以布置,具体代码如下:
函数声明及调用
//开始布置雷
void set_board(char board[ROWS][COLS], char row, char col);
//布置雷
set_board(mine, ROW, COL);
int main()
{
srand((unsigned int)time(NULL));//整一个时间戳
test();
函数的实现
// 布置雷函数
void set_board(char board[ROWS][COLS], char row, char col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
显示函数
我们也需要集成一个显示函数,方便我们布置完雷或者排完雷后,显示到界面上,为了显示时候清楚地看到坐标对应的行列方便,我们先利用for循环,显示一行的坐标序号,然后换行后,在利用两层for循环将每个坐标显示出来,为了方便,每行显示之前,加个列坐标序号,具体实现代码如下:
函数声明及调用
//显示雷场景
void display_board(char board[ROWS][COLS], char row, char col);
display_board(mine, ROW, COL);
display_board(show, ROW, COL);
函数的实现
// 显示雷场景函数
void display_board(char board[ROWS][COLS], char row, char col)
{
printf("************扫雷游戏******************\n");
for (int j = 0; j <= col; j++)
{
printf("%d ",j);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i );
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
初始化完成后,我们将,雷盘打印出来看一下
排雷函数
我们的目的是把雷场上除了地雷的位置都给挖掘出来,当我们给定一个坐标之后,跟雷场坐标进行比较,如果是雷,那游戏结束,如果不是雷,我们还需要再进一步判断这个坐标有没有被排除过,如果没有,那我们就可以进行周围雷个数的计数了,只有进入计数这一步,雷盘上的总坐标才会减少1,当 厂上的坐标个数和雷的个数相同时候那么我们就赢了。具体实现代码如下:
函数声明及调用
//开始排雷
void get_board(char mine[ROWS][COLS], char show[ROWS][COLS], char row, char col);
//排雷
get_board(mine,show, ROW, COL);
函数的实现
需要提的一点是,坐标上显示的是数字字符,所以我们在统计周围雷个数的时候,可以将周围所有的字符数加起来 减去 8 乘以‘0’,这样函数返回的就是数字个数,在加上字符零就又可以显示数字字符例如:
排雷函数
// 排雷函数
void get_board(char mine[ROWS][COLS], char show[ROWS][COLS], char row, char col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - COUNT)
{
printf("请输入你要排雷的位置->");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (show[x][y] != '*')
{
printf("改坐标已经被排查\n");
continue;
}
if (mine[x][y] == '1')
{
printf("不好意思,你被炸死了\n");
display_board(mine, ROW, COL);
break;
}
else
{
int ret = count_mine(mine,x,y);
show[x][y] = ret + '0';
display_board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
if (win == row * col - COUNT)
{
printf("恭喜你,通关\n");
display_board(mine, ROW, COL);
}
}
}
统计雷数
//统计这个点周围有几个雷,并显示出来
int count_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');
}
三、整体代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
// ***********************扫雷游戏******************************//
void menu()
{
printf("*********************************************\n");
printf("*************** 1.play **********************\n");
printf("*************** 0.play **********************\n");
printf("*********************************************\n");
}
void game()
{
char mine[ROWS][COLS];//定义一个布置雷的雷场
char show[ROWS][COLS];//定义一个显示雷的雷场
//进行初始化雷场
// 将布置的雷场全部初始化为字符0
// 将显示的雷场景全部初始化为字符*
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
//将初始化好场景显示出来
//display_board(mine, ROW, COL);
//display_board(show, ROW, COL);
//布置雷
set_board(mine, ROW, COL);
display_board(mine, ROW, COL);
//排雷
get_board(mine,show, ROW, COL);
}
void test()
{
int input = 0;
do
{
menu();
printf("请玩家输入->");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("欢迎下次来玩\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));//整一个时间戳
test();
return 0;
}
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化雷场景函数
void init_board(char board[ROWS][COLS], char rows, char cols,char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
// 显示雷场景函数
void display_board(char board[ROWS][COLS], char row, char col)
{
printf("************扫雷游戏******************\n");
for (int j = 0; j <= col; j++)
{
printf("%d ",j);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i );
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
// 布置雷函数
void set_board(char board[ROWS][COLS], char row, char col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
//统计这个点周围有几个雷,并显示出来
int count_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 get_board(char mine[ROWS][COLS], char show[ROWS][COLS], char row, char col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - COUNT)
{
printf("请输入你要排雷的位置->");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (show[x][y] != '*')
{
printf("改坐标已经被排查\n");
continue;
}
if (mine[x][y] == '1')
{
printf("不好意思,你被炸死了\n");
display_board(mine, ROW, COL);
break;
}
else
{
int ret = count_mine(mine,x,y);
show[x][y] = ret + '0';
display_board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
if (win == row * col - COUNT)
{
printf("恭喜你,通关\n");
display_board(mine, ROW, COL);
}
}
}
game.h
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define COUNT 8
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//初始化雷场景
void init_board(char board[ROWS][COLS], char rows,char cols,char set);
//显示雷场景
void display_board(char board[ROWS][COLS], char row, char col);
//开始布置雷
void set_board(char board[ROWS][COLS], char row, char col);
//开始排雷
void get_board(char mine[ROWS][COLS], char show[ROWS][COLS], char row, char col);
四、效果演示