This is an additional exercise for chapter 7 of my text. Chapter 7 and this web page will prepare you to write a minesweeper game in Python. A minesweeper is a wooden hulled ship that is used by the Navy to find and disarm mines in the ocean. My father-in-law served on a minesweeper in the late 50's and early 60's. They are not as big as most ships and riding out a typhoon on a minesweeper is not advisable. Just ask my father-in-law.
The game of minesweeper has been around for a while. It is a Windows application that has been included on Windows since the early versions of Windows, including Windows 3.1.
The game begins with a 9x9 matrix of tiles that cover 10 mines. Some tiles hide mines, most do not. You play the game by clicking on tiles to reveal what lies beneath. If you expose a mine, you blow yourself up and the game is over. Your goal is to expose all the tiles that do not cover bombs in as little time as possible.
You are aided along the way when you uncover tiles by numbers that appear in squares that are adjacent to a bomb. When a square is adjacent to zero bombs, the square appears blank. When one or more bombs are adjacent to a square (either left, right, up, down, or diagonally adjacent) then the number of adjacent bombs is displayed.
To help you keep track of which tiles you have inferrred that cover bombs, you flag squares by turning flags on. When flags are turned on and you click on a tile you are flagging it as a bomb and not exposing what is underneath. If you incorrectly flag a tile as a bomb then when the game is over the program will show you that you incorrectly flagged a bomb by displaying a flag with an X through it.
To help you see how the game works you can download a compiled version of my program to try out. You should unzip this file. Then you play the game by double-clicking the mac-osx-minesweeper file if you are a Mac user or you can double-click the windows-minesweeper.bat file if you are a Windows user.
You are being provided with the code that you can download here. Your challenge is to complete the program given this code. You should read chapter 7 of the text and understand the examples and do the practice exercises before attempting to complete this assignment.
To complete this assignment, you should make your minesweeper program work as much like the compiled version provided above as possible. Here is the criteria considered for grading purposes.
- Your program should include two more buttons, a New Game button and a Flags On/Flags Off button. The New Game button should start a new game by calling the newgame function to restart the game. The Flags On/Flags Off button should toggle a boolean value between False and True. It should also set the text of the button to reflect the current state of the program. Initially the text of the button should read "Flags Off". When clicked it should toggle to "Flags On" and again to "Flags Off" if clicked again. To change the text of a button called toggleFlags to "Flags On" you can write toggleFlags.config(text="Flags On"). You can use the data dictionary to store values that can be set in your event handlers. The data dictionary can be used to store a mapping from "flags" to True or False. So, data["flags"] = False is what flags is initially set to in the newGame function. Your button should toggle that value between False and True. HINT: You can use the not operator to do this very easily. If you tried to write flags = False in your button's event handler, Python would create a local copy of the flags variable and the contents would be lost as soon as you returned from the event handler. By using the data dictionary in this fashion you can access it from the enclosing scope and mutate it (with the example code above) and your changes will remain in place, even when the event handler is done.
- You must add logic to the clickHandler event handler to detect if a game is over. A game is over when a bomb is exposed or when the only unexposed turtles (i.e. tiles) are bombs. There are two things you should understand when writing this code. Each tile in the two dimensional tile matrix is a Turtle as well. So tiles can do anything a Turtle can do. You can hide them with the hideturtle method. You can change their shape with the shape method. The other thing you will want to understand is the purpose of the data dictionary. Writing data["gameOver"] = True will map the string "gameOver" to True in the data dictionary.
- When the game is over you should reveal all the tiles in the game. To do this, write a new method for the Tile class called reveal. In this method, if a Tile is both armed (i.e. it is a bomb) and flagged you should change its shape to bombflag.gif. If it is armed, but not flagged, its shape should be set to bomb.gif. If it is flagged, but not armed, its shape should be set to badflag.gif. Finally if it is neither armed nor flagged, you should hide the Tile. Once you've written this method, you need to call it on each Tile once the game is over.
- Finally, you need to add logic to the expose method of the Tile class to correctly handle exposing a tile when it is clicked. The expose method returns True if an armed Tile is exposed (indicating you just blew up) or False (indicating that you are still alive). You will want to add one more parameter to the expose method, a boolean value to indicate if flags are on or off.
- You should begin the expose method by checking to see if the tile has already been exposed. If it has, then you should return false immediately. You can keep track of whether the tile has been exposed or not by setting the exposed instance variable in the object to true when it it has been exposed and you can check that value at the beginning of the expose method to see if has already been exposed.
- If flags are on, you should simply turn on or off the flagged instance variable depending on what it was last set to. You should also change the shape of the tile from self.shape("tile.gif") to self.shape("flag.gif") or vice-versa depending on the current state of the flagged instance variable.
- If flags are not on, then you can set the exposed instance variable to true.
- If the current tile is armed, then the exposed method should return True and set its shape to bomb.gif (this is already in the starter code).
- Otherwise, the expose method must count the number of neighbors of itself that are armed. You can get a list of the neighbors of a tile by calling the neighbors function. If zero neighbors are armed, the Tile should hide itself and call expose on all the neighbors of itself. If one or more neighbors is armed, it should hide itself and write a number into its square indicating the number of neighbors that are armed.
The description given here is a pretty detailed description of what remains to be done, but the final say on what to implement should be determined by running my copy of the program and making your program behave like mine.
Various extras could be done as well. For instance, you could have the program record high scores. You might store the high scores in XML format and you might add a widget to display the high scores in the window. You might identify a particular game by a unique number (like solitaire does) so you can select a certain game to play. You might allow the user to pick from a difficulty level by increasing the size of the matrix and the number of bombs. You could have an intermediate 16x16 matrix or a hard 25x25 matrix with 20 and 40 bombs respectively. You could allow the user to select their difficulty level using a Listbox or some other sort of widget. Of course, colors can be changed, you can change the gifs that are used, or you can add animation when the game is one or lost if you like. Have fun with it!