贪吃蛇改进

这次做了改进,移动之后直接在目标位置打印蛇身,不再重新画图,所以图再大也没问题了。

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<time.h>
#include<windows.h>

#define WIGTH  50
#define HEIGHT 25
//const int iWight  = 50;
//const int iHeight = 20;
char cBody   = '#';
char cFloor  = ' ';
char cFood   = '$';

typedef struct SnakeNode
{
    int  x;
    int  y;
    struct SnakeNode *pNext;
} SnakeNode;

char GlobleMap[HEIGHT][WIGTH] = {0};
int iLengt = 2;
int iSpeed = 150;

SnakeNode* CreatSnake();                                                // 创建一个蛇头
SnakeNode* CreatFood(SnakeNode *pHead);                                                 // 创建一个食物
int  IsBump(SnakeNode *pFood, SnakeNode *pHead);                        // 检测与食物、墙壁、身体的碰撞
void Menu();
char MoveSnake(char cDirection, SnakeNode **pHead);                     // 给一个方向移动蛇,要改变蛇头,所以这里用二级指针
void Init();                                                            // 初始化地图
void GetKey(int x);
void Elongate(char cDirection, SnakeNode *pHead);                       // 增加设的长度
void PrintSnake(SnakeNode *pHead);                                      // 将蛇身打印在地图里
void PrintFood(SnakeNode *pFood);                                       // 将食物打印在地图里
void UpData();                                                          // 更新数据
void Free(SnakeNode *pHead);                                            // 释放内存,结束时调用
void GoToXY(int x, int y);                                              // 辅助函数

int main()
{
    SnakeNode *pHead = CreatSnake();
    SnakeNode *pFood = CreatFood(pHead);
    Menu();
    int t;
    char cDirection = 'd';
    system("cls");
    Init();
    while(1)
    {
        Sleep(iSpeed);
//        PrintSnake(pHead);
        PrintFood(pFood);
        UpData();
//------------这一段没写在函数里,有点麻烦------------
        if(!kbhit())
        {
            cDirection = MoveSnake(cDirection, &pHead);                 // 如果没有按键盘,按原来的方向走
        }
        else
        {
            char cTmp = getch();                                             // 按下键盘,检测键盘的值,确保不会回头
            if(cDirection == 's' && cTmp == 'w')
                MoveSnake(cDirection, &pHead);
            else if(cDirection == 'w' && cTmp == 's')
                MoveSnake(cDirection, &pHead);
            else if(cDirection == 'd' && cTmp == 'a')
                MoveSnake(cDirection, &pHead);
            else if(cDirection == 'a' && cTmp == 'd')
                MoveSnake(cDirection, &pHead);
            else
                cDirection = MoveSnake(cTmp, &pHead);
        }
//--------------按下键盘,检测键盘的值,确保不会回头--------------
//--------------碰撞检测--------------------
        t = IsBump(pFood, pHead);
        if(t == 1)                                                      // 吃到食物, 蛇身边长,更新食物
        {
            Elongate(cDirection, pHead);
            pFood = CreatFood(pHead);
        }
        else if(t == 2)                                       // 撞到墙或者身体,游戏结束
        {
            GoToXY(WIGTH+10, 12);
            printf("\aGAME OVER");
            GoToXY(WIGTH+10, 14);
            printf("哎呀,你撞到墙了!");
            break;
        }
        else if(t == 3)                                       // 撞到墙或者身体,游戏结束
        {
            GoToXY(WIGTH+10, 12);
            printf("\aGAME OVER");
            GoToXY(WIGTH+10, 14);
            printf("哎呀,你吃到自己了!");
            break;
        }
//-------------------------------------------
    }
    Free(pHead);
    getchar();
    GoToXY(0, HEIGHT+1);
    return 0;
}
void Init()
{
    GoToXY(0, 0);                                                       // 回到左上角的位置,重新打印地图
    for(int i=0; i<HEIGHT; i++)
    {
        for(int j=0 ;j<WIGTH; j++)
        {
            if(i==0 || i==HEIGHT-1)
                GlobleMap[i][j] = '_';
            else if(j==0 || j==WIGTH-1)
                GlobleMap[i][j] = '|';
            else
                GlobleMap[i][j] = ' ';
        }
    }
    for(int i=0; i<HEIGHT; i++)
    {
        for(int j=0 ;j<WIGTH; j++)
        {
            printf("%c", GlobleMap[i][j]);
        }
        printf("\n");
    }
}

void Menu()
{
    GoToXY(40, 10);
    printf("开始之前请将输入法设置为英文小写");
    GoToXY(40, 12);
    printf("w--上;s--下;a--左;d--右;其他键暂停");
    GoToXY(40, 14);
    printf("1--慢速;2--中速;3--快速");
    GoToXY(40, 16);
    printf("请选择:");
    switch(getchar())
    {
        case '1': iSpeed = 200; break;
        case '2': iSpeed = 150; break;
        case '3': iSpeed = 100; break;
        default : iSpeed = 150; break;
    }
}
SnakeNode* CreatSnake()
{
    SnakeNode *pHead = malloc(sizeof(SnakeNode));                       // 创建两个节点的蛇
    pHead->x = 6;
    pHead->y = 9;
    pHead->pNext = malloc(sizeof(SnakeNode));
    pHead->pNext->x = 7;
    pHead->pNext->y = 9;
    pHead->pNext->pNext = NULL;
    return pHead;
}
SnakeNode* CreatFood(SnakeNode *pHead)
{
    srand(time(NULL));
    SnakeNode *pFood = malloc(sizeof(SnakeNode));
    pFood->pNext = NULL;
    SnakeNode *pNon  = pHead;

    int x = rand()%(WIGTH-2) + 1;                                           // 确保食物不会出现在边界, 还应该写 食物不能出现在蛇身,我暂时没写
    int y = rand()%(HEIGHT-3) + 1;

    while(pNon != NULL)
    {
        if(pNon->x == x && pNon->y == y)
        {
            x = rand()%(WIGTH-2) + 1;
            y = rand()%(HEIGHT-3) + 1;
            pNon = pHead;
        }
        pNon = pNon->pNext;
    }

    pFood->x = x;
    pFood->y = y;
    return pFood;
}

