C语言小游戏——2、扫雷游戏
一、要求
开发一个经典的扫雷程序,在软件界面中提供菜单选项以选择继续游玩或退出游戏功能
2.棋盘是 9*9 的格子,默认随机布置10个雷;
3.该程序能够识别地 mines. 如果当前单元格不是地 mine, 则会显示相邻区域共有多少个地 mines; 如果当前单元格是地 mine, 则会触发游戏结束.
4.把除10个雷之外的所有雷都找出来,排雷成功,游戏结束。
二、分析
1、数据结构的分析
在排雷信息中进行存储,在这个位置放置地mine时,则记录为1;若未放置地mine,则记录为0。此时我们需要一个数组来完成这一存储任务。

当我们扫描位于(2,5)坐标的单元格时,在其上下左右共8个相邻单元格的位置进行检查后发现该区域存在1枚地雷;然而在扫描位于(8,6)坐标的单元格时,在其上下左右共8个相邻单元格的位置进行检查过程中会发现其中三个坐标超出边界范围。为了避免索引溢出,在设计算法时将数组扩展一圈以包围原始区域,并使得地雷仍均匀分布于扩展后的新中心区域(9x9)中即可满足要求。因此,在算法实现中建议将二维数组初始化为较大的尺寸(如11x11)以确保数据的有效存储与引用。

在棋盘上安置了地雷阵,在棋盘上的信息标记分为两种:一种标记为1的位置表示有地雷存在;另一种标记为0的位置则表示没有地雷存在。假设我们在某个特定的位置进行排查,则该位置确实没有放置任何地雷。观察该位置周围的相邻格子时发现恰好存在一个地埋着地mine.为了便于后续排雷工作的准确性与效率,在此次排查后需对已探测到的地埋数量进行详细记录并保存相关信息,并生成排布结果供参考使用。
如果将布置雷的数据存储到一个数组中,则可能会导致信息混淆以及打印过程中的问题
例如,在处理雷区标记时,默认不应采用数字表示区分地 mines 和 non-mines 区域。相反地,则可以通过特定符号来表示这些区域的状态。这种做法能够有效避免混淆问题的发生。然而,在实际操作中若继续沿用这种方法,则会在棋盘上同时包含区分地 mines 和 non-mines 的信息以及已排查出 mines 的数量统计信息。这不仅会导致信息呈现混乱状态,并且会使操作变得不够直观与便捷。
我们专门在两个棋盘(分别对应两个数组)中放置雷的信息:一个是mine棋盘用于存放预先布置好的地雷标记;另一个是show棋盘用于记录排查出的地雷位置信息。
这样就互不⼲扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型⼀致,可以使用同⼀套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。
对应的数组应该是:
char mine[11][11] = {0};//⽤来存放布置好的雷的信息
char show[11][11] = {0};//⽤来存放排查出的雷的个数信息
2、文件结构设计
在之前的阶段中学习过多种文件形式用于函数声明与定义,在这里我们进行实践操作一下。我们需要搭建三个文件:
test.c //⽂件中写游戏的测试逻辑
game.c //⽂件中写游戏中函数的实现等
game.h //⽂件中写游戏需要的数据类型和函数声明等
三、实现
1.game.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define EASY_COUNT 10
#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);
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
2.game.c
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏-------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
//布置10个雷
//⽣成随机的坐标,布置雷
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--;
}
}
}
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
{
//该位置不是雷,就统计这个坐标周围有⼏个雷
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("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
3.test.c
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏-------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
//布置10个雷
//⽣成随机的坐标,布置雷
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--;
}
}
}
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
{
//该位置不是雷,就统计这个坐标周围有⼏个雷
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("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
学习很苦 鸡汤来补!
