跳到主要内容

扫雷

· 阅读需 5 分钟
#include <algorithm>
#include <array>
#include <chrono>
#include <cctype>
#include <iomanip>
#include <iostream>
#include <queue>
#include <random>
#include <string>
#include <utility>
#include <vector>


using namespace std;


class Minesweeper {
private:
static constexpr int ROWS = 9;
static constexpr int COLS = 9;
static constexpr int MINES = 10;


struct Cell {
bool mine = false;
bool revealed = false;
bool flagged = false;
int adjacentMines = 0;
};


array<array<Cell, COLS>, ROWS> board{};
bool initialized = false;
bool gameOver = false;
bool won = false;


mt19937 randomEngine{
static_cast<unsigned int>(
chrono::steady_clock::now().time_since_epoch().count()
)
};


bool isInside(int row, int col) const {
return row >= 0 && row < ROWS && col >= 0 && col < COLS;
}


vector<pair<int, int>> getNeighbors(int row, int col) const {
vector<pair<int, int>> neighbors;


for (int dr = -1; dr <= 1; ++dr) {
for (int dc = -1; dc <= 1; ++dc) {
if (dr == 0 && dc == 0) {
continue;
}


int nextRow = row + dr;
int nextCol = col + dc;


if (isInside(nextRow, nextCol)) {
neighbors.emplace_back(nextRow, nextCol);
}
}
}


return neighbors;
}


void initializeBoard(int safeRow, int safeCol) {
vector<pair<int, int>> availableCells;


for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (row != safeRow || col != safeCol) {
availableCells.emplace_back(row, col);
}
}
}


shuffle(availableCells.begin(), availableCells.end(), randomEngine);


for (int i = 0; i < MINES; ++i) {
auto [row, col] = availableCells[i];
board[row][col].mine = true;
}


for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (board[row][col].mine) {
continue;
}


int count = 0;


for (auto [neighborRow, neighborCol] : getNeighbors(row, col)) {
if (board[neighborRow][neighborCol].mine) {
++count;
}
}


board[row][col].adjacentMines = count;
}
}


initialized = true;
}


void revealEmptyArea(int startRow, int startCol) {
queue<pair<int, int>> cellsToReveal;
cellsToReveal.emplace(startRow, startCol);


while (!cellsToReveal.empty()) {
auto [row, col] = cellsToReveal.front();
cellsToReveal.pop();


Cell& currentCell = board[row][col];


if (currentCell.revealed || currentCell.flagged) {
continue;
}


currentCell.revealed = true;


if (currentCell.adjacentMines != 0) {
continue;
}


for (auto [neighborRow, neighborCol] : getNeighbors(row, col)) {
Cell& neighbor = board[neighborRow][neighborCol];


if (!neighbor.revealed &&
!neighbor.flagged &&
!neighbor.mine) {
cellsToReveal.emplace(neighborRow, neighborCol);
}
}
}
}


void revealAllMines() {
for (auto& row : board) {
for (Cell& cell : row) {
if (cell.mine) {
cell.revealed = true;
}
}
}
}


bool checkWin() const {
for (const auto& row : board) {
for (const Cell& cell : row) {
if (!cell.mine && !cell.revealed) {
return false;
}
}
}


return true;
}


void revealSatisfiedNeighbors() {
bool revealedNewCell;


do {
revealedNewCell = false;


for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
Cell& currentCell = board[row][col];


if (!currentCell.revealed || currentCell.mine ||
currentCell.adjacentMines == 0) {
continue;
}


int flaggedCount = 0;


for (auto [neighborRow, neighborCol] : getNeighbors(row, col)) {
if (board[neighborRow][neighborCol].flagged) {
++flaggedCount;
}
}


if (flaggedCount != currentCell.adjacentMines) {
continue;
}


for (auto [neighborRow, neighborCol] : getNeighbors(row, col)) {
Cell& neighbor = board[neighborRow][neighborCol];


if (neighbor.revealed || neighbor.flagged) {
continue;
}


if (neighbor.mine) {
neighbor.revealed = true;
revealAllMines();
gameOver = true;
won = false;
cout << "\n你踩到地雷了!\n";
return;
}


revealEmptyArea(neighborRow, neighborCol);
revealedNewCell = true;
}
}
}
} while (revealedNewCell && !gameOver);
}


