井字过三关(算法分析)

思路分析:
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。呵呵,是不是很爽呢?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值