CommonLounge Archive

Hands-on Project: Minesweeper

September 21, 2018

In this project, you’ll write a program that allows you to Minesweeper — a single-player puzzle video game. The objective of the game is to clear a rectangular board containing hidden “mines” (or bombs) without detonating any of them, with help from clues about the number of neighboring mines in each field.

If you don’t have C++ installed on your computer, you can do your work here: C++ Shell. Make sure you keep saving your work! Or, you can see C++ installation instructions here.

Minesweeper rules

Below is brief description of Minesweeper and its rules:


The player is initially presented with a grid of undifferentiated squares. Some randomly selected squares, unknown to the player, are designated to contain mines. Typically, the size of the grid and the number of mines are set by the user, by selecting from defined difficulty levels.

Beginner level. 9 x 9 grid with 10 mines total (top left). Top right is the timer.


In each move, the player can choose whether to reveal a particular square (if he / she thinks it is not a mine) or flagging it (if he / she thinks it is a mine).

If a square containing a mine is revealed, the player loses the game.

When starting your game, the first few moves will depend on your luck to find mine-free regions. But once you open a few amount of regions, you can use logic to find the regions that contain mines.

If no mine is revealed, a digit is instead displayed in the square, indicating how many adjacent squares contain mines; if no mines are adjacent, the square becomes blank, and all adjacent squares will be recursively revealed. (see image below).

Game board after one “reveal” move.


The player uses this information to deduce the contents of other squares, and may either safely reveal each square or flag the square as containing a mine.

Game board after a few moves.

If a square containing a mine is revealed, the player loses the game.


The game is won when all mine-free squares are revealed (since all mines have been located).

A (different) game where the player won.


You can try playing Minesweeper yourself here: Play it online.

In fact, we highly recommend you play the beginner level game a few times at-least so that you have a clear understanding of how the game works.

Once you are well versed with the rules, it’s time to start thinking about how to implement the Minesweeper game.


Since you are going to be writing a C++ program, here’s what the sample interaction would look like:

Enter the Difficulty Level
Press 0 for BEGINNER     (9  * 9  cells and 10 mines)
Press 1 for INTERMEDIATE (16 * 16 cells and 40 mines)
Press 2 for ADVANCED     (16 * 30 cells and 99 mines)
0
    0 1 2 3 4 5 6 7 8
0   . . . . . . . . .   0
1   . . . . . . . . .   1
2   . . . . . . . . .   2
3   . . . . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
10 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 0 0 s
    0 1 2 3 4 5 6 7 8
0   0 1 . . . . . . .   0
1   0 1 1 . . . . . .   1
2   0 0 1 . . . . . .   2
3   1 1 1 . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
10 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 0 2 f
    0 1 2 3 4 5 6 7 8
0   0 1 F . . . . . .   0
1   0 1 1 . . . . . .   1
2   0 0 1 . . . . . .   2
3   1 1 1 . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
9 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 1 3 s
    0 1 2 3 4 5 6 7 8
0   0 1 F . . . . . .   0
1   0 1 1 1 . . . . .   1
2   0 0 1 . . . . . .   2
3   1 1 1 . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
9 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 1 4 s
    0 1 2 3 4 5 6 7 8
0   0 1 F 1 0 0 0 1 .   0
1   0 1 1 1 0 0 0 1 .   1
2   0 0 1 1 2 1 2 1 .   2
3   1 1 1 . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
9 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 3 3 f
    0 1 2 3 4 5 6 7 8
0   0 1 F 1 0 0 0 1 .   0
1   0 1 1 1 0 0 0 1 .   1
2   0 0 1 1 2 1 2 1 .   2
3   1 1 1 F . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
8 flags left
...
... after many more moves
... 
Enter your move, (row, column, safe(s)/flag(f)) -> 2 8 s
    0 1 2 3 4 5 6 7 8
0   0 1 F 1 0 0 0 1 .   0
1   0 1 1 1 0 0 0 1 .   1
2   0 0 1 1 2 1 2 1 1   2
3   1 1 1 F 3 . 4 . 2   3
4   F 1 1 1 3 . 4 . 2   4
5   1 2 1 1 1 1 2 2 2   5
6   0 1 F 1 0 0 0 1 .   6
7   0 1 1 1 0 0 0 1 1   7
8   0 0 0 0 0 0 0 0 0   8
    0 1 2 3 4 5 6 7 8
6 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 1 8 s
    0 1 2 3 4 5 6 7 8
0   0 1 F 1 0 0 0 1 F   0
1   0 1 1 1 0 0 0 1 F   1
2   0 0 1 1 2 1 2 1 1   2
3   1 1 1 F 3 F 4 F 2   3
4   F 1 1 1 3 F 4 F 2   4
5   1 2 1 1 1 1 2 2 2   5
6   0 1 F 1 0 0 0 1 F   6
7   0 1 1 1 0 0 0 1 1   7
8   0 0 0 0 0 0 0 0 0   8
    0 1 2 3 4 5 6 7 8
You won!!! :)

