DP版扫雷

单击此处返回游戏库

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <windows.h> // 用于彩色输出
#include <conio.h>   // 用于_getch()
#include <iomanip>   // 用于setw()

using namespace std;

// 颜色定义
const int BLACK = 0;
const int BLUE = 1;
const int GREEN = 2;
const int CYAN = 3;
const int RED = 4;
const int MAGENTA = 5;
const int YELLOW = 6;
const int WHITE = 7;
const int GRAY = 8;
const int BRIGHT_BLUE = 9;
const int BRIGHT_GREEN = 10;
const int BRIGHT_CYAN = 11;
const int BRIGHT_RED = 12;
const int BRIGHT_MAGENTA = 13;
const int BRIGHT_YELLOW = 14;
const int BRIGHT_WHITE = 15;

// 设置控制台颜色
void setColor(int foreground, int background = BLACK) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), foreground | (background << 4));
}

// 游戏状态
enum GameState {
    MENU,
    SETUP,
    PLAYING,
    GAME_OVER,
    WIN
};

class Minesweeper {
private:
    int n, m;               // 地图大小
    int totalMines;         // 总雷数
    int remainingMines;     // 剩余雷数
    int flags;              // 已标记数
    time_t startTime;       // 游戏开始时间
    vector<vector<int>> board;      // 游戏板 (-1=雷, 0-8=周围雷数)
    vector<vector<bool>> revealed;  // 已揭示的格子
    vector<vector<bool>> marked;    // 已标记的格子
    GameState state;        // 游戏状态
    bool firstClick;        // 是否是第一次点击

public:
    Minesweeper() : state(MENU), firstClick(true) {}

    // 显示主菜单
    void showMenu() {
        system("cls");
        setColor(BRIGHT_WHITE);
        cout << "======================" << endl;
        cout << "      扫雷游戏       " << endl;
        cout << "======================" << endl;
        cout << "1. 开始游戏" << endl;
        cout << "2. 退出游戏" << endl;
        cout << "======================" << endl;
        cout << "请选择: ";
    }

    // 设置游戏参数
    void setupGame() {
        system("cls");
        setColor(BRIGHT_WHITE);
        cout << "======================" << endl;
        cout << "      游戏设置       " << endl;
        cout << "======================" << endl;

        // 输入地图大小
        cout << "输入地图大小 (行 列, 如 10 10): ";
        cin >> n >> m;

        // 输入雷数
        char choice;
        cout << "随机生成雷数? (y/n): ";
        cin >> choice;

        if (choice == 'y' || choice == 'Y') {
            // 随机生成雷数 (10%-20%的格子是雷)
            int minMines = n * m * 0.1;
            int maxMines = n * m * 0.2;
            totalMines = minMines + rand() % (maxMines - minMines + 1);
        } else {
            cout << "输入雷数 (不超过 " << n*m << "): ";
            cin >> totalMines;
        }

        remainingMines = totalMines;
        flags = 0;

        // 初始化游戏板
        board = vector<vector<int>>(n, vector<int>(m, 0));
        revealed = vector<vector<bool>>(n, vector<bool>(m, false));
        marked = vector<vector<bool>>(n, vector<bool>(m, false));

        state = PLAYING;
        firstClick = true;
    }

    // 放置地雷 (避开第一次点击的位置)
    void placeMines(int firstX, int firstY) {
        int minesPlaced = 0;
        while (minesPlaced < totalMines) {
            int x = rand() % n;
            int y = rand() % m;

            // 避开第一次点击的位置及其周围8格
            if ((abs(x - firstX) <= 1 && abs(y - firstY) <= 1) || board[x][y] == -1) {
                continue;
            }

            board[x][y] = -1; // -1表示雷
            minesPlaced++;

            // 更新周围格子的数字
            for (int i = max(0, x-1); i <= min(n-1, x+1); i++) {
                for (int j = max(0, y-1); j <= min(m-1, y+1); j++) {
                    if (board[i][j] != -1) {
                        board[i][j]++;
                    }
                }
            }
        }
    }

    // 显示游戏板
    void displayBoard() {
        system("cls");

        // 显示游戏信息
        setColor(BRIGHT_WHITE);
        cout << "雷数: " << remainingMines << "  用时: " << (time(nullptr) - startTime) << "秒" << endl;

        // 显示列号
        cout << "   ";
        for (int j = 0; j < m; j++) {
            cout << setw(2) << j << " ";
        }
        cout << endl;

        // 显示游戏板
        for (int i = 0; i < n; i++) {
            cout << setw(2) << i << " ";
            for (int j = 0; j < m; j++) {
                if (marked[i][j]) {
                    setColor(BRIGHT_RED);
                    cout << " F "; // 旗帜标记
                } else if (!revealed[i][j]) {
                    setColor(GRAY);
                    cout << " ■ "; // 未揭示的格子
                } else if (board[i][j] == -1) {
                    setColor(BRIGHT_RED);
                    cout << " * "; // 雷
                } else if (board[i][j] == 0) {
                    setColor(WHITE);
                    cout << "   "; // 空白
                } else {
                    setColor(board[i][j]); // 数字用不同颜色显示
                    cout << " " << board[i][j] << " ";
                }
                setColor(WHITE);
            }
            cout << endl;
        }

        // 显示操作提示
        cout << "\n操作: (WASD移动, 空格揭示, F标记, Q退出)" << endl;
        cout << "提示: 当数字周围标记数等于数字时,可以自动翻开周围格子" << endl;
    }

