Home

Minesweeper

Minesweeper

In this project we will implement the Minesweeper game.

Minesweeper is played on a rectangle grid. We will use "ASCII art" to represent this grid, as follows (for a 4x4 grid):

  1 2 3 4 
A   1 . 1 
B 1 2 # 1 
C . . 1 1 
D . 2     

When the game starts, a number of bombs are hidden on random positions on the field. In every round, the player "touches" a cell of the field. If the cell contains a bomb, it explodes, the game ends, and the player loses. Otherwise, the cell is uncovered to show the number of bombs in the vicinity, that is, the number of neighboring cells that contain bombs. (Each cell has eight neighbors, including the diagonal neighbors. If the number is zero, the cell is displayed as a blank.)

When the user believes she knows the position of a bomb, she can flag that cell with a marker. It is allowed to change one's mind later and uncover a flagged cell.

If all cells are either uncovered or flagged, and the number of flags is equal to the number of bombs, then the game ends and the player wins.

Here is a run on a small field, ending in a defeat:

 
> minesweeper.scala 4 4 5
                    
  1 2 3 4 
A . . . . 
B . . . . 
C . . . . 
D . . . . 

What cell do you want to check? r3
I don't understand.
What cell do you want to check? a1
                    
  1 2 3 4 
A 1 . . . 
B . . . . 
C . . . . 
D . . . . 

What cell do you want to check? a3
                    
  1 2 3 4 
A 1 . 2 . 
B . . . . 
C . . . . 
D . . . . 

What cell do you want to check? b1
                    
  1 2 3 4 
A 1 . 2 . 
B 3 . . . 
C . . . . 
D . . . . 

What cell do you want to check? d1

BOOM BOOM BOOM BOOM BOOM
                    
  1 2 3 4 
A 1 . 2 . 
B 3 * . * 
C * * . . 
D * . . . 
And a more positive run:
> minesweeper.scala 4 4 5
                    
  1 2 3 4 
A . . . . 
B . . . . 
C . . . . 
D . . . . 

What cell do you want to check? a1
                    
  1 2 3 4 
A 2 . . . 
B . . . . 
C . . . . 
D . . . . 

What cell do you want to check? c1
                    
  1 2 3 4 
A 2 . . . 
B . . . . 
C 2 . . . 
D . . . . 

What cell do you want to check? a3
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . . . 
D . . . . 

What cell do you want to check? c3
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . 3 . 
D . . . . 

What cell do you want to check? d4
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . 3 . 
D . . .   

What cell do you want to check? a1
This cell is already uncovered.
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . 3 . 
D . . .   

What cell do you want to check? d3
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . 3 . 
D . . 1   

What cell do you want to check? c4
                    
  1 2 3 4 
A 2 . 4 . 
B . . . . 
C 2 . 3 1 
D . . 1   

What cell do you want to check? #b2
                    
  1 2 3 4 
A 2 . 4 . 
B . # . . 
C 2 . 3 1 
D . . 1   

What cell do you want to check? d1
                    
  1 2 3 4 
A 2 . 4 . 
B . # . . 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? c1
This cell is already uncovered.
                    
  1 2 3 4 
A 2 . 4 . 
B . # . . 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? b1
                    
  1 2 3 4 
A 2 . 4 . 
B 2 # . . 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? #a2
                    
  1 2 3 4 
A 2 # 4 . 
B 2 # . . 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? #a4
                    
  1 2 3 4 
A 2 # 4 # 
B 2 # . . 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? b4
                    
  1 2 3 4 
A 2 # 4 # 
B 2 # . 2 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? #b3
                    
  1 2 3 4 
A 2 # 4 # 
B 2 # # 2 
C 2 . 3 1 
D 1 . 1   

What cell do you want to check? c2
                    
  1 2 3 4 
A 2 # 4 # 
B 2 # # 2 
C 2 3 3 1 
D 1 . 1   

What cell do you want to check? #d2
                    
  1 2 3 4 
A 2 # 4 # 
B 2 # # 2 
C 2 3 3 1 
D 1 # 1   

YOU WIN!

Task

Write a Scala script minesweeper.scala to implement this game. If there are exactly three command line arguments, they are the number of rows, number of columns, and number of bombs. Otherwise, use 8 rows, 8 columns, and 6 bombs.

The field display should look as follows:

> minesweeper.scala 12 36 50
                    1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 
  1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 
A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
E . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
F . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
G . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
H . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
K . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
L . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
You can assume that the number of rows is at most 26 (so the letters A...Z will suffice), and the number of columns is at most 99.

The user enters a cell as g13, that is a letter (uppercase or lowercase is fine) followed by a number.

The user can also flag a cell by entering #g13.

If the cell is already uncovered, an error message is printed. (But it is okay to uncover a cell that was flagged before.)

When a bomb explodes, the whole field is displayed again, this time with the bombs.

Implementation

Since we want to practice case classes and sets, start your script like this:

 
var rows = 8
var cols = 8

case class Pos(row: Int, col: Int)
val bombs = scala.collection.mutable.Set[Pos]()
var field: Array[Array[Char]] = null

These are global variables storing the number of rows, number of columns, the positions of the bombs, and the current state of the field. When the game starts, set all cells of field to '.', and update them as cells are uncovered or flagged.

Note that field is a two-dimensional array, or, more precisely, an array of arrays. Two create such an array, you first need to create the outer array:

field = new Array[Array[Char]](rows)
At this point, every slot of field is null. You have to fill them in one by one:
for (row <- 0 until rows)
  field(row) = new Array[Char](cols)
Once this is done, you can use field as a two-dimensional grid. The cell at position row, col can be accessed as field(row)(col).