• WINAPI实现简易扫雷游戏


      1 //扫雷
      2 #include <windows.h>
      3 #include <windowsx.h>
      4 #include <strsafe.h>
      5 #include <time.h>
      6 //格子区域大小(DIVISIONS * DIVISIONS)
      7 #define DIVISIONS 20
      8 //地雷数
      9 #define MINECOUNT 40
     10 
     11 //消息处理
     12 LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
     13 //写入地雷
     14 void SetMine(int(*pChess)[DIVISIONS]);
     15 //获取随机数
     16 unsigned int GetRand();
     17 //判断胜利
     18 bool Win(int(*pChess)[DIVISIONS], int *pNotClickCount, int *pClickCount);
     19 //重置
     20 void Reset(HWND hWnd,int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS]);
     21 
     22 int WINAPI WinMain(
     23     HINSTANCE hInstance,    // 当前实例句柄
     24     HINSTANCE hPrevInstance,// 前一实例句柄
     25     LPSTR lpCmdLine,        // 指向命令行参数的指针
     26     int nCmdShow            // 窗口的显示方式
     27     )
     28 {
     29     HWND hWnd;
     30     MSG msg;
     31     WNDCLASS wndClass;
     32 
     33     wndClass.cbClsExtra = 0;
     34     wndClass.cbWndExtra = 0;
     35     wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//画刷背景BLACK_PEN
     36     wndClass.hCursor = LoadCursor(NULL, IDI_APPLICATION);
     37     wndClass.hIcon = LoadIcon(NULL, IDC_ARROW);
     38     wndClass.hInstance = hInstance;
     39     wndClass.lpfnWndProc = DealMessage;
     40     wndClass.lpszClassName = TEXT("MineSweeping");
     41     wndClass.lpszMenuName = NULL;
     42     wndClass.style = CS_HREDRAW | CS_VREDRAW;
     43 
     44 
     45 
     46     if (!RegisterClass(&wndClass))
     47     {
     48         MessageBox(NULL, TEXT("注册类失败,请检查参数是否成功设置"), TEXT("提示"), MB_OK);
     49     }
     50 
     51     hWnd = CreateWindow(TEXT("MineSweeping"),    // 窗口类名称
     52         TEXT("扫雷"),            // 窗口标题栏名称
     53         WS_OVERLAPPEDWINDOW,            // 窗口样式
     54         CW_USEDEFAULT,                    // 窗口水平位置
     55         CW_USEDEFAULT,                    // 窗口垂直位置
     56         CW_USEDEFAULT,                    // 窗口宽度
     57         CW_USEDEFAULT,                    // 窗口高度
     58         NULL,                            // 父窗口句柄
     59         NULL,                            // 窗口菜单句柄
     60         hInstance,                        // 窗口实例句柄
     61         NULL);                            // 窗口创建参数
     62     if (!hWnd)  // 新窗口创建失败
     63     {
     64         return FALSE;
     65     }
     66 
     67     ShowWindow(hWnd, SW_SHOWNORMAL);
     68     UpdateWindow(hWnd);
     69 
     70     while (GetMessage(&msg, NULL, 0, 0))
     71     {
     72         TranslateMessage(&msg);
     73         DispatchMessage(&msg);
     74     }
     75 
     76     return msg.wParam;
     77 }
     78 
     79 
     80 LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     81 {
     82     //初始化要用到的变量
     83     HDC hdc;
     84     PAINTSTRUCT ps;
     85     RECT rect;
     86     HBRUSH hBrush, hOldBrush;
     87     TEXTMETRIC tm;
     88     static int cxChar,cxCaps,cyChar;
     89 
     90     //输出整形用的缓冲区
     91     TCHAR szBuffer[128];
     92     size_t iTarget;
     93 
     94     //地雷以及点击后雷个数存储区
     95     static int iChess[DIVISIONS][DIVISIONS];
     96     int(*pChess)[DIVISIONS] = iChess;
     97     //点击后记录点击事件存储区
     98     static int iClick[DIVISIONS][DIVISIONS];
     99     int(*pClick)[DIVISIONS] = iClick;
    100 
    101     //pSize:当前一个格子宽高
    102     //p:通用
    103     static POINT pSize = { 0,0 }, p;
    104 
    105     //未点击的格子
    106     int iNotClickCount;
    107     //已经点击的格子
    108     int iClickCount;
    109 
    110     switch (uMsg)
    111     {
    112     case WM_CREATE:
    113         hdc = GetDC(hWnd);
    114         //初始化
    115         Reset(hWnd, pChess, pClick);
    116         //获取字体高度
    117         GetTextMetrics(hdc, &tm);
    118         cyChar = tm.tmHeight + tm.tmExternalLeading;
    119         //平均宽度
    120         cxChar = tm.tmAveCharWidth;
    121         //判断是否等宽字体
    122         cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
    123 
    124         ReleaseDC(hWnd, hdc);
    125         return 0;
    126     case WM_KEYDOWN:
    127         //在没有鼠标的情况下,键盘模拟鼠标操作
    128         ShowCursor(false);
    129         GetCursorPos(&p);
    130         ScreenToClient(hWnd, &p);
    131         p.x = max(0, min(DIVISIONS - 1, p.x / pSize.x));
    132         p.y = max(0, min(DIVISIONS - 1, p.y / pSize.y));
    133 
    134         switch (wParam)
    135         {
    136         case VK_UP:
    137             p.y = max(0, p.y - 1);
    138             break;
    139         case VK_DOWN:
    140             p.y = min(DIVISIONS - 1, p.y + 1);
    141             break;
    142         case VK_LEFT:
    143             p.x = max(0, p.x - 1);
    144             break;
    145         case VK_RIGHT:
    146             p.x = min(DIVISIONS - 1, p.x + 1);
    147             break;
    148         case VK_HOME:
    149             p.x = p.y = 0;
    150             break;
    151         case VK_END:
    152             p.x = p.y = DIVISIONS - 1;
    153             break;
    154         case VK_RETURN:
    155         case VK_SPACE:
    156             SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(p.x * pSize.x, p.y * pSize.y));
    157             break;
    158         }
    159         p.x = (p.x * pSize.x) + (pSize.x / 2);
    160         p.y = (p.y * pSize.y) + (pSize.y / 2);
    161 
    162         ClientToScreen(hWnd, &p);
    163         SetCursorPos(p.x, p.y);
    164         ShowCursor(true);
    165         return 0;
    166     case WM_SIZE:
    167         //pSize每个格子大小,X宽,Y高
    168         pSize.x = LOWORD(lParam) / DIVISIONS;
    169         pSize.y = HIWORD(lParam) / DIVISIONS;
    170         return 0;
    171     case WM_LBUTTONDOWN:
    172         p.x = GET_X_LPARAM(lParam) / pSize.x;
    173         p.y = GET_Y_LPARAM(lParam) / pSize.y;
    174         if (p.x >= DIVISIONS || p.y >= DIVISIONS)
    175         {
    176             MessageBeep(0);
    177             return 0;
    178         }
    179         switch (iClick[p.x][p.y])
    180         {
    181         case 1:
    182             MessageBeep(0);
    183             return 0;
    184         case 2:
    185             MessageBeep(0);
    186             return 0;
    187         }
    188         //判断是否点击到了雷
    189         if (iChess[p.x][p.y] == -1)
    190         {
    191             if (MessageBox(hWnd, TEXT("你踩到地雷了!"), TEXT("boom"), MB_OK) == IDOK) 
    192             {
    193                 Reset(hWnd,pChess, pClick);
    194                 InvalidateRect(hWnd, NULL, true);
    195             }
    196         }
    197         else
    198         {
    199             //当前位置修改为点击过
    200             iClick[p.x][p.y] = 1;
    201             //判断是否胜利
    202             if (Win(pClick, &iNotClickCount, &iClickCount))
    203             {
    204                 if (MessageBox(hWnd, TEXT("you win!"), TEXT("wow"), MB_OK) == IDOK)
    205                 {
    206                     Reset(hWnd,pChess, pClick);
    207                 }
    208             }
    209             //标题显示信息
    210             StringCchPrintf(szBuffer, sizeof(szBuffer), TEXT("当前已经翻出的区域数%d,剩余区域数%d"), iClickCount, iNotClickCount);
    211             StringCchLength(szBuffer, sizeof(szBuffer), &iTarget);
    212             SetWindowText(hWnd, szBuffer);
    213             //计算当前点击位置附近雷数
    214             for (int x = -1; x <= 1; x++)
    215             {
    216                 for (int y = -1; y <= 1; y++)
    217                 {
    218                     //超出客户区的格子不计算
    219                     if (p.x + x >= 0 && p.x + x < DIVISIONS && p.y + y >= 0 && p.y + y < DIVISIONS)
    220                     {
    221                         //当前格子不计算
    222                         if (x != 0 || y != 0) {
    223                             if (iChess[p.x + x][p.y + y] == -1)
    224                             {
    225                                 iChess[p.x][p.y]++;
    226                             }
    227                         }
    228                     }
    229                 }
    230             }
    231         }
    232         //重绘格子
    233         rect.left = p.x * pSize.x;
    234         rect.top = p.y * pSize.y;
    235         rect.right = (p.x + 1) * pSize.x;
    236         rect.bottom = (p.y + 1) * pSize.y;
    237         InvalidateRect(hWnd, &rect, true);
    238         return 0;
    239     case WM_RBUTTONDOWN:
    240         p.x = GET_X_LPARAM(lParam) / pSize.x;
    241         p.y = GET_Y_LPARAM(lParam) / pSize.y;
    242         if (p.x >= DIVISIONS || p.y >= DIVISIONS)
    243         {
    244             MessageBeep(0);
    245             return 0;
    246         }
    247         //当前格子标记地雷状态切换
    248         switch (iClick[p.x][p.y])
    249         {
    250         case 0:
    251             iClick[p.x][p.y] = 2;
    252             break;
    253         case 2:
    254             iClick[p.x][p.y] = 0;
    255             break;
    256         default:
    257             MessageBeep(0);
    258             return 0;
    259         }
    260         //重绘格子
    261         rect.left = p.x * pSize.x;
    262         rect.top = p.y * pSize.y;
    263         rect.right = (p.x + 1) * pSize.x;
    264         rect.bottom = (p.y + 1) * pSize.y;
    265         InvalidateRect(hWnd, NULL, true);
    266         return 0;
    267     case WM_MOUSEMOVE:
    268         //鼠标超出扫雷区域鼠标禁止点击
    269         p.x = GET_X_LPARAM(lParam) / pSize.x;
    270         p.y = GET_Y_LPARAM(lParam) / pSize.y;
    271         if (p.x >= DIVISIONS || p.y >= DIVISIONS)
    272         {
    273             SetCursor(LoadCursor(NULL, IDC_NO));
    274         }
    275         else
    276         {
    277             SetCursor(LoadCursor(NULL, IDC_ARROW));
    278         }
    279         return 0;
    280     case WM_PAINT:
    281         hdc = BeginPaint(hWnd, &ps);
    282         //扫雷棋盘绘画
    283         for (int i = 1; i < DIVISIONS; i++)
    284         {
    285             MoveToEx(hdc, pSize.x * i, 0, NULL);
    286             LineTo(hdc, pSize.x * i, pSize.y * DIVISIONS);
    287 
    288             MoveToEx(hdc, 0, pSize.y * i, NULL);
    289             LineTo(hdc, pSize.x * DIVISIONS, pSize.y * i);
    290         }
    291         //扫雷点击绘画
    292         for (int x = 0; x < DIVISIONS; x++)
    293         {
    294             for (int y = 0; y < DIVISIONS; y++)
    295             {
    296                 //0:未开过的格子,1:开过的格子,2:标记过的格子
    297                 switch (iClick[x][y])
    298                 {
    299                 case 1:
    300                     //每个格子背景变成灰色
    301                     hBrush = CreateSolidBrush(RGB(220, 220, 220));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
    302                     Rectangle(hdc, x * pSize.x, y * pSize.y, (x + 1) * pSize.x, (y + 1) * pSize.y);
    303                     SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush);
    304 
    305                     //当前格子写入地雷数
    306                     //地雷数颜色随地雷数改变
    307                     switch (iChess[x][y])
    308                     {
    309                     case 0:
    310                         SetTextColor(hdc, RGB(0, 0, 0)); break;
    311                     case 1:
    312                         SetTextColor(hdc, RGB(0, 0, 255)); break;
    313                     case 2:
    314                         SetTextColor(hdc, RGB(144, 238, 144)); break;
    315                     case 3:
    316                         SetTextColor(hdc, RGB(255, 0, 0)); break;
    317                     case 4:
    318                         SetTextColor(hdc, RGB(255, 0, 255)); break;
    319                     case 5:
    320                         SetTextColor(hdc, RGB(139, 0, 0)); break;
    321                     case 6:
    322                         SetTextColor(hdc, RGB(0, 100, 0)); break;
    323                     }
    324                     SetBkMode(hdc, TRANSPARENT);
    325                     StringCchPrintf(szBuffer, sizeof(szBuffer), TEXT("%d"), iChess[x][y]);
    326                     StringCchLength(szBuffer, sizeof(szBuffer), &iTarget);
    327                     TextOut(hdc, (x * pSize.x) + (pSize.x / 2) - (cxChar / 2), (y * pSize.y) + (pSize.y / 2) - (cyChar / 2), szBuffer, iTarget);
    328                     break;
    329                 case 2:
    330                     hBrush = CreateSolidBrush(RGB(255, 0, 0));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
    331                     //画一个标记的旗子
    332                     MoveToEx(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) - 5, NULL);
    333                     LineTo(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) + 5);
    334 
    335                     Rectangle(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) - 5, (x * pSize.x) + (pSize.x / 2) + 10, (y * pSize.y) + (pSize.y / 2));
    336 
    337                     MoveToEx(hdc, (x * pSize.x) + (pSize.x / 2) - 3, (y * pSize.y) + (pSize.y / 2) + 5, NULL);
    338                     LineTo(hdc, (x * pSize.x) + (pSize.x / 2) + 3, (y * pSize.y) + (pSize.y / 2) + 5);
    339 
    340                     SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush);
    341                     break;
    342                 }
    343 
    344             }
    345         }
    346 
    347         EndPaint(hWnd, &ps);
    348         return 0;
    349     case WM_DESTROY:
    350         PostQuitMessage(0);
    351         break;
    352     default:
    353         return DefWindowProc(hWnd, uMsg, wParam, lParam);
    354     }
    355     return 0;
    356 }
    357 //产生随机数
    358 unsigned int GetRand()
    359 {
    360     int r = rand();
    361     r = r % DIVISIONS;
    362     return r;
    363 }
    364 
    365 //写入地雷
    366 void SetMine(int(*pChess)[DIVISIONS])
    367 {
    368     POINT pPoint;
    369     for (int i = 0; i < MINECOUNT; i++)
    370     {
    371         while (true)
    372         {
    373             pPoint.x = GetRand();
    374             pPoint.y = GetRand();
    375             if (pChess[pPoint.x][pPoint.y] != -1)
    376             {
    377                 pChess[pPoint.x][pPoint.y] = -1;
    378                 break;
    379             }
    380         }
    381     }
    382     return;
    383 }
    384 
    385 //判断获胜
    386 bool Win(int(*pChess)[DIVISIONS],int *pNotClickCount,int *pClickCount)
    387 {
    388     int i = 0;
    389     for (int x = 0; x < DIVISIONS; x++)
    390     {
    391         for (int y = 0; y < DIVISIONS; y++)
    392         {
    393             if (pChess[x][y] == 0|| pChess[x][y] == 2)
    394             {
    395                 i++;
    396             }
    397         }
    398     }
    399     *pNotClickCount = i;
    400     *pClickCount = (DIVISIONS * DIVISIONS) - i;
    401     if (i - MINECOUNT == 0)
    402     {
    403         return true;
    404     }
    405     return false;
    406 }
    407 
    408 //重置
    409 void Reset(HWND hWnd,int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS])
    410 {
    411     //防止随机数重复
    412     srand((unsigned(time(NULL))));
    413     SetCursor(LoadCursor(NULL, IDC_WAIT));
    414     for (int x = 0; x < DIVISIONS; x++)
    415     {
    416         for (int y = 0; y < DIVISIONS; y++)
    417         {
    418             pChess[x][y] = 0;
    419             pClick[x][y] = 0;
    420         }
    421     }
    422     //memset(pChess, 0, sizeof(pChess));
    423     //memset(pClick, 0, sizeof(pClick));
    424     SetMine(pChess);
    425     SetCursor(LoadCursor(NULL, IDC_ARROW));
    426     InvalidateRect(hWnd, NULL, true);
    427     return;
    428 }
  • 相关阅读:
    数据结构与算法参考答案(第十三周)
    数据结构与算法参考答案(第十二周)
    数据结构与算法参考答案(第十一周)
    数据结构与算法参考答案(第十周)
    数学建模国赛注意事项(持续更新)
    数值分析期末复习秘籍
    2020第十一届蓝桥杯软件类省赛第二场C/C++ 大学 B 组 填空题题解(未拿省一的个人反思)
    2020第十一届蓝桥杯软件类省赛第二场C/C++ 大学 B 组 E: 七段码(DFS,二进制枚举+并查集判重)
    递归实现指数型枚举(DFS)
    Codeforces Round #678 (Div. 2)B. Prime Square(矩阵均匀构造)
  • 原文地址:https://www.cnblogs.com/weijunyu/p/10330328.html
Copyright © 2020-2023  润新知