博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[游戏学习22] MFC 井字棋 双人对战
阅读量:6325 次
发布时间:2019-06-22

本文共 10607 字,大约阅读时间需要 35 分钟。

 

>_<:太多啦,感觉用英语说的太慢啦,没想到一年做的东西竟然这么多.....接下来要加速啦!

>_<:注意这里必须用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 #include 
2 #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 }

 

转载地址:http://ffgaa.baihongyu.com/

你可能感兴趣的文章
shell脚本路径写法的注意点
查看>>
Testng生成的测试报告乱码解决办法
查看>>
vim快速入门
查看>>
大杂烩 -- 单向链表是否存在环或是否相交
查看>>
关键字检索高亮标出-javasript/jQuery代码实现
查看>>
Vijos P1785 同学排序【模拟】
查看>>
人物关系网络图可视化
查看>>
关于ADO.Net SqlConnection的性能优化
查看>>
docker安装及加速配置
查看>>
MRF能量优化
查看>>
什么是.Net, IL, CLI, BCL, FCL, CTS, CLS, CLR, JIT
查看>>
Atlas Control ToolKit 发布
查看>>
Dundas 系列
查看>>
Windows的命令行查看,修改,删除,添加环境变量
查看>>
iOS 图文混排
查看>>
GC是什么? 为什么要有GC?
查看>>
JQuery EasyUi之界面设计——母版页以及Ajax的通用处理(三)
查看>>
童年记忆
查看>>
Selenium Python bindings 文档一
查看>>
directX的16位和24位的色彩模式
查看>>