    // 检查数字周围标记是否正确
    bool checkSurroundingMarks(int x, int y) {
        if (board[x][y] <= 0) return false;

        int markedCount = 0;
        for (int i = max(0, x-1); i <= min(n-1, x+1); i++) {
            for (int j = max(0, y-1); j <= min(m-1, y+1); j++) {
                if (marked[i][j]) markedCount++;
            }
        }

        return markedCount == board[x][y];
    }

    // 揭示数字周围的格子
    void revealAroundNumber(int x, int y) {
        if (!checkSurroundingMarks(x, y)) return;

        for (int i = max(0, x-1); i <= min(n-1, x+1); i++) {
            for (int j = max(0, y-1); j <= min(m-1, y+1); j++) {
                if (!marked[i][j] && !revealed[i][j]) {
                    reveal(i, j);
                }
            }
        }
    }

    // 揭示格子
    void reveal(int x, int y) {
        if (x < 0 || x >= n || y < 0 || y >= m || revealed[x][y] || marked[x][y]) {
            return;
        }

        revealed[x][y] = true;

        // 如果是第一次点击,放置地雷
        if (firstClick) {
            placeMines(x, y);
            firstClick = false;
            startTime = time(nullptr);
        }

        // 如果是雷,游戏结束
        if (board[x][y] == -1) {
            state = GAME_OVER;
            return;
        }

        // 如果是空白格子,递归揭示周围格子
        if (board[x][y] == 0) {
            for (int i = max(0, x-1); i <= min(n-1, x+1); i++) {
                for (int j = max(0, y-1); j <= min(m-1, y+1); j++) {
                    if (!revealed[i][j]) {
                        reveal(i, j);
                    }
                }
            }
        }

        // 检查是否胜利
        checkWin();
    }

    // 标记格子
    void toggleMark(int x, int y) {
        if (revealed[x][y]) {
            // 如果是已揭示的数字,尝试自动翻开周围格子
            if (board[x][y] > 0) {
                revealAroundNumber(x, y);
            }
            return;
        }

        if (marked[x][y]) {
            marked[x][y] = false;
            flags--;
            remainingMines++;
        } else {
            marked[x][y] = true;
            flags++;
            remainingMines--;
        }

        // 检查是否胜利
        checkWin();
    }

    // 检查是否胜利
    void checkWin() {
        // 所有非雷格子都被揭示
        bool allRevealed = true;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] != -1 && !revealed[i][j]) {
                    allRevealed = false;
                    break;
                }
            }
            if (!allRevealed) break;
        }

        if (allRevealed) {
            state = WIN;
        }
    }

    // 游戏主循环
    void playGame() {
        int cursorX = 0, cursorY = 0;

        while (state == PLAYING) {
            displayBoard();

            // 显示光标位置
            COORD cursorPos;
            cursorPos.X = 4 + cursorY * 3;
            cursorPos.Y = 2 + cursorX;
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos);
            setColor(BRIGHT_YELLOW, BLACK);
            cout << "[" << (revealed[cursorX][cursorY] ? (board[cursorX][cursorY] == -1 ? "*" : (board[cursorX][cursorY] == 0 ? " " : to_string(board[cursorX][cursorY]))) : (marked[cursorX][cursorY] ? "F" : " ") ) << "]";
            setColor(WHITE);

            // 获取输入
            char input = _getch();

            // 处理输入
            switch (tolower(input)) {
                case 'w': if (cursorX > 0) cursorX--; break;
                case 'a': if (cursorY > 0) cursorY--; break;
                case 's': if (cursorX < n-1) cursorX++; break;
                case 'd': if (cursorY < m-1) cursorY++; break;
                case ' ': reveal(cursorX, cursorY); break;
                case 'f': toggleMark(cursorX, cursorY); break;
                case 'q': state = MENU; break;
            }
        }

        // 游戏结束处理
        if (state == GAME_OVER || state == WIN) {
            // 揭示所有格子
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    revealed[i][j] = true;
                }
            }

            displayBoard();

            if (state == GAME_OVER) {
                setColor(BRIGHT_RED);
                cout << "\n游戏结束! 你踩到雷了!" << endl;
            } else {
                setColor(BRIGHT_GREEN);
                cout << "\n恭喜你赢了! 用时: " << (time(nullptr) - startTime) << "秒" << endl;
            }

            setColor(WHITE);
            cout << "按任意键返回主菜单...";
            _getch();
            state = MENU;
        }
    }

    // 运行游戏
    void run() {
        while (true) {
            if (state == MENU) {
                showMenu();
                char choice = _getch();
                if (choice == '1') {
                    state = SETUP;
                    setupGame();
                } else if (choice == '2') {
                    break;
                }
            } else if (state == PLAYING) {
                playGame();
            }
        }
    }
};

int main() {
    srand(time(nullptr)); // 初始化随机数种子

    Minesweeper game;
    game.run();

    return 0;
}