|
|
【程序】贪吃蛇 |
一派護法 十九級 |
|
一派護法 十九級 |
【代码】 【头文件main.h】 #ifndef _MAIN_H #define _MAIN_H
void StartTimer(HWND hWnd); void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT iTimerID, DWORD dwTime); LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif 【头文件snake.h】 #ifndef _SNAKE_H #define _SNAKE_H
#define WALL_WIDTH 15 #define WALL_HEIGHT 15 #define BLOCK_SIZE 16 #define OVERLAP_MAX 5
#define GAP_SIZE 4
typedef struct tagSNAKE { int x; int y; struct tagSNAKE *pPrev; struct tagSNAKE *pNext; } SNAKE, *PSNAKE;
typedef struct tagFOOD { int x; int y; char ch; // 食物的字符表示 } FOOD, *PFOOD;
int AvoidOverlap(PSNAKE pHead, PFOOD pFood); void CreateFood(PFOOD pFood); void DestroySnake(PSNAKE pHead); void InitSnake(PSNAKE pSnake); BOOL LoadSnake(LPSTR szFileName, PSNAKE pHead, PSNAKE *ppRear, PFOOD pFood, PINT pDirection); BOOL MeetFood(PSNAKE pSnake, PFOOD pFood); void ResetFoodLocation(PSNAKE pHead, PFOOD pFood, int direction); BOOL SaveSnake(LPSTR szFileName, PSNAKE pHead, PFOOD pFood, PINT pDirection); BOOL SnakeAteItself(PSNAKE pHead); PSNAKE SnakeGrow(PSNAKE pHead); void SnakeMove(PSNAKE pHead, PSNAKE pRear, int direction, PBOOL pbGameOver); void UpdateFood(PSNAKE pSnake, PFOOD pFood, int direction);
#endif
|
一派護法 十九級 |
【源文件main.c】 #include <tchar.h> #include <time.h> #include <Windows.h> #include "snake.h" #include "main.h"
#define IDR_TIMER1 1
BOOL bGameOver = FALSE; int direction = VK_RIGHT; // 蛇的移动方向 FOOD food = {5, 8, 'A'}; SIZE size; // 画布大小 SNAKE snake; // 蛇头 PSNAKE pRear;
void StartTimer(HWND hWnd) { SetTimer(hWnd, IDR_TIMER1, 500, TimerProc); }
void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT iTimerID, DWORD dwTime) { SnakeMove(&snake, pRear, direction, &bGameOver); if (MeetFood(&snake, &food)) { pRear = SnakeGrow(&snake); UpdateFood(&snake, &food, direction); } if (!bGameOver && SnakeAteItself(&snake)) bGameOver = TRUE; if (bGameOver) KillTimer(hWnd, IDR_TIMER1); // 当游戏结束时关闭计时器
InvalidateRect(hWnd, NULL, FALSE); // 刷新游戏画面 }
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { BOOL bDrawn; char szText[2]; HBITMAP hbmp; HDC hdc, hdcMem; int x, y; PAINTSTRUCT ps; RECT rect, rcClient, rcText;
PSNAKE pSnake;
switch (uMsg) { case WM_CHAR: // 字符输入检测 switch (wParam) { case 's': // 保存到文件 if (!bGameOver) SaveSnake("game.dat", &snake, &food, &direction); break; case 'l': if (LoadSnake("game.dat", &snake, &pRear, &food, &direction)) { if (bGameOver) { bGameOver = FALSE; StartTimer(hWnd); } InvalidateRect(hWnd, NULL, FALSE); } } break; case WM_CREATE: srand((UINT)time(NULL)); InitSnake(&snake); pRear = &snake; StartTimer(hWnd); break; case WM_DESTROY: KillTimer(hWnd, IDR_TIMER1); DestroySnake(&snake); PostQuitMessage(0); break; case WM_ERASEBKGND: // 防止窗口闪烁 break; case WM_KEYDOWN: // 按键检测 if (direction == VK_UP || direction == VK_DOWN) { if (wParam == VK_LEFT || wParam == VK_RIGHT) direction = wParam; } else if (direction == VK_LEFT || direction == VK_RIGHT) { if (wParam == VK_UP || wParam == VK_DOWN) direction = wParam; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hdcMem = CreateCompatibleDC(hdc); hbmp = CreateCompatibleBitmap(hdc, size.cx, size.cy); SelectObject(hdcMem, hbmp); SetBkMode(hdcMem, TRANSPARENT); SelectObject(hdcMem, GetStockObject(WHITE_PEN)); SelectObject(hdcMem, GetStockObject(NULL_BRUSH)); SetRect(&rcText, 2, 2, 4 + WALL_WIDTH * (BLOCK_SIZE + GAP_SIZE), 4 + WALL_HEIGHT * (BLOCK_SIZE + GAP_SIZE)); Rectangle(hdcMem, rcText.left, rcText.top, rcText.right, rcText.bottom);
SetTextColor(hdcMem, RGB(255, 255, 255)); for (x = 0; x < WALL_WIDTH; x++) { for (y = 0; y < WALL_HEIGHT; y++) { rect.left = 5 + x * (BLOCK_SIZE + GAP_SIZE); rect.top = 5 + y * (BLOCK_SIZE + GAP_SIZE); rect.right = rect.left + BLOCK_SIZE; rect.bottom = rect.top + BLOCK_SIZE;
bDrawn = FALSE; pSnake = &snake; while (pSnake != NULL) { if (x == pSnake->x && y == pSnake->y) { if (pSnake == &snake) FillRect(hdcMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); else Rectangle(hdcMem, rect.left, rect.top, rect.right, rect.bottom); bDrawn = TRUE; break; } pSnake = pSnake->pNext; } if (!bDrawn) { if (x == food.x && y == food.y) { szText[0] = szText[1] = food.ch; TextOutA(hdcMem, rect.left, rect.top, szText, 2); } } } }
if (bGameOver) { SetTextColor(hdcMem, RGB(240, 235, 200)); DrawText(hdcMem, TEXT("GAME OVER"), 9, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE); }
GetClientRect(hWnd, &rcClient); StretchBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, hdcMem, 0, 0, size.cx, size.cy, SRCCOPY);
DeleteDC(hdcMem); DeleteObject(hbmp); EndPaint(hWnd, &ps); 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 rcClient; WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX); wcex.cbClsExtra = wcex.cbWndExtra = 0; wcex.hbrBackground = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hIcon = wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wcex.hInstance = hInstance; wcex.lpfnWndProc = WndProc; wcex.lpszClassName = TEXT("MainWindow"); wcex.lpszMenuName = NULL; wcex.style = CS_HREDRAW | CS_VREDRAW; RegisterClassEx(&wcex); size.cx = 6 + WALL_WIDTH * (BLOCK_SIZE + GAP_SIZE); size.cy = 6 + WALL_HEIGHT * (BLOCK_SIZE + GAP_SIZE); SetRect(&rcClient, 0, 0, size.cx, size.cy); AdjustWindowRect(&rcClient, WS_OVERLAPPEDWINDOW, FALSE);
hWnd = CreateWindow(wcex.lpszClassName, TEXT("贪吃蛇"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, NULL, NULL, hInstance, NULL); if (!hWnd) return 0; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
|
一派護法 十九級 |
【源文件snake.c】 #include <stdio.h> #include <Windows.h> #include "snake.h"
int AvoidOverlap(PSNAKE pHead, PFOOD pFood) { int nTimes = 0; BOOL bFlag = TRUE; PSNAKE pSnake; while (bFlag && nTimes <= OVERLAP_MAX) { bFlag = FALSE; nTimes++;
pSnake = pHead; while (pSnake) { if (MeetFood(pSnake, pFood)) { bFlag = TRUE; CreateFood(pFood); // 如果重叠, 则再次生成食物 break; } pSnake = pSnake->pNext; } } return nTimes; }
void CreateFood(PFOOD pFood) { pFood->x = rand() % WALL_WIDTH; pFood->y = rand() % WALL_HEIGHT; pFood->ch = 'A' + rand() % 26; // 随机大写字母 }
// 将蛇身全部删除并释放内存,仅保留蛇头 void DestroySnake(PSNAKE pHead) { PSNAKE pSnake = pHead->pNext; // 无需删除蛇头 PSNAKE pTemp; while (pSnake != NULL) { pTemp = pSnake->pNext; free(pSnake); pSnake = pTemp; } pHead->pNext = NULL; }
void InitSnake(PSNAKE pSnake) { pSnake->x = pSnake->y = 1; pSnake->pPrev = pSnake->pNext = NULL; }
BOOL LoadSnake(LPSTR szFileName, PSNAKE pHead, PSNAKE *ppRear, PFOOD pFood, PINT pDirection) { FILE *fp; PSNAKE pSnake = NULL; fopen_s(&fp, szFileName, "rb"); if (fp == NULL) return FALSE; DestroySnake(pHead); fread(pFood, sizeof(FOOD), 1, fp); fread(pDirection, sizeof(*pDirection), 1, fp); while (fgetc(fp), !feof(fp)) { fseek(fp, -1, SEEK_CUR); if (pSnake != NULL) { pSnake->pNext = (PSNAKE)malloc(sizeof(SNAKE)); pSnake->pNext->pPrev = pSnake; pSnake = pSnake->pNext; } else pSnake = pHead; fread(&pSnake->x, sizeof(pSnake->x), 1, fp); fread(&pSnake->y, sizeof(pSnake->y), 1, fp); } pSnake->pNext = NULL; // 最后一个节点的pNext必须为NULL *ppRear = pSnake; fclose(fp); return TRUE; }
BOOL MeetFood(PSNAKE pSnake, PFOOD pFood) { return (pSnake->x == pFood->x && pSnake->y == pFood->y); }
void ResetFoodLocation(PSNAKE pHead, PFOOD pFood, int direction) { switch (direction) { case VK_LEFT: pFood->x = pHead->x - 1; pFood->y = pHead->y; break; case VK_UP: pFood->x = pHead->x; pFood->y = pHead->y - 1; break; case VK_RIGHT: pFood->x = pHead->x + 1; pFood->y = pHead->y; break; case VK_DOWN: pFood->x = pHead->x; pFood->y = pHead->y + 1; break; }
if (pFood->x < 0) pFood->x = WALL_WIDTH - 1; if (pFood->x >= WALL_WIDTH) pFood->x -= WALL_WIDTH; if (pFood->y < 0) pFood->y = WALL_HEIGHT - 1; if (pFood->y >= WALL_HEIGHT) pFood->y -= WALL_HEIGHT; }
BOOL SaveSnake(LPSTR szFileName, PSNAKE pHead, PFOOD pFood, PINT pDirection) { FILE *fp; PSNAKE pSnake = pHead; fopen_s(&fp, szFileName, "wb"); if (fp == NULL) return FALSE; fwrite(pFood, sizeof(FOOD), 1, fp); fwrite(pDirection, sizeof(*pDirection), 1, fp); while (pSnake != NULL) { fwrite(&pSnake->x, sizeof(pSnake->x), 1, fp); fwrite(&pSnake->y, sizeof(pSnake->y), 1, fp); pSnake = pSnake->pNext; } fclose(fp); return TRUE; }
BOOL SnakeAteItself(PSNAKE pHead) { PSNAKE pSnake = pHead->pNext; while (pSnake != NULL) { if (pHead->x == pSnake->x && pHead->y == pSnake->y) return TRUE; // 当蛇头碰到蛇身, 游戏结束 pSnake = pSnake->pNext; } return FALSE; }
PSNAKE SnakeGrow(PSNAKE pHead) { PSNAKE pNew = (PSNAKE)malloc(sizeof(SNAKE)); PSNAKE pRear = pHead; while (pRear->pNext != NULL) pRear = pRear->pNext; pNew->pPrev = pRear; pRear->pNext = pNew; pNew->pNext = NULL; return pNew; }
void SnakeMove(PSNAKE pHead, PSNAKE pRear, int direction, PBOOL pbGameOver) { // 移动身体 PSNAKE pSnake = pRear; while (pSnake != pHead) { pSnake->x = pSnake->pPrev->x; pSnake->y = pSnake->pPrev->y; pSnake = pSnake->pPrev; }
// 移动蛇头 switch (direction) { case VK_LEFT: if (pHead->x > 0) pHead->x--; else *pbGameOver = TRUE; break; case VK_UP: if (pHead->y > 0) pHead->y--; else *pbGameOver = TRUE; break; case VK_RIGHT: if (pHead->x + 1 < WALL_WIDTH) pHead->x++; else *pbGameOver = TRUE; break; case VK_DOWN: if (pHead->y + 1 < WALL_WIDTH) pHead->y++; else *pbGameOver = TRUE; break; } }
void UpdateFood(PSNAKE pSnake, PFOOD pFood, int direction) { int nOverlap; CreateFood(pFood); nOverlap = AvoidOverlap(pSnake, pFood); if (nOverlap > OVERLAP_MAX) ResetFoodLocation(pSnake, pFood, direction); }
|
一派護法 十九級 |
【说明】 这是一个用C语言编写出来的窗口程序。 游戏中按S键可存档,按L键可读档。
|
|
|
|