PROWAREtech
Windows MFC: Tic-Tac-Toe Example
An example tic-tac-toe game built with Microsoft Foundation Classes.
This Tic-Tac-Toe game is the simpliest example of an MFC application. It contains one source file and implements one window class, the most basic one - CWnd. Compare it to the .NET Tic-Tac-Toe example and WinAPI Tic-Tac-Toe example. Download the source code: MFCTICTACTOE.ZIP
#include "TicTac.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// THERE MACROS ARE VERY IMPORTANT TO MFC - DO NOT REMOVE
BEGIN_MESSAGE_MAP(CMainWindow, CWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_MBUTTONDOWN()
ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()
const CRect CMainWindow::m_rcSquares[9] = {
CRect( 16, 16,112,112),
CRect(128, 16,224,112),
CRect(240, 16,336,112),
CRect( 16,128,112,224),
CRect(128,128,224,224),
CRect(240,128,336,224),
CRect( 16,240,112,336),
CRect(128,240,224,336),
CRect(240,240,336,336)
};
CMainWindow::CMainWindow()
{
m_nNextChar = EX;
::ZeroMemory(m_nGameGrid, 9 * sizeof(int));
// register a WNDCLASS
CString strWndClass = AfxRegisterWndClass(CS_DBLCLKS,
AfxGetApp()->LoadStandardCursor(IDC_ARROW),
(HBRUSH)(COLOR_3DFACE+1),
AfxGetApp()->LoadStandardIcon(IDI_WINLOGO));
// create a window
this->CreateEx(0, strWndClass, _T("TicTacToe -L, R, M mouse buttons"),
WS_OVERLAPPED|WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL);
//size the window
CRect rect (0, 0, 352, 352);
CalcWindowRect(&rect);
this->SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);
}
void CMainWindow::PostNcDestroy()
{
delete this;
}
void CMainWindow::OnPaint()
{
CPaintDC dc(this);
DrawBoard(&dc);
}
void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
// Do nothing if it is O's turn, if the click occured outside
// the tic-tac-toe grid, or if a non empty was clicked
if(m_nNextChar != EX)
return;
int nPos = GetRectID(point);
if((nPos == -1) || (m_nGameGrid[nPos] != 0))
return;
// add an X to the game grid and toggle m_nNextChar
m_nGameGrid[nPos] = EX;
m_nNextChar = OH;
// draw and X on the screen and see if there's a winner
CClientDC dc(this);
DrawX(&dc, nPos);
CheckForGameOver();
}
void CMainWindow::OnRButtonDown(UINT nFlags, CPoint point)
{
// Do nothing if it is X's turn, if the click occured outside
// the tic-tac-toe grid, or if a non empty was clicked
if(m_nNextChar != OH)
return;
int nPos = GetRectID(point);
if((nPos == -1) || (m_nGameGrid[nPos] != 0))
return;
// add an O to the game grid and toggle m_nNextChar
m_nGameGrid[nPos] = OH;
m_nNextChar = EX;
// draw and O on the screen and see if there's a winner
CClientDC dc(this);
DrawO(&dc, nPos);
CheckForGameOver();
}
void CMainWindow::OnMButtonDown(UINT nFlags, CPoint point)
{
// reset the game when the middle button is clicked
ResetGame();
}
int CMainWindow::GetRectID(CPoint point)
{
// hit-test each of the 9 squares and return a rectangle
// ID (0-8) if (point.x , point.y) lies inside a square
for(int i = 0; i < 9; i++)
{
if(m_rcSquares[i].PtInRect(point))
return i;
}
return -1;
}
void CMainWindow::DrawBoard(CDC *pDC)
{
// draw the lines
CPen pen(PS_SOLID, 16, RGB(0, 0, 0));
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->MoveTo(120, 16);
pDC->LineTo(120, 336);
pDC->MoveTo(232, 16);
pDC->LineTo(232, 336);
pDC->MoveTo(16, 120);
pDC->LineTo(336, 120);
pDC->MoveTo(16, 232);
pDC->LineTo(336, 232);
// draw the X's and O's
for(int i = 0; i < 9; i++)
{
if(m_nGameGrid[i] == EX)
DrawX(pDC, i);
else if(m_nGameGrid[i] == OH)
DrawO(pDC, i);
}
pDC->SelectObject(pOldPen);
}
void CMainWindow::DrawX(CDC *pDC, int nPos)
{
CPen pen(PS_SOLID, 16, RGB(255,0,0));
CPen *pOldPen = pDC->SelectObject(&pen);
CRect rect = m_rcSquares[nPos];
rect.DeflateRect(16, 16);
pDC->MoveTo(rect.left, rect.top);
pDC->LineTo(rect.right, rect.bottom);
pDC->MoveTo(rect.left, rect.bottom);
pDC->LineTo(rect.right, rect.top);
pDC->SelectObject(pOldPen);
}
void CMainWindow::DrawO(CDC *pDC, int nPos)
{
CPen pen(PS_SOLID, 16, RGB(0,0,255));
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->SelectStockObject(NULL_BRUSH);
CRect rect = m_rcSquares[nPos];
rect.DeflateRect(16, 16);
pDC->Ellipse(rect);
pDC->SelectObject(pOldPen);
}
void CMainWindow::CheckForGameOver()
{
int nWinner;
//if the grid contains three consecutive X's or O's, declare a winner and start a new game
if(nWinner = IsWinner())
{
CString string = (nWinner == EX) ? _T("X wins!") : _T("O wins!");
MessageBox(string,_T("Game Over"), MB_ICONEXCLAMATION|MB_OK);
ResetGame();
}
else if(IsDraw())
{
MessageBox(_T("It's a draw!"), _T("Game Over"), MB_ICONEXCLAMATION|MB_OK);
ResetGame();
}
}
int CMainWindow::IsWinner()
{
static int nPattern[8][3] = {
0, 1, 2,
3, 4, 5,
6, 7, 8,
0, 3, 6,
1, 4, 7,
2, 5, 8,
0, 4, 8,
2, 4, 6
};
for(int i = 0; i < 8 ; i++)
{
if((m_nGameGrid[nPattern[i][0]] == EX) &&
(m_nGameGrid[nPattern[i][1]] == EX) &&
(m_nGameGrid[nPattern[i][2]] == EX))
return EX;
if((m_nGameGrid[nPattern[i][0]] == OH) &&
(m_nGameGrid[nPattern[i][1]] == OH) &&
(m_nGameGrid[nPattern[i][2]] == OH))
return OH;
}
return 0;
}
BOOL CMainWindow::IsDraw()
{
for(int i = 0; i < 9; i++)
{
if(m_nGameGrid[i] == 0)
return FALSE;
}
return TRUE;
}
void CMainWindow::ResetGame()
{
m_nNextChar = EX;
::ZeroMemory(m_nGameGrid, 9 * sizeof(int));
this->Invalidate();
}
Comment