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 }

 

posted on 2019-01-28 15:12  韦俊宇  阅读(516)  评论(1编辑  收藏  举报