目录
一、引言
扫雷是一款经典的单人电脑游戏,玩家需要根据数字提示在没有地雷的区域揭开方块,同时避免揭开地雷。在这篇文章中,我们将介绍用C语言实现的简单扫雷游戏。
二、扫雷的魅力所在
- 策略性:扫雷需要玩家根据已知信息推断未知区域的情况,这种策略性的思考过程是其魅力之一。
- 挑战性:扫雷的难度可以根据需求调整,高难度的扫雷可以带来极大的挑战。
- 成就感:成功完成扫雷后带来的成就感也是吸引人的重要因
三、代码结构
game.h
头文件和常量定义
我们首先引入了一些必要的库文件,如 `<stdio.h>`, `<Windows.h>`, `<stdlib.h>`, 和 `<time.h>`。接下来,我们定义了游戏棋盘的常量和游戏难度级别。
函数声明
在声明部分,我们列出了所有后续将要实现的函数,包括初始化函数 `class_mine_show`、打印棋盘函数 `display_board`、布置雷函数 `layout_board`、和扫雷函数 `sweep`。
// 头文件
#include<stdio.h> // 标准输入输出库
#include<Windows.h> // Windows 库
#include<stdlib.h> // 随机数生成库:srand(unsigned int)
#include<time.h> // 时间函数库:time(NULL)
// 棋盘格子数
#define ROW 9 // 行数(不包括边界)
#define COL 9 // 列数(不包括边界)
#define ROWS ROW+2 // 实际行数(包括边界)
#define COLS COL+2 // 实际列数(包括边界)
// 游戏难度
#define easy_count 1 // 简单模式雷的数量
#define Difficult_count 20 // 困难模式雷的数量
#define extremely_difficult_count 30 // 极难模式雷的数量
// 声明
void class_mine_show(char board[ROWS][COLS], int rows, int cols, char set); // 初始化棋盘并显示设置字符
void display_board(char board[ROWS][COLS], int row, int col); // 打印棋盘
void layout_board(char board[ROWS][COLS], int row, int col); // 布置雷
void sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); // 排雷
-
初始化函数
`class_mine_show` 函数用于将棋盘的格子全部设置为指定的字符。在这里,我们通过两个嵌套的循环遍历整个棋盘,将每个格子的值设置为传入的字符。
-
打印棋盘函数
`display_board` 函数用于在控制台打印当前游戏棋盘的状态。通过两个嵌套的循环,我们遍历整个棋盘并输出每个格子的值。这个函数还包括一些控制台输出的延迟,以提高用户体验。
-
布置雷函数
`layout_board` 函数负责在棋盘上随机布置指定数量的雷。通过使用 `rand()` 函数和时间戳来生成随机的坐标,确保雷的位置是随机的且不重复。
-
获取雷数量函数
`get_main` 函数用于获取指定格子周围的雷的数量。通过对周围8个格子进行检查,统计其中的地雷数量。
-
扫雷函数
`sweep` 函数是整个游戏的核心。玩家通过输入坐标来进行排雷操作。如果玩家揭开的是地雷,游戏结束;否则,揭开的格子会显示周围地雷的数量。
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化
void class_mine_show(char board[ROWS][COLS], int rows, int cols,char set) {
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++) {
board[i][j] = set;
}
}
};
//打印棋盘
void display_board(char board[ROWS][COLS], int row, int col) {
for (int i = 0; i <= COL; i++)
{
//Sleep(100);
printf(" %d", i);
}
printf("\n");
for (int i = 1; i <= ROW; i++)//第0行不打印
{
printf(" %d", i);
Sleep(100);
for (int j = 1; j <= COL ; j++)//第0列不打印
{
printf(" %c", board[i][j]);
}
printf("\n");
}
printf("\n");
};
//布置雷
void layout_board(char board[ROWS][COLS], int row, int col) {
int count = easy_count;
while (count)
{
int x = rand() % row + 1;//通过时间戳来随机布置雷
int y = rand() % col + 1;//1-9
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
};
get_main(char mine[ROWS][COLS], int x, int y) {
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
};
//
void sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0;
int y = 0;
int win = 0;
//81-10
//判断是否合法
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");
display_board(mine, row, col);//给玩家看雷的位置
break;
}
//非雷
else
{
int count = get_main(mine, x, y);//这边传x,y
//排雷的信息
show[x][y] = count + '0';
display_board(show, row, col);//打印所有雷的位置
win++;
}
}
else
{
printf("不合法,请重新输入\n");
}
}
if (win == row * col - easy_count)
{
printf("恭喜你排雷成功!!\n");
display_board(mine, row, col);//打印所有雷的位置
}
}
text.c
#include "game.h" // 包含游戏相关函数的头文件
void game() {
// 布置雷的信息
char mine[ROWS][COLS] = { 0 }; // 初始化为11*11的棋盘
// 排雷的信息
char show[ROWS][COLS] = { 0 };
// 初始化:设置mine和show棋盘上所有格子为0和*
class_mine_show(mine, ROWS, COLS, '0');
class_mine_show(show, ROWS, COLS, '*');
// 打印棋盘(当前显示的是排雷信息)
display_board(show, ROW, COL); // 显示9*9的棋盘
// 布置雷
layout_board(mine, ROW, COL); // 在9*9的棋盘上布置雷
// 打印棋盘(此时是真正的雷区布局,仅用于调试)
// display_board(mine, ROW, COL);
// 排雷操作
sweep(mine, show, ROW, COL);
}
void meun() {
printf("************************\n");
printf("****1 开始 0 结束*****\n");
printf("************************\n");
};
void text() {
int input = 0;
srand((unsigned int)time(NULL)); // 利用时间戳初始化随机数生成器
do {
meun();
printf("请选择>");
scanf("%d", &input);
switch (input) {
case 1:
printf("扫雷开始\n");
game(); // 进行一次扫雷游戏
break;
case 0:
break; // 退出程序
default:
printf("不合法,请重新选择\n");
break;
}
} while (input);
}
int main() {
text(); // 运行游戏主界面
return 0;
}
游戏函数
`game` 函数初始化并执行整个游戏流程。首先,初始化地雷和展示的两个棋盘,然后显示展示棋盘,布置地雷,最后执行排雷操作。
- 菜单函数
`menu` 函数提供开始和结束选项的简单文本菜单。
主函数
在 `text` 函数中,我们通过一个简单的循环展示菜单,根据用户选择执行游戏或结束程序。
注:为什么要先初始化11*11?
在扫雷游戏中,通常需要在棋盘的边缘添加一个边界来帮助计算相邻地雷的数量。因此,在实际编程中,我们需要创建一个比实际游戏面板(99)大2的数组(1111),将边界部分标记为特定字符。
这样做的原因是为了简化计算相邻地雷数量的逻辑。例如,当玩家点击棋盘中心的一个格子时,它有8个邻居(上、下、左、右和四个对角线方向)。如果我们直接使用9*9的数组,那么对于边界上的格子来说,它们的邻居可能超出了数组范围。这会导致我们在处理边界格子时需要额外编写特殊逻辑。
而通过使用11*11的数组并用边界包围真实的游戏区域,我们可以确保所有格子都有完整的8个邻居。这样在计算相邻地雷数量时,我们只需要检查周围8个单元格即可,无需担心越界问题。
在代码中,ROWS
和COLS
分别表示1111的大棋盘大小,而ROW
和COL
则表示实际游戏区域的大小(99)。初始化时,我们将整个大棋盘都设置为'0'或'',然后在其中的99区域随机布置地雷,并进行后续的操作。
输出效果
四、总结
这个简单的扫雷游戏展示了C语言的基本语法和控制台输入输出的应用。通过使用随机数、循环、和条件语句,我们成功地实现了一个可玩的扫雷游戏