踩地雷 (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:
- There are a specified amount of mines hidden in the field.
- A player can select an arbitrary cell (x, y) to open or to make a mark.
- If the opened cell contains a mine, the game stops immediately and all the
mines remaining are shown.
- If the opened cell does not contain a mine, the game shows you the count
of mines in its neighbouring eight cells.
- 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.
- 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.
- gotoxy(x_coordinate, y_coordinate); // move cursor to x, y
- setTextColor(color); // color: FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE,
or combinations
- 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:
- 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}
- 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.)
- 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..
- There should be an outer loop asking to repeatedly play a game or not.
- 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.
- 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.
- 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:
- make sure that the array of deployed mines and the counts of surrounding
mines are correct.
- make sure that the choice of a user is checked against the correct array
- play with the gotoxy(), clrsrc(), priintf("*"), setTextColor()
in a separate program to make sure that you know exactly how they work.
- 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
- void initializeProgram();
The UI program calls this function at the start of the program. You might
like to fill in some initialization codes here.
- 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.
- 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.
- 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.
- 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,
- 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).
- 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).
- 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).
- When the player chooses the menu item "新遊戲", the effect is exactly
the same as he click the reset smile button.
- 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).
- 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).
- 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).
- 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:
- void a_showFrawn();
call this function to show a frawn face over the reset button
- void a_showSmile();
call this function to show a smile face over the reset button
- void a_showCell(int irow, int icol);
call this function to show the unopened cell at (icol, irow)
- void a_showFlag(int irow, int icol);
call this function to show a red flag at the cell (icol, irow)
- void a_showBlastedMine(int irow, int icol);
call this function to show a blasted mine at the cell (icol, irow)
- void a_showMine(int irow, int icol);
call this function to show a unblasted mine at the cell (icol, irow)
- void a_showQuestionMark(int irow, int icol);
call this function to show a question mark at the cell (icol, irow)
- void a_showSurroundingMineCount(int count,
int irow, int icol);
call this function to show the number of surrounding mines of the cell (icol,
irow)
- void a_setRemainingMineCount(int count);
call this function to show the number of remaining mines at the left top corner
- void a_startClock();
call this function to start the clock ticking
- void a_stopClock();
call this function to stop the clock
- 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:
- dbwin32
- TRACE("show me the variable %d\n", variable);
all the result will show in the dbwin32 window
example executable program