贪吃蛇智能版(专家)

2023-11-10

在高级版本的基础之上,主要针对以下问题进行了处理:
当长度逐渐变成,超过100之后,随机wander+追尾有比较大的随机性,弄不好就把自己围死了,这个时候已经不能再看到实物马上就去吃了,在吃之前必须先调整好自身的状态,等到认为调整的差不多的时候,在冲过去吃实物。

主要增加了判断往上下,往左右那个更有的判断方法,并且加上了判断当前状态是否已经调整就绪,在吃完实物之后重新调整状态,再次出发。

总之:
这个版本的贪吃蛇已经学会在安全性和吃东西之间找到一个相对平衡的点了。

参考代码:
easysnake.h

#pragma once

#include <stdio.h>
#include <graphics.h>
#include <windows.h>
#include <mmsystem.h>
#include <time.h>
#include <conio.h>
#include <queue>
#include "resource.h"
#pragma comment(lib, "winmm.lib")

#define SNAKE_DRAW_SIZE     15

#define WND_WIDTH           1000
#define WND_HEIGHT          600

#define REGION_WIDTH        600
#define REGION_HEIGHT       600

#define RIGHT_EDGE_WIDTH    10

#define HEADLINE_POSX       265
#define HEADLINE_POSY       120

#define TEXT_TIME_POSX      670
#define TEXT_TIME_POSY      100

#define TEXT_LEVEL_POSX     670
#define TEXT_LEVEL_POSY     100

#define TEXT_SCORE_POSX     670
#define TEXT_SCORE_POSY     200

#define TEXT_LEN_POSX       670
#define TEXT_LEN_POSY       300

#define TEXT_HIGHSCORE_POSX 670
#define TEXT_HIGHSCORE_POSY 400

#define TEXT_HIGHLEVEL_POSX 670
#define TEXT_HIGHLEVEL_POSY 500

#define SNAKE_INIT_PT_X     (REGION_WIDTH / SNAKE_DRAW_SIZE / 5)
#define SNAKE_INIT_PT_Y     (REGION_HEIGHT / SNAKE_DRAW_SIZE / 2)

#define FIRST_ITEM_POSX     375
#define FIRST_ITEM_POSY     250
#define FIRST_ITEM_WIDTH    220
#define FIRST_ITEM_HEIGHT   30

#define SECOND_ITEM_POSX    375
#define SECOND_ITEM_POSY    350
#define SECOND_ITEM_WIDTH   220
#define SECOND_ITEM_HEIGHT  30

#define THIRD_ITEM_POSX     375
#define THIRD_ITEM_POSY     450
#define THIRD_ITEM_WIDTH    220
#define THIRD_ITEM_HEIGHT   30

#define BIGFOOD_SHOWTIME    6000
#define BIGFOOD_STEPTIME    100

#define FOOD_SCORE          1
#define BIG_FOOD_SCORE      5

#define INIT_SPEED          60
#define MINUS_SPEED         15

#define TOTAL_TIME          100

#define MAX_YOUNG_LEN       100

#define SNAKE_MAX ((REGION_WIDTH / SNAKE_DRAW_SIZE) * (REGION_HEIGHT / SNAKE_DRAW_SIZE))
#define BREAKTHROUGHAPPNAME L"BreakThrough"
#define BREAKTHROUGHSCORE   L"HighScore"
#define BREAKTHROUGHLEVEL   L"HighLevel"

#define TIMELIMITEDAPPNAME  L"TimeLimited"
#define TIMELIMITEDSCORE    L"HighScore"

#define AIAPPNAME           L"Intelligence"
#define AISCORE             L"HighScore"
#define AILEVEL             L"HighLevel"

int arrScore[] = { 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 85, 95, 118, 130, 155, 170, 190, 210, 230, 250, 270, 300, 350, 400,
460, 500, 550, 600, 650, 700, 750, 810, 880, 950, 1000, 1100,
1250, 1400, 1600, 1850, 2100, 2400, 2700, 3000, 3400, 3800, 4200, 4600, 5000,
5500, 5900, 6300, 6800, 7500, 8000, 8500, 9000, 9500, 10000 };

enum EmPattern
{
    emBreakThroughPattern = 1,
    emTimeLimitedPattern,
    emIntelligencePattern,
};

enum EmStage
{
    emChooseStage = 1,
    emPlayStage,
};

enum EmDir
{
    emDirUp = 72,
    emDirDown = 80,
    emDirLeft = 75,
    emDirRight = 77,
};

struct Point
{
    int x;
    int y;
};

struct Snake
{
    int nCount;
    Point pt[SNAKE_MAX];
    EmDir dir;
};

struct Food
{
    Point fpt;
    char isEat;
};

struct BigFood
{
    Point fpt;
    char isEat;
};

bool mp[REGION_WIDTH / SNAKE_DRAW_SIZE + 1][REGION_HEIGHT / SNAKE_DRAW_SIZE + 1];//存放地图标记
bool isVisit[REGION_WIDTH / SNAKE_DRAW_SIZE + 1][REGION_HEIGHT / SNAKE_DRAW_SIZE + 1];//存放访问标记
Point parent[REGION_WIDTH / SNAKE_DRAW_SIZE + 1][REGION_HEIGHT / SNAKE_DRAW_SIZE + 1];//存放父节点指向
std::queue<Point>tempQ;//存放搜索过程中的节点
std::queue<Point>pathQ;//存放路径节点
int search_dir[4][2] = { { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 } };
EmStage stage = EmStage::emChooseStage;
EmPattern pattern = EmPattern::emBreakThroughPattern;
Snake snake;
Food food;
BigFood bigFood;
bool bUserInput = false;
int nBigFoodTimer = 0;
int nCurLevel = 1;
int nCurScore = 0;
int nSnakeLen = 3;
int nHighLevel = 0;
int nHighScore = 0;
int nCurSpeed = INIT_SPEED;
int nRemainTime = TOTAL_TIME;
int nTimePast = 0;
int nCurChaseTailTimes = 0;//最大追尾长度
bool bIsAdjustOk;//蛇身是否调整好

void SetMouseNormal();
void SetMouseHand();
void SetLevelText();
void SetHoverStyle();
void SetNormalStyle();
void TackleMouseMove(int x, int y);
void TackleLeftButtonDown(int x, int y);
void TackleMouseAction();
void InitFirstScene();
void InitSecondBackGround();
void InitSecondScene();
bool SearchSnakePath(Point startPt, Point endPt);
void InitMap();
void GameInit();
void PlayGame();
int IsFoodPosOk(int x, int y, Point endPt);
void ProduceFood();
void DrawFood();
int ProduceBigFood();
void DrawBigFood();
int IsEatBigFood();
void EatFood();
void LevelUp();
void DrawSnake();
bool IsEatFoodSafe();
void AIChangeDir();
bool IsSnakeAdjustOk();
bool IsGoLeftBetter();
bool IsGoUpBetter();
bool IsLeftDeadWay(int x, int y);
bool IsRightDeadWay(int x, int y);
bool IsGoDownDeadWay(int x, int y);
bool IsGoUpDeadWay(int x, int y);
bool MustWinModeGoDown();
bool MustWinModeGoUp();
bool MustWinModeGoLeft();
bool MustWinModeGoRight();
bool StartMustWinStrategy();
void AIRealChangeDirection();
bool AISearchBigFood();
bool AISearchSmallFood();
bool AISearchNearTail();
bool AIWanderSearch1(bool isTryAgain = false);
bool AIWanderSearch2(bool isTryAgain = false);
bool AIWanderSearch3(bool isTryAgain = false);
bool AIWanderSearch4(bool isTryAgain = false);
bool AISearchFourCorner(int x, int y);
void SnakeMove();
void ChangeDir();
void BreakSnake();
void WriteRecord();
void BigFoodDisappear();
void TimeEclipse();
void DecideHeadDirection();
void DrawSnakeHead(int nIndex);
void DecideCornerDirection(int idx);
void DrawCorner(int nIndex, int idx);
void DecideBodyDirection(int idx);
void DrawBody(int nIndex, int idx);
void DecideTailDirection(int idx);
void DrawTail(int nIndex, int idx);
void BreakThroughPattern();
void TimeLimitedPattern();

