一.游戏规则
<游戏背景>:在一个9 * 9(初级),16 * 16(中级),16 * 30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷。初级为10个,中级为40个,高级为99个。
<游戏目标>:玩家需要尽快找出雷区中的所有不是地雷的方块,而不许踩到地雷。每个没有地雷的格子点开后显示相邻8个格子里面存在地雷的数目,周边没有地雷显示空白。
<胜利条件>:玩家标记出所有地雷即为游戏胜利。
<失败条件>:玩家翻开的方块有地雷,则游戏结束。
下面以初级9*9的背景,10个雷为例
希望大家看完有所收获。
二.实现思路
1.前提
- 首先需要创建9 * 9的背景,自然而然想到创建二维数组,在这个背景上布置雷,排查雷,指定一个坐标时显示相邻8个雷的个数,但是位于四个边缘的坐标周围并没有8个元素,因此我们只需多加两行两列,
创建11 * 11的二维数组
即可,在之后打印棋盘的时候打印9 * 9的,且布置雷的时候也在9 * 9的范围上弄就行 - 因为后续需要布置雷和排查雷,所以在创建的数组中
是雷就放1,不是雷就放0
,这样也便于后续统计雷的个数 - 如果创建一个数组,因为当指定一个坐标时不是雷需要在内放入相邻雷个数,这样就会在排查出1个雷时与雷的1产生歧义,因此我们
创建两个数组
:一个存放雷,一个存放排查出的雷的信息 - 为了增加程序的可复用性,我们用#define来给行和列定义标识符 赋值,格式如下:
#定义 标识符 内容
#define name stuff
#define ROW 9//9行
#define COL 9//9列
#define ROWS ROW+2
#define COLS COL+2
- 我们用
多文件
的形式来进行扫雷游戏的实现:
- test.c----扫雷游戏的测试
- game.c----扫雷游戏的实现
- game.h-----函数的声明
2.创建主函数
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"//自定义函数的头文件用""括起来
//只需包含game.h头文件即可 那里有所需函数的声明
//打印菜单
void Menu()
{
printf("**********************\n");
printf("**********************\n");
printf("****** 1.Play ******\n");
printf("****** 0.Exit ******\n");
printf("**********************\n");
printf("**********************\n");
}
void Game()
{
//扫雷游戏的实现,后续只在此函数中作添加
}
int main()
{
int n = 0;
do //不管三七二十一上来先执行一次打印菜单
{
Menu();
printf("请选择:");
scanf("%d", &n);
switch (n)
{
case 0:
printf("游戏退出");
break;//跳出switch语句
case 1:
Game();//扫雷游戏具体实现
break;
default:
printf("请输入0或1\n");
break;
}
}while (n);//输入0退出游戏,也跳出do while语句,结束循环
return 0;
}
3.初始化棋盘
- 把存放雷的数组初始化全为0,便于后续雷的布置
- 把存放排查后雷的数目的数组(即玩家能看到的数组)初始化全为*,保持神秘感
因为一个是0 一个是*,不是一个类型的,这样定义的数组也不是一个类型的,会使后续函数调用时造成麻烦,所以统一定义两个数组为char类型
代码如下:
game.h:
#pragma once
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);
game.c:
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;//自定义填充符号
}
}
}
test.c:
void Game()
{
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
//1.mine全是0
//2.show全是*
Initboard(mine, ROWS, COLS, '0');
Initboard(show, ROWS, COLS, '*');
}
4.打印棋盘
打印棋盘的同时为了便于玩家指定位置,所以要打上行标和列标
game.c:
void Displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("--------扫雷-------\n");
for (i = 0; i <= col; i++)//行标也占一列 所以列标从0开始 使第i列与i相对应
{
printf("%d ", i);//打印列标
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行标
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
game.h:
#pragma once
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
test.c:
void Game()
{
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
//1.mine全是0
//2.show全是*
Initboard(mine, ROWS, COLS, '0');
Initboard(show, ROWS, COLS, '*');
//打印棋盘
Displayboard(show, ROW, COL);
}
5.布置雷
- 布置的雷得是随机的,所以要用
rand()
函数生成随机数,因其生成的随机数是伪随机数,所以每次运行,它的随机值是固定的,这样会使得雷每次都在一个固定位置,因此要想它变化,引入了随机数发生器的初始化函数srand()
,需要提供一个种子,这个种子会对应一个随机数,所以如果要生成不同的随机数,就要让种子变化,因此引入了time()
函数用于返回当前日历时间- 在数组的1~ 9行,1~ 9列布置雷
game.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>//srand和rand的头文件
#include <time.h>//time的头文件
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINECOUNT 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);
game.c:
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = MINECOUNT;//雷的总数
srand((unsigned int)time(NULL));
//rand生成的是非负整数,所以种子类型要强转一下unisgned int类型
while (count)
{
int x = rand() % row + 1;//rand()%row生成0~8之间的数,+1生成1~9之间的数
int y = rand() % col + 1;
if (board[x][y] == '0')//为了防止在一个地方重复布雷所以加个判断
{
board[x][y] = '1';
count--;//每布置一个雷的总数减一直到所有雷布完count减为0跳出循环
}
}
}
test.c:
void Game()
{
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
//1.mine全是0
//2.show全是*
Initboard(mine, ROWS, COLS, '0');
Initboard(show, ROWS, COLS, '*');
//打印棋盘
Displayboard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
}
5.排查雷
- 输入一个坐标
- 判断其是否越界
- 判断是不是雷:是雷就炸死游戏结束,不是就统计周围雷的个数,游戏继续
game.c:
//统计指定坐标周围雷个数
//法1:
int AroundMine(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y - 1] +
mine[ x ][y - 1] +
mine[x + 1][y - 1] +
mine[x - 1][ y ] +
mine[x + 1][ y ] +
mine[x - 1][y + 1] +
mine[ x ][y + 1] +
mine[x + 1][y + 1] - 8 * '0');//数字字符转换位相应数字的方式:减字符0即‘0’
//因为字符间相减用ASCII码值,‘0’:48
}
//法2:
int AroundMine(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
for (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)
{//死循环的在排雷,所以用while循环
int x = 0;
int y = 0;
int win = 0;
while (win < ROW * COL - MINECOUNT)
{
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 = AroundMine(mine, x, y);
show[x][y] = count + '0';//数字转换成相应数字字符+‘0’
Displayboard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法x(1~9) y(1~9),请重新输入:");
}
}
if (win == ROW * COL - MINECOUNT)
{//因为踩雷和全部雷排查完均会跳出循环,所以要判断一下
printf("恭喜你,扫雷成功\n");
}
}
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 MINECOUNT 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);
test.c
void Game()
{
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
//1.mine全是0
//2.show全是*
Initboard(mine, ROWS, COLS, '0');
Initboard(show, ROWS, COLS, '*');
//打印棋盘
Displayboard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}