void Elongate(char cDirection, SnakeNode *pHead)
{
    SnakeNode *pNon = pHead;
    SnakeNode *pTmp;
    while(pNon->pNext != NULL)
    {
        pNon = pNon->pNext;
    }
    pTmp = malloc(sizeof(SnakeNode));                                   // 在蛇尾添加一个节点
    pNon->pNext = pTmp;
    pTmp->pNext = NULL;

    switch(cDirection)                                                  // 添加的节点,与移动方向相反,例如:蛇头往上走,添加的节点在尾巴的下面
    {
        case 's': pTmp->y = pNon->y - 1;
                  pTmp->x = pNon->x;
                  break;
        case 'w': pTmp->y = pNon->y + 1;
                  pTmp->x = pNon->x;
                  break;
        case 'd': pTmp->x = pNon->x - 1;
                  pTmp->y = pNon->y;
                  break;
        case 'a': pTmp->x = pNon->x + 1;
                  pTmp->y = pNon->y;
                  break;
        default : break;
    }
//    printf("\a");
}

void PrintFood(SnakeNode *pFood)
{
        GoToXY(pFood->x, pFood->y);
        printf("%c", cFood);                              // 将食物的位置,标注在地图上
}

char MoveSnake(char cDirection, SnakeNode **pHead)
{
    SnakeNode *pNon = *pHead;
    while(pNon->pNext->pNext != NULL)                                   // 找到尾节点的前一个节点
        pNon = pNon->pNext;

    SnakeNode *pTmp = pNon->pNext;                                      // pTmp为尾节点

    int x = pTmp->x;                                                    // 记录尾节点的坐标
    int y = pTmp->y;

    pTmp->pNext = *pHead;                                               // pTmp指向头节点

    pNon->pNext = NULL;                                                 // pNon变成尾节点
    *pHead = pTmp;                                                      // 更新*pHead
    switch(cDirection)                                                  // 整体思想是 把蛇尾拿下来,放在蛇头上,来蛇的整体实现移动
    {
        case 'w': (*pHead)->y = (*pHead)->pNext->y - 1;
                  (*pHead)->x = (*pHead)->pNext->x;
                  break;
        case 's': (*pHead)->y = (*pHead)->pNext->y + 1;
                  (*pHead)->x = (*pHead)->pNext->x;
                  break;
        case 'a': (*pHead)->x = (*pHead)->pNext->x - 1;
                  (*pHead)->y = (*pHead)->pNext->y;
                  break;
        case 'd': (*pHead)->x = (*pHead)->pNext->x + 1;
                  (*pHead)->y = (*pHead)->pNext->y;
                  break;
        default : break;
    }
    //打印新蛇头
    GoToXY((*pHead)->x, (*pHead)->y);
    printf("%c", cBody);
    GoToXY(0, HEIGHT);
    //新打印蛇尾
    GoToXY(pTmp->x, pTmp->y);
    printf("%c", cBody);
    GoToXY(0, HEIGHT);
    //删除旧蛇尾
    GoToXY(x, y);
    printf("%c", cFloor);
    GoToXY(0, HEIGHT);
    return cDirection;
}

void UpData()
{
    GoToXY(WIGTH+10, 3);
    printf("您的得分是:%d", (iLengt-2)*5);
    GoToXY(WIGTH+10, 6);
    printf("食物一共有:%d", iLengt-2);
    GoToXY(WIGTH+10, 9);
    printf("蛇的长度是:%d", iLengt);
}
void Free(SnakeNode *pHead)
{
    SnakeNode *pNon = pHead->pNext;
    free(pHead);
    while(pNon != NULL)
    {
        SnakeNode *pTmp = pNon->pNext;
        free(pNon);
        pNon = pTmp;
    }
}

int IsBump(SnakeNode *pFood, SnakeNode *pHead)
{
    int iFlag = 0;
    if(pHead->x == pFood->x && pHead->y == pFood->y)
    {
        iLengt++;
        iFlag = 1;                                           // 1 代表吃到食物
    }
    else if(pHead->x == 0 || pHead->x == WIGTH-1 || pHead->y == 0 || pHead->y == HEIGHT-1)
    {
        iFlag = 2;                                           // 2 代表撞墙
    }

    SnakeNode *pNon = pHead->pNext;
    while(pNon != NULL)
    {
        if(pHead->x == pNon->x && pHead->y == pNon->y)
        {
            iFlag = 3;                                       // 3 表示撞到自己
            break;
        }
        pNon = pNon->pNext;
    }

    return iFlag;
}
//  抄的API
void GoToXY(int x, int y)                                   // x, y代表想去的像素点的位置,其他不用知道
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(handle, coord);
}