>_<:太多啦,感觉用英语说的太慢啦,没想到一年做的东西竟然这么多.....接下来要加速啦!
>_<:注意这里必须用MFC和前面的Win32不一样啦!
>_<:这也是第一次出现MFC游戏,其框架和逻辑的写法和Win32有很大的区别,建议先看一下MFC的基础再理解代码:
>_<:TicTac.h
1 #define EX 1 //该点左鼠标 2 #define OH 2 //该点右鼠标 3 4 class CMyApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance (); 8 }; 9 10 class CMainWindow : public CWnd //不是继承CFrameWnd 因此需要在CMainWindow()自己定义窗口类了11 {12 protected:13 static const CRect m_rcSquares[9]; // Grid coordinates14 int m_nGameGrid[9]; // 9个格子的状态是否被下0没下;1左下了;2右下了15 int m_nNextChar; // 下一个鼠标状态左or右 (EX or OH)16 bool ptab[9][8]; //玩家的获胜的状态表17 bool ctab[9][8]; //电脑的获胜的状态表18 int win[2][8]; //每种状态表里的棋子数19 20 int GetRectID (CPoint point);21 void DrawBoard (CDC* pDC);22 void DrawX (CDC* pDC, int nPos);23 void DrawO (CDC* pDC, int nPos);24 void CpDraw(CDC* pDC);25 void InitGame();26 void out();27 void ResetGame ();28 bool CheckForGameOver ();29 int IsWinner ();30 BOOL IsDraw ();31 32 public:33 CMainWindow ();34 35 protected:36 virtual void PostNcDestroy ();//在程序终止之前销毁CMainWindow对象37 38 afx_msg void OnPaint ();39 afx_msg void OnLButtonDown (UINT nFlags, CPoint point);40 afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point);41 afx_msg void OnRButtonDown (UINT nFlags, CPoint point);42 43 DECLARE_MESSAGE_MAP ()44 };
>_<:TicTac.cpp
1 #include2 #include "TicTac.h" 3 #include 4 #include 5 #include 6 using namespace std; 7 CMyApp myApp; 8 /*ofstream Cout("out.txt"); 9 void CMainWindow::out(){ 10 Cout<<"ptab[][]=:\n"; 11 for(int i=0;i<9;i++){ 12 for(int j=0;j<8;j++) 13 Cout< < <<' '; 14 Cout<<'\n'; 15 } 16 Cout<<"ctab[][]=:\n"; 17 for(int i=0;i<9;i++){ 18 for(int j=0;j<8;j++) 19 Cout< < <<' '; 20 Cout<<'\n'; 21 } 22 Cout<<"win[][]=:\n"; 23 for(int i=0;i<2;i++){ 24 for(int j=0;j<8;j++) 25 Cout< < <<' '; 26 Cout<<'\n'; 27 } 28 }*/ 29 / 30 // CMyApp member functions 31 32 BOOL CMyApp::InitInstance () 33 { 34 m_pMainWnd = new CMainWindow; 35 m_pMainWnd->ShowWindow (m_nCmdShow); 36 m_pMainWnd->UpdateWindow (); 37 return TRUE; 38 } 39 40 / 41 // CMainWindow message map and member functions 42 43 BEGIN_MESSAGE_MAP (CMainWindow, CWnd) 44 ON_WM_PAINT () 45 ON_WM_LBUTTONDOWN () 46 ON_WM_LBUTTONDBLCLK () 47 ON_WM_RBUTTONDOWN () 48 END_MESSAGE_MAP () 49 50 //9个矩形区域用来判定鼠标是否点进某一区域 51 const CRect CMainWindow::m_rcSquares[9] = { 52 CRect ( 16, 16, 112, 112), 53 CRect (128, 16, 224, 112), 54 CRect (240, 16, 336, 112), 55 CRect ( 16, 128, 112, 224), 56 CRect (128, 128, 224, 224), 57 CRect (240, 128, 336, 224), 58 CRect ( 16, 240, 112, 336), 59 CRect (128, 240, 224, 336), 60 CRect (240, 240, 336, 336) 61 }; 62 63 CMainWindow::CMainWindow () 64 { 65 //初始化游戏 66 InitGame(); 67 68 69 70 //注册一个 WNDCLASS 窗口类. 71 CString strWndClass = AfxRegisterWndClass ( 72 CS_DBLCLKS, // Class style(有双击时间发生的窗口类型) 73 AfxGetApp ()->LoadStandardCursor (IDC_ARROW), // Class cursor(加载一个系统光标,也可自己定义) 74 (HBRUSH) (COLOR_3DFACE + 1), // Background brush(每次::BeginPaint时用它清空客户区);COLOR_3DFACE+1是指定窗口具有与按钮对话框一致的背景色和其他一些3D属性;默认为灰亮色 75 AfxGetApp ()->LoadStandardIcon (IDI_WINLOGO) // Class icon(加载系统图标,也可自己定义) 76 ); 77 78 //调用CWnd::CreateEx()创建主窗口 79 //第一个参数表示0个或是多个WS_EX标志组合;2:AfxRegisterWndClass()返回的WNDCLASS名称; 80 //3、标题;4、窗口样式 81 CreateEx (0, strWndClass, _T ("井字棋"), 82 WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, //WS_THICKFRAME窗口可调大小属性(这里不用) 83 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, //初始位置和大小,这里用CW_USEDEFAULT让Windows拾取窗口和大小 84 NULL, NULL); 85 86 //处理窗口位置和尺寸 87 CRect rect (0, 0, 352, 352); //理想客户区窗口矩形形状 88 CalcWindowRect (&rect); //根据分辨率、菜单...计算窗口矩形大小(必须在窗口创建后调用) 89 90 SetWindowPos (NULL, 0, 0, rect.Width (), rect.Height (), 91 SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW); 92 } 93 94 //在程序结束之前销毁创建的CMainWindow对象 95 void CMainWindow::PostNcDestroy () 96 { 97 delete this; 98 } 99 100 //OnPaint()响应每次重绘棋盘101 void CMainWindow::OnPaint ()102 {103 CPaintDC dc (this);104 DrawBoard (&dc); 105 }106 107 108 //单击鼠标左键响应109 void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)110 {111 CClientDC dc (this);112 113 //如果不该左键响应(即不该左键下,返回)114 if (m_nNextChar != EX){115 return ;116 }117 118 //获得点击矩形区域编号119 //如果没有点中或者已经被下棋了,返回120 int nPos = GetRectID (point); 121 if ((nPos == -1) || (m_nGameGrid[nPos] != 0)) 122 return;123 124 //标记已下并改变下一个点击状态125 m_nGameGrid[nPos] = EX;126 m_nNextChar = OH;127 128 //画上图并判断游戏是否结束129 DrawX (&dc, nPos);130 if(CheckForGameOver ())return;131 132 //后续改变胜利表和各人、机各胜利组合的棋子数133 for(int i=0;i<8;i++){134 if(ptab[nPos][i]){135 win[0][i]++;136 ctab[nPos][i]=false;137 win[1][i]=5;138 }139 }140 141 //电脑下棋142 CpDraw(&dc);143 if(CheckForGameOver ())return;144 }145 146 //单击鼠标右键响应(同左键)147 void CMainWindow::OnRButtonDown (UINT nFlags, CPoint point)148 {149 if (m_nNextChar != OH)150 return;151 152 int nPos = GetRectID (point);153 if ((nPos == -1) || (m_nGameGrid[nPos] != 0))154 return;155 156 m_nGameGrid[nPos] = OH;157 m_nNextChar = EX;158 159 CClientDC dc (this);160 DrawO (&dc, nPos);161 CheckForGameOver ();162 }163 164 //左键双击边框重新开始165 //dc.GetPixel (Point point)获取当前光标下像素颜色判断与黑色匹配166 void CMainWindow::OnLButtonDblClk (UINT nFlags, CPoint point)167 {168 CClientDC dc (this);169 if (dc.GetPixel (point) == RGB (0, 0, 0))170 ResetGame ();171 }172 173 //判定鼠标是否点进矩形某一区域,点进返回区域编号,没有返回-1174 //此处用了一个rect.PtInRect(Point point)函数帮助判定175 int CMainWindow::GetRectID (CPoint point)176 {177 for (int i=0; i<9; i++) {178 if (m_rcSquares[i].PtInRect (point))179 return i;180 }181 return -1;182 }183 184 //画上棋盘并画上圈和叉185 void CMainWindow::DrawBoard (CDC* pDC)186 {187 //画上棋盘188 CPen pen (PS_SOLID, 16, RGB (0, 0, 0));189 CPen* pOldPen = pDC->SelectObject (&pen);190 191 pDC->MoveTo (120, 16);192 pDC->LineTo (120, 336);193 194 pDC->MoveTo (232, 16);195 pDC->LineTo (232, 336);196 197 pDC->MoveTo (16, 120);198 pDC->LineTo (336, 120);199 200 pDC->MoveTo (16, 232);201 pDC->LineTo (336, 232);202 203 //画上叉和圈204 for (int i=0; i<9; i++) {205 if (m_nGameGrid[i] == EX)206 DrawX (pDC, i);207 else if (m_nGameGrid[i] == OH)208 DrawO (pDC, i);209 }210 pDC->SelectObject (pOldPen);211 }212 213 //画叉函数214 void CMainWindow::DrawX (CDC* pDC, int nPos)215 {216 CPen pen (PS_SOLID, 16, RGB (255, 0, 0));//宽为16像素的红笔217 CPen* pOldPen = pDC->SelectObject (&pen);218 219 CRect rect = m_rcSquares[nPos];220 rect.DeflateRect (16, 16);//把矩形每个方向都缩进16个像素作为线条边框221 pDC->MoveTo (rect.left, rect.top);222 pDC->LineTo (rect.right, rect.bottom);223 pDC->MoveTo (rect.left, rect.bottom);224 pDC->LineTo (rect.right, rect.top);225 226 pDC->SelectObject (pOldPen);227 }228 229 //画圈函数230 void CMainWindow::DrawO (CDC* pDC, int nPos)231 {232 CPen pen (PS_SOLID, 16, RGB (0, 0, 255));//宽为16像素的红笔233 CPen* pOldPen = pDC->SelectObject (&pen);234 pDC->SelectStockObject (NULL_BRUSH); //空画刷是为了防止画出的圆内部出现白色遮住背景235 236 CRect rect = m_rcSquares[nPos];237 rect.DeflateRect (16, 16);//把矩形每个方向都缩进16个像素作为圆的边框238 pDC->Ellipse (rect);239 240 pDC->SelectObject (pOldPen);241 }242 243 //电脑画图244 void CMainWindow::CpDraw(CDC* pDC)245 {246 int grades[2][9];247 int m,i,max=0;248 int u;249 250 for(m=0;m<9;m++)251 {252 grades[0][m]=0;253 grades[1][m]=0;254 255 if(m_nGameGrid[m]==0)256 {257 for(i=0;i<8;i++)258 {259 //计算玩家在空棋格上的获胜分数260 if(ptab[m][i] && win[0][i]!=5)261 {262 switch(win[0][i])263 {264 case 0:265 grades[0][m]+=1;266 break;267 case 1:268 grades[0][m]+=2000;269 break;270 case 2:271 grades[0][m]+=10000;272 break;273 }274 }275 276 //计算计算机在空格上的获胜分数277 if(ctab[m][i] && win[1][i]!=5)278 {279 switch(win[1][i])280 {281 case 0:282 grades[1][m]+=1;283 break;284 case 1:285 grades[1][m]+=2001;286 break;287 case 2:288 grades[1][m]+=10001;289 break;290 }291 }292 }293 294 if(max==0)u=m;295 296 if(grades[0][m]>max){297 max=grades[0][m];298 u=m; 299 }300 else if(grades[0][m]==max){301 if(grades[1][m]>grades[1][u])u=m;302 }303 304 if(grades[1][m]>max){305 max=grades[1][m];306 u=m; 307 }308 else if(grades[1][m]==max){309 if(grades[0][m]>grades[0][u])u=m;310 }311 }312 }313 314 //标记已下并改变下一个点击状态315 m_nGameGrid[u]=OH;316 m_nNextChar = EX;317 318 //画上图319 DrawO(pDC,u);320 321 //后续改变胜利表和各人、机各胜利组合的棋子数322 for(i=0;i<8;i++){323 if(ctab[u][i]){324 win[1][i]++;325 ptab[u][i]=false;326 win[0][i]=5;327 }328 }329 }330 331 //响应胜利结束的函数332 bool CMainWindow::CheckForGameOver ()333 {334 int nWinner;335 336 //通过调用IsWinner ()函数获取谁获胜;并用MessageBox输出胜利消息;响应OK后重开一局337 //==Message(CString,_T(标题),类型)338 if (nWinner = IsWinner ()) {339 CString string = (nWinner == EX) ?340 _T ("X wins!") : _T ("O wins!");341 MessageBox (string, _T ("Game Over"), MB_ICONEXCLAMATION | MB_OK);342 ResetGame ();343 return 1;344 }345 346 //通过IsDraw ()函数判断是否平局347 else if (IsDraw ()) {348 MessageBox (_T ("It's a draw!"), _T ("Game Over"),349 MB_ICONEXCLAMATION | MB_OK);350 ResetGame ();351 return 1;352 }353 return 0;354 }355 356 //判断输赢EX左胜;OH右胜;0没有胜357 int CMainWindow::IsWinner ()358 {359 //用静态数组存储获胜组合360 static int nPattern[8][3] = {361 0, 1, 2,362 3, 4, 5,363 6, 7, 8,364 0, 3, 6,365 1, 4, 7,366 2, 5, 8,367 0, 4, 8,368 2, 4, 6369 };370 371 for (int i=0; i<8; i++) {372 if ((m_nGameGrid[nPattern[i][0]] == EX) &&373 (m_nGameGrid[nPattern[i][1]] == EX) &&374 (m_nGameGrid[nPattern[i][2]] == EX))375 return EX;376 377 if ((m_nGameGrid[nPattern[i][0]] == OH) &&378 (m_nGameGrid[nPattern[i][1]] == OH) &&379 (m_nGameGrid[nPattern[i][2]] == OH))380 return OH;381 }382 return 0;383 }384 385 //判断是否平局函数386 BOOL CMainWindow::IsDraw ()387 {388 for (int i=0; i<9; i++) {389 if (m_nGameGrid[i] == 0)390 return FALSE;391 }392 return TRUE;393 }394 395 //初始化游戏396 void CMainWindow::InitGame()397 {398 399 int i,k;400 int count=0;401 402 //设定玩家与计算机在各个获胜组合中的棋子数403 for(i=0;i<8;i++)404 {405 win[0][i]=0;406 win[1][i]=0;407 }408 409 //初始化棋盘状态410 ::ZeroMemory (m_nGameGrid,9*sizeof(int));411 memset(ctab,0,sizeof(ctab));412 memset(ptab,0,sizeof(ptab));413 //设定水平方向的获胜组合414 for(i=0;i<=6;i+=3)415 {416 for(k=0;k<3;k++)//3个棋子1个获胜组合417 {418 ptab[i+k][count]=true;419 ctab[i+k][count]=true;420 }421 count++;422 }423 424 //设定垂直方向的获胜组合425 for(k=0;k<3;k++)426 {427 for(i=0;i<=6;i+=3)//3个棋子1个获胜组合428 {429 ptab[i+k][count]=true;430 ctab[i+k][count]=true;431 }432 count++;433 }434 435 436 //设定对角线方向上的获胜组合437 for(i=2;i<=6;i+=2){438 ptab[i][count]=true;439 ctab[i][count]=true;440 }count++;441 for(i=0;i<=8;i+=4){442 ptab[i][count]=true;443 ctab[i][count]=true;444 }445 446 447 srand(unsigned(time(NULL)));448 449 m_nNextChar = EX;//玩家先走450 }451 //重新开始初始化452 void CMainWindow::ResetGame ()453 {454 InitGame();455 Invalidate (); //使控件的整个图面无效并导致重绘控件456 }