此文纯属个人分析!高手绕道!
源码如下:(代码这么长,晕倒!? 跳过代码看分析!)
头文件:(TicTac.h)
//#include<afxwin.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]; int m_nGameGrid[9]; int m_nNextChar; 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 OnRButtonDown(UINT nFlags,CPoint point); afx_msg void OnLButtonDblClk(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_RBUTTONDOWN() ON_WM_LBUTTONDBLCLK() 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 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 occured ouside 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 gird 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);//selects an object into the device context. //draw the first perpendicular pDC->MoveTo(120,16);//MoveTo(x,y):moves the current position to the point(x,y) pDC->LineTo(120,336);//LineTo(x,y):draws a line from the current position up to,but not including,the point(x,y) pDC->MoveTo(232,16);//second perpendicular pDC->LineTo(232,336); pDC->MoveTo(16,120);//first horizon pDC->LineTo(336,120); pDC->MoveTo(16,232); pDC->LineTo(336,232); //Draw the Xs ans 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);//deflate(缩?小?) rect pDC->Ellipse(rect); pDC->SelectObject(pOldPen); } void CMainWindow::CheckForGameOver() { int nWinner; //if the grid contains three cosecutive Xs orOs,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()//draw(平?局?) { 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(); }
左键单击画X,右键单击画O,X、O轮流画!出现三个连续的图形就是赢!或者画完所有矩形区没有输赢就是平局!(这是单人游戏还是双人游戏?例子而已)
先看程序中的数据:9个矩形和矩形状态
一个矩形三种状态:空白是0,画X是1(#define EX 1),画O是2(#define OH 2)。
代码中:存储9个矩形的数组m_rcSquares[9](全程都在用,设为全局变量)。
存储9个矩形的状态的数组m_nGameGrid[9]。用m_nNextChar标记该画哪个了?。(局部变量)
程序处理流程分析:其实就这么些事情,不过函数定义的多了,相互调用的就多了。
InitInstance功能:创建窗口类的对象,显示出来,画出来。(一成不变的三步策略!)
CMainWindow功能:窗口类的构造函数()
(On开头的程序都是消息处理响应函数):
OnPaint功能:绘制窗口里的游戏界面(井字网格)
OnLButtonDown:鼠标点哪个矩形上了?轮到画X了吗?画X!检查游戏状态(有人赢了?谁赢了?平局了?新开一局!)
OnRButtonDown:鼠标点哪个矩形上了?轮到画O了吗?画O!检查游戏状态(有人赢了?谁赢了?平局了?新开一局!)
OnLButtonDblClk:鼠标点到黑色网格了?新开一局!
(以下函数是自定义的函数,实现特定的功能):
GetRectID:判断画到哪个矩形里了?
DrawBoard:绘制出窗口里的游戏界面
DrawX:画X
DrawO:画O
ResetGame:新开一局!
CheckForGameOver:有人赢了?谁赢了?原来平局了! 来新开一局!
IsWinner:判断谁赢了!?
IsDraw:平局了!?