309 lines
9.4 KiB
C++
309 lines
9.4 KiB
C++
// File Name: Minesweeper.cpp
|
|
// Author: Zhang Anjun
|
|
// Date: 2025-07-16
|
|
// Version: 1.0
|
|
// © 2025 Zhang Anjun. All rights reserved.
|
|
|
|
#include "Minesweeper.h"
|
|
|
|
int main()
|
|
{
|
|
static bool firstGame = true;
|
|
if (firstGame) {
|
|
std::cout << "Welcome to Minesweeper" << std::endl;
|
|
firstGame = false;
|
|
minesweeper();
|
|
}
|
|
int menuChoice = 1;
|
|
while (menuChoice == 1) {
|
|
std::cout << "[1] Play again" << std::endl
|
|
<< "[2] Exit" << std::endl
|
|
<< std::endl
|
|
<< "Please enter your choice: ";
|
|
char ch;
|
|
if (!(std::cin >> menuChoice)) {
|
|
std::cin.clear();
|
|
while (std::cin.get(ch) && ch != '\n') {}
|
|
invalidOption();
|
|
continue;
|
|
}
|
|
while (std::cin.get(ch) && ch != '\n') {}
|
|
if (menuChoice == 1)
|
|
{
|
|
minesweeper();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void minesweeper()
|
|
{
|
|
char command;
|
|
int row = 0, col = 0, mineCount = 10;
|
|
const int maxRow = 8;
|
|
const int maxCol = 8;
|
|
Cell board[maxRow + 1][maxCol + 1] = {};
|
|
Cell(*ptrBoard)[maxCol + 1] = board;
|
|
int result = 0; // 0 == in progress, 1 == win, 2 == lose
|
|
initBoard(board, maxRow, maxCol);
|
|
while (result == 0) {
|
|
displayBoard(board, result, row, col, maxRow, maxCol, mineCount);
|
|
prompt(command, row, col, maxRow, maxCol);
|
|
flipCell(board, command, row, col, maxRow, maxCol);
|
|
result = getResult(board, maxRow, maxCol, mineCount);
|
|
}
|
|
displayBoard(board, result, row, col, maxRow, maxCol, mineCount);
|
|
if (result == 1) {
|
|
std::cout << "Congratulations, you won this game!" << std::endl;
|
|
}
|
|
else if (result == 2) {
|
|
std::cout << "Sorry, you lost this game." << std::endl;
|
|
}
|
|
}
|
|
|
|
void initBoard(Cell(*ptrBoard)[9], const int maxRow, const int maxCol) {
|
|
genMine(ptrBoard, maxRow, maxCol);
|
|
int row = 0, col = 0;
|
|
while (row <= maxRow) {
|
|
while (col <= maxCol) {
|
|
ptrBoard[row][col].status = 0;
|
|
int mineFlag[2] = {};
|
|
countCell(ptrBoard, mineFlag, row, col, maxRow, maxCol);
|
|
ptrBoard[row][col].mineNum = mineFlag[0];
|
|
col = col + 1;
|
|
}
|
|
col = 0;
|
|
row = row + 1;
|
|
}
|
|
}
|
|
|
|
int random(const int lBound, const int uBound) {
|
|
std::uniform_int_distribution<int> dist(lBound, uBound);
|
|
return dist(randomEngine());
|
|
}
|
|
|
|
static std::mt19937& randomEngine() {
|
|
static std::mt19937 eng{ std::random_device{}() };
|
|
return eng;
|
|
}
|
|
|
|
void genMine(Cell(*ptrBoard)[9], const int maxRow, const int maxCol) {
|
|
int i = 0, mine = 10, mineRow = 0, mineCol = 0;
|
|
while (mine > 0) {
|
|
mineRow = random(0, maxRow);
|
|
mineCol = random(0, maxCol);
|
|
if (!(ptrBoard[mineRow][mineCol].mine)) {
|
|
ptrBoard[mineRow][mineCol].mine = true;
|
|
mine = mine - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void countCell(Cell(*ptrBoard)[9], int (*mineFlag), const int row, const int col, const int maxRow, const int maxCol) {
|
|
int i = -1, j = -1;
|
|
while (i <= 1) {
|
|
while (j <= 1) {
|
|
if (row + i >= 0 && col + j >= 0 && row + i <= maxRow && col + j <= maxCol && !(i == 0 && j == 0)) {
|
|
if (ptrBoard[row + i][col + j].mine) {
|
|
mineFlag[0] = mineFlag[0] + 1;
|
|
}
|
|
if (ptrBoard[row + i][col + j].status == 1) {
|
|
mineFlag[1] = mineFlag[1] + 1;
|
|
}
|
|
}
|
|
j = j + 1;
|
|
}
|
|
j = -1;
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
void prompt(char &command, int &row, int &col, const int maxRow, const int maxCol)
|
|
{
|
|
bool valid = false;
|
|
char ch;
|
|
while (!valid) {
|
|
std::cout << "Command: F - Flag a cell R - Reveal a cell" << std::endl;
|
|
std::cout << "Input format: Command Row Column" << std::endl;
|
|
std::cout << "Please enter your move: ";
|
|
if (!(std::cin >> command >> row >> col)) {
|
|
std::cin.clear();
|
|
while (std::cin.get(ch) && ch != '\n') {}
|
|
invalidOption();
|
|
continue;
|
|
}
|
|
while (std::cin.get(ch) && ch != '\n') {}
|
|
if ((command == 'R' || command == 'F' || command == 'r' || command == 'f') && row <= maxRow+1 && row >= 1 && col <= maxCol+1 && col >= 1) {
|
|
valid = true;
|
|
if (command == 'r') {
|
|
command = 'R';
|
|
}
|
|
else if (command == 'f') {
|
|
command = 'F';
|
|
}
|
|
row = row - 1;
|
|
col = col - 1;
|
|
}
|
|
else {
|
|
invalidOption();
|
|
}
|
|
}
|
|
}
|
|
|
|
void displayBoard(Cell(&board)[9][9], int result, const int row, const int col, const int maxRow, const int maxCol, int mineCount) {
|
|
int i = 0, j = 0, flippedCell = 0, mineRemain = mineCount;
|
|
std::cout << std::endl << " | ";
|
|
while (j <= maxCol) {
|
|
setColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
std::cout << j + 1;
|
|
resetColor();
|
|
std::cout << " | ";
|
|
j = j + 1;
|
|
}
|
|
std::cout << std::endl;
|
|
j = 0;
|
|
while (i <= maxRow) {
|
|
displayLine(maxRow, maxCol);
|
|
std::cout << " ";
|
|
setColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
std::cout << i + 1;
|
|
resetColor();
|
|
std::cout << " ";
|
|
while (j <= maxCol) {
|
|
if (board[i][j].status == 0 && result == 0) {
|
|
std::cout << "| # ";
|
|
}
|
|
else if (board[i][j].status == 1) {
|
|
std::cout << "| ";
|
|
setColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
std::cout << "F";
|
|
resetColor();
|
|
std::cout << " ";
|
|
mineRemain = mineRemain - 1;
|
|
}
|
|
else if (board[i][j].mine && (board[i][j].status == 2 || result != 0)) {
|
|
std::cout << "| ";
|
|
setColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
std::cout << "*";
|
|
resetColor();
|
|
std::cout << " ";
|
|
}
|
|
else if (!(board[i][j].mine) && (board[i][j].status == 2 || result != 0)) {
|
|
std::cout << "| ";
|
|
if (board[i][j].mineNum != 0) {
|
|
setColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
std::cout << board[i][j].mineNum;
|
|
resetColor();
|
|
}
|
|
else {
|
|
std::cout << " ";
|
|
}
|
|
std::cout << " ";
|
|
}
|
|
j = j + 1;
|
|
}
|
|
std::cout << "|" << std::endl;
|
|
j = 0;
|
|
i = i + 1;
|
|
}
|
|
displayLine(maxRow, maxCol);
|
|
std::cout << std::endl;
|
|
if (result == 0) {
|
|
std::cout << "Mines remaining: " << mineRemain << std::endl << std::endl;
|
|
}
|
|
}
|
|
|
|
void displayLine(const int maxRow, const int maxCol) {
|
|
int j = 0;
|
|
std::cout << "---";
|
|
while (j <= maxCol) {
|
|
std::cout << "+---";
|
|
j = j + 1;
|
|
}
|
|
std::cout << "+" << std::endl;
|
|
}
|
|
|
|
void setColor(WORD color) {
|
|
static HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
SetConsoleTextAttribute(hConsole, color);
|
|
}
|
|
|
|
void resetColor() {
|
|
setColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
}
|
|
|
|
void flipCell(Cell(&board)[9][9], const char command, const int row, const int col, const int maxRow, const int maxCol) {
|
|
if (command == 'F') {
|
|
if (board[row][col].status == 0) {
|
|
board[row][col].status = 1;
|
|
}
|
|
else if (board[row][col].status == 1) {
|
|
board[row][col].status = 0;
|
|
}
|
|
else {
|
|
invalidOption();
|
|
}
|
|
}
|
|
else if (command == 'R') {
|
|
if (board[row][col].status == 0 && !(board[row][col].mine)) {
|
|
board[row][col].status = 2;
|
|
chord(board, row, col, maxRow, maxCol);
|
|
}
|
|
else if (board[row][col].status == 0 && board[row][col].mine) {
|
|
board[row][col].status = 2;
|
|
}
|
|
else if (board[row][col].status == 1) {
|
|
board[row][col].status = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void chord(Cell(&board)[9][9], const int row, const int col, const int maxRow, const int maxCol) {
|
|
int mineFlag[2] = {};
|
|
countCell(board, mineFlag, row, col, maxRow, maxCol);
|
|
if (mineFlag[1] == board[row][col].mineNum) {
|
|
int i = -1, j = -1;
|
|
while (i <= 1) {
|
|
while (j <= 1) {
|
|
if (row + i >= 0 &&
|
|
col + j >= 0 &&
|
|
row + i <= maxRow &&
|
|
col + j <= maxCol &&
|
|
!(i == 0 && j == 0) &&
|
|
board[row + i][col + j].status == 0) {
|
|
flipCell(board, 'R', row + i, col + j, maxRow, maxCol);
|
|
}
|
|
j = j + 1;
|
|
}
|
|
j = -1;
|
|
i = i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int getResult(Cell(&board)[9][9], const int maxRow, const int maxCol, int mineCount) {
|
|
int i = 0, j = 0, flippedCell = 0, result = 0;
|
|
while (i <= maxRow) {
|
|
while (j <= maxCol) {
|
|
if (board[i][j].mine && board[i][j].status == 2) {
|
|
result = 2;
|
|
}
|
|
else if (!(board[i][j].mine) && board[i][j].status == 2) {
|
|
flippedCell = flippedCell + 1;
|
|
}
|
|
j = j + 1;
|
|
}
|
|
j = 0;
|
|
i = i + 1;
|
|
}
|
|
if (result == 0 && flippedCell == (maxRow + 1) * (maxCol + 1) - mineCount) {
|
|
result = 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void invalidOption() {
|
|
std::cout << "Invalid option. Please try again." << std::endl << std::endl;
|
|
}
|