1.扫雷游戏分析
1.1我们设计的是一个9*9的格子
1.2默认随机布置10个雷
1.3排查雷
- 如果位置是雷,就游戏失败
- 如果位置不为雷,就显示周围有几颗雷(周边八个网格中)
- 把不是雷的位置都排查出来,排雷成功,游戏结束
1.4数据的结构分析
- 首先我们要想如何存储雷的信息,我们需要在9*9的格子中布置雷的信息和排查雷,所以我们首先想到用数组来存储信息,创建一个9*9的数组
![](https://i-blog.csdnimg.cn/blog_migrate/1141e34b873cc7801dc663df2f1fcff7.png)
1.5布置雷和排雷
- 是雷我们这个位置放1,不是雷放0,这个数组我们取为mine数组
- 假设我们要排查数组下标为 (2,2)这个坐标,(2,2)这个坐标不是雷,接着我们要访问周围一圈8个红色区域
- (2,2)这个坐标周围有两颗雷,接着我们要把(2,2)中的0改为2,说明周围有两颗雷
- 这时有个问题,若这个坐标周围只有一颗雷,我们把它改为1,这就存在错误,所以雷和非雷的信息用int类型容易搞混或弄错,这时我们想着用另外有个相同类型的数组show数组来展现周围有几个雷,同时这个数组要保持神秘感,玩家要一个一个排查,所以我们用char类型来表示show数组,里面初始化为字符‘*’
- 我们要排查数组下标为 (2,2)这个坐标,然后根据mine数组,(2,2)这个坐标不是雷,接着求出周围有2颗雷,然后把2转化为char类型的字符‘2’放入show数组(2,2)这个坐标中,接着展示
- 为了保持两个数组类型的一致,所以mine数组和show数组都用char类型,mine数组中为雷用字符‘1’表示,不为雷用字符‘0’表示
- 还有一个问题,就是假设我们排查的是下标是(8,2),我们要访问周围的一圈8个位置,当访问下面三个坐标时会越界,要对(0,?)、(8,?)、(?,8)、(?,0)这些坐标进行特殊处理,还有一种方法是对整个数组扩大一圈,这样就不会出现数组越界的情况了,这里我们选择对数组扩大一圈,扩大的一圈中的数据都为字符‘0’,表示不是雷
show数组也扩大一圈,扩大的一圈中的数据都为字符‘*’
2.扫雷游戏实现
2.1文件结构设计
- 这里我们采用多文件的形式对函数声明和定义
- 设计三个文件,mine.h(文件中写扫雷游戏需要的数据类型和函数声明等),mine.c(文件中写游戏函数实现等),game.c(文件中写游戏的测试逻辑)
2.2扫雷游戏代码实现
1)定义常量
- 根据以上分析我们定义一些常量,后面函数会用这些,后面忘了可以回来看看
// 显示出的行
#define ROW 9
// 显示出的列
#define COL 9
// 实际的行
#define ROWS ROW + 2
// 实际的列
#define COLS COL + 2
// 地雷个数
#define EASY_COUNT 10
2)初始化棋盘
- 第一步我们先来初始化棋盘,我们要把mine数组和show数组初始化,mine数组初始化为字符‘0’,show数组初始化字符‘*’,不同数组要初始化为不同字符,该怎么写一个函数才能实现呢,这里我们对函数参数多添加一个参数为char set,你调用函数初始化为set字符
// 初始化棋盘
void InitBoard(char arr[ROWS][COLS],char set)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
arr[i][j] = set;
}
}
}
3)显示棋盘
- 初始化后我们想看看初始化是否成功,这时就要写一个打印函数,显示棋盘
// 显示棋盘
void DisplayBoard(char arr[ROWS][COLS])
{
// 显示的行和列
for (int i = 1; i <= ROW; i++)
{
for (int j = 1; j <= COL; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
这段代码打印棋盘是可以,但效果不怎么行,总觉得少了些什么,对少了每一行的下标,所以进行优化
// 显示棋盘
void DisplayBoard(char arr[ROWS][COLS])
{
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 ", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
打印效果展示,第一个为mine数组,第二个为show数组
4)布置雷
- 接下来我们就要进行布置雷了
这是布置完雷的mine数组,外面这一圈黄色部分里面存放的全是是字符‘0’(表示不是雷),这里我们观察一下mine数组,雷放置的位置是行下标为1-9,列下标也为1-9,这就好办了,这里我们用rand函数来产生随机数,用rand函数前要先用srand函数产生一个随机种子,rand函数才能产生真正的随机数,同时srand函数只需要调用一次就行,不要把srand的函数放到自己写的函数中,防止重复调用。
// 布置雷
void SetMine(char arr[ROWS][COLS])
{
// 定义雷的个数
int mines = EASY_COUNT;
while (mines)
{
// 产生1-9的随机数
// 用于数组下标赋值为雷
int r = rand() % ROW + 1;
int c = rand() % COL + 1;
if (arr[r][c] == '0')
{
// 把雷设为字符'1',便于运算
arr[r][c] = '1';
mines--;
}
}
}
5)排雷
- 布置完雷后,我们开始来排雷,这里我们根据1.5布置雷和排雷中的分析进行写
假设我们这里排查的坐标是(3,3),当该位置不是雷时(是雷就结束游戏),显示周围一圈的雷的个数,周围雷的个数为2,所以赋值给show数组,,同时展示show数组
我们知道周围有2颗雷,可计算机不知道,我们要拿出周围八个位置中的字符,拿一个减去一个字符‘0’,把八个都拿出来就减去8个字符‘0’,最后把结果用int接收。(这些字符都有对应的ASCII码表,字符‘1’ - 字符‘0’ == 1)
// 求出它周围一圈的地雷个数
int ItAround(char mine[ROWS][COLS], int r, int c)
{
// 地雷数组里面放的是字符
int result = mine[r - 1][c - 1] + mine[r - 1][c] + mine[r - 1][c + 1] + // 上面三个数据
mine[r][c - 1] + mine[r][c + 1] + // 中间两个数据
mine[r + 1][c - 1] + mine[r + 1][c] + mine[r + 1][c + 1] - // 下面三个数据
8 * '0'; // 每个数组数据都要减一个'0',在转换为int类型就为地雷个数
return result;
}
- 我们怎么说明排雷成功呢,当我们将不是地雷的位置都排查完了就说明排查结束,所以我们定义一个变量,每排查一次坐标就加加,不是雷的个数有ROW*COL - EASY_COUNT这么多
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
int r = 0;
int c = 0;
int win = 0;
while (win < (ROW * COL - 10))
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &r, &c);
// 当排查的坐标上为炸弹时
if (mine[r][c] == '1')
{
printf("你踩到地雷了,游戏失败\n");
DisplayBoard(mine);
break;
}
else
{
// 排查的位置不为地雷
// 要求出它周围一圈的地雷个数
int num = ItAround(mine, r, c);
show[r][c] = num + '0';
win++;
DisplayBoard(show);
}
}
if (win == (ROW * COL - 10))
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine);
}
}
6)菜单
- 这里我们实现一个简单的菜单函数
// 菜单
void Menu()
{
printf("--------------------------------------\n");
printf("------- 扫雷游戏 --------\n");
printf("------- 1.play --------\n");
printf("------- 0.exit --------\n");
printf("--------------------------------------\n");
}
2.3整体实现
1)mine.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 Menu();
// 初始化棋盘
void InitBoard(char arr[ROWS][COLS],char set);
// 显示棋盘
void DisplayBoard(char arr[ROWS][COLS]);
// 布置雷
void SetMine(char arr[ROWS][COLS]);
// 求出它周围一圈的地雷个数
int ItAround(char mine[ROWS][COLS], int r, int c);
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS]);
2)mine.c
#include "Mine.h"
// 菜单
void Menu()
{
printf("--------------------------------------\n");
printf("------- 扫雷游戏 --------\n");
printf("------- 1.play --------\n");
printf("------- 0.exit --------\n");
printf("--------------------------------------\n");
}
// 初始化棋盘
void InitBoard(char arr[ROWS][COLS],char set)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
arr[i][j] = set;
}
}
}
// 显示棋盘
void DisplayBoard(char arr[ROWS][COLS])
{
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 ", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
// 求出它周围一圈的地雷个数
int ItAround(char mine[ROWS][COLS], int r, int c)
{
// 地雷数组里面放的是字符
int result = mine[r - 1][c - 1] + mine[r - 1][c] + mine[r - 1][c + 1] + // 上面三个数据
mine[r][c - 1] + mine[r][c + 1] + // 中间两个数据
mine[r + 1][c - 1] + mine[r + 1][c] + mine[r + 1][c + 1] - // 下面三个数据
8 * '0'; // 每个数组数据都要减一个'0',在转换为int类型就为地雷个数
return result;
}
// 布置雷
void SetMine(char arr[ROWS][COLS])
{
// 定义雷的个数
int mines = EASY_COUNT;
while (mines)
{
// 产生1-9的随机数
// 用于数组下标赋值为雷
int r = rand() % ROW + 1;
int c = rand() % COL + 1;
if (arr[r][c] == '0')
{
// 把雷设为字符'1',便于运算
arr[r][c] = '1';
mines--;
}
}
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
int r = 0;
int c = 0;
int win = 0;
while (win < (ROW * COL - 10))
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &r, &c);
// 当排查的坐标上为炸弹时
if (mine[r][c] == '1')
{
printf("你踩到地雷了,游戏失败\n");
DisplayBoard(mine);
break;
}
else
{
// 排查的位置不为地雷
// 要求出它周围一圈的地雷个数
int num = ItAround(mine, r, c);
show[r][c] = num + '0';
win++;
DisplayBoard(show);
}
}
if (win == (ROW * COL - 10))
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine);
}
}
3)game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Mine.h"
int main()
{
// 产生随机种子
srand((unsigned int)time(NULL));
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
int flag = 0;
do
{
Menu();
printf("请输入相关操作\n");
scanf("%d", &flag);
switch (flag)
{
case 1:
printf("玩游戏\n");
InitBoard(mine, '0');
InitBoard(show, '*');
SetMine(mine);
DisplayBoard(show);
FindMine(mine, show);
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (flag);
return 0;
}