A Memory Game

This is a simple game which displays 25 push buttons in the client area. On top of each button a bitmap of a different animal is loaded. When you play the game the computer on its own depresses a sequence of buttons at random. As each button is depressed, the color of the animal changes to gray and the button goes in. Both these things are done to give the user a feeling that a button has indeed been depressed. As the computer depresses a sequence of buttons the user is supposed to memorize the sequence. Once the computer has done its job the user is then supposed to depress the buttons by clicking the left mouse button in the same sequence as done by computer. If the user's sequence matches the computer's sequence then a message box appears telling the user of his success and then the computer proceeds to depress another sequence of buttons. This time it depresses one more button than what it did last time around. The user is again supposed to repeat the sequence by clicking the push buttons with the mouse. This goes on till the time the users' sequence matches the computer's sequence. In case of a mismatch the game ends. The following figure shows the game window.

As seen from the figure, there are three items in the menu. Selecting the first menu item results into starting a fresh game. Using the 'Sound' menu item it can be decided whether we want a sound to accompany the depressing of a button. The third menu item is the usual 'About' menu item. The only additional feature in this program's 'About' menu item is that it displays the application icon in the 'About' dialog box. This article explains how to create the memory game. This project has an application class, myapp, and a frame window class, myframe. During execution the real action begins when the control reaches the myframe constructor to create the window. Since we want a user-defined icon and a stock brush of light gray color, we have called the AfxRegisterWndClass( ) function to register the window class with these properties. Once registered, we have created a window based on this class by calling CFrameWnd::Create( ). Once the window is created a WM_CREATE message gets posted into the message queue. In response to this message the function myframe::OnCreate( ) gets called. In this function we have created 25 buttons and loaded 25 different bitmaps on them. b[ ] is an array of CBitmapButton objects. The class CBitmapButton encapsulates owner-drawn push buttons that display pictures in place of text. In the OnCreate( ) handler we have called CBitmapButton::Create( ) to create each button. The first parameter passed to Create( ) is NULL since we don't want any text to be displayed on the button. The window style WS_CHILD indicates that each button is a child of the frame window. The style WS_VISIBLE ensures that each button is shown automatically when its parent (the frame window) gets displayed. The style BS_OWNERDRAW must be included to indicate that the button is owner-drawn. The CRect passed to Create( ) indicates the size of the button, whereas, the this pointer passed to it is the pointer to the parent window object. The last parameter passed to Create( ) stands for the id given to each button. Once the 25 buttons are created, a pair of bitmaps is loaded for each button. These bitmaps must be created using the Resource Editor. When drawing the bitmaps for a CBitmapButton-style button you should follow a few simple rules to ensure that the button's appearance is consistent with that of normal push buttons. Each bitmap is of size 50 * 50 pixels. It is important that both the bitmaps are of same size. For best results, the button's face and edges should be drawn with the colors. Start with the up bitmap, and then create the down bitmap by reversing the border and moving the image on the face of the button right and down by one pixel. This gives the illusion of the button going down when it is clicked. Note that it is not necessary that you should strictly style your buttons as per these guidelines and you are free to decide your own style. However, the users are likely to judge your application more favourably if the appearance and behaviour of their controls are similar to that of the controls in other Windows applications. In fact a CBitmapButton can use as many as four bitmapped images: one depicting the button in its normal, undepressed, state; a second depicting the button when it is depressed; a third depicting the button when it is undepressed and has the input focus; and a fourth depicting the button when it is disabled. At a minimum, you should supply "up" and "down" bitmaps showing the button in its normal and depressed states. When the button is clicked, the framework redraws the button using the down bitmap so that the button appears to recede into the screen. When the button is released it is redrawn with the up bitmap. The "focus" and "disable" bitmaps are optional, hence we have not used them in our program. The parameters passed to CBitmapButton::LoadBitmaps( ) specify, in order, the up bitmap, the down bitmap, the focus bitmap and the disabled bitmap. Since we are using only the up and down bitmaps we have passed 0 (NULL) for the focus and the disabled bitmap as shown below:
b[i].LoadBitmaps ( IDB_BITMAP1 + i, IDB_BITMAP26 + i, 0, 0 ) ;
Once the frame window with 25 buttons and 25 bitmaps atop them appears we can start the game by clicking on the 'New game' menu item. On doing so, the newgame( ), menu handler gets called. This function calls function myframe::computersmove( ) with a value 1. The 1 here indicates that first time the computer should depress only one button. The computersmove( ) is a general function which can depress as many buttons as you ask it to and record the id of each button depressed in an array m_computerplayed[ ]. To show the depressing and releasing of the button the CBitmapButton::SetState( ) function is called by computersmove( ). The SetState( ) function is passed a value 1 or 0 for showing the depressed and the released state respectively. The User's Move The user can imitate the computer's sequence of moves by attempting to click the buttons with the left mouse button in the same sequence. On clicking any button the control reaches usersmove( ). This has been ensured by the following entry in the message map:
ON_CONTROL_RANGE ( BN_CLICKED, 1, 25, usersmove )
This entry maps the group of control with ids 1 to 25 to a single handler usersmove( ). Note that when usersmove( ) gets called the id of the button which prompted the call would be passed to this function. In this function first it is checked whether the user is trying to make a move before the computer makes its move. That is cheating and a message box is immediately popped up to indicate this and the control is returned from the function. If the move is found to be legal then the id of the depressed button is stored in the array m_userplayed[ ]. It is then verified whether this move made by the user matches with the computer's move or not. If it doesn't match then it is promptly reported so and the user is asked to restart the game. If all the moves made by the user match the ones made by the computer then it is concluded that the user has imitated the computer's moves correctly. In such a case the computersmove( ) function is called again to let the computer make its next move. However, the value passed to computersmove( ) this time is one more than what was passed to it last time around . Thus the degree of difficulty of the game goes on increasing as the game proceeds.
As the computer or the user makes a move (depresses a button) we can create a sound by calling ::MessageBeep( ) API function. However, the choice of creating or not creating such a sound has been left to the user. He can exercise his choice by selecting the item 'Yes' or 'No' from the 'Sound' menu. To keep track of what he has selected a variable m_state has been used. To ensure that if he selects 'Yes' then he should not be allowed to select 'Yes' again, the program disables the 'Yes' menu item once it is selected. A similar procedure is adopted for the 'No' menu item. To implement this functionality we have defined the function enable_disable( ) which gets called whenever the sound menu is being displayed. Note that this function is called twice, once when the 'Yes' menu item is being displayed and next when the 'No' menu item is being displayed. A call to this function is arranged through the message map entry,
ON_UPDATE_COMMAND_UI_RANGE ( 201, 202, enable_disable )
On getting called, this function appropriately disables the menu item using the value in the m_state variable. The actual enabling or disabling is done by the function CCmdUI::Enable( ). The CCmdUI MFC class contains lot of other functions to update user interface items. Icon in The 'About' Dialog If we want to show the icon in the 'About' dialog box we should make a provision for it while creating the dialog box in the Resource Editor. The steps involved in doing this are as follows:
Create an icon and save it.
Create a dialog box.
Select 'Picture' control from the 'Controls' toolbar.
Place this control in the dialog box.
Double click on this control in the dialog to get the 'Picture Properties' dialog box.
Select 'General' property page, select 'Icon' from the 'Type' combobox.
Select id of the icon which you wish to include from the 'Image' combobox.
Save the dialog.

    No comments: