作者共发了5篇帖子。 字体大小:较小 - 100% (默认)▼  内容转换:不转换▼
 
点击 回复
299 4
【程序】贪吃蛇
一派护法 十九级
1楼 发表于:2016-5-4 20:36
一派护法 十九级
2楼 发表于:2016-5-4 20:37
【代码】
【头文件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
一派护法 十九级
3楼 发表于:2016-5-4 20:37
【源文件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;
}
一派护法 十九级
4楼 发表于:2016-5-4 20:38
【源文件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);
}
一派护法 十九级
5楼 发表于:2016-5-4 20:39
【说明】
这是一个用C语言编写出来的窗口程序。
游戏中按S键可存档,按L键可读档。

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:299 回复数:4
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2016-5-4 20:39
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。