WINAPI实现简易扫雷游戏

Posted 韦俊宇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WINAPI实现简易扫雷游戏相关的知识,希望对你有一定的参考价值。

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

 

以上是关于WINAPI实现简易扫雷游戏的主要内容,如果未能解决你的问题,请参考以下文章

纯原生JS用面向对象class方法实现简易扫雷小游戏

练手WPF——扫雷小游戏的简易实现(中)

练手WPF——扫雷小游戏的简易实现(下)

C | 简易版扫雷的实现

C语言实现简易版扫雷

原生JS实现简易扫雷游戏