easysnake.cpp

#include "easysnake.h"

BOOL KExpandEnvironmentString(IN LPCTSTR lpEnvironmentString, OUT LPTSTR lpExpandString, IN ULONG ulExpandStringLength)
{
    BOOL bResult = FALSE;

    LPTSTR lpBuffer = NULL;
    ULONG ulRetLength = 0;

    if (!lpEnvironmentString || !lpExpandString || 1 > ulExpandStringLength)
    {
        goto _abort;
    }

    ulRetLength = ::ExpandEnvironmentStrings(lpEnvironmentString, NULL, 0);
    if (1 > ulRetLength || ulRetLength > ulExpandStringLength - 1)
    {
        goto _abort;
    }

    __try
    {
        lpBuffer = new TCHAR[ulRetLength];
        if (!lpBuffer)
        {
            goto _abort;
        }

        ::RtlZeroMemory(lpBuffer, sizeof(TCHAR)* ulRetLength);

        ulRetLength = ::ExpandEnvironmentStrings(lpEnvironmentString, lpBuffer, ulRetLength);
        if (ulRetLength && ulRetLength <= ulExpandStringLength - 1)
        {
            _tcsncpy_s(lpExpandString, ulExpandStringLength - 1, lpBuffer, ulRetLength);
            bResult = TRUE;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        bResult = FALSE;
    }

_abort:

    if (lpBuffer)
    {
        delete[] lpBuffer;
        lpBuffer = NULL;
    }

    return bResult;
}

void SetMouseNormal()
{
    HCURSOR hcur = LoadCursor(NULL, IDC_ARROW);
    HWND hwnd = GetHWnd();
    SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
}

void SetMouseHand()
{
    HCURSOR hcur = LoadCursor(NULL, MAKEINTRESOURCE(32649));
    HWND hwnd = GetHWnd();
    SetClassLong(hwnd, GCL_HCURSOR, (long)hcur);
}

void SetLevelText()
{
    switch (pattern)
    {
    case EmPattern::emBreakThroughPattern:
        outtextxy(FIRST_ITEM_POSX, FIRST_ITEM_POSY, L"闯关模式");
        break;
    case EmPattern::emTimeLimitedPattern:
        outtextxy(SECOND_ITEM_POSX, SECOND_ITEM_POSY, L"限时模式");
        break;
    case EmPattern::emIntelligencePattern:
        outtextxy(THIRD_ITEM_POSX, THIRD_ITEM_POSY, L"智能模式");
        break;
    default:
        break;
    }
}

void SetHoverStyle()
{
    settextcolor(RGB(255, 0, 119));
    SetLevelText();
    SetMouseHand();
}

void SetNormalStyle()
{
    settextcolor(BROWN);
    SetLevelText();
    SetMouseNormal();
}

void TackleMouseMove(int x, int y)
{
    if (stage == EmStage::emPlayStage)
        return;

    if (x > FIRST_ITEM_POSX && x < FIRST_ITEM_POSX + FIRST_ITEM_WIDTH && y >FIRST_ITEM_POSY && y < FIRST_ITEM_POSY + FIRST_ITEM_HEIGHT)
    {
        pattern = EmPattern::emBreakThroughPattern;
        SetHoverStyle();
        return;
    }
    else
    {
        pattern = EmPattern::emBreakThroughPattern;
        SetNormalStyle();
    }

    if (x > SECOND_ITEM_POSX && x < SECOND_ITEM_POSX + SECOND_ITEM_WIDTH && y > SECOND_ITEM_POSY && y < SECOND_ITEM_POSY + SECOND_ITEM_HEIGHT)
    {
        pattern = EmPattern::emTimeLimitedPattern;
        SetHoverStyle();
        return;
    }
    else
    {
        pattern = EmPattern::emTimeLimitedPattern;
        SetNormalStyle();
    }

    if (x > THIRD_ITEM_POSX && x < THIRD_ITEM_POSX + THIRD_ITEM_WIDTH && y > THIRD_ITEM_POSY && y < THIRD_ITEM_POSY + THIRD_ITEM_HEIGHT)
    {
        pattern = EmPattern::emIntelligencePattern;
        SetHoverStyle();
    }
    else
    {
        pattern = EmPattern::emIntelligencePattern;
        SetNormalStyle();
    }
}

void TimeLimitedPattern()
{
    settextstyle(20, 20, L"楷体", 0, 0, 900, 0, 0, 0);
    setbkmode(TRANSPARENT);
    settextcolor(RGB(255, 0, 0));

    WCHAR szCurRemainTime[32];
    WCHAR szCurScore[32];
    WCHAR szSnakeLen[32];
    WCHAR szHighScore[32];

    swprintf_s(szCurRemainTime, L"剩余时间: %d", nRemainTime);
    swprintf_s(szCurScore, L"当前得分: %d", nCurScore);
    swprintf_s(szSnakeLen, L"蛇身长度: %d", snake.nCount);
    swprintf_s(szHighScore, L"最高分数: %d", nHighScore);

    outtextxy(TEXT_TIME_POSX, TEXT_TIME_POSY, szCurRemainTime);
    outtextxy(TEXT_SCORE_POSX, TEXT_SCORE_POSY, szCurScore);
    outtextxy(TEXT_LEN_POSX, TEXT_LEN_POSY, szSnakeLen);
    outtextxy(TEXT_HIGHSCORE_POSX, TEXT_HIGHSCORE_POSY, szHighScore);
}

void BreakThroughPattern()
{
    settextstyle(20, 20, L"楷体", 0, 0, 900, 0, 0, 0);
    setbkmode(TRANSPARENT);
    settextcolor(RGB(255, 0, 0));

    WCHAR szCurLevel[32];
    WCHAR szCurScore[32];
    WCHAR szSnakeLen[32];
    WCHAR szHighLevel[32];
    WCHAR szHighScore[32];

    swprintf_s(szCurLevel, L"当前级别: %d", nCurLevel);
    swprintf_s(szCurScore, L"当前得分: %d", nCurScore);
    swprintf_s(szSnakeLen, L"蛇身长度: %d", snake.nCount);
    swprintf_s(szHighLevel, L"最高级别: %d", nHighLevel);
    swprintf_s(szHighScore, L"最高分数: %d", nHighScore);

    outtextxy(TEXT_LEVEL_POSX, TEXT_LEVEL_POSY, szCurLevel);
    outtextxy(TEXT_SCORE_POSX, TEXT_SCORE_POSY, szCurScore);
    outtextxy(TEXT_LEN_POSX, TEXT_LEN_POSY, szSnakeLen);
    outtextxy(TEXT_HIGHSCORE_POSX, TEXT_HIGHSCORE_POSY, szHighLevel);
    outtextxy(TEXT_HIGHLEVEL_POSX, TEXT_HIGHLEVEL_POSY, szHighScore);
}

void TackleLeftButtonDown(int x, int y)
{
    if (x > FIRST_ITEM_POSX && x < FIRST_ITEM_POSX + FIRST_ITEM_WIDTH && y >FIRST_ITEM_POSY && y < FIRST_ITEM_POSY + FIRST_ITEM_HEIGHT)
    {
        pattern = EmPattern::emBreakThroughPattern;
        InitSecondScene();
    }

    if (x > SECOND_ITEM_POSX && x < SECOND_ITEM_POSX + SECOND_ITEM_WIDTH && y > SECOND_ITEM_POSY && y < SECOND_ITEM_POSY + SECOND_ITEM_HEIGHT)
    {
        pattern = EmPattern::emTimeLimitedPattern;
        InitSecondScene();
    }


    if (x > THIRD_ITEM_POSX && x < THIRD_ITEM_POSX + THIRD_ITEM_WIDTH && y > THIRD_ITEM_POSY && y < THIRD_ITEM_POSY + THIRD_ITEM_HEIGHT)
    {
        pattern = EmPattern::emIntelligencePattern;
        InitSecondScene();
    }
}

void TackleMouseAction()
{
    MOUSEMSG msg;
    while (1)
    {
        msg = GetMouseMsg();//获取鼠标信息
        switch (msg.uMsg)
        {
        case WM_LBUTTONDOWN://处理鼠标的左键点击消息
            TackleLeftButtonDown(msg.x, msg.y);
            break;
        case WM_MOUSEMOVE://处理鼠标的左键点击消息
            TackleMouseMove(msg.x, msg.y);
            break;
        default:
            break;
        }
    }
}

void InitFirstScene()
{
    cleardevice();
    stage = EmStage::emChooseStage;

    IMAGE img;
    for (int i = 0; i < 22; i++)
    {
        loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG1), SNAKE_DRAW_SIZE * 3, SNAKE_DRAW_SIZE * 3);
        putimage(4 + SNAKE_DRAW_SIZE * 3 * i, 0, &img);
    }

    settextstyle(38, 38, L"楷体", 0, 0, 1000, 0, 0, 0);
    setbkmode(TRANSPARENT);
    settextcolor(RGB(255, 0, 0));
    outtextxy(HEADLINE_POSX, HEADLINE_POSY, L"贪吃蛇大作战");

    settextstyle(28, 28, L"楷体", 0, 0, 1000, 0, 0, 0);
    setbkmode(TRANSPARENT);
    pattern = EmPattern::emBreakThroughPattern;
    SetNormalStyle();
    pattern = EmPattern::emTimeLimitedPattern;
    SetNormalStyle();
    pattern = EmPattern::emIntelligencePattern;
    SetNormalStyle();
    pattern = EmPattern::emBreakThroughPattern;
    TackleMouseAction();
}

void InitSecondBackGround()
{
    setlinecolor(RED);
    setlinestyle(PS_SOLID, RIGHT_EDGE_WIDTH);
    line(REGION_WIDTH + 4, 0, REGION_WIDTH + 4, REGION_HEIGHT);
    if (EmPattern::emBreakThroughPattern == pattern)
        BreakThroughPattern();
    else if (EmPattern::emTimeLimitedPattern == pattern)
        TimeLimitedPattern();
    else if (EmPattern::emIntelligencePattern == pattern)
        BreakThroughPattern();
}

void InitSecondScene()
{
    cleardevice();
    stage = EmStage::emPlayStage;

    GameInit();
    InitSecondBackGround();
    PlayGame();
}

bool IsSearchPointOk(int x, int y, Point endPt)
{
    if (x < 0 || x > REGION_WIDTH / SNAKE_DRAW_SIZE - 1 || y < 0 || y > REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
        return false;

    if (isVisit[x][y])
        return false;

    if (mp[x][y] && (x != endPt.x || y != endPt.y))
        return false;

    return true;
}

void PushPath(Point startPt, Point endPt)
{
    if (endPt.x != startPt.x || endPt.y != startPt.y)
    {
        PushPath(startPt, parent[endPt.x][endPt.y]);
        pathQ.push(endPt);
    }
}

bool SearchSnakePath(Point startPt, Point endPt)
{
    while (!pathQ.empty())
        pathQ.pop();

    while (!tempQ.empty())
        tempQ.pop();

    tempQ.push(startPt);
    isVisit[startPt.x][startPt.y] = true;
    Point nextPoint;
    while (!tempQ.empty())
    {
        Point firstPoint = tempQ.front();
        tempQ.pop();
        if (firstPoint.x == endPt.x && firstPoint.y == endPt.y)
            return true;
        for (int i = 0; i < 4; i++)
        {
            nextPoint.x = firstPoint.x + search_dir[i][0];
            nextPoint.y = firstPoint.y + search_dir[i][1];
            if (IsSearchPointOk(nextPoint.x, nextPoint.y, endPt))
            {
                isVisit[nextPoint.x][nextPoint.y] = true;
                tempQ.push(nextPoint);
                parent[nextPoint.x][nextPoint.y].x = firstPoint.x;
                parent[nextPoint.x][nextPoint.y].y = firstPoint.y;
            }
        }
    }
    return false;
}

void InitMap()
{
    memset(mp, false, sizeof(mp)); //把地图标记还原
    memset(isVisit, false, sizeof(isVisit));
    for (int i = 0; i < snake.nCount; i++)
        mp[snake.pt[i].x][snake.pt[i].y] = true;
    for (int i = 0; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)
    {
        for (int j = 0; j < REGION_HEIGHT / SNAKE_DRAW_SIZE; j++)
        {
            parent[i][j].x = -1;
            parent[i][j].y = -1;
        }
    }
}

void GameInit()
{
    snake.dir = EmDir::emDirRight;
    snake.nCount = 3;
    for (int i = 0; i < snake.nCount; i++)
    {
        snake.pt[i].x = SNAKE_INIT_PT_X - i;
        snake.pt[i].y = SNAKE_INIT_PT_Y;
    }
    food.isEat = 1;
    bigFood.isEat = 1;
    nBigFoodTimer = 0;

    InitMap();//初始化地图标记
    WCHAR szTempPath[MAX_PATH] = { 0 };
    WCHAR szIniPath[MAX_PATH] = { 0 };
    KExpandEnvironmentString(L"%TEMP%", szTempPath, MAX_PATH);
    swprintf_s(szIniPath, L"%s\\record.ini", szTempPath);
    nCurSpeed = INIT_SPEED;
    nCurLevel = 1;
    nCurScore = 0;
    nSnakeLen = 3;
    nRemainTime = TOTAL_TIME;
    nTimePast = 0;
    nCurChaseTailTimes = 0;
    bIsAdjustOk = false;
    if (pattern == EmPattern::emTimeLimitedPattern)
        nHighScore = ::GetPrivateProfileInt(TIMELIMITEDAPPNAME, TIMELIMITEDSCORE, 0, szIniPath);
    else if (pattern == EmPattern::emBreakThroughPattern)
    {
        nHighScore = ::GetPrivateProfileInt(BREAKTHROUGHAPPNAME, BREAKTHROUGHSCORE, 0, szIniPath);
        nHighLevel = ::GetPrivateProfileInt(BREAKTHROUGHAPPNAME, BREAKTHROUGHLEVEL, 1, szIniPath);
    }
    else if (pattern == EmPattern::emIntelligencePattern)
    {
        nHighScore = ::GetPrivateProfileInt(AIAPPNAME, AISCORE, 0, szIniPath);
        nHighLevel = ::GetPrivateProfileInt(AIAPPNAME, AILEVEL, 1, szIniPath);
    }
}

void PlayGame()
{
    while (1)
    {
        while (!_kbhit())
        {
            ProduceFood();
            BigFoodDisappear();
            TimeEclipse();
            BeginBatchDraw();
            cleardevice();
            InitSecondBackGround();
            DrawFood();
            DrawBigFood();
            EatFood();
            ProduceFood();
            SnakeMove();
            DrawSnake();
            Sleep(nCurSpeed);
            EndBatchDraw();
            BreakSnake();
        }
        ChangeDir();
    }
}

void TimeEclipse()
{
    if (pattern != EmPattern::emTimeLimitedPattern)
        return;

    nTimePast += nCurSpeed;
    nRemainTime = TOTAL_TIME - nTimePast / 1000;
    nCurSpeed = INIT_SPEED - nTimePast / 1000;
    if (nRemainTime < 0)
    {
        WCHAR szScore[32] = { 0 };
        swprintf_s(szScore, L"你的得分是: %d", nCurScore);
        ::MessageBox(0, szScore, L"时间到", 0);
        WriteRecord();
        InitFirstScene();
    }
}

void BigFoodDisappear()
{
    if (1 == bigFood.isEat)
        return;

    nBigFoodTimer += BIGFOOD_STEPTIME;//大食物定时消失
    if (nBigFoodTimer >= BIGFOOD_SHOWTIME)
    {
        bigFood.isEat = 1;
        nBigFoodTimer = 0;
    }
}

void WriteRecord()
{
    WCHAR szTempPath[MAX_PATH] = { 0 };
    WCHAR szIniPath[MAX_PATH] = { 0 };
    WCHAR szHighScore[8] = { 0 };
    WCHAR szHighLevel[8] = { 0 };
    KExpandEnvironmentString(L"%TEMP%", szTempPath, MAX_PATH);
    swprintf_s(szIniPath, L"%s\\record.ini", szTempPath);

    if (pattern == EmPattern::emBreakThroughPattern)
    {
        nHighLevel = ::GetPrivateProfileInt(BREAKTHROUGHAPPNAME, BREAKTHROUGHLEVEL, 1, szIniPath);
        nHighScore = ::GetPrivateProfileInt(BREAKTHROUGHAPPNAME, BREAKTHROUGHSCORE, 0, szIniPath);
        if (nCurScore > nHighScore)
        {
            swprintf_s(szHighScore, L"%d", nCurScore);
            WritePrivateProfileString(BREAKTHROUGHAPPNAME, BREAKTHROUGHSCORE, szHighScore, szIniPath);
        }
        if (nCurLevel > nHighLevel)
        {
            swprintf_s(szHighLevel, L"%d", nCurLevel);
            WritePrivateProfileString(BREAKTHROUGHAPPNAME, BREAKTHROUGHLEVEL, szHighLevel, szIniPath);
        }
    }
    else if (pattern == EmPattern::emTimeLimitedPattern)
    {
        nHighScore = ::GetPrivateProfileInt(TIMELIMITEDAPPNAME, TIMELIMITEDSCORE, 0, szIniPath);
        if (nCurScore > nHighScore)
        {
            swprintf_s(szHighScore, L"%d", nCurScore);
            WritePrivateProfileString(TIMELIMITEDAPPNAME, TIMELIMITEDSCORE, szHighScore, szIniPath);
        }
    }
    else if (pattern == EmPattern::emIntelligencePattern)
    {
        nHighLevel = ::GetPrivateProfileInt(AIAPPNAME, AILEVEL, 1, szIniPath);
        nHighScore = ::GetPrivateProfileInt(AIAPPNAME, AISCORE, 0, szIniPath);
        if (nCurScore > nHighScore)
        {
            swprintf_s(szHighScore, L"%d", nCurScore);
            WritePrivateProfileString(AIAPPNAME, AISCORE, szHighScore, szIniPath);
        }
        if (nCurLevel > nHighLevel)
        {
            swprintf_s(szHighLevel, L"%d", nCurLevel);
            WritePrivateProfileString(AIAPPNAME, AILEVEL, szHighLevel, szIniPath);
        }
    }
}

void BreakSnake()
{
    if (snake.pt[0].x < 0 || snake.pt[0].x > REGION_WIDTH / SNAKE_DRAW_SIZE - 1 || snake.pt[0].y < 0 || snake.pt[0].y > REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
    {
        ::MessageBox(0, L"你撞墙了", L"游戏失败", 0);
        WriteRecord();
        InitFirstScene();
    }

    for (int i = snake.nCount - 2; i > 0; i--)
    {
        if (snake.pt[i].x == snake.pt[0].x && snake.pt[i].y == snake.pt[0].y)
        {
            ::MessageBox(0, L"你咬到自己了", L"游戏失败", 0);
            WriteRecord();
            InitFirstScene();
        }
    }
}

void ChangeDir()
{
    int dir = _getch();
    switch (dir)
    {
    case EmDir::emDirUp:
        bUserInput = true;
        if (snake.dir != EmDir::emDirDown)
            snake.dir = EmDir::emDirUp;
        break;
    case EmDir::emDirDown:
        bUserInput = true;
        if (snake.dir != EmDir::emDirUp)
            snake.dir = EmDir::emDirDown;
        break;
    case EmDir::emDirLeft:
        bUserInput = true;
        if (snake.dir != EmDir::emDirRight)
            snake.dir = EmDir::emDirLeft;
        break;
    case EmDir::emDirRight:
        bUserInput = true;
        if (snake.dir != EmDir::emDirLeft)
            snake.dir = EmDir::emDirRight;
        break;
    default:
        break;
    }
}

void AIRealChangeDirection()
{
    Point pt = pathQ.front();
    pathQ.pop();
    if (pt.x == snake.pt[0].x)
    {
        if (pt.y - snake.pt[0].y == -1)
            snake.dir = EmDir::emDirUp;
        else if (pt.y - snake.pt[0].y == 1)
            snake.dir = EmDir::emDirDown;
    }
    else if (pt.y == snake.pt[0].y)
    {
        if (pt.x - snake.pt[0].x == -1)
            snake.dir = EmDir::emDirLeft;
        else if (pt.x - snake.pt[0].x == 1)
            snake.dir = EmDir::emDirRight;
    }
}

bool AISearchBigFood()
{
    if (bigFood.isEat)
        return false;

    InitMap();
    if (!SearchSnakePath(snake.pt[0], bigFood.fpt))
        return false;

    PushPath(snake.pt[0], bigFood.fpt);
    AIRealChangeDirection();

    return true;
}

bool AISearchSmallFood()
{
    InitMap();
    if (!IsEatFoodSafe())
        return false;

    if (!SearchSnakePath(snake.pt[0], food.fpt))
        return false;

    PushPath(snake.pt[0], food.fpt);
    AIRealChangeDirection();

    return true;
}

bool AISearchNearTail()
{
    InitMap();
    if (!SearchSnakePath(snake.pt[0], snake.pt[snake.nCount - 1]))
        return false;

    PushPath(snake.pt[0], snake.pt[snake.nCount -1]);
    AIRealChangeDirection();

    nCurChaseTailTimes++;
    return true;
}

bool AIWanderSearch1(bool isTryAgain)
{
    InitMap();
    if (snake.pt[0].x < 2)
        return false;

    if (mp[snake.pt[0].x - 1][snake.pt[0].y] )
        return false;

    if (!isTryAgain && mp[snake.pt[0].x - 2][snake.pt[0].y])//第二次进来时可以靠着蛇身走
        return false;

    Point tempPoint;
    tempPoint.x = snake.pt[0].x - 1;
    tempPoint.y = snake.pt[0].y;
    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();

    return true;
}

bool AIWanderSearch2(bool isTryAgain)
{
    InitMap();
    if (snake.pt[0].x + 1 > REGION_WIDTH / SNAKE_DRAW_SIZE - 2)
        return false;

    if (mp[snake.pt[0].x + 1][snake.pt[0].y])
        return false;

    if (!isTryAgain && mp[snake.pt[0].x + 2][snake.pt[0].y])
        return false;

    Point tempPoint;
    tempPoint.x = snake.pt[0].x + 1;
    tempPoint.y = snake.pt[0].y;
    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();

    return true;
}

bool AIWanderSearch3(bool isTryAgain)
{
    InitMap();
    if (snake.pt[0].y < 2)
        return false;

    if (mp[snake.pt[0].x][snake.pt[0].y - 1])
        return false;

    if (!isTryAgain && mp[snake.pt[0].x][snake.pt[0].y - 2])
        return false;

    Point tempPoint;
    tempPoint.x = snake.pt[0].x;
    tempPoint.y = snake.pt[0].y - 1;
    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();

    return true;
}

bool AIWanderSearch4(bool isTryAgain)
{
    InitMap();
    if (snake.pt[0].y + 1 > REGION_HEIGHT / SNAKE_DRAW_SIZE - 2)
        return false;

    if (mp[snake.pt[0].x][snake.pt[0].y + 1])
        return false;

    if (!isTryAgain && mp[snake.pt[0].x][snake.pt[0].y + 2])
        return false;

    Point tempPoint;
    tempPoint.x = snake.pt[0].x;
    tempPoint.y = snake.pt[0].y + 1;
    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();
    return true;
}

bool AISearchFourCorner(int x, int y)
{
    InitMap();
    Point tempPoint;
    tempPoint.x = x;
    tempPoint.y = y;
    if (tempPoint.x == snake.pt[0].x && tempPoint.y == snake.pt[0].y)
        nCurChaseTailTimes = 0;

    if (mp[tempPoint.x][tempPoint.x])
        return false;

    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();

    return true;
}

bool IsEatFoodSafe()
{
    if (food.fpt.y == 0 && mp[food.fpt.x][food.fpt.y + 1])
    {
        for (int i = 0; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)
            if (mp[i][food.fpt.y])
                return false;
    }

    if (food.fpt.y == REGION_HEIGHT / SNAKE_DRAW_SIZE - 1 && mp[food.fpt.x][REGION_HEIGHT / SNAKE_DRAW_SIZE - 2])
    {
        for (int i = 0; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)
            if (mp[i][food.fpt.y])
                return false;
    }

    if (food.fpt.x == 0 && mp[food.fpt.x + 1][food.fpt.y])
    {
        for (int i = 0; i < REGION_HEIGHT / SNAKE_DRAW_SIZE; i++)
            if (mp[food.fpt.x][i])
                return false;
    }

    if (food.fpt.x == REGION_WIDTH / SNAKE_DRAW_SIZE - 1 && mp[REGION_WIDTH / SNAKE_DRAW_SIZE - 2][food.fpt.y])
    {
        for (int i = 0; i < REGION_HEIGHT / SNAKE_DRAW_SIZE; i++)
            if (mp[food.fpt.x][i])
                return false;
    }

    if (mp[food.fpt.x - 1][food.fpt.y] && mp[food.fpt.x + 1][food.fpt.y])
    {
        for (int i = 0; i < REGION_HEIGHT / SNAKE_DRAW_SIZE; i++)
            if (mp[food.fpt.x][i])
                return false;
    }

    if (mp[food.fpt.x][food.fpt.y - 1] && mp[food.fpt.x][food.fpt.y + 1])
    {
        for (int i = 0; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)
            if (mp[i][food.fpt.y])
                return false;
    }

    return true;
}

bool IsGoLeftBetter()
{//是否开启大方向往左的模式
    InitMap();
    if (snake.pt[0].x == 0)
        return false;

    if (snake.pt[0].x == REGION_WIDTH / SNAKE_DRAW_SIZE - 1)
        return true;

    int nCntLeft = 0;
    int nCntRight = 0;
    for (int i = snake.pt[0].x + 1; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)//统计右边个数
    {
        for (int j = 0; j < REGION_HEIGHT / SNAKE_DRAW_SIZE; j++)
        {
            if (mp[i][j])
                nCntRight++;
        }
    }

    for (int i = 0; i < snake.pt[0].x; i++)//统计左边个数
    {
        for (int j = 0; j < REGION_HEIGHT / SNAKE_DRAW_SIZE; j++)
        {
            if (mp[i][j])
                nCntLeft++;
        }
    }
    return nCntLeft < nCntRight;
}

bool IsGoUpBetter()
{
    if (snake.pt[0].y == 0)
        return false;

    if (snake.pt[0].y == REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
        return true;

    int upPoint = 0;
    for (int i = snake.pt[0].y - 1; i >= 0; i--)//往上找能到达的点
    {
        if (mp[snake.pt[0].x][i])
        {
            upPoint = i;
            break;
        }
    }

    int downPoint = REGION_HEIGHT / SNAKE_DRAW_SIZE - 1;
    for (int i = snake.pt[0].y + 1; i < REGION_HEIGHT / SNAKE_DRAW_SIZE; i++)
    {
        if (mp[snake.pt[0].x][i])
        {
            downPoint = i;
            break;
        }
    }

    if (snake.pt[0].y - upPoint > downPoint - snake.pt[0].y)
        return true;
    return false;
}

bool IsLeftDeadWay(int x, int y)
{
    if (mp[x - 1][y] || (x >= 2 && mp[x - 2][y]) || (x >= 3 && mp[x - 3][y]))
        return true;

    return false;
}

bool IsRightDeadWay(int x, int y)
{
    if (mp[x + 1][y] || (x <= REGION_WIDTH / SNAKE_DRAW_SIZE - 3 && mp[x + 2][y]) || (x <= REGION_WIDTH / SNAKE_DRAW_SIZE - 4 && mp[x + 3][y]))
        return true;

    return false;
}

bool IsGoDownDeadWay(int x, int y)
{
    if (x < 1 || x > REGION_WIDTH / SNAKE_DRAW_SIZE - 2)
        return false;

    InitMap();
    int nCnt = 0;
    bool IsOtherPointThisColumn = false;
    for (int i = y + 1; i < REGION_HEIGHT / SNAKE_DRAW_SIZE; i++)
    {
        if (IsLeftDeadWay(x, i) && IsRightDeadWay(x, i))
            nCnt++;

        if (mp[x][i])
            IsOtherPointThisColumn = true;
    }

    if (IsOtherPointThisColumn && nCnt > 2)
        return true;

    return false;
}

bool IsGoUpDeadWay(int x, int y)
{
    if (x < 1 || x > REGION_WIDTH / SNAKE_DRAW_SIZE - 2)
        return false;

    InitMap();
    int nCnt = 0;
    bool IsOtherPointThisColumn = false;
    for (int i = 0; i < y; i++)
    {
        if (mp[x - 1][i] && mp[x + 1][i])
            nCnt++;

        if (mp[x][i])
            IsOtherPointThisColumn = true;
    }

    if (IsOtherPointThisColumn && nCnt > 2)
        return true;

    return false;
}

bool MustWinModeGoDown()
{
    InitMap();
    Point tempPoint;
    tempPoint.x = snake.pt[0].x;
    tempPoint.y = snake.pt[0].y + 1;
    if (mp[tempPoint.x][tempPoint.y])
        return false;

    if (tempPoint.y > REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
        return false;

    if (tempPoint.y > REGION_HEIGHT / SNAKE_DRAW_SIZE - 2 && tempPoint.x != REGION_WIDTH / SNAKE_DRAW_SIZE - 1 && tempPoint.x != 0)
        return false;

    if (IsGoDownDeadWay(tempPoint.x, tempPoint.y))
        return false;

    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();
    return true;
}

bool MustWinModeGoUp()
{
    InitMap();
    Point tempPoint;
    tempPoint.x = snake.pt[0].x;
    tempPoint.y = snake.pt[0].y - 1;
    if (mp[tempPoint.x][tempPoint.y])
        return false;

    if (tempPoint.y < 0)
        return false;

    if (tempPoint.y < 1 && tempPoint.x != REGION_WIDTH / SNAKE_DRAW_SIZE - 1 && tempPoint.x != 0)
        return false;

    if (IsGoUpDeadWay(tempPoint.x, tempPoint.y))
        return false;

    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();
    return true;
}

bool MustWinModeGoLeft()
{
    InitMap();
    Point tempPoint;
    tempPoint.x = snake.pt[0].x - 1;
    tempPoint.y = snake.pt[0].y;
    if (tempPoint.x < 0 || mp[tempPoint.x][tempPoint.y])
        return false;

    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();
    return true;
}

bool MustWinModeGoRight()
{
    InitMap();
    Point tempPoint;
    tempPoint.x = snake.pt[0].x + 1;
    tempPoint.y = snake.pt[0].y;
    if (tempPoint.x > REGION_WIDTH / SNAKE_DRAW_SIZE - 1 || mp[tempPoint.x][tempPoint.y])
        return false;

    if (!SearchSnakePath(snake.pt[0], tempPoint))
        return false;

    PushPath(snake.pt[0], tempPoint);
    AIRealChangeDirection();
    return true;
}

bool IsSnakeAdjustOk()
{
    InitMap();
    int nCnt[REGION_WIDTH / SNAKE_DRAW_SIZE + 1] = { 0 };
    for (int i = 0; i < snake.nCount; i++)
        nCnt[snake.pt[i].x]++;

    int nonZeroCnt = 0;
    for (int i = 0; i < REGION_WIDTH / SNAKE_DRAW_SIZE; i++)
    {
        if (nCnt[i] > 0)
            nonZeroCnt++;
    }

    if (nonZeroCnt <= snake.nCount / (REGION_HEIGHT / SNAKE_DRAW_SIZE - 2) + 2)
        return true;
    return false;
}

bool StartMustWinStrategy()
{
    if (bIsAdjustOk)
        return false;

    bIsAdjustOk = IsSnakeAdjustOk();

    if (snake.pt[0].y != REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
    {
        if (IsGoUpBetter())
        {
            if (MustWinModeGoUp() || MustWinModeGoDown())
                return true;
        }
        else
        {
            if (MustWinModeGoDown() || MustWinModeGoUp())
                return true;
        }
    }
    else
    {
        if (IsGoLeftBetter())
        {
            if (MustWinModeGoLeft() || MustWinModeGoRight())
                return true;
        }
        else
        {
            if (MustWinModeGoRight() || MustWinModeGoLeft())
                return true;
        }
    }

    return false;
}

void AIChangeDir()
{
    if (pattern != EmPattern::emIntelligencePattern)
        return;

    if (bUserInput)
        return;

    if (snake.nCount > MAX_YOUNG_LEN)
        if (StartMustWinStrategy())
            return;

    if (AISearchBigFood())
        return;

    if (AISearchSmallFood())
        return;

    if (nCurChaseTailTimes > snake.nCount)
    {
        int nRand = rand() % 4;
        if (nRand == 0 && AISearchFourCorner(1, 1))
        {
            nCurChaseTailTimes -= 100;
            return;
        }

        if (nRand == 1 && AISearchFourCorner(1, REGION_HEIGHT / SNAKE_DRAW_SIZE - 2))
        {
            nCurChaseTailTimes -= 100;
            return;
        }

        if (nRand == 2 && AISearchFourCorner(REGION_WIDTH / SNAKE_DRAW_SIZE - 2, REGION_HEIGHT / SNAKE_DRAW_SIZE - 2))
        {
            nCurChaseTailTimes -= 100;
            return;
        }

        if (nRand == 3 && AISearchFourCorner(REGION_WIDTH / SNAKE_DRAW_SIZE - 2, 1))
        {
            nCurChaseTailTimes -= 100;
            return;
        }
    }

    if (AISearchNearTail())
        return;

    if (AIWanderSearch1())
        return;

    if (AIWanderSearch2())
        return;

    if (AIWanderSearch3())
        return;

    if (AIWanderSearch4())
        return;

    if (AIWanderSearch1(true))
        return;

    if (AIWanderSearch2(true))
        return;

    if (AIWanderSearch3(true))
        return;

    if (AIWanderSearch4(true))
        return;
}

void SnakeMove()
{
    for (int i = snake.nCount; i > 0; i--)
    {
        snake.pt[i].x = snake.pt[i - 1].x;
        snake.pt[i].y = snake.pt[i - 1].y;
    }

    AIChangeDir();
    switch (snake.dir)
    {
    case EmDir::emDirUp:
        snake.pt[0].y--;
        break;
    case EmDir::emDirDown:
        snake.pt[0].y++;
        break;
    case EmDir::emDirLeft:
        snake.pt[0].x--;
        break;
    case EmDir::emDirRight:
        snake.pt[0].x++;
        break;
    default:
        break;
    }
    bUserInput = false;
}

void DecideHeadDirection()
{
    if (snake.pt[0].y == snake.pt[1].y && snake.pt[0].x > snake.pt[1].x)//头往右走
        DrawSnakeHead(0);
    else if (snake.pt[0].y == snake.pt[1].y && snake.pt[0].x < snake.pt[1].x)//头往左走
        DrawSnakeHead(1);
    else if (snake.pt[0].x == snake.pt[1].x && snake.pt[0].y < snake.pt[1].y)//头往上走
        DrawSnakeHead(2);
    else if (snake.pt[0].x == snake.pt[1].x && snake.pt[0].y > snake.pt[1].y)//头往下走
        DrawSnakeHead(3);
}

void DrawSnakeHead(int nIndex)
{
    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG2 + nIndex), SNAKE_DRAW_SIZE, SNAKE_DRAW_SIZE);
    putimage(snake.pt[0].x * SNAKE_DRAW_SIZE, snake.pt[0].y * SNAKE_DRAW_SIZE, &img);
}

void DecideCornerDirection(int idx)
{
    if (snake.pt[idx].y > snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x && snake.pt[idx].y == snake.pt[idx + 1].y && snake.pt[idx].x > snake.pt[idx + 1].x)
        DrawCorner(0, idx);//右->上
    else if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x > snake.pt[idx - 1].x && snake.pt[idx].y > snake.pt[idx + 1].y && snake.pt[idx].x == snake.pt[idx + 1].x)
        DrawCorner(0, idx);//下->左

    else if (snake.pt[idx].y > snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x && snake.pt[idx].y == snake.pt[idx + 1].y && snake.pt[idx].x < snake.pt[idx + 1].x)
        DrawCorner(1, idx);//左->上
    else if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x < snake.pt[idx - 1].x && snake.pt[idx].y > snake.pt[idx + 1].y && snake.pt[idx].x == snake.pt[idx + 1].x)
        DrawCorner(1, idx);//下->右

    else if (snake.pt[idx].y < snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x && snake.pt[idx].y == snake.pt[idx + 1].y && snake.pt[idx].x > snake.pt[idx + 1].x)
        DrawCorner(2, idx);//右->下
    else if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x > snake.pt[idx - 1].x && snake.pt[idx].y < snake.pt[idx + 1].y && snake.pt[idx].x == snake.pt[idx + 1].x)
        DrawCorner(2, idx);//上->左

    else if (snake.pt[idx].y < snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x && snake.pt[idx].y == snake.pt[idx + 1].y && snake.pt[idx].x < snake.pt[idx + 1].x)
        DrawCorner(3, idx);//左->下
    else if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x < snake.pt[idx - 1].x && snake.pt[idx].y < snake.pt[idx + 1].y && snake.pt[idx].x == snake.pt[idx + 1].x)
        DrawCorner(3, idx);//上->右
    else
        DecideBodyDirection(idx);//不是corner的情况在考虑body
}

void DrawCorner(int nIndex, int idx)
{
    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG6 + nIndex), SNAKE_DRAW_SIZE, SNAKE_DRAW_SIZE);
    putimage(snake.pt[idx].x * SNAKE_DRAW_SIZE, snake.pt[idx].y * SNAKE_DRAW_SIZE, &img);
}

void DecideBodyDirection(int idx)
{
    if (snake.pt[idx].x == snake.pt[idx - 1].x || snake.pt[idx].x == snake.pt[idx + 1].x)//上下
        DrawBody(0, idx);
    else if (snake.pt[idx].y == snake.pt[idx - 1].y || snake.pt[idx].y == snake.pt[idx + 1].y)//左右
        DrawBody(1, idx);
}

void DrawBody(int nIndex, int idx)
{
    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG10 + nIndex), SNAKE_DRAW_SIZE, SNAKE_DRAW_SIZE);
    putimage(snake.pt[idx].x * SNAKE_DRAW_SIZE, snake.pt[idx].y * SNAKE_DRAW_SIZE, &img);
}

void DecideTailDirection()
{
    int idx = snake.nCount - 1;
    if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x < snake.pt[idx - 1].x)//往右
        DrawTail(0, idx);
    else if (snake.pt[idx].y == snake.pt[idx - 1].y && snake.pt[idx].x > snake.pt[idx - 1].x)//往左
        DrawTail(1, idx);
    else if (snake.pt[idx].y > snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x)//往上
        DrawTail(2, idx);
    else if (snake.pt[idx].y < snake.pt[idx - 1].y && snake.pt[idx].x == snake.pt[idx - 1].x)//往下
        DrawTail(3, idx);
}

