CS 124, Spring 2014
Lab 9: MineSweeper

For this lab, you will create a simple version of the classic MineSweeper game. Start by creating an Eclipse project named lab9. Add a copy of the file MineSweeper.java to the project's src folder. You can find a copy in /classes/cs124.

Your work from this lab is due at the start of lab next Tuesday, as usual.


MineSweeper 101. The classic MineSweeper shows a 10-by-10 grid of squares. Ten of the squares are "mined." If you click on a square that is mined, you get blown up and you lose the game. If you click on a square that is not mined, the square is marked as safe (by changing color), and a number is shown in the square. The number tells how many mines there are in neighboring squares. (The neighbors are the squares next to the one that you clicked, horizontally, vertically, or diagonally. A square has eight neighbors if it is in the middle of the board. Squares along the edges of the board have fewer neighbors.) It is also possible to "flag" a square that you think is mined by right-clicking on the square. You win the game if you have marked every square as either safe or flagged and all the the squares that you have flagged are in fact mined. Note that if you change your mind about a flagged square, you can right-click it again, and it will change back to its initial unknown state.

Here are some screen shots of my version of the game, shown here at half-size. The left image shows a game in progress, the middle image shows a game that has been lost, and the right image shows a game that has been won:


Pink squares are squares that the user has flagged. Light green squares have been marked as safe. Dark green squares are in their original hidden state. Each safe square shows the number of mines in neighboring squares. When a game is over, I show a "B" in each square that actually does contain a mine. In the middle image, the user has lost by trying to mark one of the mined squares as safe. In the image on the right, the user has correctly flagged all the "B" squares and has marked all the other squares as safe. (Note that, usually, the zeros are not actually shown as they are in my game; only the non-zero mine counts are displayed.)

There is a MineSweeper game on the lab computers. It is called just "Mines" and is in the "Games" section of the applications menu. It works essentially like the version of the game described here, with more options and better graphics. If you have never played mines, it would be a good idea to try it out before trying to program it!

You should create a classic MineSweeper game by editing the file MineSweeper.java. This file is an exact copy of Grid.java from Lab 7, except that I have changed the name of the class to MineSweeper and I have changed the number of rows and the number of columns to 10.

This is not a trivial program to write! Remember that there are several aspects to GUI programming: instance variables hold the "state" of the program; the constructor sets things up when the program first starts up; event handlers such as mousePressed respond to user actions by changing the state; and paintComponent draws on the screen using the state of the program to decide what to draw. You will work on all of these aspects. The only event handler that you have to work on is mousePressed. But you will want to add a few more instance methods to carry out some of the tasks that you need to do.

Think first about the state of the game. You need to know which squares are mined. You can keep track of that with an array of boolean values. This would be an instance variable such as:

private boolean[][] mined;

You also need to know whether each square is marked as safe or flagged. There are three possibilities for a square: its original hidden state, marked as safe, or marked as flagged. There are several ways to keep track of this information. The easiest way is probably to use the gridColor color array that already exists, and use three different colors to represent the three different possibilities. If you do that, it is very useful to have constants to represent the three colors. In my program, I use

private final static Color HIDDEN_STATE = new Color(80,180,80);
private final static Color FLAGGED_STATE = Color.PINK;
private final static Color SAFE_STATE = new Color(150,250,150);

Finally, you need to know whether the game has ended or is still in progress. The need for this is not so obvious, but what you draw on the screen and how the program responds to mouse clicks can be very different when the game is progress and when it is over. You can use an instance varialbe of type boolean to record whether the game is over.

You will want to define a few new instance methods to do some of the tasks that are necessary to implement the program. You will definitely want a method for counting the number of mines in neighboring squares. This would be defined as

private int countMines(int r, int c) {  . . .

and it would return the number of mines in the squares that surround the square at row r, column c. You need to be careful at the edges of the board. Just remember that mined[r][c] is only valid when r is between 0 and 7 and c is between 0 and 7. You might also want a method that tests whether the user has won, since that's a relatively complicated test to make.

I'd also advise writing a setUpGame method to do all the set up that needs to be done before the start of the game. You can call this method in the constructor to set up the first game. It will be convenient to have this method if you decide to let the user start a new game after a game ends.

Part of setting up the game is to create the mined and colorGrid arrays and to fill them with appropriate values. The colorGrid should simply be filled with HIDDEN_STATE. For the mines, you have to place 10 mines at random locations on the board -- that means setting 10 positions in the array to true.

You need to program mousePressed to respond when the user clicks on a square. You need to think carefully about what needs to happen. For example, if the game is over, then clicking on a square should have no effect. If the user right-clicks on a hidden square, you should set the state of the square to FLAGGED. If the click is not a right-click when the user clicks on a HIDDEN square, two things can happen: Either there's a mine at that position — in which case the game ends and the user loses — or the position is safe — in which case the state of the state of the square is changed to SAFE. This is not a complete description of what happens when you click. You need to do some analysis.

To program the mouserPressed method, you need to know how to distinguish right-clicks from other clicks. One way to do this is by calling evt.isMetaDown(). This boolean-valued method is true if the user clicked with the right-mouse button. (On Mac, it is also true if the user holds down the Command key while clicking, so Mac users have an alternative way of right-clicking.)

You will want to notify the user when the game ends. One way to do that is with the JOptionPane.showMessageDialog method that was mentioned in Lab 7. For example, the statement

JOptionPane.showMessageDialog(this, "You Won!");

can be used at a point in mousePressed when the user has won the game by correctly marking all the squares as either SAFE or FLAGGED.

You are only required to implement a basic version of the MineSweeper game for this lab. But here are some things that you could do to improve it. Except for the first one listed, you can get some extra credit for these.