扫雷游戏相关介绍
扫雷这种经典益智小游戏,相信不少人都玩过或听说过,若没听过,也没事下面有扫雷玩法的介绍。
扫雷的规则如下:
盘面上有许多方格,方格中随机分布着一些雷。你的目标是避开雷,打开其他所有格子。一个非雷格中的数字表示其相邻8格中的雷数,你可以利用这个信息推导出安全格和雷的位置。
看完上面的规则介绍,相信你已经对扫雷已经有一定的了解了,那么现在就让我们利用C语言,开发出一款属于我们自己的简易扫雷游戏。
注:游戏里面涉及的知识为函数,数组,循环,分支以及C语言头文件的创建,开发环境为 Visual Studio 2022。
扫雷游戏实现
首先我们先考虑完成扫雷游戏需要那写步骤:
第一步:开始菜单
第二步:布置棋盘
第三步:打印棋盘
第四步:布置雷
第五步:排查雷
注:
这个游戏一共分为三个部分来实现
test.c(写游戏的主函数)
game.c(写游戏中函数的实现等)
game.h(写游戏需要的库函数引用和函数声明等)
开始菜单
最开始,我们肯定是先打印出菜单,然后输入内容,来判断是开始游戏还是退出游戏去执行相应的分支。先输出再判断,C语言中的 do{ } while()语句 正好也是先执行再判断正好满足,接着我们打印菜单,写的过程中我们发现,打印菜单的代码写在主函数中,在阅读程序的时候主函数看上去不会很简洁,这个时候我们选择把打印菜单的语句放到menu()这个函数里。然后用switch语句来执行选择的不同分支,此外选择 0 作为退出游戏是因为在C语言中 0为假 非0为真,到while这里进行判断如果是0就直接退出,若为其他则不会退出 do{ } while()语句。game()这个函数放在game.c中。
void menu()
{
printf("**********************\n");
printf("***** 1:开始游戏 *****\n");
printf("***** 0:退出游戏 *****\n");
printf("**********************\n");
}
int input = 0;
do
{
menu();
printf("请选择:> \n");
scanf("%d",&input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
布置棋盘
利用两个for循环把指定的字符存入整个二维数组,第一个for循环是遍历行,第二个for循环是遍历数组的列。就比如这个3行5列的二维数组,我们先是从i=0,j=0;的位置开始向后遍历一直遍历完j=5后,i++ i变成1,然后接着往后面循环,一直到整个二维数组遍历完。还有就是数组初始化的行和列一定要比实际显示出来的内容要多2行2列,因为在排查雷的时候,一个非雷格中的数字要表示其相邻8格中的雷数,若不这样去实现的话会造成数组的越界,出现程序运行错误。
/*
* 函数功能:将指定的字符存入到二维字符数组
* 输入值:二维字符数组,数组的行,数组的列,存的值
* 返回值:无
*/
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board[i][j] = set;
}
}
}
打印棋盘
打印棋盘的原理和上面布置棋盘的原理几乎相同,只是把给数组赋值变为打印数组内容,从数组的1行1列开始一直到9行九列结束,并且在每打印完一行后进行换行。但是为了方便查找,又加了打印每个元素相对应的坐标,首先用一个for循环打印最上面的纵坐标,记得加上空格使数字间隔开来,接着在每一行开始处打印 i的值与空格,做为横坐标。
/*
* 函数功能:打印二维字符数组的内容到屏幕上
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:无
*/
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
//打印纵坐标
for (int j = 0; j <= row; 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");
}
}
布置雷
布置雷这里我们,需要用到C语言的两个库
#include “stdlib.h”
#include “time.h”
分别引用 srand()初始化rand函数的种子,rand()返回一个范围在 0 到 RAND_MAX 之间的伪随机数,time()返回一个自 1970-01-01 起到现在时间相距的秒数 这三个函数,此外srand()和time()只需要调用一次所以就被放在game()函数里。
/*
* 函数功能:将字符'1'存放到随机的二维数组中,一共存放10个
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:无
*/
void SetMine(char Board[ROWS][COLS], int row, int col)
{
int count = MODEL;//MOBEL为布置雷的个数,他的声明放在game.h中
while (count)
{
int x = rand() % (row + 1);//得到10以内的随机数赋值给变量X
int y = rand() % (col + 1);
if (x >= 1 && y >= 1 )//保证在扫雷的区域范围内
{
if (Board[x][y] == '0')//如果不是雷,就布置一个雷
{
Board[x][y] = '1';
count--;//当count减到0时表示雷已经布置完成退出循环
}
}
}
}
排查雷
这部分,我们需要抓住三个点:
1:输入两个值来确定这个扫雷棋盘要扫的雷的位置。
2:如果输入的值不在我们棋盘的范围,就提示错误重新输入防止数组越界。
3:判断是否为雷,如果是雷结束,若不是获取当前输入坐标周围的雷的个数,显示到要排查除雷的信息的数字里。
这个时候我们就可以用到while循环保证一直多次输入,if判断是否超过范围,然后再来一个if来判断输入的坐标在棋盘里面是否为雷,要是为雷退出循环,若不为雷就调用GetMine()函数获取当前输入坐标周围的雷的个数,并且存入到在要排查雷的数组里。
这里面最为特殊的就是整形类型的数字转字符类型的数字,通过加减字符’0’来进行转换。
下图为要显示附近雷的个数的坐标图:
/*
* 函数功能:查找一个非雷格中的数字要表示其相邻8格中的雷数
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:相邻8格中的雷数
*/
static int GetMine(char Board[ROWS][COLS], int x, int y)
{
return (Board[x - 1][y - 1] + Board[x - 1][y] +
Board[x - 1][y + 1] + Board[x][y - 1] +
Board[x][y + 1] + Board[x + 1][y - 1] +
Board[x + 1][y] + Board[x + 1][y + 1]-8*'0');
}
/*
* 函数功能:排查雷
* 输入值:二维字符数组,二维字符数组,数组的行,数组的列
* 返回值:无
*/
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0,win=0;
while (win<(row*col-MODEL))//判断雷是否全部排完
{
printf("请输入坐标:>\n");
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
{
//将GetMine()函数返回的int类型的数字,转换字符型数字
Show[x][y]=GetMine(Mine,x,y)+'0';
//打印要排查除雷的信息
DisplayBoard(Show, ROW, COL);
win++;
}
}
else
{
printf("输入的坐标非法!,重新输入\n");
}
}
//如果雷全部找完了
if (win == (row * col - MODEL))
{
printf("恭喜你,排雷成功\n");
DisplayBoard(Mine, ROW, COL);
}
}
三个文件的所有代码
test.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//菜单
void menu()
{
printf("**********************\n");
printf("***** 1:开始游戏 *****\n");
printf("***** 0:退出游戏 *****\n");
printf("**********************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:> \n");
scanf("%d",&input);
switch (input)
{
case 1:
game();
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 MODEL 10
//游戏
void game();
//初始化棋盘
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 Board[ROWS][COLS],int row,int col);
//排查雷
void FindMine(char Mine[ROWS][COLS],char Show[ROWS][COLS],int row ,int col);
game.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void game()
{
srand((unsigned int)time(NULL));
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放要排查除雷的信息
//布置棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine,show,ROW,COL);
}
/*
* 函数功能:将指定的字符存入到二维字符数组
* 输入值:二维字符数组,数组的行,数组的列,存的值
* 返回值:无
*/
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board[i][j] = set;
}
}
}
/*
* 函数功能:打印二维字符数组的内容到屏幕上
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:无
*/
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
//打印纵坐标
for (int j = 0; j <= row; 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'存放到随机的二维数组中,一共存放10个
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:无
*/
void SetMine(char Board[ROWS][COLS], int row, int col)
{
int count = MODEL;//MOBEL为布置雷的个数,他的声明放在game.h中
while (count)
{
int x = rand() % (row + 1);//得到10以内的随机数赋值给变量X
int y = rand() % (col + 1);
if (x >= 1 && y >= 1 )//保证在扫雷的区域范围内
{
if (Board[x][y] == '0')//如果不是雷,就布置一个雷
{
Board[x][y] = '1';
count--;//当count减到0时表示雷已经布置完成退出循环
}
}
}
}
/*
* 函数功能:查找一个非雷格中的数字要表示其相邻8格中的雷数
* 输入值:二维字符数组,数组的行,数组的列
* 返回值:相邻8格中的雷数
*/
static int GetMine(char Board[ROWS][COLS], int x, int y)
{
return (Board[x - 1][y - 1] + Board[x - 1][y] +
Board[x - 1][y + 1] + Board[x][y - 1] +
Board[x][y + 1] + Board[x + 1][y - 1] +
Board[x + 1][y] + Board[x + 1][y + 1]-8*'0');
}
/*
* 函数功能:排查雷
* 输入值:二维字符数组,二维字符数组,数组的行,数组的列
* 返回值:无
*/
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0,win=0;
while (win<(row*col-MODEL))//判断雷是否全部排完
{
printf("请输入坐标:>\n");
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
{
//将GetMine()函数返回的int类型的数字,转换字符型数字
Show[x][y]=GetMine(Mine,x,y)+'0';
//打印要排查除雷的信息
DisplayBoard(Show, ROW, COL);
win++;
}
}
else
{
printf("输入的坐标非法!,重新输入\n");
}
}
//如果雷全部找完了
if (win == (row * col - MODEL))
{
printf("恭喜你,排雷成功\n");
DisplayBoard(Mine, ROW, COL);
}
}