void handleWin() {
if (!checkWin()) {
return;
}


gameOver = true;
won = true;


for (auto& boardRow : board) {
for (Cell& boardCell : boardRow) {
if (boardCell.mine) {
boardCell.flagged = true;
}
}
}


cout << "\n恭喜你,扫雷成功!\n";
}


void printCell(const Cell& cell) const {
if (cell.flagged && !cell.revealed) {
cout << " F ";
} else if (!cell.revealed) {
cout << " # ";
} else if (cell.mine) {
cout << " * ";
} else if (cell.adjacentMines == 0) {
cout << " ";
} else {
cout << ' ' << cell.adjacentMines << ' ';
}
}


public:
void printBoard() const {
cout << "\n ";


for (int col = 1; col <= COLS; ++col) {
cout << setw(3) << col;
}


cout << "\n ";


for (int col = 0; col < COLS; ++col)
if( col )
{
cout << "---";
}
else cout<<"+--";


cout << "-\n";


for (int row = 0; row < ROWS; ++row) {
cout << setw(2) << row + 1 << " |";


for (int col = 0; col < COLS; ++col) {
printCell(board[row][col]);
}


cout << "\n";
}


cout << "\n# = 未翻开 F = 旗帜 * = 地雷\n";
}


void reveal(int row, int col) {
if (!isInside(row, col)) {
cout << "坐标超出范围。\n";
return;
}


Cell& cell = board[row][col];


if (cell.flagged) {
cout << "该格已插旗,请先取消旗帜。\n";
return;
}


if (cell.revealed) {
cout << "该格已经翻开。\n";
return;
}


if (!initialized) {
initializeBoard(row, col);
}


if (board[row][col].mine) {
board[row][col].revealed = true;
revealAllMines();
gameOver = true;
won = false;
cout << "\n你踩到地雷了!\n";
return;
}


revealEmptyArea(row, col);


handleWin();
}


void toggleFlag(int row, int col) {
if (!isInside(row, col)) {
cout << "坐标超出范围。\n";
return;
}


Cell& cell = board[row][col];


if (cell.revealed) {
cout << "已经翻开的格子不能插旗。\n";
return;
}


cell.flagged = !cell.flagged;
cout << (cell.flagged ? "已插旗。\n" : "已取消旗帜。\n");


if (cell.flagged && initialized) {
revealSatisfiedNeighbors();
}


if (!gameOver) {
handleWin();
}
}


void run() {
cout << "=============================\n";
cout << " C++ 控制台扫雷\n";
cout << "=============================\n";
cout << "棋盘大小: " << ROWS << " x " << COLS << "\n";
cout << "地雷数量: " << MINES << "\n";
cout << "操作格式:\n";
cout << " r 行 列 翻开格子,例如: r 3 5\n";
cout << " f 行 列 插旗或取消旗帜,例如: f 3 5\n";
cout << " q 退出游戏\n";


while (!gameOver) {
printBoard();


cout << "\n请输入操作: ";


char command;
cin >> command;


if (!cin) {
cout << "输入结束,游戏退出。\n";
return;
}


command = static_cast<char>(tolower(static_cast<unsigned char>(command)));


if (command == 'q') {
cout << "游戏结束。\n";
return;
}


if (command != 'r' && command != 'f') {
cout << "无效操作,请输入 r、f 或 q。\n";
string restOfLine;
getline(cin, restOfLine);
continue;
}


int row;
int col;


if (!(cin >> row >> col)) {
cout << "坐标格式错误。\n";
cin.clear();


string restOfLine;
getline(cin, restOfLine);
continue;
}


--row;
--col;


if (command == 'r') {
reveal(row, col);
} else {
toggleFlag(row, col);
}
}


printBoard();


if (won) {
cout << "\n游戏胜利!\n";
} else {
cout << "\n游戏失败!\n";
}
}
};


int main() {
Minesweeper game;
game.run();


return 0;
}