一派掌門 二十級              | 
          
            
            
             
              【main.c】 #include <tchar.h> #include <time.h> #include <Windows.h> #include "diamonds.h"
  BOOL bGameOver = FALSE; BOOL bPaused = FALSE; HWND hwndMain; extern LPTSTR lpMsg;
  // 窗口过程函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {     HDC hdc;     PAINTSTRUCT ps;
      switch (uMsg)     {     case WM_CHAR:         switch (wParam)         {         case '+':         case '=':             if (!bGameOver && !bPaused)                 AdjustDiamond(DOWN);             break;         case '-':         case '_':             if (!bGameOver && !bPaused)                 AdjustDiamond(UP);             break;         case 'l':         case 'L':             if (!bPaused)                 LoadGame();             break;         case 'p':         case 'P':             if (!bGameOver)             {                 bPaused = !bPaused;                 UpdateGame();             }             break;         case 's':         case 'S':             if (bGameOver)                 ShowMsg(TEXT("游戏结束时不能存档"));             else                 SaveGame();             break;         }         break;     case WM_CREATE:         hwndMain = hWnd;         srand((UINT)time(NULL));         InitGame();         break;     case WM_DESTROY:         KillTimer(hWnd, IDT_TIMER1); // 关闭定时器         ExitGame(); // 清理游戏数据         PostQuitMessage(0); // 结束消息循环         break;     case WM_KEYDOWN:         if (bPaused)             break; // 游戏暂停时不能执行这些操作         switch (wParam)         {         case VK_DOWN:             if (!bGameOver)                 MoveDiamond(DOWN);             break;         case VK_ESCAPE:             RestartGame();             break;         case VK_LEFT:             if (!bGameOver)                 MoveDiamond(LEFT);             break;         case VK_RIGHT:             if (!bGameOver)                 MoveDiamond(RIGHT);             break;         }         break;     case WM_PAINT:         hdc = BeginPaint(hWnd, &ps);         Draw(hdc, &ps);         EndPaint(hWnd, &ps);         break;     case WM_TIMER:         switch (wParam)         {         case IDT_TIMER1:             if (!bPaused)                 MoveDiamond(DOWN);             break;         case IDT_TIMER2:             lpMsg = NULL;             UpdateGame();             KillTimer(hWnd, IDT_TIMER2);         }         break;     default:         return DefWindowProc(hWnd, uMsg, wParam, lParam);     }     return FALSE; }
  // 主函数 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {     HWND hWnd;     MSG msg;     RECT rect;     WNDCLASSEX wcex;
      // 注册窗口类     ZeroMemory(&wcex, sizeof(wcex));     wcex.cbSize = sizeof(WNDCLASSEX);     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);     wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);     wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);     wcex.hInstance = hInstance;     wcex.lpfnWndProc = WndProc;     wcex.lpszClassName = TEXT("Diamonds");     wcex.style = CS_HREDRAW | CS_VREDRAW;     RegisterClassEx(&wcex);
      // 计算窗口大小     SetRect(&rect, 0, 0, GAME_WIDTH, GAME_HEIGHT);     AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
      // 创建并显示窗口     hWnd = CreateWindow(wcex.lpszClassName, TEXT("宝石方块"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL);     if (!hWnd)         return 1;     ShowWindow(hWnd, nCmdShow);     UpdateWindow(hWnd);
      // 消息循环     while (GetMessage(&msg, NULL, 0, 0))     {         TranslateMessage(&msg);         DispatchMessage(&msg);     }     return msg.wParam; }              
                       | 
        
                
          
            
                         一派掌門 二十級              | 
          
            
            
             
              【diamonds.c】 #include <stdio.h> #include <tchar.h> #include <Windows.h> #include <strsafe.h> #include "diamonds.h"
  COLORREF clrColorList[] = {0xa00000, 0xa05000, 0xa0a000, 0xc000, 0xa0a0, 0x4040c0, 0xa000a0, 0x808080, 0x577ab9, 0xec9ff}; // 颜色表
  /* 应写入文件的游戏数据 */ GAME game; BYTE bMap[DIAMOND_VERT_NUM][DIAMOND_VERT_NUM]; // 已释放的宝石列表
  /* 不应写入文件的游戏数据*/ HFONT hFont = NULL; HFONT hfontWarning = NULL; LPTSTR lpMsg = NULL;
  extern BOOL bGameOver, bPaused; extern HWND hwndMain;
  // 调整宝石顺序  void AdjustDiamond(BYTE bDirection) {     if (bDirection == UP)     {         game.bDiamond = (game.bDiamond >> 2) | ((game.bDiamond & 0x03) << 4);         game.wColor = (game.wColor >> 4) | ((game.wColor & 0x0f) << 8);     }     else     {         game.bDiamond = ((game.bDiamond << 2) & 0x3c) | (game.bDiamond >> 4);         game.wColor = ((game.wColor << 4) & 0xff0) | (game.wColor >> 8);     }     UpdateGame(); }
  // 检查模型是否与已有宝石碰撞  BOOL CheckCrash(void) {     int i, y;     for (i = 0; i < 3; i++)     {         y = game.ptDiamond.y + i;         if (bMap[y][game.ptDiamond.x] & 0x80)             return TRUE;     }     return FALSE; }
  // 随机生成模型  void CreateDiamond(void) {     DWORD dwData[2] = {0};     int nColors = _countof(clrColorList); // 颜色表中的颜色数     int i, j, n;     if (!(game.bNextDiamond & 0x80))     {         // 如果创建的不是第一个模型         n = 1;         game.bDiamond = game.bNextDiamond;         game.wColor = game.wNextColor;     }     else         n = 2;              for (i = 0; i < n; i++)     {         // 形状: 00CCBBAA         for (j = 0; j < 3; j++)         {             dwData[i] <<= 2;             dwData[i] |= rand() % 4;         }                  // 颜色: 0000CCCC BBBBAAAA         for (j = 0; j < 3; j++)         {             dwData[i] <<= 4;             dwData[i] |= rand() % nColors;         }     }          game.bNextDiamond = dwData[0] >> 12;     game.wNextColor = dwData[0] & 0xfff;     if (n == 2)     {         game.bDiamond = dwData[1] >> 12;         game.wColor = dwData[1] & 0xfff;     }          game.ptDiamond.y = 0;     if (CheckCrash())         GameOver(); }
  // 绘制游戏主界面  void Draw(HDC hdc, LPPAINTSTRUCT lpps) {     HBITMAP hbmp;     HDC hdcMem;     LPCTSTR pStr;     RECT rect, rcClient;     TCHAR szText[50];
      // 创建画布     hbmp = CreateCompatibleBitmap(hdc, GAME_WIDTH, GAME_HEIGHT);     hdcMem = CreateCompatibleDC(hdc);     SelectObject(hdcMem, hbmp);          // 将整个画布刷成白色     if (lpps->fErase)     {         SetRect(&rect, 0, 0, GAME_WIDTH, GAME_HEIGHT);         FillRect(hdcMem, &rect, GetSysColorBrush(COLOR_WINDOW));     }
      // 绘制当前方块的矩形框     SelectObject(hdcMem, GetStockObject(BLACK_PEN)); // 黑色边框     SelectObject(hdcMem, GetStockObject(NULL_BRUSH)); // 不填充     rect.left = rect.top = 10;     rect.right = rect.left + DIAMOND_WIDTH * 4;     rect.bottom = rect.top + DIAMOND_HEIGHT * 4;     Rectangle(hdcMem, rect.left, rect.top, rect.right + 1, rect.bottom + 1);     DrawDiamondIn(hdcMem, &rect, game.bNextDiamond, game.wNextColor);
      // 绘制游戏操作说明矩形框     OffsetRect(&rect, 0, rect.bottom - rect.top + 26); // 向下移动矩形     rect.bottom = rect.top + 190;     Rectangle(hdcMem, rect.left, rect.top, rect.right, rect.bottom);          // 在框中显示文字     SelectObject(hdcMem, hFont); // 选择字体     InflateRect(&rect, -4, -4); // 设置文本边界空白宽度(padding)     pStr = TEXT("游戏操作说明\n1.按“←”左移\n2.按“→”右移\n3.按“+”“-”调整顺序\n4.按“↓”加速下落\n5.按“S”存档\n6.按“L”读档\n7.按“ESC”重新开始\n8.按“P”暂停\n9.三个形状一样的宝石相遇即可消除");     DrawText(hdcMem, pStr, lstrlen(pStr), &rect, DT_WORDBREAK);
      // 绘制游戏区域框     rect.top = 10;     rect.left = rect.right + 36;     rect.right = rect.left + DIAMOND_HORZ_NUM * DIAMOND_WIDTH;     rect.bottom = rect.top + DIAMOND_VERT_NUM * DIAMOND_HEIGHT;     Rectangle(hdcMem, rect.left, rect.top, rect.right + 1, rect.bottom + 1);
      // 绘制游戏区域     DrawGameDistrict(hdcMem, &rect);
      // 在游戏区域下面显示分数     rect.top = rect.bottom;     rect.bottom = GAME_HEIGHT;     StringCchPrintf(szText, _countof(szText), TEXT("分数: %u"), game.uScore);     SelectObject(hdcMem, hfontWarning);     SetTextColor(hdcMem, RGB(163, 73, 164));     SetBkMode(hdcMem, TRANSPARENT);     DrawText(hdcMem, szText, lstrlen(szText), &rect, DT_CENTER);
      SetRect(&rect, 0, GAME_HEIGHT / 2 - 26, GAME_WIDTH, GAME_HEIGHT / 2 + 16);     if (lpMsg != NULL)     {         SetTextColor(hdcMem, RGB(255, 128, 0));         rect.bottom += 20;         DrawText(hdcMem, lpMsg, lstrlen(lpMsg), &rect, DT_CENTER);     }     else if (bGameOver || bPaused)     {         SetTextColor(hdcMem, RGB(255, 0, 0));         if (bGameOver)             pStr = TEXT("GAME OVER");         else if (bPaused)             pStr = TEXT("GAME PAUSED");                  DrawText(hdcMem, pStr, lstrlen(pStr), &rect, DT_CENTER);     }          // 将画布上的内容显示到屏幕上     GetClientRect(hwndMain, &rcClient);     StretchBlt(hdc, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, GAME_WIDTH, GAME_HEIGHT, SRCCOPY);
      // 删除画布     DeleteDC(hdcMem);     DeleteObject(hbmp); }
  // 绘制宝石模型  void DrawDiamond(HDC hdc, int x, int y, BYTE bDiamond, WORD wColor) {     HBRUSH hbrOld;     HPEN hpenOld;     int i;     RECT rect;          rect.left = x;     rect.top = y;     rect.right = rect.left + DIAMOND_WIDTH + 1;          hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(DC_BRUSH));     hpenOld = (HPEN)SelectObject(hdc, GetStockObject(BLACK_PEN)); // 选择黑色画笔     for (i = 0; i < 3; i++)     {         SetDCBrushColor(hdc, clrColorList[wColor & 0x0f]);         rect.bottom = rect.top + DIAMOND_HEIGHT + 1;         DrawShape(hdc, &rect, bDiamond & 0x03);              rect.top += DIAMOND_HEIGHT;         bDiamond >>= 2;         wColor >>= 4;     }
      // 还原之前的画刷和画笔     SelectObject(hdc, hbrOld);     SelectObject(hdc, hpenOld); }
  // 在指定区域内绘制宝石模型  void DrawDiamondIn(HDC hdc, LPRECT lpRect, BYTE bDiamond, WORD wColor) {     int x, y;     x = lpRect->left + (lpRect->right - lpRect->left - DIAMOND_WIDTH) / 2;     y = lpRect->top + (lpRect->bottom - lpRect->top - DIAMOND_HEIGHT * 3) / 2;     DrawDiamond(hdc, x, y, bDiamond, wColor); }
  void DrawGameDistrict(HDC hdc, LPRECT lpRect) {     HBRUSH hbrOld;     HPEN hpenOld;     int x, y;     RECT rect;          // 已释放的宝石      hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(DC_BRUSH));     hpenOld = (HPEN)SelectObject(hdc, GetStockObject(BLACK_PEN));     for (y = 0; y < DIAMOND_VERT_NUM; y++)     {         for (x = 0; x < DIAMOND_HORZ_NUM; x++)         {             if (bMap[y][x] & 0x80)             {                 rect.left = lpRect->left + x * DIAMOND_WIDTH;                 rect.top = lpRect->top + y * DIAMOND_HEIGHT;                 rect.right = rect.left + DIAMOND_WIDTH + 1;                 rect.bottom = rect.top + DIAMOND_HEIGHT + 1;                 SetDCBrushColor(hdc, clrColorList[bMap[y][x] & 0x0f]);                 DrawShape(hdc, &rect, (bMap[y][x] >> 4) & 0x03);             }         }     }
      // 正在下落的宝石      if (!bGameOver)         DrawDiamond(hdc, lpRect->left + game.ptDiamond.x * DIAMOND_WIDTH, lpRect->top + game.ptDiamond.y * DIAMOND_HEIGHT, game.bDiamond, game.wColor);
      SelectObject(hdc, hbrOld);     SelectObject(hdc, hpenOld); }
  void DrawShape(HDC hdc, LPRECT lpRect, BYTE bShape) {     POINT pt[4];     switch (bShape)     {     case 0:         Rectangle(hdc, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);         break;     case 1:         Ellipse(hdc, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);         break;     case 2:         pt[0].x = lpRect->left + (lpRect->right - lpRect->left) / 2;         pt[0].y = lpRect->top;         pt[1].x = lpRect->right - 1;         pt[1].y = lpRect->top + (lpRect->bottom - lpRect->top) / 2;         pt[2].x = pt[0].x;         pt[2].y = lpRect->bottom - 1;         pt[3].x = lpRect->left;         pt[3].y = pt[1].y;         Polygon(hdc, pt, 4);         break;     case 3:         pt[0].x = lpRect->left + (lpRect->right - lpRect->left) / 2;         pt[0].y = lpRect->top;         pt[1].x = lpRect->left;         pt[1].y = lpRect->bottom - 1;         pt[2].x = lpRect->right - 1;         pt[2].y = pt[1].y;         Polygon(hdc, pt, 3);     } }
  void ExitGame(void) {     DeleteObject(hFont);     DeleteObject(hfontWarning); }
  void GameOver(void) {     KillTimer(hwndMain, IDT_TIMER1);     bGameOver = TRUE; }
  void InitFont(void) {     LOGFONT lf;     ZeroMemory(&lf, sizeof(lf));     lf.lfCharSet = GB2312_CHARSET;     StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName), TEXT("宋体"));     lf.lfHeight = 12;     hFont = CreateFontIndirect(&lf);
      lf.lfCharSet = DEFAULT_CHARSET;     StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName), TEXT("Times New Roman"));     lf.lfHeight = 30;     lf.lfWeight = FW_BOLD;     hfontWarning = CreateFontIndirect(&lf); }
  void InitGame(void) {     game.ptDiamond.x = 5;     game.uScore = 0;     if (hFont == NULL)     {         InitFont();         game.bNextDiamond = 0x80; // 只有游戏最开始时才同时随机生成当前块和下一块     }     ZeroMemory(bMap, sizeof(bMap));          CreateDiamond();     StartTimer(); }
  BOOL LoadGame(void) {     FILE *fp;     fopen_s(&fp, FILENAME, "rb");     if (fp == NULL)     {         ShowMsg(TEXT("读取游戏进度失败\n无法打开文件"));         return FALSE;     }     fread(&game, sizeof(GAME), 1, fp);     fread(bMap, sizeof(bMap), 1, fp);     fclose(fp);
      if (bGameOver)     {         bGameOver = FALSE;         StartTimer();     }
      UpdateGame();     return TRUE; }
  void MoveDiamond(int iDirection) {     BOOL bRecreate = FALSE;     POINT ptOld = game.ptDiamond;
      switch (iDirection)     {     case LEFT:         if (game.ptDiamond.x > 0)             game.ptDiamond.x--;         break;     case RIGHT:         if (game.ptDiamond.x + 1 < DIAMOND_HORZ_NUM)             game.ptDiamond.x++;         break;     case DOWN:         if (game.ptDiamond.y + 3 < DIAMOND_VERT_NUM)             game.ptDiamond.y++;         else             bRecreate = TRUE;     }     if (!bRecreate)     {         if (CheckCrash())         {             game.ptDiamond = ptOld;             if (iDirection == DOWN)                 bRecreate = TRUE; // 只有当移动方向向下时才重新创建方块         }     }     if (bRecreate)     {         ReleaseDiamond();         RemoveDiamonds();         CreateDiamond();     }     UpdateGame(); }
  // 释放宝石模型  void ReleaseDiamond(void) {     BYTE bData;     BYTE bShape = game.bDiamond;     int i;     WORD wColor = game.wColor;     for (i = 0; i < 3; i++)     {         bData = 0x80; // 最高位表示是否有宝石          bData |= (bShape & 0x03) << 4;         bData |= wColor & 0x0f;         bMap[game.ptDiamond.y + i][game.ptDiamond.x] = bData;         bShape >>= 2;         wColor >>= 4;     } }
  // 消除相同颜色的宝石  BOOL RemoveDiamonds(void) {     BOOL bRemoved;     BYTE bShape;     int x, y, m, n;     int score_unit = SCORE;     do     {         // 寻找可消除的宝石         bRemoved = FALSE;         for (y = 0; y < DIAMOND_VERT_NUM; y++)         {             for (x = 0; x < DIAMOND_HORZ_NUM; x++)             {                 if (!(bMap[y][x] & 0x80))                     continue; // 如果没有宝石则跳过                                      // 向右                 bShape = bMap[y][x] & 0xb0; // 当前方块的形状                 n = 1; // 找到的相同方块个数(包括本身)                 for (m = 1; x + m < DIAMOND_HORZ_NUM; m++)                 {                     // 如果有宝石且形状相同                     if ((bMap[y][x + m] & 0xb0) == bShape)                         n++;                     else                         break; // 形状不同时立即退出                  }                 if (n >= 3)                  {                     // 找到3个及以上相同的宝石时可消除                      bRemoved = TRUE;                     game.uScore += score_unit;                     for (m = 0; m < n; m++)                         bMap[y][x + m] |= 0x40; // 标记删除                  }                                  // 向下                 n = 1;                 for (m = 1; y + m < DIAMOND_VERT_NUM; m++)                 {                     if ((bMap[y + m][x] & 0xb0) == bShape)                         n++;                     else                         break;                 }                 if (n >= 3)                 {                     bRemoved = TRUE;                     game.uScore += score_unit;                     for (m = 0; m < n; m++)                         bMap[y + m][x] |= 0x40;                 }                                  // 向右下                 n = 1;                 for (m = 1; x + m < DIAMOND_HORZ_NUM && y + m < DIAMOND_VERT_NUM; m++)                 {                     if ((bMap[y + m][x + m] & 0xb0) == bShape)                         n++;                     else                         break;                 }                 if (n >= 3)                 {                     bRemoved = TRUE;                     game.uScore += score_unit;                     for (m = 0; m < n; m++)                         bMap[y + m][x + m] |= 0x40;                 }                                  // 向左下                 n = 1;                 for (m = 1; x - m >= 0 && y + m < DIAMOND_VERT_NUM; m++)                 {                     if ((bMap[y + m][x - m] & 0xb0) == bShape)                         n++;                     else                         break;                 }                 if (n >= 3)                 {                     bRemoved = TRUE;                     game.uScore += score_unit;                     for (m = 0; m < n; m++)                         bMap[y + m][x - m] |= 0x40;                 }             }         }                  if (bRemoved)         {             score_unit *= 1.5;             for (x = 0; x < DIAMOND_HORZ_NUM; x++)             {                 // 清除加了标记的宝石                 for (y = 0; y < DIAMOND_VERT_NUM; y++)                 {                     if (bMap[y][x] & 0x40)                         bMap[y][x] = 0;                 }                                  // 消除腾出的空位                  m = -1; // 空白区域开始处                 for (y = DIAMOND_VERT_NUM - 1; y >= 0; y--)                 {                         if (bMap[y][x] & 0x80)                     {                         if (m != -1)                         {                             // 移动宝石                             while (y >= 0 && (bMap[y][x] & 0x80))                             {                                 bMap[m][x] = bMap[y][x];                                 bMap[y][x] = 0;                                 m--;                                 y--;                             }                             y = m + 1;                             m = -1;                         }                     }                     else                     {                         if (m == -1)                             m = y;                     }                 }             }         }     } while (bRemoved); // 只要有宝石被消去,就必须重新检查 }
  void RestartGame(void) {     if (bGameOver)         bGameOver = FALSE;     else         KillTimer(hwndMain, IDT_TIMER1);          ZeroMemory(bMap, sizeof(bMap)); // 删除所有之前的宝石      InitGame();     UpdateGame(); }
  BOOL SaveGame(void) {     FILE *fp;     fopen_s(&fp, FILENAME, "wb");     if (fp == NULL)     {         ShowMsg(TEXT("保存游戏进度失败\n无法打开文件"));         return FALSE;     }     fwrite(&game, sizeof(GAME), 1, fp);     fwrite(bMap, sizeof(bMap), 1, fp);     fclose(fp);     return TRUE; }
  void ShowMsg(LPTSTR lpStr) {     lpMsg = lpStr;     UpdateGame();     SetTimer(hwndMain, IDT_TIMER2, 3000, NULL); }
  void StartTimer(void) {     SetTimer(hwndMain, IDT_TIMER1, 800, NULL); }
  void UpdateGame(void) {     // 重绘整个窗口     InvalidateRect(hwndMain, NULL, TRUE); }              
             |