PROWAREtech

articles » current » windows » application-programming-interface » tic-tac-toe

Windows API: Tic-Tac-Toe Example

A fun game; written in C/C++.

Compare this code to the MFC and .NET versions. Download the source code: WINAPITICTACTOE.zip

#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

HWND hwndMain;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	 static TCHAR szAppName[] = _T("TicTacToeWin");
	 MSG msg;
	 WNDCLASSEX wndclass;

	 wndclass.cbSize = sizeof (wndclass);
	 wndclass.style = CS_HREDRAW | CS_VREDRAW;
	 wndclass.lpfnWndProc = WndProc;
	 wndclass.cbClsExtra = 0;
	 wndclass.cbWndExtra = 0;
	 wndclass.hInstance = hInstance;
	 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
	 wndclass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
	 wndclass.lpszMenuName = NULL;
	 wndclass.lpszClassName = szAppName;
	 wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);

	 RegisterClassEx (&wndclass);

	 hwndMain = CreateWindowEx (0,
					szAppName,
					_T("TicTacToe -L, R, M mouse buttons"),
					WS_OVERLAPPED|WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					366,
					382,
					NULL,
					NULL,
					hInstance,
					NULL);

	 ShowWindow (hwndMain, iCmdShow);
	 UpdateWindow (hwndMain);

	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg);
		DispatchMessage (&msg);
	}
	return msg.wParam;
}

#define EX 1
#define OH 2

const RECT m_rcSquares[9] = {
{ 16, 16,112,112},
{128, 16,224,112},
{240, 16,336,112},
{ 16,128,112,224},
{128,128,224,224},
{240,128,336,224},
{ 16,240,112,336},
{128,240,224,336},
{240,240,336,336}
};

int m_nNextChar = EX, m_nGameGrid[9];

int GetRectID(int x, int y)
{
	for(int i = 0; i < 9; i++)
	{
		if(x > m_rcSquares[i].left && x < m_rcSquares[i].right &&
			y > m_rcSquares[i].top && y < m_rcSquares[i].bottom)
			return i;
	}
	return -1;
}

void DrawX(HDC hdc, int nPos)
{
	HPEN pen = ::CreatePen(PS_SOLID, 16, RGB(255,0,0));
	void *oldPen = ::SelectObject(hdc, pen);

	RECT rect = m_rcSquares[nPos];
	
	rect.top += 10;
	rect.bottom -= 10;
	rect.left += 10;
	rect.right -= 10;

	::MoveToEx(hdc, rect.left, rect.top, NULL);
	::LineTo(hdc, rect.right, rect.bottom);
	::MoveToEx(hdc, rect.left, rect.bottom, NULL);
	::LineTo(hdc, rect.right, rect.top);

	::SelectObject(hdc, (HGDIOBJ)oldPen);
	::DeleteObject(pen);
}

void DrawO(HDC hdc, int nPos)
{
	HPEN pen = ::CreatePen(PS_SOLID, 16, RGB(0,0,255));
	void *oldPen = ::SelectObject(hdc, pen);
	void *oldBr = ::SelectObject(hdc, GetStockObject (NULL_BRUSH));

	RECT rect = m_rcSquares[nPos];

	rect.top += 10;
	rect.bottom -= 10;
	rect.left += 10;
	rect.right -= 10;

	::Ellipse(hdc, rect.left, rect.top, rect.right, rect.bottom);
	::SelectObject(hdc, (HGDIOBJ)oldBr);
	::SelectObject(hdc, (HGDIOBJ)oldPen);
	::DeleteObject(pen);
}

void DrawBoard(HDC hdc)
{
	// draw the lines
	HPEN pen = ::CreatePen(PS_SOLID, 16, RGB(0,0,0));
	void *oldPen = ::SelectObject(hdc, pen);

	::MoveToEx(hdc, 120, 16, NULL);
	::LineTo(hdc, 120, 336);

	::MoveToEx(hdc, 232, 16, NULL);
	::LineTo(hdc, 232, 336);

	::MoveToEx(hdc, 16, 120, NULL);
	::LineTo(hdc, 336, 120);

	::MoveToEx(hdc, 16, 232, NULL);
	::LineTo(hdc, 336, 232);

	// draw the X's and O's
	for(int i = 0; i < 9; i++)
	{
		if(m_nGameGrid[i] == EX)
			DrawX(hdc, i);
		else if(m_nGameGrid[i] == OH)
			DrawO(hdc, i);
	}
	::SelectObject(hdc, (HGDIOBJ)oldPen);
	::DeleteObject(pen);
}

int 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 IsDraw()
{
	for(int i = 0; i < 9; i++)
	{
		if(m_nGameGrid[i] == 0)
			return FALSE;
	}
	return TRUE;
}

void ResetGame()
{
	RECT rect;

	m_nNextChar = EX;
	::ZeroMemory(m_nGameGrid, 9 * sizeof(int));
	::GetClientRect(hwndMain, &rect);
	::InvalidateRect(hwndMain, &rect, TRUE);
}

void 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())
	{
		MessageBox(hwndMain, ((nWinner == EX) ? _T("X wins!") : _T("O wins!")),
			_T("Game Over"), MB_ICONEXCLAMATION|MB_OK);
		ResetGame();
	}
	else if(IsDraw())
	{
		MessageBox(hwndMain, _T("It's a draw!"), _T("Game Over"), MB_ICONEXCLAMATION|MB_OK);
		ResetGame();
	}
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	int nPos;

	switch (iMsg)
	{
	case WM_LBUTTONDOWN:
		if(m_nNextChar != EX)
			return 0;
		nPos = GetRectID(LOWORD(lParam), HIWORD(lParam));
		if((nPos == -1) || (m_nGameGrid[nPos] != 0))
			return 0;
		m_nGameGrid[nPos] = EX;
		m_nNextChar = OH;
		hdc = ::GetDC(hwndMain);
		DrawX(hdc, nPos);
		CheckForGameOver();
		return 0;

	case WM_RBUTTONDOWN:
		if(m_nNextChar != OH)
			return 0;
		nPos = GetRectID(LOWORD(lParam), HIWORD(lParam));
		if((nPos == -1) || (m_nGameGrid[nPos] != 0))
			return 0;
		m_nGameGrid[nPos] = OH;
		m_nNextChar = EX;
		hdc = ::GetDC(hwndMain);
		DrawO(hdc, nPos);
		CheckForGameOver();
		return 0;

	case WM_MBUTTONDOWN:
		ResetGame();
		return 0;

	case WM_PAINT:
		hdc = BeginPaint (hwnd, &ps);

		//GetClientRect (hwnd, &rect);
		SetBkMode(hdc, TRANSPARENT);
		DrawBoard(hdc);
		
		EndPaint (hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage (0);
		return 0;
	}

	return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

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