踩地雷 (MineSweeper)


大家都玩過 Microsoft 在 Windows 作業系統上提供的 踩地雷 這個簡單的遊戲程式

這個作業裡讓你練習寫這個簡單遊戲的邏輯部份. You'll also need to learn how to control the cursor movement with a cmd window (which is called a console in VC's terminology).

Let's describe the rules for playing the minesweeper as follows:

  1. There are a specified amount of mines hidden in the field.
  2. A player can select an arbitrary cell (x, y) to open or to make a mark.
  3. If the opened cell contains a mine, the game stops immediately and all the mines remaining are shown.
  4. If the opened cell does not contain a mine, the game shows you the count of mines in its neighbouring eight cells.
  5. According to the counts of those opened cell, the player could deduce whether an unopened cell contains a mine. If the cell should hide a mine, the player can mark the cell with a flag.
  6. The game continues until the player steps on a mine or all the fields are swept.

As the following figure shows, you need to treat the screen as a 80 by 24 character grid and show the mine field with characters. For example, you can show the unopened cell with '-', the opened cell with a digit from 0 to 8, a flagged cell with '#', a blasted mine with '@', and a dead mine with '*'.

You have to use the following functions in visual studio 2005 to contol the cursor position and the text color.

  1. gotoxy(x_coordinate, y_coordinate); // move cursor to x, y
  2. setTextColor(color); // color: FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE, or combinations
  3. clrscr(); // clear the screen

see the sample in testUtilWin32.c, download VCConsoleIO

Some hints about the control and data of the game are as follows:

  1. You need to define a two-dimensional array to keep track of the position of the deployed mines. You should deploy the mines randomly using rand() in <stdlib>. You can keep track of a mine in its corresponding cell, e.g. save a -1 value in data[x_position][y_position].
    each call of rand() returns a uniformly distributed integer in the set {0, 1, ..., RAND_MAX}
  2. You can precalculate all the surrounding mines for each cell and keep them in the same array such that when the user open a cell, you can check whether it is a mine or not and display the count of surrounding mines immediately. (You don't need this value for a cell that has a mine in it. If this cell is opened, it just goes blasted and you don't need to display the amount of surrounding mines.)
  3. There should be another array to keep track of whether a cell is opened, marked, questioned, such that the program can display the current condition of the field according to the value saved in this array..
  4. There should be an outer loop asking to repeatedly play a game or not.
  5. There should be an inner loop asking the user to pick a cell to open or mark. Inside this loop, the program checks whether the opened cell is a mine according to the array of the deployed mines in step 1 and 2. Each choice of the user has to be checked against the cell status array in step 3 such that an operation to an opened cell does not count.
  6. If a cell with zero surrounding mines is opened, the program should open the surrounding cell automatically and this process continues if the surrounding cell still has zero surrounding mines.
  7. If a cell with a mine is opened, the program opens and displays the remaining unopenned mines and halts.

Basically, the design of this program is somewhat different from the previous assignments and practices. Especially, the structural top-down design methodology does not work that well as it is for the previous examples. Thus, you are not required to provide the top-down design documents if you think it does not apply.

One way to do this assignment step by step is to ensure the following:

  1. make sure that the array of deployed mines and the counts of surrounding mines are correct.
  2. make sure that the choice of a user is checked against the correct array
  3. play with the gotoxy(), clrsrc(), priintf("*"), setTextColor() in a separate program to make sure that you know exactly how they work.
  4. a last thing to remind you: if you finish the game as directed, you might still find out an annoying problem that each time you restart your program, the mines deployed by the rand() function is always at the same place. One last step: #include <time.h>, call srand(time(0)); once when your program starts. Now the random sequence rand() returns should be different if you restart your program not within a second.

example executable program

Bonus (60%)

You might think that the game above is not really attracting to any player due to its dumb keyboard interface. Indeed, without the mouse, a player can have quite limited interaction with the computer. However, you still need to learn quite a few techniques to write a good interface for this game. In the following, I'll provide a user interface for you to design your game. This is not a general interface, it can only be used to design this minesweeper game.

download the minesweeper UI framework

Decompress the above archive and you will have a win32mine folder somewhere in your computer. Open the win32mine.sln with visual studio 2005. Build the project and run it. You will see a window popup exactly like the windows supplied winmine.exe. However, as you click the interface, you will notice that there seems no response at all.

As the following figure shows: if you check out the menu item "中級", you will find the game board expands to 16x16 and the mine count is set to 40, if you check out the menu item "高級", you will find the game board expands again to 30x16 and the mine count is set to 99. The menu item "新遊戲" and the smile face button both have the same effect of renew a game.

Now check out the project folder Application, you'll see the following empty functions

  1. void initializeProgram();
    The UI program calls this function at the start of the program. You might like to fill in some initialization codes here.
  2. void exitProgram();
    The UI program calls this function when the user hit the cross at the title bar or choose exit from the menu. You might like to do some last moment data saving in this routine.
  3. void newGame(int width, int height, int mineCount);
    The UI program calls this function when the user hit the reset button, choose the menu item "初級", "中級", or "高級". This fuction is invoked with three important parameters: the number of rows, the number of columns, and the number of mines to be deployed. Your program should take these parameters as they are specified by the user. For "初級", width is 9, height is 9, and mineCount is 10. For "中級", width is 16, height is 16, and mineCount is 40. For "高級", width is 40, height is 16, and mineCount is 100. This GUI does not implement a dialog for users to specify their own width, height, and mineCount. But in general, these three value should satisfy a. width and height are positive integers and b. mineCount is less than width * height.
  4. void cellMark(int irow, int icol);
    The UI program call this function when the player right click the cell (icol, irow). Note that icol is an integer in the range from 0 to width-1 and irow is an integer in the range from 0 to height-1.
  5. void cellOpen(int irow, int icol);
    The UI program call this function when the player left click the cell (icol, irow). Note that icol is an integer in the range from 0 to width-1 and irow is an integer in the range from 0 to height-1.

The model of window-based programs are different from the examples you learned before. It uses the event-driven model. The first thiing you should notice is that there is no function main that controls the overall logic of your program. You design your program in response to some events, which the player is in control. For example,

  1. When the player clicks a cell with the left button of the mouse, your program will be notified by an event, in the form of a call to the function cellOpen(irow, icolumn).
  2. When the player clicks a cell with the right button of the mouse, your program will be notified by an event, in the form of a call to the function cellMark(irow, icolumn).
  3. When the player clicks the reset smile button with the left button of the mouse, your program will be notified by an event, in the form of a call to the function OnNewgame(irows, icolumns, iCountOfMines).
  4. When the player chooses the menu item "新遊戲", the effect is exactly the same as he click the reset smile button.
  5. When the player chooses the menu item "初級", your program will be notified by an event, in the form of a call to the function OnNewgame(9, 9, 10).
  6. When the player chooses the menu item "中級", your program will be notified by an event, in the form of a call to the function OnNewgame(16, 16, 40).
  7. When the player chooses the menu item "高級", your program will be notified by an event, in the form of a call to the function OnNewgame(30, 16, 99).
  8. When the player chooses the menu iterm "離開" or click the cross button on the window's title bar, your program will be notified by an eventlk in the form of a call to the function exitProgram(),

Now let's see what kind of graphic interfaces I provide you to finish the design of the game:

  1. void a_showFrawn();
    call this function to show a frawn face over the reset button
  2. void a_showSmile();
    call this function to show a smile face over the reset button
  3. void a_showCell(int irow, int icol);
    call this function to show the unopened cell at (icol, irow)
  4. void a_showFlag(int irow, int icol);
    call this function to show a red flag at the cell (icol, irow)
  5. void a_showBlastedMine(int irow, int icol);
    call this function to show a blasted mine at the cell (icol, irow)
  6. void a_showMine(int irow, int icol);
    call this function to show a unblasted mine at the cell (icol, irow)
  7. void a_showQuestionMark(int irow, int icol);
    call this function to show a question mark at the cell (icol, irow)
  8. void a_showSurroundingMineCount(int count, int irow, int icol);
    call this function to show the number of surrounding mines of the cell (icol, irow)
  9. void a_setRemainingMineCount(int count);
    call this function to show the number of remaining mines at the left top corner
  10. void a_startClock();
    call this function to start the clock ticking
  11. void a_stopClock();
    call this function to stop the clock
  12. void a_resetClock();
    call this function to reset the clock to "000"

Note that all the above interrface function assumes that icol is an integer in the range from 0 to width-1 and irow is an integer in the range from 0 to height-1.

You need some tools to debug this program:

  1. dbwin32
  2. TRACE("show me the variable %d\n", variable);
    all the result will show in the dbwin32 window

example executable program

 

back to Programming Assignments, back to Computer Programming

created at 2008/10/12 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@ntu.edu.tw