mfc 井字游戏程序分析,描述整个程序处理过程。

此文纯属个人分析!高手绕道!

源码如下:(代码这么长,晕倒!?   跳过代码看分析!)


头文件:(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();
}

井字游戏:游戏窗口如下图,将窗口分成了9个矩形区,

mfc 井字游戏程序分析,描述整个程序处理过程。_第1张图片

左键单击画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:平局了!?




你可能感兴趣的:(C++,mfc)