|   一派掌門 二十級 | 
              【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);
 }
 
 |