minesweeper
The object of this assignment is to write a program that allows a user to play the game "MINESWEEPER".
Minesweeper is one of the games that comes with the Windows Operating System. It consists of a grid of squares that represents a mine field. The object of the game is to locate all the mines without tripping any. If you manage to mark all mines or uncover all the squares that don't contain mines, you win. If you click on a square that contains a mine, you loose.
The links provided above will allow you to familiarize yourself with the game.
The Windows game keeps track of your score by keeping track of your time as well as the number of mines left to mark. We have not dealt with timing of computer or user actions yet so in our game we will keep score by assigning points to each guess that the user makes. In the Demo, uncovering a square that is not a mine 'costs' 1 point, marking a mine costs 0 points and incorrectly marking a mine costs 10 points.
We also do not yet have the experience to create a fully graphical display, so our game will be displayed using simple ASCII graphics. The demo game in the course directory will give you some idea of how this can work. In the demo, a covered square is represented with the character '~'. Each square contains a number which indicates how many mines are in the 8 squares adjacent to it. '0' means there are none, 8 is the maximum.
The squares themselves can easily be identified using (x,y) coordinates (x=row, y=column). Looking at a portion of the mine field:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
On the screen, mines will appear as an 'X'. The numbers indicate how many mines are in the adjacent squares.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|||||
|
|
|
|
|
|
||||
|
|
|
|
|
|
||||
|
|
|
|
|
|
||||
|
|
|
|
|
|
The demo:
There are, as always, a number of ways to solve this problem. The issue of how to clear an area when the user chooses an empty square is somewhat challenging and so this part of the program will be provided for you. You are, of course free to write your own.
It is suggested that you get the rest of the program working without the clear function before incorporating the one already written or writing your own.
It is not necessary for the internal representation of the mine field to be exactly the same as the one the user displayed [this is an extremely common and useful approach]. In fact, it is easier to manage the game if the internal representation is NOT the same as that displayed.
Since keeping track of neighbouring mines is of prime importance, representing the field internally as an array of integers makes the most sense. Then we can simply store the count of neighbouring mines at each location. 0 = clear (none); 1->8 = number of mines. 8 is the maximum number of mines that can be adjacent to one square. This leaves the problem of how to represent a mine itself. Since the value '9' is not being used for a count so it can be used to represent a mine.
When the board is displayed, the values 1-8 can be displayed as themselves, '0' is displayed as a blank, and a mine (9) is displayed as an 'X'.
A square that is still covered can be displayed using '~' instead of its count.
Dealing with the Edges of the Board
When marking squares, one would normally check the neighbouring squares:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This is fairly straight-forward when the square being examined is in the middle of the field but presents a problem on the edges since we need to treat them as special cases to avoid indexing problems (and the subsequent segmentation errors). A simple solution to this problem is to declare a board (array) with a built-in "cushion". A 10X10 board would then be declared: int field[12][12]; This way, rows 0 & 11 and columns 0 & 11 can be used when checking neighbours but are ignored otherwise (DO NOT place mines in the border!) The borders are not displayed. They are for internal use only.
There are 2 main approaches to keeping track of which squares have been uncovered. The first (assumed by ClickEmptySquare) is to keep a second array the same size as the first which is of type boolean. It starts off with all the elements set to "covered" (if the array is called "covered" then this would mean setting them all to true) and then resets each square as the user uncovers it.
The second approach lets us keep all the information in one array. If we assume the array contains counts of neighbouring mines, then our values will range from 0-9 (0=none; 9=this square has a mine). What we need is a way to distinguish between covered and uncovered squares without destroying the value we need for the count. Using the compliment (-3 vs 3) would work EXCEPT we have no way of representing a covered '0' (since there is no '-0'). What we can do though is use a different order of magnitude: 30 vs 3. This way 90 represents a covered mine, 10 represents a covered blank, 30 represents a covered square with 3 neighbouring mines. A covered square can easily be uncovered by dividing it's value by 10. Make sure you check to see if it's covered or not (val >= 10) BEFORE dividing by 10.
Anyone who has played computer games to any extent will be aware of the existence of "Cheat Codes": special keys or codes you can enter at various points in the game that will either reveal parts of the solution or make your character invulnerable somehow. These cheat codes were built in to the game by the developers for their own purposes. With them they can more thoroughly test their game before release. It allows them to get to specific parts (like different levels) quickly and try certain actions they would not otherwise be able to try easily. It allows them to do things like face "certain death" in the game and survive, thereby making sure the next step will work even if the character is not killed. Setting the cheat codes make the game predictable which is an absolute necessity when testing - especially if the game relies on the use of pseudo-random numbers. These cheat codes were NOT intended for the users (notice they're called 'CHEAT').
You may find it useful to build a cheat code into your program that will allow you to peek under the covered squares - thus enabling you to test each branch in your program without actually having to play an entire game.
The demo program includes a cheat code.
prototype: void ClickEmptySquare( int i, int j);
This function operates directly on the board and assumes the presence of a number of global variables:
The "field" is assumed to contain integers which indicate the number of neighbouring mines. 0= clear; 9= mine
The function also assumes the presence of another function called Uncover which does AT LEAST the following: (you must provide this yourself)
To use the ClickEmptySquare function in your program: copy the following files into your directory:
Modify the makefile to use your version if it is called something other than "mines.cc"
The use of functions is required for this assignment.
The 'board' may be displayed after each turn or you may use easycurses to allow screen addressing.
'Clicking' on a square can be accomplished by simply entering the coordinates of the square you wish to uncover.
Clearing a portion of the board: when you 'click' on an empty square (one with no mines nearby), the game usually opens up as much of the board as it can until it comes up against squares with mine-neighbours. A function called 'ClickEmptySquare' has been provided for you that accomplishes this task. The approriate makefile has also been provided.
There are several 'stages' at which this assignment can be considered complete. Note the maximum grade possible at each stage. This means that if you have a working program that meets the requirements of the first stage the maximum grade attainable will be a C+. NO exceptions. The maximum grade will only be given to programs that are well designed and properly documented. Poor design or inadequate documentation will lower your grade by at least half a letter grade.
STAGE 1: For a maximum grade of C+:
STAGE 2: For a maximum grade of B+:
STAGE 3: For a maximum grade of A:
Stage 1:
Stage 2:
Stage 3:
Updated: