前言
想必大家都玩过扫雷小游戏,博主也是从小就开始玩扫雷小游戏的,毕竟在很久以前,科技不怎么发达的时候,有一款游戏就已经是非常令人心动的了。
如果读者没有玩过扫雷小游戏,博主这边也非常热情的推荐一个游戏网址:扫雷游戏网页版 - Minesweeper供读者体验。
完整代码自提看本文的最后。
游戏设计
那么我们的正篇正式开始,首先我们先联想一下,扫雷游戏需要一个9x9的方格供我们进行排查,那么我们需要使用一个二维数组进行存放,考虑到展示的数组与真实雷所在的数组不同,我们这边需要使用两个数组进行存放,一个用于存放雷的信息,一个用于存放排查出的信息。
考虑在排查雷的过程中,我们需要计算所选择位置的周围8个位置的雷的数目,并且我们还需要考虑到边界值的情况,那么为了方便计算,我们统一将二维数组的长度设置为11x11。
并且考虑到难度选择上,我们将雷的数目设为简单10,中等20,困难30。
我们这里使用预处理指令#define将这些固定数字进行预处理。
// 本文所需要引用的库
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
// 这里的雷的数目大家可自行决定
#define EASY_COUNT 10
#define MEDIUM_COUNT 20
#define DIFFICULTY_COUNT 30
入口函数
然后我们进行主函数main进行设置,main函数主要是一个入口函数,我们只需将进入游戏以及选择难度编写进main函数即可,其余功能统一抽象成方法。
int main() {
// 选择是否游戏
int input = 0;
// 选择难度
int level = 0;
// 设置随机值
srand((unsigned int)time(NULL));
// 循环游戏
do {
printf("*******************\n");
printf("***** 1. play *****\n");
printf("***** 0. exit *****\n");
printf("*******************\n");
printf("请选择游戏:>\n");
scanf("%d", &input);
// 判断是否进行游戏
switch (input) {
case 1:
printf("请选择难度(1简单 2中等 3困难):>\n");
scanf("%d", &level);
// 当难度不符合预设值的时候,提示用户重新选择
if (level < 1 || level > 3) {
printf("选择错误,请重新选择\n");
} else {
// 当进入游戏并且选择难度成功后,正式进入游戏
game(level);
break;
}
case 0:
printf("结束游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
} while (input);
return 0;
}
棋盘初始化
当我们完成游戏的入口后,便要开始正式进入游戏功能的编写了。
首先,我们进入游戏后,屏幕上应该展示出的是一个9x9的正方形棋盘,并且展示的是未知数,毕竟在我们进行选择之前,我们是不知道这个位置是否是雷。那么我们首先需要一个数组进行展示,我们为其进行创建char show[ROWS][COLS],以及我们还需要一个数组,用于存放真实雷的信息,我们也对其进行创建char mine[ROWS][COLS]。
那么我们接下来需要对棋盘进行初始化,用于存放真实数据,对于展示的棋盘,我们统一使用*来展示,保留出一点的神秘感哈哈。而真实存放雷的数组呢,我们使用0进行存放,后续在雷的位置上,我们使用1来表示雷,这样可以方便我们计算雷的数目。
// 初始化数组代码
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0;
for (i = 0; i < rows; i++) {
int j = 0;
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
展示棋盘
对数组进行设置成功后,我们需要将数组展示出来查看,这里便是简单的两次循环即可,我们需要注意的一点是我们只需要将棋盘的中间9x9展示出来即可,其余位置并不需要展示。而为了方便后续对于雷位置的选择,我们需要在上边栏和左边栏输出当前的行列号。
// 展示数组
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
int i = 0;
printf("-------扫雷-------\n");
for (i = 0; i <= col; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
}
那么棋盘初始化成功,我们便要根据难度对存放雷的数组进行随机存放雷,我们首先,根据我们上述进入游戏时选择的游戏难度123进行判断,判断返回所存放雷的数目,只需要简单的if语句或者switch语句判断即可。
// 根据选择难度返回雷的数目
int SelectLevel(int level) {
if (level == 1) {
return EASY_COUNT;
} else if(level == 2) {
return MEDIUM_COUNT;
} else {
return DIFFICULTY_COUNT;
}
}
布置雷
成功确认雷的数目后,我们就需要向存放雷的数组中进行存放雷,我们这里使用坐标来进行存放雷,并且数组长宽为11x11,因此我们所存放雷的位置也就是1-9。那么接下来,我们可以使用到rand()函数产生随机数来确定雷所存放的坐标,并且我们在存放之前,需要判断该位置是否已经存放了雷,如果没来,我们便将该位置设为1,用于表示雷,并且将雷的总数-1即可。
// 随机在棋盘坐标上布置雷
void SetMine(char mine[ROWS][COLS], int count) {
int x = 0;
int y = 0;
while (count) {
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] != '1') {
mine[x][y] = '1'; // 布置一个雷
count--;
}
}
}
排查雷
我们将雷设置成功后,便要开始我们的正戏了,那便是排查雷。排查雷是一个重复的过程,我们需要不断去判断是否踩到雷,这里直接使用死循环即可,如果踩到雷或者将所有非雷排查成功,便可以结束循环,告诉玩家游戏成功或失败。
首先我们需要提示玩家输入所要排查的坐标,因为我们的棋盘所展示的是中间的9x9,因此玩家在输入坐标的范围也要控制在1-9的范围内,如果越界,便提示用户越界,并将坐标范围重新输出来提示玩家。如果输入坐标符合范围,那么我们就需要进行判断该位置是否是雷,如果是雷,那么恭喜玩家,游戏失败,游戏失败后我们便需要将该棋盘展示,让玩家输的明明白白,此后便结束死循环;如果该坐标不是雷,我们便统计该坐标周围雷的数目,由于我们在初始化棋盘的时候是将所有位置使用0来表示,而存放雷的位置使用1来表示,所以我们这里判断也是非常的easy,只需要将排查坐标周围的数相加即可,考虑到我们使用的是字符数,那么我们相加后-8*0即可统计出雷的总数。当我们统计出雷的总数后,我们便需要将展示的棋盘上该坐标的位置用周围雷的数目来填充,然后将该棋盘打印出来告诉玩家。
最后,当所排查的坐标数目=81-雷的数目时,游戏便成功了,此时也需要提示玩家游戏成功,并且跳出死循环。
// 统计周围雷的数目
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
int count = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
count += mine[x + i][y + j] - '0';
}
}
return count;
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int level) {
int x = 0;
int y = 0;
int count = 0;
int win = 0;
while (1) {
printf("请输入要排查的坐标(中间用空格隔开):");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
// 判断该坐标是否是雷
if (mine[x][y] == '1') {
printf("踩雷失败,游戏结束\n");
DisplayBoard(mine, ROW, COL);
break;
} else {
// 统计该坐标周围雷的数目
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
if (win == 81 - level) {
printf("踩雷成功,游戏结束\n");
break;
}
} else {
printf("输入坐标越界,请重新输入,x取值范围(1-9),y取值范围(1-9)");
}
}
}
总结
到此为止,我们的小游戏便正式结束啦,其实该游戏还是有很多可以改进的地方,例如可以根据难度调整棋盘大小(由于博主使用的是Visual Studio,不支持变长数组,便没有展示出来),以及选择排查坐标后,根据当前坐标周围雷的数目是否为0,如果为0,则会将周围坐标同样为0的情况下一起展示出来,这里使用到了递归算法,这一功能还是非常值得尝试的,博主实力不济,便不献丑了。同样的,也可以将游戏时间,游戏步数,以及可能是雷的位置使用一个特殊符合标记,这些都是可以完成的额外功能,有待读者自行完成哦~~
那么最后,博主也将完整代码展示在最后啦。
完整代码展示
main.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu() {
printf("*******************\n");
printf("***** 1. play *****\n");
printf("***** 0. exit *****\n");
printf("*******************\n");
}
void game(int level) {
char mine[ROWS][COLS]; // 存放雷的信息
char show[ROWS][COLS]; // 存放排查出的雷的信息
// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 打印棋盘 (检验所用
//DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
// 选择难度
int count = SelectLevel(level);
// 布置雷
SetMine(mine, ROW, COL, count);
DisplayBoard(show, ROW, COL);
// 排查雷
FindMine(mine, show, ROW, COL, count);
}
int main() {
int input = 0;
int level = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请选择游戏:>\n");
scanf("%d", &input);
switch (input) {
case 1:
printf("请选择难度(1简单 2中等 3困难):>\n");
scanf("%d", &level);
if (level < 1 || level > 3) {
printf("选择错误,请重新选择\n");
} else {
game(level);
break;
}
case 0:
printf("结束游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
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 EASY_COUNT 10
#define MEDIUM_COUNT 20
#define DIFFICULTY_COUNT 30
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
// 布置雷
void SetMine(char mine[ROWS][COLS], int row, int col, int level);
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int level);
// 选择难度
int SelectLevel(int level);
game.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0;
for (i = 0; i < rows; i++) {
int j = 0;
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
int i = 0;
printf("-------扫雷-------\n");
for (i = 0; i <= col; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
}
int SelectLevel(int level) {
if (level == 1) {
return EASY_COUNT;
} else if(level == 2) {
return MEDIUM_COUNT;
} else {
return DIFFICULTY_COUNT;
}
}
// 随机在棋盘坐标上布置雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count) {
int x = 0;
int y = 0;
while (count) {
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] != '1') {
mine[x][y] = '1'; // 布置一个雷
count--;
}
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
int count = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
count += mine[x + i][y + j] - '0';
}
}
return count;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int level) {
int x = 0;
int y = 0;
int count = 0;
int win = 0;
while (1) {
printf("请输入要排查的坐标(中间用空格隔开):");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
// 判断该坐标是否是雷
if (mine[x][y] == '1') {
printf("踩雷失败,游戏结束\n");
DisplayBoard(mine, ROW, COL);
break;
} else {
// 统计该坐标周围雷的数目
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
if (win == row * col - level) {
printf("踩雷成功,游戏结束\n");
break;
}
} else {
printf("输入坐标越界,请重新输入,x取值范围(1-%d),y取值范围(1-%d)", row, col);
}
}
}