CS 124, Spring 2021
Lab 7: Minesweeper
This is a two-week lab. There is not a huge amount of code to write, but conceptually, it is non-trivial. You will be required to turn in some work for each of the two weeks. The first part is due by midnight on Monday, March 22 and will not be accepted late; you will get 10 points for turning in some working code at that point, as specified below. The completed program will be due by midnight on Monday, March 29, for full credit. It will be accepted late until noon on Saturday, April 3, with a 10% penalty. The late due date has been changed. Lab 7 will be accepted late until midnight on Monday, April 5.
Furthermore, you have the option of working with a partner on this lab. The coding for the two-person version is a little more complex than for the one-person version.
The starting point for either version is the file Minesweeper.java. This is a JavaFX program, and it needs to be in an Eclipse project that supports JavaFX.
Minesweeper Games
A minesweeper game is played on a grid of squares. Each square might hold one hidden mine. If you click a square that holds a mine, you get blown up and lose the game. If you click a safe square, the square changes color and your mine detector can tell you how many mines there are in neighboring squares. If there is a mine in any of those squares, then the number of mines in neighboring squares is shown in the square that you clicked. If there is no mine in any neighboring square, the square that you clicked is left blank. "Neighboring" means next to the square horizontally, vertically, or diagonally. A square that is not at the edge of the board has eight neighbors. A square on an edge has fewer neighbors.
You can "flag" a square that you think contains a mine by shift-clicking it. In my version of the game, a flagged square is colored pink. If you shift-click a flagged square, you remove the flag and the square goes back to its original state. Clicking a flagged square without holding down the shift key has no effect; this protects you from accidentally clicking a mine. Note that a flagged square does not necessarily contain a mine. The flag just means that the user believes that it contains a mine.
The classic version of the game, which I think of as the Windows version since that is where I first encountered it, is played on a 10-by-10 grid. The user can click any square at any time. In my version, there are 10 mines. The user wins by flagging all the squares that contains mines and marking all the other squares as safe. Writing this version of the game is the single-person assignment for this lab. You should make sure that you understand the game before you start. You can play the game here. (If you want to start a new game, you have to reload this page.)
Minesweepter (Windows Style)
There is an alternative version of the game that I think of the Macintosh version, since I originally played it on my old Macintosh computer. in this version, the goal is to find a safe path from the square in the upper left corner of the grid to the square in the lower right corner of the grid. You do not need to find all the mines. Because you are trying to find a safe path, you can only click on a square that is next to a square that has already been marked as safe. Clicking on any other square will be ignored. As in the previous version, you can flag a square that you think contains a mine by shift-clicking it. My version is played on a 20-by-20 grid with 50 mines. Writing this version of the game is the two-person assignment for this lab. You should make sure that you understand the game before you start. You can play the game here. (If you want to start a new game, you have to reload this page.)
Minesweepter (Macintosh Style)
Because the grid is so large, clicking your way across the grid can get tedious. Note that if you click a square, and there are no mines in any neighboring square, then it is completely safe to click each of the neighboring squares. It is possible to automate that process, so that when you click on a square, the program will reveal not just that square but also any connected squares that can be seen to be safe. To apply this idea to the above Macintosh version of the game, turn on the "Use recursion" option. Implementing this option is not a required part of the assignment, but you might want to do it for a couple points of extra credit. If so, you should discuss with me how to go about coding the implementation.
Writing the Program
Note first of all that the assignment here is not just to produce a working program. You are expected to write a well-designed program using subroutines to break the program up into reasonable-sized chunks, and using global variables only to represent the global state of the program. Don't just start coding! Think about the problem first. Read the whole lab carefully. Discuss the problem with your partner if you have one.
The starting point for the program is Minesweeper.java. The program already has a 400-by-400 pixel "canvas" on which you will draw the game board. The program is already set up to call the doMousePressed() method when the user clicks the canvas. The program has a menu bar containing a "Control" menu with "New Game" and "Quit" commands, and it is set up to call the newGame() method when the user chooses the "New Game" command. You will need to do some work on both of these methods and add some new subroutines as well.
Global data. Since you are working with a grid, it will be natural to use two-dimensional arrays to store information about the squares in the grid. You need to represent two things: First, each square either contains or does not contain a mine, and you need to keep track of the answer for each square. Second, each square can be in three different states, represented by the three colors: I think of a dark green square as being in the "hidden" state, a light green square as being "shown", and a pink square as being "flagged". You should also keep track of whether or not a game is currently in progress, since the appearance of the board and the response to mouse clicks changes when the game is over.
Starting a game. The newGame() method is responsible for starting a new game. You should think about what has to happen when a game begins. The global data has to be set up correctly for the start of a game, and the board has to be drawn in its initial state.
Drawing the board. The easiest way to manage the screen in a game is to always redraw the entire board from scratch whenever any change needs to be made. To do that, you will need a method for drawing the board. That method should not have any parameters. It should use the global data, and based on that data it should decide exactly what to draw. When the program makes any change to the global data, it should call the draw method to redraw the board. That will ensure that the appearance of the board is always consistent with the data.
Responding to mouse clicks. The mousePressed() method has to be programmed to take appropriate actions when the user clicks the canvas. You will need to know which of the squares in the grid was clicked; that is,you will need the row number and the column number. The row number can be computed easily from the y-coordinate of the mouse position. The column number can be computed easily from the x-coordinate of the mouse position. (It's easy to get this confused, since we usually think of row/column and x/y, but its the y-direction that corresponds to rows and the x-direction that corresponds to columns.)
For the Macintosh version, you should only process a click on a square if that square is next to a square that is already in the "shown" state. For the Windows version, process a click on any square.
If the user shift-clicks a "hidden" square, its state should change to "flagged". If the user shift-clicks a "flagged" square, its state should change to "hidden". Shift-clicking a "shown" square has no effect.
For a click without holding down the shift key, if the user clicks a "hidden" square, it should be "shown". At that point, if the square contains a mine, the game is over and the user loses. If the user clicks a "flagged" square or a "shown" square, nothing happens.
Requirement for Lab7a. You will need to turn in some working code by midnight on Monday, March 22. This assignment will not be accepted late. You should turn in a copy of Minesweeper.java for Lab7a on the submission web site. The program should be able to draw the basic grid of squares, and it should be able to respond to mouse clicks by changing the color of a square that is clicked. Ideally, you should also have placed the mines on the board at the beginning of the game, but that is not a requirement at this point. If you do place the mines, I suggest drawing them on the board, to make sure they are there, even though they would not ordinarily be drawn until the game ends.
You get 10 points for turning in the program with at least the minimal features implemented. There are no requirements at this point for style or commenting.
If you are submitting a program for two people, make sure that both names are in the comment at the top of the program.
Requirements for Lab7b. The final program is due by midnight on Monday, March 29 (or late by noon on Saturday, April 3). You should turn in a copy of the completed Minesweeper.java for Lab7b on the submission web site. At that point, you should have a fully working program that uses good programming style and commenting. I also encourage you to add some feature for a little extra credit. That might be recursively showing safe squares. Or you might add commands to the "Control" menu to make it possible to start the game with different numbers of mines (to make the game easier or harder). Or maybe add a "Theme" menu that makes it possible to change the colors that are used to draw the board. Or show images of mines and flags (see Section 6.2.3 and the g.drawImage() method is Section 6.2.4). If you are working on your own, you could also get extra credit for doing the Macintosh version instead of the Windows version. Maybe you can think of some other feature to add.
For the Windows version: You will use a 10-by-10 grid of squares. At the start of a game, you need to place 10 mines at random positions on the board. The user wins that game by correctly "flagging" the 10 squares that contain mines, and marking the other 90 squares as "shown".
For the Macintosh version: You will use a 20-by-20 grid. (If you think that the squares are too small, you can change the start() method to increase the size of the board to some larger multiple of 20.) At the start of the game, you need to place 50 mines at random positions on the board. Furthermore, there cannot be a mine in the square in the upper left corner or in the square in the lower right corner. (In my version, I actually ensure that there are no mines in the four squares in the upper left, since a mine in one of those squares can make it very difficult for the user to get started on the path to the finish.) The square in the upper left corner must be automatically marked as "shown" at the start of the game. (My version marks the four squares in the upper left corner, since they are all known to be safe.) The user wins when they mark the square in the lower right corner as "shown". Don't forget that the user is only allowed click a square that is next to some square that is already shown.
For both versions. If the game is over, nothing should happen when the user clicks the board. The user can use the "New Game" menu command to start a new game. When the draw method draws a square that is in the "shown" state, it must show the number of mines in neighboring squares. This means you have to count the mines. We will have some discussion in class about how to do that. If the game has ended, you should show the position of every mine on the board. And you should give some indication that the game has been won or lost. In my programs, I draw "Winner!" or "Boom!" in large translucent text across the board. (Writing the text diagonally like I do in the versions on this page requires some advanced graphics. If you want to try it, see Section 13.2.3 and discuss the details with me.)