PROWAREtech

articles » current » windows » microsoft-foundation-classes » tic-tac-toe

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();
}

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
ACCEPT REJECT