|  | 
        
          |  | 【程序】贪吃蛇 |  
          |   一派掌門 二十級 |  |  
          |   一派掌門 二十級 | 
              【代码】【头文件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键可读档。
 
 |  |
 
         |  |  |