1。游戏解析:算法轮流画X和O,知道有一方出现横竖斜的3个X或O,或者全部摆满都没有分出胜负。那么如何判断胜负呢?我们就设置一个IsWinner的函数吧。在#define EX 1 #define OH 2 而把所有网格状态存储在一个数组m_nGameGrid,初始化为0,而有画X时就记录为EX,画O时就记录为EO。这样使用下面的函数就可以判断胜负了。
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;
}
2。使用左键点击出现X,右键点击出现O。在每次点击的时候都要判断是出现结局了,是否可以画,是应该画哪个?
这样就应该记录下一个应该画的符合,使用变量m_nNextChar,因此,点击右键的时候,就把EO和m_nNextChar对比,同时判断m_nGameGrid[i]是否为0(是否已经画过)
3。为了知道点击的位置在那个区域,应该保存其中的9个区域,如
const CRect 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)
};
这样就可以使用GetRectID (CPoint point)来取得点所在的网格。可以使用PtInRect函数,如
for (int i=0; i<9; i++) {
if (m_rcSquares[i].PtInRect (point))
return i;
}
这样就可以知道是在0~8中的哪个网格。
4。思路应该挺清楚的吧!再来看看:程序初始化->点击(左键或者右键)->判断在哪个网格->是否可以画(是否已画?没画但是画错?)->画图->判断是否结束->如果没结束,继续点击。。。。
5。完整的代码(不是完美的代码!):
头文件:TicTac.h
#define EX 1
#define OH 2
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance ();
};
class CMainWindow : public CWnd
{
protected:
static const CRect m_rcSquares[9]; // Grid coordinates
int m_nGameGrid[9]; // Grid contents
int m_nNextChar; // Next character (EX or OH)
int GetRectID (CPoint point);
void DrawBoard (CDC* pDC);
void DrawX (CDC* pDC, int nPos);
void DrawO (CDC* pDC, int nPos);
void ResetGame ();
void CheckForGameOver ();
int IsWinner ();
BOOL IsDraw ();
public:
CMainWindow ();
protected:
virtual void PostNcDestroy ();
afx_msg void OnPaint ();
afx_msg void OnLButtonDown (UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point);
afx_msg void OnRButtonDown (UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP ()
};
源文件:TicTac.cpp
#include <afxwin.h>
#include "TicTac.h"
CMyApp myApp;
/
// CMyApp member functions
BOOL CMyApp::InitInstance ()
{
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow (m_nCmdShow);
m_pMainWnd->UpdateWindow ();
return TRUE;
}
/
// CMainWindow message map and member functions
BEGIN_MESSAGE_MAP (CMainWindow, CWnd)
ON_WM_PAINT ()
ON_WM_LBUTTONDOWN ()
ON_WM_LBUTTONDBLCLK ()
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, // Class style
AfxGetApp ()->LoadStandardCursor (IDC_ARROW), // Class cursor
(HBRUSH) (COLOR_3DFACE + 1), // Background brush
AfxGetApp ()->LoadStandardIcon (IDI_WINLOGO) // Class icon
);
//
// Create a window.
//
CreateEx (0, strWndClass, _T ("Tic-Tac-Toe"),
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);
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''s O''s turn, if the click occurred outside the
// tic-tac-toe grid, or if a nonempty square 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 an X on the screen and see if either player has won.
//
CClientDC dc (this);
DrawX (&dc, nPos);
CheckForGameOver ();
}
void CMainWindow::OnRButtonDown (UINT nFlags, CPoint point)
{
//
// Do nothing if it''s X''s turn, if the click occurred outside the
// tic-tac-toe grid, or if a nonempty square 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 an O on the screen and see if either player has won.
//
CClientDC dc (this);
DrawO (&dc, nPos);
CheckForGameOver ();
}
void CMainWindow::OnLButtonDblClk (UINT nFlags, CPoint point)
{
//
// Reset the game if one of the thick black lines defining the game
// grid is double-clicked with the left mouse button.
//
CClientDC dc (this);
if (dc.GetPixel (point) == RGB (0, 0, 0))
ResetGame ();
}
int CMainWindow::GetRectID (CPoint point)
{
//
// Hit-test each of the grid''s nine 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 that define the tic-tac-toe grid.
//
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 Xs and Os.
//
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 Xs or Os, 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 ();
}
//
// If the grid is full, declare a draw and start a new game.
//
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));
Invalidate ();
}
6。呵呵,是不是很爽呢?