void DrawTail(int nIndex, int idx)
{
    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG12 + nIndex), SNAKE_DRAW_SIZE, SNAKE_DRAW_SIZE);
    putimage(snake.pt[idx].x * SNAKE_DRAW_SIZE, snake.pt[idx].y * SNAKE_DRAW_SIZE, &img);
}

void DrawSnake()
{
    for (int i = 0; i < snake.nCount; i++)
    {
        if (0 == i)
            DecideHeadDirection();
        else if (i == snake.nCount - 1)
            DecideTailDirection();
        else
            DecideCornerDirection(i);
    }
}

int IsEatBigFood()
{
    if (1 == bigFood.isEat)
        return 0;

    for (int i = bigFood.fpt.x; i < bigFood.fpt.x + 3; i++)
    {
        for (int j = bigFood.fpt.y; j < bigFood.fpt.y + 3; j++)
        {
            if (snake.pt[0].x == i && snake.pt[0].y == j)
                return 1;
        }
    }
    return 0;
}

void LevelUp()
{
    if (pattern == EmPattern::emTimeLimitedPattern)
        return;

    //WCHAR szLevelUp[32];
    if (nCurScore > arrScore[nCurLevel])
    {
        //swprintf_s(szLevelUp, L"恭喜你,升到第%d级", nCurLevel + 1);
        //::MessageBox(0, szLevelUp, L"升级啦", 0);
        nCurLevel++;
        if (nCurSpeed > 0)
            nCurSpeed -= MINUS_SPEED;
    }
}

