[C++] 井字棋游戲源碼


 

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 coordinates
14     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 <afxwin.h>
  2 #include "TicTac.h"
  3 #include <fstream>
  4 #include <iostream>
  5 #include<iomanip>
  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<<setw(3)<<ptab[i][j]<<' ';
 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<<setw(3)<<ctab[i][j]<<' ';
 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<<setw(3)<<win[i][j]<<' ';
 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 //判定鼠標是否點進矩形某一區域,點進返回區域編號,沒有返回-1
174 //此處用了一個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, 6
369     };
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 }

人機對戰井字棋

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM