## C语言实现经典扫雷游戏:控制台版详解

 

扫雷是Windows经典的益智游戏,自1990年诞生以来风靡全球。本文将带你用C语言在控制台环境下完整实现这款经典游戏,并深入讲解核心算法和实现细节。

 

### 游戏规则与核心机制

1. **游戏目标**:在不触雷的情况下揭开所有非雷格子

2. **核心玩法**:

   - 数字表示周围8格中的地雷数量

   - 空格(0值)自动展开相邻安全区域

   - 支持标记可疑地雷位置

3. **操作方式**:

   - W/A/S/D:移动光标

   - 空格:翻开格子

   - M:标记/取消标记地雷

   - R:重新开始游戏

   - Q:退出游戏

 

### 完整代码实现

 

```c

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <conio.h>

 

#define ROWS 9

#define COLS 9

#define MINES 10

 

// 游戏状态

enum GameState {

    PLAYING,

    WIN,

    LOSE

};

 

// 游戏数据结构

typedef struct {

    char board[ROWS][COLS]; // 实际棋盘(包含地雷和数字)

    char display[ROWS][COLS]; // 显示给玩家的棋盘

    int mines[MINES][2]; // 地雷位置

    int cursorRow, cursorCol; // 光标位置

    int remaining; // 剩余安全格子数

    enum GameState state; // 游戏状态

} Game;

 

// 初始化游戏

void initGame(Game* game) {

    game->state = PLAYING;

    game->remaining = ROWS * COLS - MINES;

    game->cursorRow = 0;

    game->cursorCol = 0;

    

    // 初始化棋盘

    for (int i = 0; i < ROWS; i++) {

        for (int j = 0; j < COLS; j++) {

            game->board[i][j] = '0';

            game->display[i][j] = '#';

        }

    }

    

    // 随机布雷

    srand(time(NULL));

    for (int i = 0; i < MINES; i++) {

        int row = rand() % ROWS;

        int col = rand() % COLS;

        

        // 确保不重复布雷

        if (game->board[row][col] == '*') {

            i--;

            continue;

        }

        

        game->board[row][col] = '*';

        game->mines[i][0] = row;

        game->mines[i][1] = col;

        

        // 更新周围数字

        for (int r = row-1; r <= row+1; r++) {

            for (int c = col-1; c <= col+1; c++) {

                if (r >= 0 && r < ROWS && c >= 0 && c < COLS && 

                    game->board[r][c] != '*') {

                    game->board[r][c]++;

                }

            }

        }

    }

}

 

// 绘制游戏界面

void drawGame(Game* game) {

    system("cls"); // 清屏

    

    printf(" 扫雷游戏 (剩余安全格子: %d)\n", game->remaining);

    printf(" 操作: WASD移动 空格翻开 M标记 R重来 Q退出\n\n");

    

    // 打印列号

    printf(" ");

    for (int j = 0; j < COLS; j++) {

        printf("%d ", j);

    }

    printf("\n +");

    for (int j = 0; j < COLS; j++) {

        printf("--");

    }

    printf("+\n");

    

    // 打印棋盘

    for (int i = 0; i < ROWS; i++) {

        printf("%d | ", i); // 行号

        for (int j = 0; j < COLS; j++) {

            // 高亮显示光标位置

            if (i == game->cursorRow && j == game->cursorCol) {

                printf("\033[1;31m"); // 红色高亮

            }

            

            printf("%c ", game->display[i][j]);

            

            if (i == game->cursorRow && j == game->cursorCol) {

                printf("\033[0m"); // 重置颜色

            }

        }

        printf("|\n");

    }

    

    printf(" +");

    for (int j = 0; j < COLS; j++) {

        printf("--");

    }

    printf("+\n");

    

    // 游戏状态提示

    if (game->state == LOSE) {

        printf("\n 游戏结束!你踩到地雷了!按R重新开始\n");

    } else if (game->state == WIN) {

        printf("\n 恭喜获胜!你成功避开了所有地雷!\n");

    }

}

 

// 展开空白区域(递归实现)

void revealEmpty(Game* game, int row, int col) {

    if (row < 0 || row >= ROWS || col < 0 || col >= COLS || 

        game->display[row][col] != '#' || game->board[row][col] == '*') {

        return;

    }

    

    game->display[row][col] = game->board[row][col];

    game->remaining--;

    

    if (game->board[row][col] == '0') {

        // 递归展开周围的空白区域

        for (int r = row-1; r <= row+1; r++) {

            for (int c = col-1; c <= col+1; c++) {

                if (!(r == row && c == col)) {

                    revealEmpty(game, r, c);

                }

            }

        }

    }

}

 

// 翻开格子

void revealCell(Game* game, int row, int col) {

    if (game->display[row][col] != '#' || game->state != PLAYING) {

        return;

    }

    

    if (game->board[row][col] == '*') {

        // 踩到地雷

        game->display[row][col] = '*';

        game->state = LOSE;

        

        // 显示所有地雷

        for (int i = 0; i < MINES; i++) {

            int r = game->mines[i][0];

            int c = game->mines[i][1];

            game->display[r][c] = '*';

        }

    } else {

        revealEmpty(game, row, col);

        

        // 检查是否获胜

        if (game->remaining == 0) {

            game->state = WIN;

        }

    }

}

 

// 标记格子

void markCell(Game* game, int row, int col) {

    if (game->display[row][col] == '#') {

        game->display[row][col] = 'M'; // 标记为地雷

    } else if (game->display[row][col] == 'M') {

        game->display[row][col] = '#'; // 取消标记

    }

}

 

// 处理键盘输入

void handleInput(Game* game) {

    int key = _getch();

    

    switch (key) {

        case 'w': case 'W': // 上移

            if (game->cursorRow > 0) game->cursorRow--;

            break;

        case 's': case 'S': // 下移

            if (game->cursorRow < ROWS-1) game->cursorRow++;

            break;

        case 'a': case 'A': // 左移

            if (game->cursorCol > 0) game->cursorCol--;

            break;

        case 'd': case 'D': // 右移

            if (game->cursorCol < COLS-1) game->cursorCol++;

            break;

        case ' ': // 翻开格子

            revealCell(game, game->cursorRow, game->cursorCol);

            break;

        case 'm': case 'M': // 标记地雷

            markCell(game, game->cursorRow, game->cursorCol);

            break;

        case 'r': case 'R': // 重新开始

            initGame(game);

            break;

        case 'q': case 'Q': // 退出游戏

            exit(0);

    }

}

 

int main() {

    Game game;

    initGame(&game);

    

    while (1) {

        drawGame(&game);

        handleInput(&game);

    }

    

    return 0;

}

```

 

