This is my favorite program. I write
it every time I change over to a new language or to a new OS.
That gives me an idea of how the language and the OS do the
drawing activity and how they respond to the mouse messages. The
output of the program is shown in the figure. The game is very
simple and you most have played it with your friends umpteen
number of times. This time the only difference is your opponent
is computer. Firstly you make a move by clicking the left mouse
button in the empty slot. As a result an 'O' appears in the
empty slot. Then the computer does some thinking and responds by
making a move by drawing a circle in another empty slot. This
goes on till three symbols 'X' or 'O' fall in one horizontal
line, one vertical line or one diagonal. The player who achieves
this first stands to win the game. The game is declared as drawn
if all nine squares are filled and neither player has managed to
claim three squares in a row horizontally, vertically or
diagonally.
Once the user has made his move it
is for the computer to make the next move. Our program logic
would guide it to do this thinking by following the steps given
below:
* Occupy a row, column or diagonal
that has two elements filled by the computer. * Occupy a
row, column or diagonal that has two cells occupied by the
player. * Occupy the center. * Occupy the first corner
available. * Occupy the first cell, which is free, when
scanning row-wise, from first to last.
The action begins in the frame
window's constructor function myframe( ). In this function we
have used AfxRegisterWndClass( ) to register a window class with
the desired attributes. These attributes include the background
color, a cursor and an icon. We have selected a light gray
background, an arrow shaped cursor and a user defined-icon. The
first parameter passed to AfxRegisterWndClass( ) is CS_DBLCLKS.
This parameter ensures that if the user double clicks in the
window then a WM_DBLCLKS message would be passed to it. The
standard cursor and the user-defined icon are loaded into the
memory by calling the functions myapp::LoadStandardCursor( ) and
myapp::LoadIcon( ) respectively. To be able to facilitate calls
to these functions firstly the address of the global application
object is obtained by calling AfxGetApp( ). Once the window
class has been registered, a window of that class has been
created by calling CFrameWnd::Create( ). Note that the window
class name returned by AfxRegisterWndClass( ) is passed as the
first parameter to CFrameWnd::Create( ). Once the window has
been created a WM_CREATE message gets posted into the message
queue. The myframe::OnCreate( ) handler reacts to this message
by initializing two arrays arr[ ] and square[ ]. The elements of
the first one are initialized to a value EMPTY to indicate that
no player has as yet made any move. The square[ ] array is an
array of CRect objects. This array has been initialized to
several CRect values that specify the coordinates of rectangles
into which the user should click the left mouse button to be
able to make a move. Once the window has been created and the
initialization in the OnCreate( ) function has been completed
the CWnd::Show-Window( ) function is called to display the
window on the screen. This results into the WM_PAINT message
getting posted into the message queue. In response to this
message the myframe::OnPaint( ) function gets called. The actual
preparation of playing field takes place in this function. This
involves drawing two vertical and horizontal lines of pen
thickness equal to 10 and calling the drawex( ) and drawoh( )
function to draw an 'X' or 'O' as per the values present in the
array arr[ ]. When the OnPaint( ) handler is called for the
first time there is no question of drawing either an 'X' or 'O'
since all the elements of the array arr[ ] are EMPTY.
The user makes his move by clicking
the left mouse button in an empty square. This results into a
call to the OnLButtonDown( ) function. Here it is first verified
whether the user has clicked in an empty square. If not then the
move is reported as invalid. This checking is done by calling
CRect::PtInRect( ). This function returns TRUE if the point
passed to it lies within the rectangle represented by the CRect
object, or FALSE if it does not. If the move is found to be
valid then the drawoh( ) function is called to draw an 'O' in
the empty square where the click occurred. Also, the
corresponding element (representing the square where the click
occurred) in the array arr[ ] is set up with a value OH to
indicate that it has now been occupied by an 'O'. Lastly, the
isgameover( ) function is called to check whether the user won
the game on making the move. If he has not won the game so far
and the game has not been drawn then the computer is given a
chance to make its move by calling the function compplay( ).
The computer plays its move by
drawing an 'X' in an empty square. Which empty square to choose
for drawing the 'X' is decided by the computer as per the
guidelines we discussed earlier. The logic has been implemented
in the compplay( ) function. The complicated set of conditions
in this function would become easier to digest if you look at
the strategies that we laid down for the computer's move.
Once a player (user or computer)
makes a move it is necessary to find out whether by making that
move did they win the game. To this effect we have called the
function diduserwin( ) from the OnLButtonDown( ) handler, that
is, after the user has made his move. Similarly, we have called
a function didcomputerwin( ) from the compplay( ) function, i.e,
after the computer has made its move. From each of these
functions we have called the findwinner( ) function to determine
the winner. As we know, the game would be drawn if no player can
claim three straight positions vertically, horizontally or
diagonally. Since it is the user who plays the first move, in
event of the drawn game it would be the user who would get to
play the ninth move. That is the reason why we have called the
isgamedrawn( ) function only from OnLButtonDown( ) and not from
compplay( ).
If the game stands drawn then from
the isgamedrawn( ) function we have called the resetgame( )
function to start a fresh game. To do this all the elements of
the array arr[ ] are reset to the value EMPTY. However, just
doing this won't be enough. The status of the array should be
reflected in the window. This is achieved by calling the
CWnd::Invalidate( ) function. This function invalidates the
entire client area resulting into the WM_PAINT message getting
posted into the message queue. In response to this message the
OnPaint( ) handler redraws the entire client area to reflect the
current status of the array arr[ ]. If the user so desires he
can restart the game by double clicking the left mouse button on
one of the horizontal or vertical lines. On double clicking the
left mouse button the OnLButtonDblClk( ) handler gets called. In
this function, again the resetgame( ) and Invalidate( ) are
called to restart the game.
The system menu usually contains six
menu items. Restore, Move, Size, Minimize, Maximize and Close.
In this program we have added two more items to the system menu,
namely About and Help. This has been achieved in the OnCreate( )
handler. Here we have called CWnd::GetSystemMenu( ) to obtain a
pointer to the system menu. The FALSE parameter passed to the
GetSystemMenu( ) function indicates that we want a pointer to a
copy of the system menu that we can modify. Passing TRUE resets
the system menu to its default state. Once the pointer to the
system menu is obtained, the AppendMenu( ) function is called
four times to add two separators and the 'About' and the 'Help'
menu items. Note that the ids for the menu items have been given
as 112 and 128 respectively. This is because it is necessary
that the ids of system menu items be multiples of 16 and the
'About' and 'Help' menu items happen to be the seventh and
eighth menu items. When the user clicks on an item from the
system menu, the window receives a WM_SYSCOMMAND message. We
have tackled this message using the handler OnSysCommand( ). The
first parameter passed to it contains in its upper 12 bits the
id of the menu item selected. The lower four bits of the
parameter are used by Windows internally. Hence, the id has been
ANDed with 0xFFF0 to strip off any bits that Windows may have
added to it. If the 'About' menu item is selected then an
appropriate message is displayed using the MessageBox( )
function. A similar procedure is carried out if the user clicks
on the 'Help' menu item. Note that it is necessary to call the
base class's OnSysCommand( ) handler so that the selection of
other system menu items gets properly processed.

|