Here’s another sample interaction:

Enter the Difficulty Level
Press 0 for BEGINNER     (9  * 9  cells and 10 mines)
Press 1 for INTERMEDIATE (16 * 16 cells and 40 mines)
Press 2 for ADVANCED     (16 * 30 cells and 99 mines)
0
    0 1 2 3 4 5 6 7 8
0   . . . . . . . . .   0
1   . . . . . . . . .   1
2   . . . . . . . . .   2
3   . . . . . . . . .   3
4   . . . . . . . . .   4
5   . . . . . . . . .   5
6   . . . . . . . . .   6
7   . . . . . . . . .   7
8   . . . . . . . . .   8
    0 1 2 3 4 5 6 7 8
10 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 0 0 s
    0 1 2 3 4 5 6 7 8
0   0 0 1 . . . . . .   0
1   0 0 1 1 1 1 2 . .   1
2   0 0 0 0 0 0 1 . .   2
3   1 1 1 0 0 0 2 . .   3
4   . . 1 0 0 0 2 . .   4
5   1 1 1 0 0 0 2 . .   5
6   0 0 0 0 0 0 1 . .   6
7   0 0 0 1 1 1 1 . .   7
8   0 0 0 1 . . . . .   8
    0 1 2 3 4 5 6 7 8
10 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> 0 3 s
    0 1 2 3 4 5 6 7 8
0   0 0 1 # . . # . .   0
1   0 0 1 1 1 1 2 . #   1
2   0 0 0 0 0 0 1 # .   2
3   1 1 1 0 0 0 2 . .   3
4   . # 1 0 0 0 2 # .   4
5   1 1 1 0 0 0 2 # .   5
6   0 0 0 0 0 0 1 . .   6
7   0 0 0 1 1 1 1 . #   7
8   0 0 0 1 # . . # .   8
    0 1 2 3 4 5 6 7 8
You lost! :(

Modeling Minesweeper

Before you start implementing Minesweeper, let’s take some time to think about how you are going to structure you code.


What do you think is a good way to store the board and game state? What variables do you need?

Think about it for a bit before seeing the answer below.


Answer:

The best way to model the Minesweeper game is by using two 2-dimensional arrays. The first array, gameBoard, is used to store the current state of the game (which squares are yet to be played on, which squares have been revealed and which squares have been flagged). The second array, mineBoard, stores where the hidden mines are.

You can use different characters to represent the current state each square.

  • . denotes the initial state of a square (all squares start this way)
  • F denotes that a flag has been placed by the user
  • chars 0 to 8 indicate the number of mines in its neighbourhood. 0 would imply that none of the squares around it are mines.
  • # denotes that a mine is present there (only used if the player loses)

Implementing Minesweeper

Okay, let’s start implementing the game.

Step 1: Initializing the boards

Have global int variables NROWS, NCOLUMNS and NMINES. Set them to some small values, such as Rows: 5, Columns: 4, Mines: 3. (this makes it easy to debug things as you are implementing).

Then, you need to initialize the mineBoard and gameBoard arrays. Clear both arrays, and then randomly choose the locations of the mines and mark them on the mineBoard.

Step 2: Choose how to display the board, and the input format

We recommend receiving the input in the format: row, column, safe(s)/flag(f). For example,

0 3 s

means mark row 0 column 3 as safe (reveal).

And

3 2 f

means flag row 3 column 2.


We recommend displaying the board like so:

    0 1 2 3 4 5 6 7 8
0   0 0 1 . . . . . .   0
1   0 0 1 1 1 1 2 . .   1
2   0 0 0 0 0 0 1 . .   2
3   1 1 1 0 0 0 2 . .   3
4   . . 1 0 0 0 2 . .   4
5   1 1 1 0 0 0 2 . .   5
6   0 0 0 0 0 0 1 . .   6
7   0 0 0 1 1 1 1 . .   7
8   0 0 0 1 . . . . .   8
    0 1 2 3 4 5 6 7 8
10 flags left

Having row numbers and column numbers on both sides makes it easier for you when playing. Don’t forget to display the number of flags left.

Step 3: Making a move

This is the main part of the project. Spend some time thinking about (a) the helper functions you will need, and (b) what happens in each case when the user inputs a move.

We strongly recommend thinking about the above two things for a while before reading below how the official solution does it.


The official solution uses the following helper functions:

bool isValid(int row, int col) - whether or not (row, col ) is within the bounds of the game.

int countAdjacentMines(int row, int col, char mineBoard[][MAXSIDE]) — count the number of mines in the adjacent squares

void uncoverBoard(char gameBoard[][MAXSIDE], char mineBoard[][MAXSIDE], int row, int col)reveal the square at (row, col). This function recursively calls itself if the revealed number is 0.

void markMines(char gameBoard[][MAXSIDE], char mineBoard[][MAXSIDE], bool won) — mark all the remaining mines on the board using F or # depending on whether won is true or false


Here’s the game logic when a move is made:

If player chooses to reveals a particular square:

  • If the cell has already been played on, (gameBoard[row][col] != '.'), then the move is illegal.
  • If the cell contains a mine (mineBoard[row][col] == '#'), then the player just lost. Display game board with all the mines uncovered, and a ‘Game Over’ message.
  • If the cell is safe, then uncover it (and recursively uncover its neighbours if current cell is 0).

If player chooses to flag a particular square:

  • If the cell is already revealed, then prompt the user that the operation is illegal.
  • Else, just toggle between F and .. That is, allow user to flag a square, or to unflag an already flagged square.

Winning condition: Once the user has uncovered all the safe cells, display a ‘You Win! message. Be creative, make it cheerful!.

Step 4: Choosing difficulty level

Finally, write a function void chooseDifficultyLevel() for choosing the difficult level. Here are the recommended values for the levels:

  • Beginner - Rows: 9, Columns: 9, Mines: 10
  • Intermediate - Rows: 16, Columns: 16, Mines: 40
  • Advanced - Rows: 16, Columns: 30, Mines: 99

Call this function right at the beginning to let the user choose the difficulty level.

Step 5: Handling more than 10 rows / columns

Finally, note that the number of rows / columns can be greater than 10. We recommend using a to refer to 11th row / column, b for 12th, c for 13th, and so on.

For example, here’s how the official solution displays the intermediate board:

Enter the Difficulty Level
Press 0 for BEGINNER     (9  * 9  cells and 10 mines)
Press 1 for INTERMEDIATE (16 * 16 cells and 40 mines)
Press 2 for ADVANCED     (16 * 30 cells and 99 mines)
1
    0 1 2 3 4 5 6 7 8 9 a b c d e f
0   . . . . . . . . . . . . . . . .   0
1   . . . . . . . . . . . . . . . .   1
2   . . . . . . . . . . . . . . . .   2
3   . . . . . . . . . . . . . . . .   3
4   . . . . . . . . . . . . . . . .   4
5   . . . . . . . . . . . . . . . .   5
6   . . . . . . . . . . . . . . . .   6
7   . . . . . . . . . . . . . . . .   7
8   . . . . . . . . . . . . . . . .   8
9   . . . . . . . . . . . . . . . .   9
a   . . . . . . . . . . . . . . . .   a
b   . . . . . . . . . . . . . . . .   b
c   . . . . . . . . . . . . . . . .   c
d   . . . . . . . . . . . . . . . .   d
e   . . . . . . . . . . . . . . . .   e
f   . . . . . . . . . . . . . . . .   f
    0 1 2 3 4 5 6 7 8 9 a b c d e f
40 flags left
Enter your move, (row, column, safe(s)/flag(f)) -> b c s
    0 1 2 3 4 5 6 7 8 9 a b c d e f
0   . . . . . . . . . . . . . . . .   0
1   . . . . . . . . . . . . . . . .   1
2   . . . . . . . . . . . . . . . .   2
3   . . . . . . . . . . . . . . . .   3
4   . . . . . . . . . . . . . . . .   4
5   . . 1 1 1 2 . . . . . . . . . .   5
6   . . 1 0 0 1 2 2 2 3 . . . . . .   6
7   . . 2 1 1 0 0 0 0 1 . . . . . .   7
8   . . . . 1 0 0 0 0 1 1 1 2 . 3 1   8
9   . . 2 1 1 0 0 0 0 0 0 0 1 . 1 0   9
a   . 2 1 0 0 0 0 0 0 0 0 0 1 1 1 0   a
b   . 1 0 0 1 1 1 0 1 1 1 0 0 0 0 0   b
c   . 1 2 1 3 . 2 1 2 . 1 0 0 0 0 0   c
d   . . . . . . . . . . 2 1 1 1 1 0   d
e   . . . . . . . . . . . . . . 1 0   e
f   . . . . . . . . . . . . . . 1 0   f
    0 1 2 3 4 5 6 7 8 9 a b c d e f
40 flags left

Using characters a, b, c, … etc allows you to display the board cleanly.


However, this means the input row and col will now be char and not int.

To display the board, you will find it helpful to define a function char indexToChar(int index).

Similarly, to handle the user inputs like b c s (as in the game above), you will find it helpful to define a function int charToIndex(char ch).

a maps to 10, b to 11, and so on.

Step 6: Disallow losing on first move (Optional)

Currently, it is possible for the user to loose the game on the very first move. In most implementations of minesweeper, the game guarantees that this cannot happen — i.e. the user must always be safe on the first move (since it would be very unfortunate to lose on the first move).

If the user uncover a cell with a mine on the first move, you need to relocate that mine to a different cell, and mark the current cell as safe.

Step 7: Play a bunch of times!!

Once you have done all of the above, play the game a few times!! After all, you already did all the hard work!

Congratulations

This was a very challenging project. Congratulations on implementing the Minesweeper game in C++. Give yourself a pat on the back! We are proud of you!

Solution

Here’s the official solution to this project: C++ Solution: Minesweeper.


© 2016-2022. All rights reserved.