### 核心算法解析

 

1. **地雷生成算法**:

   - 使用`srand(time(NULL))`初始化随机数种子

   - 循环生成随机坐标,确保位置不重复

   - 更新周围格子的数字计数

 

2. **递归展开算法**:

   - 当翻开空白格子(值为0)时递归展开相邻格子

   - 边界检查防止越界访问

   - 终止条件:遇到数字或已翻开的格子

 

3. **游戏状态管理**:

   - 使用`remaining`变量追踪剩余安全格子

   - 当翻开地雷时进入LOSE状态

   - 当安全格子全部翻开时进入WIN状态

 

### 编译与运行方法

 

1. 保存代码为`minesweeper.c`

2. 使用GCC编译:

   ```bash

   gcc minesweeper.c -o minesweeper

   ```

3. 运行游戏:

   ```bash

   ./minesweeper # Linux/Mac

   minesweeper.exe # Windows

   ```

 

### 游戏界面示例

```

  扫雷游戏 (剩余安全格子: 71)

 操作: WASD移动 空格翻开 M标记 R重来 Q退出

 

   0 1 2 3 4 5 6 7 8 

  +-----------------+

0 | # # # # # # # # # |

1 | # # # # # # # # # |

2 | # # # # # # # # # |

3 | # # # # # # # # # |

4 | # # # # # # # # # |

5 | # # # # # # # # # |

6 | # # # # # # # # # |

7 | # # # # # # # # # |

8 | # # # # # # # # # |

  +-----------------+

```

 

### 扩展改进方向

 

1. **难度分级**:

   ```c

   // 初级:9x9,10雷

   // 中级:16x16,40雷

   // 高级:16x30,99雷

   ```

 

2. **计时功能**:

   ```c

   #include <time.h>

   time_t startTime = time(NULL);

   // 游戏过程中显示已用时间

   ```

 

3. **首次翻开保护**:

   ```c

   // 确保第一次翻开不会是地雷

   if (firstMove) {

       while (board[row][col] == '*') {

           // 重新布置地雷

       }

   }

   ```

 

4. **保存/加载游戏**:

   ```c

   // 将游戏状态写入文件

   FILE *saveFile = fopen("savegame.dat", "wb");

   fwrite(game, sizeof(Game), 1, saveFile);

   fclose(saveFile);

   ```

 

本实现完整展示了扫雷游戏的核心逻辑,使用纯C语言标准库实现,不依赖第三方库,适合学习C语言的项目实践。通过这个项目,可以深入理解二维数组操作、递归算法、状态机等编程核心概念。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