• [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[][]=:
    ";
     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<<'
    ';
     15     }
     16     Cout<<"ctab[][]=:
    ";
     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<<'
    ';
     21     }
     22     Cout<<"win[][]=:
    ";
     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<<'
    ';
     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 }

    人机对战井字棋

  • 相关阅读:
    美团配送系统架构演进实践
    系统学习NLP(二十一)--SWEM
    转:大众点评信息流基于文本生成的创意优化实践
    从Encoder到Decoder实现Seq2Seq模型
    从YOLOv1到YOLOv3,目标检测的进化之路
    23岁融了一千万,被创新工场投资,创业就是解决问题。专访丨陈海沙
    通俗理解word2vec
    关于眼下分词的想法
    Angular 2的12个经典面试问题汇总(文末附带Angular測试)
    ubuntu14.04-64位机配置android开发环境,ADT,sdk,eclipsea
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/3625291.html
Copyright © 2020-2023  润新知