void EatFood()
{
    if (snake.pt[0].x == food.fpt.x && snake.pt[0].y == food.fpt.y)
    {
        nCurScore += FOOD_SCORE;
        snake.nCount++;
        food.isEat = 1;
        bIsAdjustOk = false;
        ProduceBigFood();
    }

    if (IsEatBigFood())
    {
        nCurScore += BIG_FOOD_SCORE;
        snake.nCount++;
        bigFood.isEat = 1;
        bIsAdjustOk = false;
    }

    LevelUp();
}

int IsFoodPosOk(int x, int y)
{
    for (int i = 0; i < snake.nCount; i++)
    {
        if (snake.pt[i].x == x && snake.pt[i].y == y)
            return 0;
    }

    if (0 == bigFood.isEat)
    {
        for (int i = bigFood.fpt.x; i < bigFood.fpt.x + 3; i++)
        {
            for (int j = bigFood.fpt.y; j < bigFood.fpt.y + 3; j++)
            {
                if (x == i && y == j)
                    return 0;
            }
        }
    }
    return 1;
}

int IsBigFoodPosOk(int x, int y)
{
    for (int i = x; i < x + 3; i++)
    {
        for (int j = y; j < y + 3; j++)
        {
            if (i < 0 || i > REGION_WIDTH / SNAKE_DRAW_SIZE - 1 || j < 0 || j > REGION_HEIGHT / SNAKE_DRAW_SIZE - 1)
                return 0;

            for (int k = 0; k < snake.nCount; k++)
            {
                if (snake.pt[k].x == i && snake.pt[k].y == j)
                    return 0;
            }
        }
    }
    return 1;
}

