目录
Ⅰ.前言
扫雷游戏想必大家都有玩过。没玩过的小伙伴也可以试着去玩一玩,这样对写扫雷游戏这个是会提供一个很好的思路的。那么本片博客就来介绍如何实现扫雷游戏的具体步骤。
本博客的扫雷代码还有许多功能需要完善,例如:
- 在周围没有雷的时候展开一片
- 标记雷
- 记录时间
Ⅱ.实现游戏思路
1.main函数整体构思
首先在main函数中写出一个游戏大概的模板思路,通过do while循环来选择是否重复游玩游戏,通过Switch来选择菜单中的选项,大致思路出来了再去写用到的每个函数。
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.game()函数的构思
game()函数即开始游戏的函数,里面包括一系列操作
1、首先创造两个数组来存放棋盘的数据
2、然后要初始化棋盘,创造一个初始化的函数
3、棋盘需要打印出来,所以要创造一个打印棋盘的函数
4、还要创造一个布置雷的函数
5、最后创造一个函数进行雷的排查
void game()
{
//创建两个棋盘
char mine[ROWS][COLS] = { 0 };//mine数组是专门存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//show数组是专门存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');//全部存‘ 0 ’
InitBoard(show, ROWS, COLS, '*');//全部存‘ * ’
//打印棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
3.棋盘的两个数组的构思
可以创建两个数组来存放信息,能够更好的存放信息
- 一个数组mine数组用于存放布置好的雷的信息
- 另一个数组show数组用于存放排查出雷的信息
这两个数组式样图如下:
Ⅲ.具体实现代码
1.创建菜单界面(menu)
菜单界面函数是用来显示玩游戏的选项,就类似去餐厅吃饭的菜单。这个函数只需要直接引用下即可。示例代码如下:
void menu()
{
printf("--------------扫雷游戏---------------\n");
pritnf(" \n");
printf("************************************\n");
printf("********** 1.play **********\n");
pritnf(" \n");
printf("********** 0.exit **********\n");
printf("************************************\n");
printf("------------------------------------\n");
}
2.实现多行多列扫雷
使用宏定义来定义棋盘的行与列,和棋盘中炸弹的数量
代码如下:
#define ROW 9 //棋盘界面的行数
#define COL 9 //棋盘界面的列数
#define ROWS ROW+2 //创建棋盘数组的总行数
#define COLS COL+2 //创建棋盘数组的总列数
#define EASY_COUNT 10 //布置总炸弹数
使用define宏定义的好处有:
- 方便程序的修改,不用对整个程序进行修改,只需对宏定义上进行修改
- 提高程序的运行效率,更加方便模块化。
3. 初始化棋盘
棋盘的初始化:
- 将mine数组全部初始化为‘ 0 ’,0代表非雷
- 将show数组全部初始化为‘ * ’
- set表示要初始化为的字符
如下所示:
InitBoard(mine, ROWS, COLS, '0');//全部存‘ 0 ’
InitBoard(show, ROWS, COLS, '*');//全部存‘ * ’
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
4.打印棋盘
打印棋盘的本质是打印数组里存放的内容
代码如下:
DisplayBoard(show, ROW, COL);
DisplayBoard(mine, ROW, COL);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("****** 扫雷 ******\n");
for (j = 0; j <= col; j++)//打印列号
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
打印效果如下:
5.布置雷
初始化完棋盘后,就要将雷布置到mine数组中
布置雷的代码如下:
SetMine(mine, ROW, COL);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//雷的数量
while (count)
{
int x = rand() % row + 1;//随机雷行坐标
int y = rand() % col + 1;//随机雷列坐标
if (mine[x][y] == '0')
{
mine[x][y] = '1';//雷标记为'1'
count--;
}
}
}
这里还用到了一个知识点【随机数】
在实际开发中,我们往往需要一定范围内的随机数,过大或者过小都不符合要求,那么,如何产生一定范围的随机数呢?我们可以利用取模的方法:
int a = rand() % 10; //产生0~9的随机数除
如果要规定上下限:
int a = rand() % 51 + 13; //产生13~63的随机数
分析:取模即取余,rand()%51+13 我们可以看成两部分:rand()%51 是产生 0~50 的随机数,后面+13保证 a 最小只能是 13,最大就是 50+13=63使用 <time.h> 头文件中的 time() 函数即可得到当前的时间(精确到秒),就像下面这样:
srand((unsigned)time(NULL));
注意:这个在程序当中是只执行一次即可!
6.排查雷
布置完雷后,就可以进行游戏的最终步骤了,排查雷。
这里输入需要排查的坐标(x,y),此处将利用while循环来循环排雷,排雷结果有以下几种可能:
- 当排到的位置存放了雷的时候,会提醒你被炸死了,游戏结束。
- 当排到的位置没在棋盘内的时候,会提醒你重新输入坐标
- 当你重复输入坐标时,会提醒你重新输入坐标
- 当你排到的位置没有雷时,这个位置将会显示周围雷的数量
- 当排的位置只剩雷没有排的时候,游戏结束,将会恭喜你排雷成功
代码如下:
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//排雷次数
while (win < row*col - EASY_COUNT)
{
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 if (show[x][y] != '*')
{
printf("坐标重复输入,请重新输入\n");
}
else
{
//如果该坐标不是雷,就要统计坐标周围有几个雷
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0'; //数字转化为字符储存在数组中
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("排查的坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你排雷成功!");
}
}
7.统计周围雷的数量
排雷的过程中,如果此处没有雷,则需要统计周围雷的数量
此函数代码如下:
//统计不是雷的坐标周围雷的数量
int GetMineCount(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');
}
此处将用到一个知识点:
- 一个数字加上一个字符0最后的结果将会等于那个数字的字符
0 + ‘ 0 ’ = ‘ 0 ’
1 + ‘ 0 ’ = ‘ 1 ’
- 反过来一个数字字符减去字符0将会等于数字字符中的数字
‘ 1 ’ - ‘ 0 ’ = 1
‘ 2 ’ - ‘ 0 ’ = 2
因为mine数组中,雷为 ‘ 1 ’,非雷为‘ 0 ’’,雷减去‘ 0 ’会等于1,非雷减去 ‘ 0 ’会得到0,所以用排查坐标的周围8个坐标中的字符都减去 ‘ 0 ’,将会得到周围雷的数量。
Ⅳ.游戏试玩结果
以下为游戏试玩截图:
Ⅴ.源代码
一:test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("--------------扫雷游戏--------------\n");
printf("************************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("************************************\n");
printf("------------------------------------\n");
}
void game()
{
//创建两个棋盘
char mine[ROWS][COLS] = { 0 };//mine数组是专门存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//show数组是专门存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');//全部存‘ 0 ’
InitBoard(show, ROWS, COLS, '*');//全部存‘ * ’
//打印棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, 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;
}
二: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;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("****** 扫雷 ******\n");
for (j = 0; j <= col; j++)//打印列号
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//雷的数量
while (count)
{
int x = rand() % row + 1;//随机雷行坐标
int y = rand() % col + 1;//随机雷列坐标
if (mine[x][y] == '0')
{
mine[x][y] = '1';//雷标记为'1'
count--;
}
}
}
//统计不是雷的坐标周围雷的数量
int GetMineCount(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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//排雷次数
while (win < row*col - EASY_COUNT)
{
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 if (show[x][y] != '*')
{
printf("坐标重复输入,请重新输入\n");
}
else
{
//如果该坐标不是雷,就要统计坐标周围有几个雷
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0'; //数字转化为字符储存在数组中
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("排查的坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你排雷成功!");
}
}
三: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 //布置总炸弹数
//初始化棋盘
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);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);