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 }