void ProduceFood()
{
    if (0 == food.isEat)
        return;

    while (1)
    {
        food.fpt.x = rand() % (REGION_WIDTH / SNAKE_DRAW_SIZE);
        food.fpt.y = rand() % (REGION_HEIGHT / SNAKE_DRAW_SIZE);
        if (IsFoodPosOk(food.fpt.x, food.fpt.y))
            break;
    }
    food.isEat = 0;
}

void DrawFood()
{
    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG1), SNAKE_DRAW_SIZE, SNAKE_DRAW_SIZE);
    putimage(food.fpt.x * SNAKE_DRAW_SIZE, food.fpt.y * SNAKE_DRAW_SIZE, &img);
}

int ProduceBigFood()
{
    if (0 == bigFood.isEat)//大食物没被吃掉,不产生
        return 0;

    if (rand() % 2 == 0)
    {
        while (1)
        {
            bigFood.fpt.x = rand() % (REGION_WIDTH / SNAKE_DRAW_SIZE);
            bigFood.fpt.y = rand() % (REGION_HEIGHT / SNAKE_DRAW_SIZE);
            if (IsBigFoodPosOk(bigFood.fpt.x, bigFood.fpt.y))
                break;
        }
        bigFood.isEat = 0;
        return 1;
    }
    return 0;
}

void DrawBigFood()
{
    if (1 == bigFood.isEat)
        return;

    IMAGE img;
    loadimage(&img, L"jpg", MAKEINTRESOURCE(IDR_JPG1), SNAKE_DRAW_SIZE * 3, SNAKE_DRAW_SIZE * 3);
    putimage(bigFood.fpt.x * SNAKE_DRAW_SIZE, bigFood.fpt.y * SNAKE_DRAW_SIZE, &img);
}

int main()
{
    HWND hWnd = GetConsoleWindow();
    SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1)));
    initgraph(WND_WIDTH, WND_HEIGHT);

    srand((unsigned int)time(NULL));
    InitFirstScene();

    getchar();
    closegraph();
    return 0;
}

运行结果:
这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

贪吃蛇智能版(专家) 的相关文章

  • Go语言面试题--基础语法(16)

    文章目录 1 f1 f2 f3 函数分别返回什么 2 下面代码段输出什么 3 关于channel的特性 下面说法正确的是 1 f1 f2 f3 函数分别返回什么 func main fmt Println f1 fmt Println f2
  • 【HDLBits 刷题 13】Buliding Larger Circuits

    目录 写在前面 Buliding Larger Circuits count1k shiftcount fsm seq fsmshift fsm fancytimer fsm onehot 写在前面 以下的解题方法不一定为最佳解决方案 有更
  • 构造树型结构数据

    构造树型结构数据 param data 数据源 param id id字段 默认 id param parentId 父节点字段 默认 parentId param children 孩子节点字段 默认 children export fu
  • AUC的计算、物理意义,

    文章目录 一 定义 二 性质 三 计算 3 1 方法一 根据定义 3 2 方法二 根据意义 3 3 方法三 方法二优化 3 4 方法四 工业场景 四 物理意义推导 一 定义 ROC曲线与坐标轴围成的面积 ROC曲线由不同阈值下 TPR Y轴
  • Linux内核在I386架构下的内存管理

    转载自 http blog csdn net li shyng article details 5545973 同类型的 http www kerneltravel net journal ii I386是Intel的x86系列CUP中一个
  • 前端页面有那三层构成,分别是什么?作用是什么?

    结构 表现和行为 其中结构主要是有HTML标签组成 结构即在页面body里面我们写入的标签都是为了页面的结构 表现即指css样式表 通过css可以是页面的结构标签更具美感 行为是指页面和用户具有一定的交互 同时页面结构或者表现发生变化 主要
  • 接口测试工具-apifox

    Apifox 是 API 文档 API 调试 API Mock API 自动化测试一体化协作平台 定位 Postman Swagger Mock JMeter 通过一套系统 一份数据 解决多个系统之间的数据同步问题 只要定义好 API 文档
  • 将一个网页设置为屏保

    有没有试过将一个网页作为屏保 最近我正好有这个需求 一些需要给家里人经常看到的提示信息 如果定闹钟多了大家嫌烦 我口头提示多了比闹钟还烦 印在A4纸上贴墙上既影响美观又不容易修改内容 打印还要花银子 自己写字又不好看 突然想到我几乎每天早上
  • Unity播放音频

    在Unity中 可以在物体上添加AudioSource组件来播放音频 AudioSource组件可以控制音频文件的播放 音量 音调 空间效果等属性 以下是在物体上添加AudioSource组件的步骤 1 在Unity中打开场景 选择您想要添
  • tensorflow-gpu 2.3.0安装 及 相关对应版本库安装(Anaconda安装)

    目录 如需转载 请标明出处 谢谢 一 安装tensorflow gpu2 3 0 二 配置其他相关的库 很多人以为安装完tensorflow gpu就是一切都结束了 但是殊不知 python中的很多库 比如numpy matplotlib等
  • 【JavaScript数据结构与算法】一、栈及leetcode实战

    栈 栈是一种遵从后进先出 LIFO 原则的有序集合 新添加或待删除的元素都保存在栈的同一端 称作栈顶 另一端就叫栈底 在栈里 新元素都靠近栈顶 旧元素都接近栈底 栈数据结构 我们需要一种数据结构来保存栈里的元素 可以选择数组 数组允许我们在
  • 数据库查询优化 --- 索引 Index

    数据库查询优化 索引 Index 什么是索引 索引的实现方法 聚集索引 和 非聚簇索引 聚集索引 非聚集索引 组合索引 索引的使用 适合使用索引的场景 不适合使用索引的场景 SQL创建索引 什么是索引 没有加索引的数据 它的数据无序的放置在
  • 串级PID与单极PID的区别

    目录 前言 一 什么是串级PID 二 串级PID与单极PID比较 1 控制小车开到某一位置 2 平衡小车速度控制 三 什么时候用串级PID 结语 前言 本文将讲述串级PID与单极PID的区别 并由此引出什么时候用串级PID 对于想深入学习P
  • electron vue3 + ts 初始化项目

    新建 vue3 ts 项目 npm init vitejs app electron demo 选择 vue ts 等待项目创建完成 安装 electron npm i electron D 确认 node modules electron
  • 【C++入门】运算符重载详解

    1 什么是运算符重载 不可重载运算符 运算符含义 成员访问运算符 gt 和 成员指针访问运算符 域运算符 sizeof 长度运算符 条件运算符 预处理符号 1 运算符 运算符分为算术运算符 关系运算符 逻辑运算符 等 2 运算符重载 给运算
  • idea授权服务器

    这里提供一个我已经搭建好的idea授权服务器 http ysk521 cn 1017
  • ctfshow萌新红包题writeup

    ctfshow萌新专属红包题writeup 题目来源 https ctf show 这一题是ctfshow平台上面2月17日更新的一个萌新红包题 当天在官方交流群内知道晚上会有一个萌新红包题之后 就有点期待了 小萌新也想拿一次红包 嘿嘿 下
  • 推挽输出和开漏输出区别

    推挽输出 可以输出高 低电平 连接数字器件 输出 0 时 N MOS 导通 P MOS 高阻 输出0 输出 1 时 N MOS 高阻 P MOS 导通 输出1 不需要外部上拉电路 开漏输出 输出端相当于三极管的集电极 要得到高电平状态需要上
  • 同步代码块、io、file常用的方法、流

    一 同步 1 同步代码块 把要实现线程安全的代码放在同步代码块中 java中任何对象内部都有一个开关 你可以理解为标志位 1就是表示关 0就表示开 2 同步方法 二 io input输入 output输出 计算机中的文件以及文件中的内容 F

随机推荐

  • maven的下载安装配置教程(详细图文)

    maven的下载安装配置教程 详细图文 一 maven的下载 https maven apache org download cgi 下载zip文件 二 maven的安装 注意 maven的安装需要依赖jdk的安装 所以必须先安装完成jdk
  • STM32的PWM相关函数TIM_SetCompare1的一定理解

    void TIM SetCompare1 TIM TypeDef TIMx uint16 t Compare1 Check the parameters assert param IS TIM LIST8 PERIPH TIMx Set t
  • mysql-mmm集群

    一 Mysql mmm集群技术概述 概述 MMM Master Master replication manager for MySQL 是一套支持双主故障切换和双主日常管理的脚本程序 MMM使用Perl语言开发 主要用来监控和管理MySQ
  • RecyclerView(一)—— ListView的使用

    ListView ListView在过去绝对可以称得上是Android中最常用的控件之一 几乎所有的应用程序都会用到它 由于手机屏幕空间比较有限 能够一次性在屏幕上显示的内容并不多 当我们的程序中有大量的数据需要展示的时候 就可以借助Lis
  • vscode给某行代码添加标签

    只有你习惯于一种编程工具风格 vscode几乎都能给你做的 Numbered Bookmarks 数字标签跳转类似与webstrom 定义 Ctrl SHIFT 数字 跳转 Ctrl 数字 vscode给某行代码添加标签 标注 用于代码间快
  • Qt使用QJSEngine执行脚本

    有时候需要进行一些计算 程序是使用c 来实现的 使用c 来进行计算不是很灵活 这时候可以使用脚本来进行计算 这样灵活 且易于扩展 写了一个简单的Demo进行验证 具体例子如下 include
  • Java - 带参的异常处理模块try(xxx){...}

    带资源的try语句 try with resource 的最简形式为 try Resource res xxx 可指定多个资源 do something try块退出时 会自动调用res close 方法 关闭资源 其实这样做的原因 主要是
  • mysql24,Mysql24之事务隔离级别和MVCC

    TOC 事前准备 为了故事的顺利发展 我们需要创建一个表 CREATE TABLE hero number INT name VARCHAR 100 country varchar 100 PRIMARY KEY number Engine
  • mmdetection 中 Mask Rcnn检测结果可视化(DICE计算、PR曲线绘制等)

    mmdetection中的Mask Rcnn是一个很不错的检测网络 既可以实现目标检测 也可以实现语义分割 官方也有很详细的doc指导 但是对新手来说并不友好 刚好之前笔者写的mmlab系列里面关于可视化都还没有一个详细的文档 也在此一并介
  • 逆向破解之破解补丁与劫持Dll

    破解补丁 前言 破解补丁的作用是修改程序内存地址的硬编码 以此来实现破解软件 这是常见的JE跳转用来判断注册码是否正确 若401108位置的汇编指令给Nop掉就能实现破解 编写代码 1 C语言 运行程序后输入要破解的程序进程ID inclu
  • yolov5源码解读--训练策略

    yolov5源码解读 训练策略 超参数解读 命令行参数 train 模型迭代 测试 超参数解读 hyp scratch yaml lr0 0 0032 初始学习率 lrf 0 12 使用余弦函数动态降低学习率 lr0 lrf momentu
  • spark入门小例子

    来源 我是码农 转载请保留出处和链接 本文链接 http www 54manong com id 1221 1 pyspark 2 spark shell spark网页管理页面 http 127 0 0 1 4040 jobs 3 设置日
  • Redis常见数据结构的常用命令及引用

    String 1 常用命令 字符串常用操作 SET key value 存入字符串键值对 MSET key value key value 批量存储字符串键值对 SETNX key value 存入一个不存在的字符串键值对 GET key
  • AVLTree-平衡二叉树-coming soon

  • php验证用户账号密码正确,php-检查用户名和密码是否正确

    因为我的代码是正确的 所以我总是得到回显 Username Passwordcorrect 用户名 密码是否匹配 我的问题是 我在下面的代码中为PHP总是回显 用户名 密码错误 而做错了什么 require privstuff dbinfo
  • jupyter notebook使用基础及其快捷键,包括对文档操作、cell操作、快捷键、markdown

    目录 Jupyter Notebook介绍 使用原因 基本操作 新建notebook文档 对文档的操作 cell操作 什么是cell Jupyter支持两种模式 鼠标操作 Jupyter快捷键操作 markdown演示 手动建导航 Jupy
  • 网络编程 - Java SSLSocketFactory 创建方式

    SSL TLS 认证需要服务端提供 KeyStore jks TrustStore jks 实现方式 优缺点 服务端提供 CA Client CRT Client Key 文件 缺点 服务端提供原始签名 不安全不建议采用 服务端提供 Key
  • linux $0命令,Linux:awk命令详解

    简单使用 awk 对于文件中一行行的独处来执行操作 awk F print 1 4 使用 来分割这一行 把这一行的第一第四个域打印出来 AWK命令介绍 awk语言的最基本功能是在文件或字符串中基于指定规则浏览和抽取信息 awk抽取信息后 才
  • 2023最新pycharm详细安装教程,小白必看

    一 python官网 Python官网主要有python的About 简介 Downloads 下载 Documentation 文档 Community 团体 Success Stories 成功案例 News 新闻 Events 事件动
  • 贪吃蛇智能版(专家)

    在高级版本的基础之上 主要针对以下问题进行了处理 当长度逐渐变成 超过100之后 随机wander 追尾有比较大的随机性 弄不好就把自己围死了 这个时候已经不能再看到实物马上就去吃了 在吃之前必须先调整好自身的状态 等到认为调整的差不多的时