此次博客分享学习到的扫雷小游戏,扫雷游戏的规则为:显示的数字代表周围8个格子中有几个地雷。非常简单,Windows电脑也常常自带一个扫雷游戏,许多对电脑游戏有兴趣的朋友也试过这个游戏。
目录
网页中的扫雷游戏如图:
1.扫雷游戏思路:
扫雷小游戏主要是对雷的存放,可以以两个不同的变量代表有无地雷,其次需要一个棋盘来供于用户进行游戏排雷,而另一个棋盘则是存放地雷。此次代码实现为多文件实现,分别为test.c,game.c,game.h。当然了,由于懒和较为简单且非本节重点,游戏进入,退出的菜单代码就不再讲解。
如图:
用户操作棋盘
用于埋放地雷的棋盘
1.1 从图片中也可以得出:
1)这样一个简单的9*9的棋盘(后续讲解,只是看到的9*9)就是相同大小的二位字符数组构成;
1.2 对雷的存放位置和用户操作分析:
1)用户可以提供相应坐标即可操作;
2)对于雷的存放则是rand函数提供的随机数,对随机数取余即可得到合适的坐标;
1.3 对棋盘数组实际大小的具体分析:
若只为9*9的数组,则四周靠边的位置会无法正常统计周围雷的个数,例如:在左上角的位置只能统计右边的,下边的,右下方的,这样的3个位置,如果单独为这些特殊位置写判断则会过于复杂,故可以通过扩大棋盘,即扩大数组,左右各一行,上下各一行,那么实际的数组大小为11*11。为了方便,两个二维数组都为11*11,但是显示棋盘,即玩家操作的棋盘只能使用9*9的大小。
1.4 对棋盘的初始化:
每次游戏开始前,需要初始化放置地雷的棋盘和排查地雷的棋盘,地雷棋盘为字符0或字符1,玩家操作棋盘初始所有均未知,故设为字符‘*’隐藏,而对棋盘初始化,就是遍历二维数组,并将元素置为所需元素。
代码如下:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
字符变量set为所置成的元素。
1.5 显示棋盘:
在每次玩家操作后的棋盘效果,需要显示,且在编写代码时需要查看棋盘的效果,故需要对棋盘的显示。
代码如下:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("-------扫雷游戏-------\n");
for (int i = 0; i <= col; i++)
{
printf("%d ", i);
}
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.6 放置地雷:
虽然给的时11*11的棋盘大小,但是雷只在中间的9*9的棋盘中,故使用rand函数随机放入时需要限制范围,即给出的随机数对9取余,然后加1,即可得出正确地雷放置坐标,而雷的数量也要确定多少,故需要一个计数器来控制存放雷的个数,该计数器也为循环控制条件。
代码如下:
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
1.7 玩家操作排查地雷的棋盘:
玩家操作棋盘,后称为显示棋盘,需要对玩家给出的坐标来提醒玩家周围地雷的个数,故需判断给出的坐标周围8个坐标是否有地雷,并统计,由于显示棋盘为字符数组,则统计地雷的个数需要减去字符'0',通过for循环判断9个坐标,其中包含给出的坐标。最后减去9个字符‘0’对应的ASCII码值,即可得出对应的数字字符,而显而易见的是周围最多存在8个地雷,故字符能够处理。
其次需要对玩家操作结果判断是否排查出所给出的地雷数目,故需要一个计数器来统计显示棋盘是否显示出结果的个数(即显示空格或数字的棋盘位置),若最终的棋盘未知位置的个数恰好等于雷的个数,即获得胜利,若踩到地雷则提示失败,并显示埋放地雷的棋盘。若坐标不在棋盘范围内,则提示坐标错误。
递归显示周围没有雷的坐标位置,没有地雷置为' ',直至遇到周围有雷的坐标停止显示:
递归的判断条件为:
1)该坐标周围没有雷,则递归;
2)如果递归的显示棋盘坐标位置元素为‘*’且不是地雷的坐标,则递归;
双层循环包裹第二个条件来对包含该坐标的9个坐标递归判断。
代码如下:
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1; j++)
{
count += mine[i][j];
}
}
return count - '0' * 9;
}
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = GetMineCount(mine, x, y);//若该位置不是雷,则统计周围有多少雷
if (count == 0)
{
show[x][y] = ' ';
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1; j++)
{
if (show[i][j] == '*'&& mine[i][j]!='1')
{
ExpandBoard(mine, show, i, j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
int win = 0;
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("很遗憾,踩到雷了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
ExpandBoard(mine,show,x,y);
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '8'))
{
win++;
}
}
}
DisplayBoard(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入\n");
}
if (win >= ROW * COL - EASY_COUNT)
{
printf("恭喜你,排除所有地雷\n");
DisplayBoard(mine, ROW, COL);
break;
}
}
}
2.重要的变量和常量:
2.1 实际棋盘大小和操作棋盘大小使用常变量定义:
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
2.2 棋盘:
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
2.3 地雷数目,使用常变量定义:
#define EASY_COUNT 10
3. 效果展示图
第一张为第一次输入坐标
第二张为多次输入坐标后最终胜利
4.完整代码:
game.h中的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#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 board[ROWS][COLS], int row, int col);
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c中的代码:
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
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)
{
printf("-------扫雷游戏-------\n");
for (int i = 0; i <= col; i++)
{
printf("%d ", i);
}
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 SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1; j++)
{
count += mine[i][j];
}
}
return count - '0' * 9;
}
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = GetMineCount(mine, x, y);//若该位置不是雷,则统计周围有多少雷
if (count == 0)
{
show[x][y] = ' ';
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1; j++)
{
if (show[i][j] == '*'&& mine[i][j]!='1')
{
ExpandBoard(mine, show, i, j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
int win = 0;
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("很遗憾,踩到雷了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
ExpandBoard(mine,show,x,y);
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '8'))
{
win++;
}
}
}
DisplayBoard(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入\n");
}
if (win >= ROW * COL - EASY_COUNT)
{
printf("恭喜你,排除所有地雷\n");
DisplayBoard(mine, ROW, COL);
break;
}
}
}
test.c中的代码:
#include"game.h"
void menu()
{
printf("************************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("************************\n");
}
void game()
{
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化两个数组
//1.初始化雷数组,即全为'0';
//2.初始化排查显示的数组即全为'*'
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//1.布置雷
SetMine(mine, ROW, COL);
DisplayBoard(mine, ROW, COL);
//2.排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
int input;
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;
}