给大家分享一个有趣的项目——扫雷游戏,来深入理解和实践 C 语言中的数组和函数。这个游戏不仅能巩固我们对数组和函数的掌握,还能让我们在实践中体会编程的乐趣。
一、扫雷游戏简介
扫雷是一款经典的益智游戏,玩家需要在一个网格中找出所有的地雷,而不触动它们。游戏的主要操作是点击格子,如果点击的格子有地雷,则游戏结束;如果没有地雷,则会显示该格子周围的地雷数量。玩家需要根据这些数字来推断哪些格子有地雷,并在游戏板上标记出来。
二、游戏开发思路
我们将使用一个二维数组来表示游戏板,其中每个元素可以表示一个格子的状态(是否有地雷、是否被打开、是否被标记等)。通过函数来实现游戏的各个功能模块,如初始化游戏板、显示游戏板、处理玩家输入、检测游戏是否结束等。
三、代码实现详解
1.头文件与全局变量定义
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROWS 8
#define COLS 8
#define MINES 10
char board[ROWS][COLS]; // 游戏板
char display[ROWS][COLS]; // 显示给玩家的游戏板
我们定义了一个 8×8 的二维数组`board`来存储游戏板的实际状态,包括地雷位置。另一个同样大小的二维数组`display`用于存储显示给玩家的状态,初始时所有格子都是隐藏的。`MINES`定义了游戏板上的地雷数量。
2.初始化游戏板
void initializeBoard() {
// 初始化显示板,所有格子设为隐藏状态(用 '*' 表示)
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
display[i][j] = '*';
}
}
// 随机放置地雷
int count = 0;
srand(time(NULL));
while (count < MINES) {
int row = rand() % ROWS;
int col = rand() % COLS;
if (board[row][col] != 'M') { // 如果该位置还没有地雷
board[row][col] = 'M'; // 放置地雷
count++;
}
}
// 计算每个非地雷格子周围的地雷数
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] != 'M') {
int mineCount = 0;
// 检查周围的 8 个格子
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
int ni = i + di;
int nj = j + dj;
if (ni >= 0 && ni < ROWS && nj >= 0 && nj < COLS && board[ni][nj] == 'M') {
mineCount++;
}
}
}
board[i][j] = mineCount == 0 ? '0' : ('0' + mineCount);
}
}
}
}
```
`initializeBoard`函数负责初始化游戏板。首先将显示板的所有格子设为隐藏状态(用''表示)。然后,在游戏板上随机放置指定数量的地雷。对于每个非地雷格子,计算其周围 8 个方向上的地雷数量,并将该数量存储在游戏板对应的格子中。用字符'0'到'8'表示周围的地雷数量,'0'表示周围没有地雷。
3.显示游戏板
void printBoard() {
printf(" ");
for (int j = 0; j < COLS; j++) {
printf("%2d ", j + 1);
}
printf("\n");
printf(" +-");
for (int j = 0; j < COLS; j++) {
printf("--");
}
printf("-+\n");
for (int i = 0; i < ROWS; i++) {
printf("%2d| ", i + 1);
for (int j = 0; j < COLS; j++) {
printf("%c ", display[i][j]);
printf(" ");
}
printf("|\n");
}
printf(" +-");
for (int j = 0; j < COLS; j++) {
printf("--");
}
printf("-+\n");
}
`printBoard`函数用于将当前的游戏状态显示给玩家。在控制台中以网格形式打印显示板,行和列都从 1 开始编号,方便玩家输入坐标。
4.处理玩家输入
void handleInput() {
int row, col;
printf("Enter row and column (1-%d 1-%d): ", ROWS, COLS);
scanf("%d %d", &row, &col);
// 调整为从 0 开始的索引
row--;
col--;
// 检查输入是否有效
if (row < 0 || row >= ROWS || col < 0 || col >= COLS || display[row][col] != '*') {
printf("Invalid input! Please try again.\n");
return;
}
// 检查是否踩到地雷
if (board[row][col] == 'M') {
printf("Game Over! You hit a mine.\n");
// 显示所有地雷
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] == 'M') {
display[i][j] = 'M';
}
}
}
printBoard();
exit(0);
} else {
// 显示该格子周围的地雷数
display[row][col] = board[row][col];
}
}
`handleInput`函数负责处理玩家的输入。提示玩家输入行和列坐标,转换为从 0 开始的索引后检查输入是否有效(是否在范围内且格子未被打开)。如果玩家踩到地雷,游戏结束并显示所有地雷;否则,将该格子的状态更新为周围地雷的数量并显示给玩家。
5.检查游戏是否胜利
int checkWin() {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
// 如果还有非地雷格子未被打开,则游戏未胜利
if (board[i][j] != 'M' && display[i][j] == '*') {
return 0;
}
}
}
return 1;
}
`checkWin`函数用于检查玩家是否胜利。遍历游戏板,如果所有非地雷格子都被打开(即显示板上对应的格子不是''),则返回 1 表示胜利,否则返回 0。
6.主函数与游戏循环
int main() {
initializeBoard();
printBoard();
while (1) {
handleInput();
printBoard();
if (checkWin()) {
printf("Congratulations! You win!\n");
break;
}
}
return 0;
}
在`main`函数中,首先调用`initializeBoard`初始化游戏板并打印初始状态。然后进入游戏循环,不断处理玩家输入、更新和显示游戏板,直到玩家胜利或踩到地雷。
四、完整代码与运行示例
以下是完整的扫雷游戏代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROWS 8
#define COLS 8
#define MINES 10
char board[ROWS][COLS];
char display[ROWS][COLS];
void initializeBoard() {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
display[i][j] = '*';
}
}
int count = 0;
srand(time(NULL));
while (count < MINES) {
int row = rand() % ROWS;
int col = rand() % COLS;
if (board[row][col] != 'M') {
board[row][col] = 'M';
count++;
}
}
for (int i = 0; i < ROWS; i++){
for (int j = 0; j < COLS; j++) {
if (board[i][j] != 'M') {
int mineCount = 0;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
int ni = i + di;
int nj = j + dj;
if (ni >= 0 && ni < ROWS && nj >= 0 && nj < COLS && board[ni][nj] == 'M') {
mineCount++;
}
}
}
board[i][j] = mineCount == 0 ? '0' : ('0' + mineCount);
}
}
}
}
void printBoard() {
printf(" ");
for (int j = 0; j < COLS; j++) {
printf("%2d ", j + 1);
}
printf("\n");
printf(" +-");
for (int j = 0; j < COLS; j++) {
printf("--");
}
printf("-+\n");
for (int i = 0; i < ROWS; i++) {
printf("%2d| ", i + 1);
for (int j = 0; j < COLS; j++) {
printf("%c ", display[i][j]);
printf(" ");
}
printf("|\n");
}
printf(" +-");
for (int j = 0; j < COLS; j++) {
printf("--");
}
printf("-+\n");
}
void handleInput() {
int row, col;
printf("Enter row and column (1-%d 1-%d): ", ROWS, COLS);
scanf("%d %d", &row, &col);
row--;
col--;
if (row < 0 || row >= ROWS || col < 0 || col >= COLS || display[row][col] != '*') {
printf("Invalid input! Please try again.\n");
return;
}
if (board[row][col] == 'M') {
printf("Game Over! You hit a mine.\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] == 'M') {
display[i][j] = 'M';
}
}
}
printBoard();
exit(0);
} else {
display[row][col] = board[row][col];
}
}
int checkWin() {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] != 'M' && display[i][j] == '*') {
return 0;
}
}
}
return 1;
}
int main() {
initializeBoard();
printBoard();
while (1) {
handleInput();
printBoard();
if (checkWin()) {
printf("Congratulations! You win!\n");
break;
}
}
return 0;
}
运行示例: 1 2 3 4 5 6 7 8
+-------------------------+
1| * * * * * * * * |
2| * * * * * * * * |
3| * * * * * * * * |
4| * * * * * * * * |
5| * * * * * * * * |
6| * * * * * * * * |
7| * * * * * * * * |
8| * * * * * * * * |
+-------------------------+
Enter row and column (1-8 1-8): 1 1
1 2 3 4 5 6 7 8
+-------------------------+
1| 0 * * * * * * * |
2| * * * * * * * * |
3| * * * * * * * * |
4| * * * * * * * * |
5| * * * * * * * * |
6| * * * * * * * * |
7| * * * * * * * * |
8| * * * * * * * * |
+-------------------------+
Enter row and column (1-8 1-8): 1 2
1 2 3 4 5 6 7 8
+-------------------------+
1| 0 1 * * * * * * |
2| * * * * * * * * |
3| * * * * * * * * |
4| * * * * * * * * |
5| * * * * * * * * |
6| * * * * * * * * |
7| * * * * * * * * |
8| * * * * * * * * |
+-------------------------+
五、总结与拓展
通过这个扫雷游戏项目,我们深入实践了 C 语言中的数组和函数。数组用于存储和管理游戏板的状态,而函数将游戏逻辑模块化,使代码更清晰、更易于维护。我们实现了游戏的初始化、玩家交互、游戏状态更新和胜利检测等功能。
对于新手来说,这是一个很好的练习项目,可以巩固数组和函数的知识,并体会如何将这些基础知识应用到实际的项目中。你可以尝试对这个游戏进行拓展,例如增加标记地雷的功能、调整游戏难度级别(通过改变游戏板大小和地雷数量)、添加计时功能等,以进一步提升你的编程技能。
希望这个项目能为你带来乐趣和收获,如果你在实现过程中有任何问题或想法,欢迎在评论区留言交流!让我们一起享受编程带来的挑战与成就感。