PROWAREtech
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);
}
Comment