一:扫雷游戏的简介
扫雷游戏起源于1973年的”方块"游戏。1992年微软发布的Windows 3.1中加入该游戏,从此风靡全世界。玩扫雷游戏,可以锻炼观察和推理能力,培养细心和耐心。
游戏目标是在最短时间根据点击各自出现的数字找出所有非雷格子,并且避免踩到雷,否则就满盘皆输。
二:扫雷游戏的分析
因为一个游戏往往需要多层次代码来描述,书写。即须将纯粹实现游戏的代码放入一个或多个函数。将游戏的各个功能封装成一个个函数,分层次来写代码。
第一:每个游戏都应该需要一个菜单,可以通过菜单给出选项供我们选择,例:输入 1 就进入游戏,输入 0 就退出游戏,等。
第二:首先这里的 进入游戏 指是进入一个游戏函数代码框架,在这一游戏函数框架里书写实现一个或多个游戏功能的函数。
第三:因为扫雷的大致框架就是一个 9*9 的二维数组,当点击一个格子后,若此坐标不是雷,则就出现此坐标周围的雷的个数,例:
第四:我们需要给该二维数组给出10(可改变)个雷。
第五:给出雷后就得进行游戏了,通过输入一个个坐标来判断周围雷的个数完成游戏。
第六:若我们要实现这个游戏,为了描述这一游戏,重点就是必须得需要两个 9*9 的二维数组,一个数组主要作用是展现给玩家的面板,另一个数组主要作用是对某一坐标周围的雷进行计数,并将计算出的雷的个数返回给第一个二维数组的某坐标位置。这一过程就是点击格子返回数字的过程。
三:扫雷游戏的实现思路讲解
首先第一步就必须要意识到要将游戏代码分为3步,至于为什么,我以前写的博文已经描述很清楚了,大家可以去看一下。
三步的实现:
1. game.h
2. game.c
3. test.c
2.1.扫雷游戏的菜单
void menu()
{
printf("*************************************************\n");
printf("************* 1.play 0.exit *******************\n");
printf("*************************************************\n");
}
在打印出菜单(1.play 0.exit)之后,该程序就会让我们输入一个数字(1/0),若玩家输入1,则就通过 switch 语句进行选择(game函数:实现游戏的具体过程步骤),若玩家输入 0,则就退出游戏。若输入其他数字,则提示用户“输入错误,请重新输入”,然后通过 do..while 循环再次让用户进行输入。并且还通过 do...while 循环进行多次的玩扫雷游戏,直到玩家不想再玩后输入 0 结束菜单。
void menu()
{
printf("*************************************************\n");
printf("************* 1.play 0.exit *******************\n");
printf("*************************************************\n");
}
int main()
{
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);
return 0;
}
2.2.对扫雷游戏的扫雷盘进行初始化
初始化之前我们先看一下扫雷盘一些特殊的坐标:
因为这些特殊的坐标,当点击这些格子时,因为要计算周围的雷的个数,遍历这一格子周围的格子,则可能会遇到数组越界,那该怎样做呢?我们就干脆建立一个 11*11 的二维数组,为了方便以后管理数组代码,我们这样定义:
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
再用一个 InitBoard 的函数对两个11*11 的二维数组扫雷盘进行初始化:
// 初始化棋盘
void InitBoard(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;
}
}
}
其中 ret 为任意字符,这就给了程序员更大的发挥空间,不过在这里我们将两个二维数组分别初始化为 '0' 和 ' * ' :
char mine[ROWS][COLS] = {0};
char show[ROWS][ROWS] = {0};
2.3.对扫雷盘进行打印
之后我们对扫雷盘进行打印,使用 for 循环进行期盼的打印操作,当然这一功能也得需要一个函数(DisplayBoard函数)对这一功能进行封装:
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
printf(" ");
for (i = 1; i <= row; i++)
{
if (i == row)
printf("-");
else
printf("--");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d|", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
对两个二维数组打印结果:
2.4.设置雷的位置(随机的)
因为需要我们玩家进行扫雷,在此之前我们并不知道雷的位置在哪里,所以设置雷的时候应该是随机不确定的,这时就得使用到 rand 函数生成随机数字来实现这个操作,使用 setBoard函数来实现这一功能。
// 设置雷-- 10个雷
void SetBoard(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int i = 10;
while (i)
{
x = rand() % 9 + 1;
y = rand() % 9 + 1;
if (board[x][y] == '0')
{
board[x][y] = '1'; // *重点,方便之后计算周围雷的数目*
i--;
}
}
}
注意:在使用 rand 函数之前,就得使用到 sand 及时间戳,并且包含两个库函数(<stdlib.h> and <time.h>)此时主函数就得改为:
#include<time.h>
#include<stdlib.h>
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入:> ");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
打印结果实例:
2.5.玩家操作,进行扫雷游戏
同样的,我们也可以自定义一个函数(SearchBoard函数)来进行排雷操作,当然若是点击到雷的话,则游戏结束;若是没有,则就在原来的坐标上返回周围雷的个数,为了使得代码的可读性更高,就再次封装成一个函数(L_count函数)来计算周围雷的个数。
int L_count(char board1[ROWS][COLS], int x, int y)
{
return (board1[x - 1][y - 1] + board1[x - 1][y + 1] + board1[x - 1][y] + board1[x][y - 1] + board1[x][y + 1] +
board1[x + 1][y - 1] + board1[x + 1][y] + board1[x + 1][y + 1] -8* '0');
}
而 SearchBoard函数如下:
void SearchBoard(char board[ROWS][COLS], char board1[ROWS][COLS], int row, int col, char ret)
{
int x = 0;
int y = 0;
int i = 0;
while (i < row * col - LEI)
{
printf("请输入要排查雷的坐标:> ");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board1[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
printf("雷的布局是:> \n");
DisplayBoard(board1, ROW, COL);
return;
}
else
{
if (board[x][y] == ret)
{
int r = L_count(board1, x, y) ;
board[x][y] = r+ '0';
i++;
DisplayBoard(board, ROW, COL);
}
else
{
printf("此坐标已被排过,请重新输入\n");
}
}
}
else
{
printf("输入坐标错误,请重新输入\n");
}
}
if (i == row * col - LEI)
printf("恭喜你,排雷成功了\n");
}
此处的LEI指的是雷的个数,这是用 #define 定义的,使得整个代码有了更高的广泛性。
四:扫雷游戏完整代码以及运行结果
1. game.h
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define LEI 10
// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char ret);
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
// 设置雷
void SetBoard(char board[ROWS][COLS], int row, int col);
// 排查雷
void SearchBoard(char board[ROWS][COLS], char board1[ROWS][COLS], int row, int col, char ret);
2. game.c
#define _CRT_SECURE_NO_WARNINGS
// 扫雷游戏函数的实现
#include "game.h"
// 初始化棋盘
void InitBoard(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 DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
printf(" ");
for (i = 1; i <= row; i++)
{
if (i == row)
printf("-");
else
printf("--");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d|", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
// 设置雷-- 10个雷
void SetBoard(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int i = 10;
while (i)
{
x = rand() % 9 + 1;
y = rand() % 9 + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
i--;
}
}
}
// 排查雷
int L_count(char board1[ROWS][COLS], int x, int y)
{
return (board1[x - 1][y - 1] + board1[x - 1][y + 1] + board1[x - 1][y] + board1[x][y - 1] + board1[x][y + 1] +
board1[x + 1][y - 1] + board1[x + 1][y] + board1[x + 1][y + 1] -8* '0');
}
void SearchBoard(char board[ROWS][COLS], char board1[ROWS][COLS], int row, int col, char ret)
{
int x = 0;
int y = 0;
int i = 0;
while (i < row * col - LEI)
{
printf("请输入要排查雷的坐标:> ");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board1[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
printf("雷的布局是:> ");
DisplayBoard(board1, ROW, COL);
return;
}
else
{
if (board[x][y] == ret)
{
int r = L_count(board1, x, y) ;
board[x][y] = r+ '0';
i++;
DisplayBoard(board, ROW, COL);
}
else
{
printf("此坐标已被排过,请重新输入\n");
}
}
}
else
{
printf("输入坐标错误,请重新输入\n");
}
}
if (i == row * col - LEI)
printf("恭喜你,排雷成功了\n");
}
3. test.c
#define _CRT_SECURE_NO_WARNINGS
// 扫雷游戏
#include "game.h"
void menu()
{
printf("*************************************************\n");
printf("************* 1.play 0.exit *******************\n");
printf("*************************************************\n");
}
void game()
{
char mine[ROWS][COLS] = {0};
char show[ROWS][ROWS] = {0};
// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 打印棋盘
DisplayBoard(show, ROW, COL);
// 设置雷
SetBoard(mine, ROW, COL);
// 排查雷
SearchBoard(show, mine, ROW, COL,'*');
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入:> ");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
程序运行实例: