C语言实现双人贪吃蛇项目(基于控制台界面)

一.贪吃蛇

贪吃蛇是一款简单而富有乐趣的游戏,它的规则易于理解,但挑战性也很高。它已经成为经典的游戏之一,并且在不同的平台上一直受到人们的喜爱和回忆。

二.贪吃蛇的功能

  1. 游戏控制:玩家可以使用键盘输入设备来控制蛇的移动方向。

  2. 蛇的移动:蛇头会根据玩家的输入方向进行移动,而蛇身会随着蛇头的移动而延长,形成一条越来越长的蛇。

  3. 食物生成:游戏界面会随机生成食物,玩家控制的蛇头需要吃掉这些食物,每吃到一块食物,蛇的身体就会增长一节。

  4. 碰撞检测:游戏会检测蛇头是否与自身的身体或者墙壁碰撞,如果碰撞则游戏结束。

  5. 分数计算:游戏会记录玩家吃到的食物数量,并根据数量进行得分计算,通常吃到的食物越多,得分越高。

  6. 难度递增:随着蛇身越来越长,游戏的难度也会逐渐增加,因为玩家需要更小心地避免碰撞

  7. 双人游戏:贪吃蛇支持双人游玩,两位玩家可以相互竞争。

三.贪吃蛇项目的实现

1.游戏前的准备

1.1游戏的状态

  • enum state 枚举类型定义了游戏的状态,包括以下常量:
    • ok:正常状态,表示游戏进行中。
    • by_wall:撞墙状态,表示蛇撞到了地图的边界。
    • by_body:撞到蛇的身体状态,表示蛇头撞到了蛇身。
    • by_self:蛇咬到自己状态,表示蛇头咬到了自己的身体。
    • by_end:游戏结束状态。
    • by_headpush:蛇头相互碰撞状态,表示两个玩家的蛇头相互碰撞。
enum state
{
    ok = 1,         // 游戏状态:正常状态
    by_wall,        // 游戏状态:撞墙
    by_body,        // 游戏状态:撞到蛇的身体
    by_self,        // 游戏状态:蛇咬到自己
    by_end,         // 游戏状态:游戏结束
    by_headpush     // 游戏状态:蛇头相互碰撞
};

1.2蛇的移动方向

  • enum direction 枚举类型定义了蛇的移动方向,包括以下常量:
    • up:表示向上移动。
    • down:表示向下移动。
    • left:表示向左移动。
    • right:表示向右移动。
enum direction
{
    up = 1,         // 方向:上
    down,           // 方向:下
    left,           // 方向:左
    right           // 方向:右
};

1.3蛇的节点

  • struct SnakeNode 结构体用于表示蛇的一个节点,包括以下成员:
    • x:节点的横坐标。
    • y:节点的纵坐标。
    • next:指向下一个节点的指针。
typedef struct SnakeNode
{
    int x;                  // 蛇节点的横坐标
    int y;                  // 蛇节点的纵坐标
    struct SnakeNode* next; // 指向下一个蛇节点的指针
} SnakeNode, * pSnakeNode;

1.4食物的位置

  • struct Food 结构体用于表示食物的位置,包括以下成员:
    • x:食物的横坐标。
    • y:食物的纵坐标。
typedef struct Food
{
    int x;                  // 食物的横坐标
    int y;                  // 食物的纵坐标
} Food, * pFood;

1.5整个贪吃蛇游戏

  • struct Snake 结构体用于表示蛇,包括以下成员:
    • _snake:蛇的头节点指针。
    • _food:食物指针。
    • _score:蛇的得分。
    • dir:蛇的移动方向。
    • sta:当前游戏状态。
    • _weight:奖励。
    • _sleeptime:游戏循环每次暂停的时间间隔。
typedef struct Snake
{
    pSnakeNode _snake;      // 蛇的头节点指针
    pFood _food;            // 食物指针
    int _score;             // 蛇的得分
    enum direction dir;     // 蛇的移动方向
    enum state sta;         // 当前游戏状态
    int _weight;            // 奖励
    int _sleeptime;         // 游戏循环每次暂停的时间间隔
} Snake, * pSnake;

2.游戏开始

2.1本地化设置

使用setlocale函数设置本地化环境为当前系统默认的环境。因为环境的差异,导致我们的中文的宽字符无法被识别,所以要本地化设置。

int main()
{
    setlocale(LC_ALL, "");  // 设置本地化环境为当前系统默认的环境

    test();  // 调用test函数进行测试

    return 0;  // 返回0表示程序正常结束
}

2.2 实现贪吃蛇的基本流程

test函数中,通过srand函数将当前时间作为随机数种子。然后使用do-while循环进行游戏的测试和循环控制。在循环内部,首先使用malloc动态分配了两个Snake结构体的内存,并将其指针赋值给snake1snake2。接着调用gamestart函数开始游戏,传入snake1snake2作为参数;然后调用gamerun函数进行游戏运行,同样传入snake1snake2作为参数;最后调用gameend函数结束游戏并释放内存,同样传入snake1snake2作为参数。之后,使用system("cls")清空控制台屏幕,然后在指定位置打印提示信息。接下来,使用getchar函数获取一个字符并赋值给变量ch,再使用getchar读取多余的换行符。最后,判断ch是否为字符'y''Y',如果是则继续进行下一轮游戏。

#include "snake.h"  // 引入自定义的头文件snake.h

void test()
{
    srand((unsigned int)time(NULL));  // 使用当前时间作为随机数种子

    int ch;
    do {
        pSnake snake1 = (pSnake)malloc(sizeof(Snake));  // 动态分配一个Snake结构体的内存,并将其指针赋给snake1
        pSnake snake2 = (pSnake)malloc(sizeof(Snake));  // 动态分配一个Snake结构体的内存,并将其指针赋给snake2

        gamestart(snake1, snake2);  // 调用gamestart函数开始游戏,传入snake1和snake2作为参数
        gamerun(snake1, snake2);    // 调用gamerun函数进行游戏运行,传入snake1和snake2作为参数
        gameend(snake1, snake2);    // 调用gameend函数结束游戏,释放内存,传入snake1和snake2作为参数

        system("cls");  // 清空控制台屏幕

        setpos(46, 15);  // 设置光标位置为(46, 15)
        printf("选择Y/N");

        setpos(50, 16);  // 设置光标位置为(50, 16)
        ch = getchar();  // 从标准输入中获取一个字符,赋值给ch
        getchar();       // 读取多余的换行符

    } while (ch == 'y' || ch == 'Y');  // 如果输入的字符是'y'或'Y',则继续进行下一轮游戏

}

2.3设置光标

该函数接受两个整型参数xy,分别表示光标的横坐标和纵坐标。在函数内部,首先调用GetStdHandle函数获取标准输出句柄,该句柄用于操作控制台窗口。然后创建一个COORD结构体变量coord,并将x赋值给coord.X,将y赋值给coord.Y,即设置光标的位置。最后,调用SetConsoleCursorPosition函数将控制台光标位置设置为指定的坐标。这样,在调用setpos函数时,控制台光标会移动到指定的位置。

void setpos(int x, int y)
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取标准输出句柄
    COORD coord;
    coord.X = x;  // 设置光标的横坐标为x
    coord.Y = y;  // 设置光标的纵坐标为y
    SetConsoleCursorPosition(handle, coord);  // 设置控制台光标位置为指定的坐标
}

 2.4游戏的初始化

该函数接受两个参数,snake1snake2,分别表示两条蛇的指针。函数开始使用断言assert,确保snake1snake2不为NULL,以保证后续操作的正确性。然后依次调用以下函数:

  • welcome:显示游戏欢迎信息,可能是在控制台输出一些欢迎文本。
  • creatmap:创建游戏地图,可能是在控制台上绘制一个游戏地图的界面。
  • initsnake:初始化蛇的身体,可能是将蛇的初始位置和长度设置为默认值。
  • creatsnake:生成两条蛇的初始位置,可能是将两条蛇放置在地图的指定位置。
  • creatfood:生成食物的位置,以供两条蛇争夺,可能是将食物随机放置在地图的空闲位置。
  • gameinfo:显示游戏信息,可能是在控制台上显示当前游戏的状态、得分等信息。
void gamestart(pSnake snake1, pSnake snake2)
{
    assert(snake1 && snake2);  // 使用断言确保snake1和snake2不为NULL

    welcome();                // 调用welcome函数,显示游戏欢迎信息
    creatmap();               // 调用creatmap函数,创建游戏地图
    initsnake(snake1);        // 调用initsnake函数,初始化snake1蛇身
    initsnake(snake2);        // 调用initsnake函数,初始化snake2蛇身
    creatsnake(snake1, snake2);// 调用creatsnake函数,生成snake1和snake2的初始位置
    creatfood(snake1, snake2);// 调用creatfood函数,生成食物的位置,以供snake1和snake2争夺
    creatfood(snake2, snake1);// 调用creatfood函数,生成食物的位置,以供snake2和snake1争夺
    gameinfo(snake1, snake2); // 调用gameinfo函数,显示游戏信息
}
2.3.1欢迎信息

该函数没有参数。在函数内部,首先调用system函数设置控制台窗口的大小为100列,30行,使用命令mode con cols=100 lines=30实现。然后调用system函数设置控制台窗口的标题为"贪吃蛇",使用命令title 贪吃蛇实现。接下来,获取标准输出句柄,并获取控制台光标信息。将光标的可见性设置为false,即不可见,然后再将修改后的光标信息设置回控制台。然后使用setpos函数设置光标位置为(40, 10),在该位置打印欢迎信息。接着,使用setpos函数设置光标位置为(40, 20),调用system("pause")暂停程序的执行,等待用户按下任意键。然后使用system("cls")清空控制台屏幕。接下来,使用setpos函数设置光标位置为(38, 10),然后依次打印游戏规则的内容。再次使用setpos函数设置光标位置为(40, 20),使用system("pause")暂停程序的执行,等待用户按下任意键。最后,使用system("cls")清空控制台屏幕。

void welcome()
{
    system("mode con cols=100 lines=30");  // 设置控制台窗口的大小为100列,30行
    system("title 贪吃蛇");                // 设置控制台窗口的标题为"贪吃蛇"

    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取标准输出句柄
    CONSOLE_CURSOR_INFO cursor;
    GetConsoleCursorInfo(handle, &cursor);  // 获取控制台光标信息
    cursor.bVisible = false;                // 将光标设置为不可见
    SetConsoleCursorInfo(handle, &cursor);  // 设置控制台光标信息

    setpos(40, 10);                          // 设置光标位置为(40, 10)
    printf("欢迎来到贪吃蛇游戏!");

    setpos(40, 20);                          // 设置光标位置为(40, 20)
    system("pause");                         // 暂停程序的执行,等待用户按下任意键

    system("cls");                           // 清空控制台屏幕

    setpos(38, 10);                          // 设置光标位置为(38, 10)
    printf("游戏规则如下:\n");
    setpos(38, 11);                          // 设置光标位置为(38, 11)
    printf("1.用← → ↑ ↓或a  d  w  s进行操作");
    setpos(38, 12);                          // 设置光标位置为(38, 12)
    printf("2.用f1进行加速,用进行f2减速");
    setpos(38, 13);                          // 设置光标位置为(38, 13)
    printf("3.空格键暂停游戏");
    setpos(38, 14);                          // 设置光标位置为(38, 14)
    printf("4.esc退出游戏");

    setpos(40, 20);                          // 设置光标位置为(40, 20)
    system("pause");                         // 暂停程序的执行,等待用户按下任意键

    system("cls");                           // 清空控制台屏幕
}
2.3.2创建地图

该函数没有参数。在函数内部,首先使用setpos函数将光标位置设置为(0, 0)。然后使用循环,每次递增2,打印墙的字符。接着,使用setpos函数将光标位置设置为(0, 25),再次使用循环,每次递增2,打印墙的字符。然后使用循环,从1到24,每次递增1,使用setpos函数将光标位置设置为(0, i),打印墙的字符。最后,使用循环,从1到24,每次递增1,使用setpos函数将光标位置设置为(56, i),打印墙的字符。这样,通过在控制台上打印一系列墙的字符,创建了游戏地图的界面。

void creatmap()
{
    setpos(0, 0);
    for (int i = 0; i < 58; i += 2)
    {
        wprintf(L"%c", Wall);
    }
    setpos(0, 25);
    for (int i = 0; i < 58; i += 2)
    {
        wprintf(L"%c", Wall);
    }
    for (int i = 1; i < 25; i++)
    {
        setpos(0, i);
        wprintf(L"%c", Wall);
    }
    for (int i = 1; i < 25; i++)
    {
        setpos(56, i);
        wprintf(L"%c", Wall);
    }
}
2.3.3初始化整个贪吃蛇游戏

该函数接受一个指向Snake结构体的指针snake作为参数。在函数内部,将蛇的链表指针_snake和食物的指针_food都设置为NULL,表示蛇链表和食物尚未创建。将分数_score初始化为0,表示初始分数为0。将蛇的初始移动方向dir设置为left,表示初始方向为向左移动。将蛇的状态sta设置为ok,表示蛇的初始状态为正常状态。将蛇的初始长度_weight设置为10,表示蛇的初始长度为10。将蛇的初始移动速度_sleeptime设置为200毫秒,表示蛇的初始移动速度为每200毫秒移动一次。通过这些初始化操作,为蛇游戏的相关数据设置了初始值,以便游戏开始时使用。

void initsnake(pSnake snake)
{
    snake->_snake = NULL;    // 将蛇的链表指针设置为NULL
    snake->_food = NULL;     // 将食物的指针设置为NULL
    snake->_score = 0;       // 将分数初始化为0
    snake->dir = left;       // 将蛇的初始移动方向设置为左侧
    snake->sta = ok;         // 将蛇的初始状态设置为正常状态
    snake->_weight = 10;     // 将蛇的初始长度设置为10
    snake->_sleeptime = 200; // 将蛇的初始移动速度设置为200毫秒
}
2.3.4创建蛇的初始状态

该函数接受两个指向Snake结构体的指针snake1snake2作为参数。在函数内部,首先创建蛇1的头结点p1,并将其位置设置为(24, 5)。然后将蛇1的链表指针_snake指向头结点p1,表示蛇1的链表的起始节点为头结点。然后在控制台上打印头结点的字符表示。接着使用循环,从1到4,每次递增1,创建蛇1的身体节点j,并将其位置设置为(24 + i * 2, 5)。然后在控制台上打印身体节点的字符表示。将头结点的next指针指向身体节点j,表示蛇1的头结点连接到身体节点。然后更新头结点为身体节点,为下一个身体节点的创建做准备。接下来,按照类似的方式,创建蛇2的头结点和身体节点。蛇2的头结点位置为(24, 22),蛇2的身体节点位置从(26, 22)开始递增。在控制台上打印蛇2的头结点和身体节点的字符表示。通过这些操作,创建了两条蛇的初始状态,每条蛇由一个头结点和四个身体节点组成。

void creatsnake(pSnake snake1, pSnake snake2)
{
    pSnakeNode p1 = (pSnakeNode)malloc(sizeof(SnakeNode)); // 创建蛇1的头结点
    assert(p1);
    p1->next = NULL;
    p1->x = 24;
    p1->y = 5;
    snake1->_snake = p1;
    setpos(p1->x, p1->y);
    wprintf(L"%c", Body);
    for (int i = 1; i < 5; i++)
    {
        pSnakeNode j = (pSnakeNode)malloc(sizeof(SnakeNode)); // 创建蛇1的身体节点
        assert(j);
        j->x = 24 + i * 2;
        j->y = 5;
        j->next = NULL;
        setpos(j->x, j->y);
        wprintf(L"%c", Body);
        p1->next = j;
        p1 = p1->next;
    }

    pSnakeNode p2 = (pSnakeNode)malloc(sizeof(SnakeNode)); // 创建蛇2的头结点
    assert(p2);
    p2->next = NULL;
    p2->x = 24;
    p2->y = 22;
    snake2->_snake = p2;
    setpos(p2->x, p2->y);
    wprintf(L"%c", Body);
    for (int i = 1; i < 5; i++)
    {
        pSnakeNode j = (pSnakeNode)malloc(sizeof(SnakeNode)); // 创建蛇2的身体节点
        assert(j);
        j->x = 24 + i * 2;
        j->y = 22;
        j->next = NULL;
        setpos(j->x, j->y);
        wprintf(L"%c", Body);
        p2->next = j;
        p2 = p2->next;
    }
}
2.3.5创建食物

该函数接受两个指向Snake结构体的指针snake1snake2作为参数。在函数内部,首先定义变量cout用于控制是否需要重新生成食物位置。然后定义变量xy用于存储食物的坐标。通过循环和条件判断,生成在游戏区域内的随机坐标(x, y),其中x是2到53之间的偶数,y是1到24之间的任意数。这样可以保证食物落在游戏区域内的空闲位置。接着,遍历蛇1和蛇2的身体节点,以及蛇2的食物位置(如果存在),检查食物位置是否与这些节点重叠。如果有重叠,则重新生成食物位置,通过goto语句返回到again标签处重新生成。如果食物位置与蛇的身体节点和食物位置都不重叠,将变量cout设置为1,表示食物位置已经确定。然后,将食物的坐标存储到蛇1的数据结构中,将食物指针指向食物结构体。在控制台上打印食物的字符表示,以显示食物的位置。通过这些操作,生成并放置了食物,并确保食物不与蛇的身体节点和其他食物重叠。

void creatfood(pSnake snake1, pSnake snake2)
{
    int cout = 0;
    int x, y;
    pFood food1 = (pFood)malloc(sizeof(Food)); // 创建食物结构体
    assert(food1);
again:
    if (cout == 0)
    {
        do {
            x = rand() % 52 + 2; // 生成一个2到53之间的随机数作为x坐标
            y = rand() % 23 + 1; // 生成一个1到24之间的随机数作为y坐标
        } while (x % 2 != 0); // 保证x坐标是偶数,以确保食物落在游戏区域内的空闲位置
        pSnakeNode p1 = snake1->_snake;
        pSnakeNode p2 = snake1->_snake;
        while (p1 && p2)
        {
            if (snake2->_food == NULL)
            {
                if (p1->x == x && p1->y == y && p2->x == x && p2->y == y)
                    goto again; // 如果食物位置与蛇1和蛇2的身体节点位置重叠,则重新生成食物位置
            }
            else if (p1->x == x && p1->y == y && p2->x == x && p2->y == y && snake2->_food->x == x && snake2->_food->y == y)
                goto again; // 如果食物位置与蛇1、蛇2的身体节点位置以及蛇2的食物位置重叠,则重新生成食物位置
            p1 = p1->next;
            p2 = p2->next;
        }
        cout = 1;
    }
    food1->x = x;
    food1->y = y;
    snake1->_food = food1; // 将食物指针存储到蛇1的数据结构中
    setpos(food1->x, food1->y);
    wprintf(L"%c", FOOD); // 在控制台上打印食物的字符表示
}
2.3.6显示游戏的规则

该函数接受两个指向Snake结构体的指针snake1snake2作为参数。在函数内部,使用setpos函数将光标定位到相应的位置,然后使用printf函数将相关信息打印在控制台上。首先打印蛇1的分数和奖励信息,分别位于(70, 6)和(70, 7)的位置。接着打印蛇2的分数和奖励信息,分别位于(70, 10)和(70, 11)的位置。然后打印游戏规则,分别位于(62, 15)到(62, 20)的位置。通过这些操作,在控制台上显示了游戏的相关信息和规则。

void gameinfo(pSnake snake1, pSnake snake2)
{
    setpos(70, 6);
    printf("分数:%d", snake1->_score); // 打印蛇1的分数
    setpos(70, 7);
    printf("奖励:%d", snake1->_weight); // 打印蛇1的奖励
    setpos(70, 10);
    printf("分数:%d", snake2->_score); // 打印蛇2的分数
    setpos(70, 11);
    printf("奖励:%d", snake2->_weight); // 打印蛇2的奖励
    setpos(62, 15);
    printf("游戏规则如下:");
    setpos(62, 16);
    printf("1.用← → ↑ ↓或a  d  w  s进行操作");
    setpos(62, 17);
    printf("2.用f1进行加速,用进行f2减速");
    setpos(62, 18);
    printf("3.空格键暂停游戏");
    setpos(62, 19);
    printf("4.esc退出游戏");
    setpos(62, 20);
    printf("5.咬到蛇身或撞墙都会死");
}

3.游戏进行中

3.1控制游戏的运行逻辑

该函数接受两个指向Snake结构体的指针snake1snake2作为参数。在函数内部,使用do-while循环来控制游戏的逻辑。在循环内部,根据按键的状态来判断玩家的操作,例如W、S、A、D键用于控制蛇1的移动方向,空格键用于暂停游戏,ESC键用于结束游戏。同时,F1和F2键用于加速和减速蛇的移动速度,上下左右箭头键用于控制蛇2的移动方向。接着调用snakemove函数来移动蛇1和蛇2的位置。然后更新显示蛇1和蛇2的分数和奖励信息。最后根据蛇1的睡眠时间进行延时,然后继续下一轮循环,直到蛇1或蛇2的状态不再为正常(游戏结束)。

void gamerun(pSnake snake1, pSnake snake2)
{
    do {
        if (Key_state(0x57) && snake1->dir != down)
        {
            snake1->dir = up; // 如果按下W键且蛇1不朝下移动,则将蛇1的方向设置为上
        }
        else if (Key_state(0x53) && snake1->dir != up)
        {
            snake1->dir = down; // 如果按下S键且蛇1不朝上移动,则将蛇1的方向设置为下
        }
        else if (Key_state(0x41) && snake1->dir != right)
        {
            snake1->dir = left; // 如果按下A键且蛇1不朝右移动,则将蛇1的方向设置为左
        }
        else if (Key_state(0x44) && snake1->dir != left)
        {
            snake1->dir = right; // 如果按下D键且蛇1不朝左移动,则将蛇1的方向设置为右
        }
        else if (Key_state(VK_SPACE))
        {
            pause(); // 如果按下空格键,则暂停游戏
        }
        else if (Key_state(VK_ESCAPE))
        {
            snake1->sta = by_end; // 如果按下ESC键,则将蛇1的状态设置为结束
            snake2->sta = by_end; // 将蛇2的状态设置为结束
        }
        else if (Key_state(VK_F1))
        {
            if (snake1->_sleeptime >= 80)
            {
                (snake1->_sleeptime) -= 20; // 如果按下F1键且蛇1的睡眠时间大于等于80,则减少蛇1的睡眠时间
                (snake1->_weight) += 2; // 增加蛇1的奖励
            }
            setpos(70, 7);
            printf("奖励:%d", snake1->_weight); // 更新蛇1的奖励显示
            if (snake2->_sleeptime >= 80)
            {
                (snake2->_sleeptime) -= 20; // 如果按下F1键且蛇2的睡眠时间大于等于80,则减少蛇2的睡眠时间
                (snake2->_weight) += 2; // 增加蛇2的奖励
            }
            setpos(70, 11);
            printf("奖励:%d", snake2->_weight); // 更新蛇2的奖励显示
        }
        else if (Key_state(VK_F2))
        {
            if (snake1->_sleeptime <= 280)
            {
                (snake1->_sleeptime) += 20; // 如果按下F2键且蛇1的睡眠时间小于等于280,则增加蛇1的睡眠时间
                (snake1->_weight) -= 2; // 减少蛇1的奖励
            }
            setpos(70, 7);
            printf("奖励:%d", snake1->_weight); // 更新蛇1的奖励显示
            if (snake2->_sleeptime <= 280)
            {
                (snake2->_sleeptime) += 20; // 如果按下F2键且蛇2的睡眠时间小于等于280,则增加蛇2的睡眠时间
                (snake2->_weight) -= 2; // 减少蛇2的奖励
            }
            setpos(70, 11);
            printf("奖励:%d", snake2->_weight); // 更新蛇2的奖励显示
        }
        else if (Key_state(VK_UP) && snake2->dir != down)
        {
            snake2->dir = up; // 如果按下上箭头键且蛇2不朝下移动,则将蛇2的方向设置为上
        }
        else if (Key_state(VK_DOWN) && snake2续:

        .dir != up)
        {
            snake2->dir = down; // 如果按下下箭头键且蛇2不朝上移动,则将蛇2的方向设置为下
        }
        else if (Key_state(VK_LEFT) && snake2->dir != right)
        {
            snake2->dir = left; // 如果按下左箭头键且蛇2不朝右移动,则将蛇2的方向设置为左
        }
        else if (Key_state(VK_RIGHT) && snake2->dir != left)
        {
            snake2->dir = right; // 如果按下右箭头键且蛇2不朝左移动,则将蛇2的方向设置为右
        }
        snakemove(snake1, snake2); // 移动蛇1
        snakemove(snake2, snake1); // 移动蛇2
        setpos(70, 6);
        printf("分数:%d", snake1->_score); // 更新蛇1的分数显示
        setpos(70, 7);
        printf("奖励:%d", snake1->_weight); // 更新蛇1的奖励显示
        setpos(70, 10);
        printf("分数:%d", snake2->_score); // 更新蛇2的分数显示
        setpos(70, 11);
        printf("奖励:%d", snake2->_weight); // 更新蛇2的奖励显示
        Sleep(snake1->_sleeptime); // 根据蛇1的睡眠时间进行延时
    } while (snake1->sta == ok && snake2->sta == ok); // 当蛇1和蛇2的状态都为正常时继续循环
}

3.2 游戏的暂停

在函数内部,使用一个无限循环while (1)来检测空格键的状态。如果空格键被按下(Key_state(VK_SPACE)返回true),则函数使用return语句立即返回,从而退出循环并继续游戏的执行。如果空格键未被按下,函数会通过Sleep(1000)函数让程序休眠1秒钟,然后再次检测空格键的状态。这样循环会一直执行,直到空格键被按下并函数返回。这样就实现了游戏的暂停功能。

void pause()
{
    while (1)
    {
        if (Key_state(VK_SPACE))
            return;
        Sleep(1000);
    }
}

3.3 蛇的移动及一系列的判断

该函数包含了以下功能:

  • 创建一个新的蛇头节点 i,并将其插入到蛇身链表的头部。
  • 根据贪吃蛇的方向更新蛇头的位置。
  • 如果贪吃蛇吃到了食物,增加贪吃蛇的得分并生成新的食物。
  • 如果贪吃蛇没有吃到食物,移动蛇尾,即删除蛇身链表的最后一个节点,并在屏幕上擦除该节点的位置。
  • 检查贪吃蛇是否撞墙,如果是则设置状态为 by_wall 并返回。
  • 检查贪吃蛇是否自噬,即蛇头是否碰到了蛇身的其他部分。
  • 绘制贪吃蛇的身体,即在屏幕上打印出每个节点的位置。
void snakemove(pSnake snake1, pSnake snake2)
{
    pSnakeNode i = (pSnakeNode)malloc(sizeof(SnakeNode));
    assert(i);
    i->next = snake1->_snake;

    // 根据贪吃蛇的方向更新蛇头的位置
    if (snake1->dir == up)
    {
        i->x = snake1->_snake->x;
        i->y = snake1->_snake->y - 1;
    }
    else if (snake1->dir == down)
    {
        i->x = snake1->_snake->x;
        i->y = snake1->_snake->y + 1;
    }
    else if (snake1->dir == left)
    {
        i->x = snake1->_snake->x - 2;
        i->y = snake1->_snake->y;
    }
    else if (snake1->dir == right)
    {
        i->x = snake1->_snake->x + 2;
        i->y = snake1->_snake->y;
    }
    else if (snake1->sta == by_end)
    {
        return;
    }

    // 更新蛇头的位置
    snake1->_snake = i;
    pSnakeNode p = snake1->_snake;

    // 判断是否吃到了食物
    if ((i->x == snake1->_food->x && i->y == snake1->_food->y) || (i->x == snake2->_food->x && i->y == snake2->_food->y))
    {
        (snake1->_score) += snake1->_weight;
        creatfood(snake1, snake2);
    }
    else
    {
        // 如果没有吃到食物,则移动蛇尾
        pSnakeNode pre = NULL;
        while (p->next != NULL)
        {
            pre = p;
            p = p->next;
        }
        pre->next = NULL;
        setpos(p->x, p->y);
        printf("  ");
        free(p);
    }

    // 判断是否撞墙
    if (snake1->_snake->x == 0 || snake1->_snake->x == 56 || snake1->_snake->y == 0 || snake1->_snake->y == 25)
    {
        snake1->sta = by_wall;
        return;
    }

    // 判断是否自噬
    kill_self(snake1, snake2);

    // 绘制蛇的身体
    p = i;
    while (p)
    {
        setpos(p->x, p->y);
        wprintf(L"%c", Body);
        p = p->next;
    }
}
3.3.1 判断贪吃蛇是否自噬或者与另一个贪吃相撞

该函数包含了以下功能:

  • 初始化两个指针 p1 和 p2 分别指向贪吃蛇1和贪吃蛇2的蛇身链表的第二个节点(跳过蛇头)。
  • 检测贪吃蛇1的蛇头是否与贪吃蛇2的蛇头位置重叠,如果是,则将两个贪吃蛇的状态都设置为 by_headpush 表示头部碰撞,并立即返回。
  • 遍历贪吃蛇1和贪吃蛇2的蛇身链表,检测贪吃蛇1的蛇头是否与贪吃蛇2的蛇身节点或贪吃蛇1自身的蛇身节点位置重叠,如果是,则将贪吃蛇1的状态设置为相应的自噬状态(by_body 或 by_self),并立即返回。

这个函数的作用是在每次贪吃蛇移动后检测是否发生自噬或者与其他贪吃蛇相撞的情况,以便在后续的游戏逻辑中处理相应的情况。

void kill_self(pSnake snake1, pSnake snake2)
{
    pSnakeNode p1 = snake1->_snake->next;
    pSnakeNode p2 = snake2->_snake->next;

    // 检测蛇头是否碰撞
    if (snake1->_snake->x == snake2->_snake->x && snake1->_snake->y == snake2->_snake->y)
    {
        snake1->sta = by_headpush;
        snake2->sta = by_headpush;
        return;
    }

    // 检测蛇头是否与蛇身碰撞
    while (p1 && p2)
    {
        if (snake1->_snake->x == p2->x && snake1->_snake->y == p2->y)
        {
            snake1->sta = by_body;
            return;
        }
        if (snake1->_snake->x == p1->x && snake1->_snake->y == p1->y)
        {
            snake1->sta = by_self;
            return;
        }
        p1 = p1->next;
        p2 = p2->next;
    }
}

4.游戏结束

该函数包含以下功能:

  • 检查贪吃蛇的状态,根据不同的情况显示相应的游戏结束提示信息。
  • 释放贪吃蛇1和贪吃蛇2所占用的内存。

具体逻辑如下:

首先,函数检查两条蛇的状态是否相同。如果状态不同,根据不同的状态显示相应的游戏结束提示信息。

  • 如果蛇1的状态是by_body,表示蛇1咬到了蛇2的身体,显示"玩家1咬到对方蛇身了,游戏结束!"的提示信息。
  • 如果蛇1的状态是by_wall,表示蛇1撞到了墙壁,显示"玩家1撞到墙了,游戏结束!"的提示信息。
  • 如果蛇2的状态是by_body,表示蛇2咬到了蛇1的身体,显示"玩家2咬到对方蛇身了,游戏结束!"的提示信息。
  • 如果蛇2的状态是by_wall,表示蛇2撞到了墙壁,显示"玩家2撞到墙了,游戏结束!"的提示信息。
  • 如果蛇1的状态是by_self,表示蛇1咬到了自己的身体,显示"玩家1咬到自己蛇身了,游戏结束!"的提示信息。
  • 如果蛇2的状态是by_self,表示蛇2咬到了自己的身体,显示"玩家2咬到自己蛇身了,游戏结束!"的提示信息。

如果两条蛇的状态相同,继续检查状态。

  • 如果蛇1的状态是by_end,表示蛇1到达了游戏结束状态,显示"游戏结束!"的提示信息。
  • 如果蛇1的状态是by_body,表示蛇1和蛇2都咬到了对方的身体,显示"玩家1和玩家2都咬到对方蛇身了,游戏结束!"的提示信息。
  • 如果蛇1的状态是by_wall,表示蛇1和蛇2都撞到了墙壁,显示"玩家1和玩家2撞到墙了,游戏结束!"的提示信息。
  • 如果蛇1的状态是by_headpush,表示蛇1和蛇2相互碰撞,显示"两位玩家相撞了,游戏结束!"的提示信息。
  • 如果蛇1的状态是by_self,表示蛇1和蛇2都咬到了自己的身体,显示"玩家1和玩家2都咬到自己蛇身了,游戏结束!"的提示信息。

接下来,函数释放贪吃蛇1所占用的内存。首先释放蛇1的食物(_food),然后依次释放蛇1的节点(_snake)所占用的内存。最后,函数释放贪吃蛇2所占用的内存。首先释放蛇2的食物(_food),然后依次释放蛇2的节点(_snake)所占用的内存。

void gameend(pSnake snake1, pSnake snake2)
{
    // 检查贪吃蛇状态,显示相应的游戏结束提示信息并释放内存

    if (snake1->sta != snake2->sta)
    {
        if (snake1->sta == by_body)
        {
            system("cls");
            setpos(42, 15);
            printf("玩家1咬到对方蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake1->sta == by_wall)
        {
            system("cls");
            setpos(38, 15);
            printf("玩家1撞到墙了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake2->sta == by_body)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家2咬到对方蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake2->sta == by_wall)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家2撞到墙了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake1->sta == by_self)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家1咬到自己蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake2->sta == by_self)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家2咬到自己蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
    }
    else
    {
        if (snake1->sta == by_end)
        {
            system("cls");
            setpos(48, 15);
            printf("游戏结束!");
            setpos(42, 22);
            system("pause");
        }
        else if (snake1->sta == by_body)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家1和玩家2都咬到对方蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake1->sta == by_wall)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家1和玩家2撞到墙了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake1->sta == by_headpush)
        {
            system("cls");
            setpos(34, 15);
            printf("两位玩家相撞了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
        else if (snake1->sta == by_self)
        {
            system("cls");
            setpos(34, 15);
            printf("玩家1和玩家2都咬到自己蛇身了,游戏结束!");
            setpos(40, 22);
            system("pause");
        }
    }

    // 释放贪吃蛇1的内存
    free(snake1->_food);
    pSnakeNode p = snake1->_snake;
    pSnakeNode pre = NULL;
    while (p)
    {
        pre = p;
        p = p->next;
        pre->next = NULL;
        free(pre);
    }
    free(snake1);

    // 释放贪吃蛇2的内存
    free(snake2->_food);
    p = snake2->_snake;
    pre = NULL;
    while (p)
    {
        pre = p;
        p = p->next;
        pre->next = NULL;
        free(pre);
    }
    free(snake2);
}

四.贪吃蛇的测试

五.贪吃蛇的源码呈现

1.snake.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<locale.h>
#include<stdbool.h>
#include<assert.h>
#include<time.h>
#define Wall L'□'
#define Body L'●'
#define FOOD L'★'
#define Key_state(KEY) ((GetAsyncKeyState(KEY)&(0x1))?1:0)
enum state
{
	ok = 1,
	by_wall,
	by_body,
	by_self,
	by_end,
	by_headpush
};
enum direction
{
	up = 1,
	down,
	left,
	right
};
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, * pSnakeNode;
typedef struct Food
{
	int x;
	int y;
}Food, * pFood;
typedef struct Snake
{
	pSnakeNode _snake;
	pFood _food;
	int _score;
	enum direction dir;
	enum state sta;
	int _weight;
	int _sleeptime;
}Snake, * pSnake;
void gamestart(pSnake snake1,pSnake snake2);
void setpos(int x, int y);
void welcome();
void creatmap();
void creatfood(pSnake snake1,pSnake snake2);
void initsnake(pSnake snake1);
void creatsnake(pSnake snake1, pSnake snake2);
void gameinfo(pSnake snake1, pSnake snake2);
void gamerun(pSnake snake1, pSnake snake2);
void pause();
void snakemove(pSnake snake1,pSnake snake2);
void kill_self(pSnake snake);
void gameend(pSnake snake);

2.test.c

#include"snake.h"
void test()
{
	srand((unsigned int)time(NULL));
	int ch;
	do {
		pSnake snake1 = (pSnake)malloc(sizeof(Snake));
		pSnake snake2 = (pSnake)malloc(sizeof(Snake));
		gamestart(snake1,snake2);
		gamerun(snake1,snake2);
		gameend(snake1,snake2);
		system("cls");
		setpos(46, 15);
		printf("选择Y/N");
		setpos(50, 16);
		ch = getchar();
		getchar();
	} while (ch == 'y' || ch == 'Y');
}
int main()
{
	setlocale(LC_ALL, "");
	test();
	return 0;
}

3.snake.c

#include"snake.h"
void setpos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD coord;
	coord.X = x;
	coord.Y = y;
	SetConsoleCursorPosition(handle, coord);
}
void welcome()
{
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursor;
	GetConsoleCursorInfo(handle, &cursor);
	cursor.bVisible = false;
	SetConsoleCursorInfo(handle, &cursor);
	setpos(40, 10);
	printf("欢迎来到贪吃蛇游戏!");
	setpos(40, 20);
	system("pause");
	system("cls");
	setpos(38, 10);
	printf("游戏规则如下:\n");
	setpos(38, 11);
	printf("1.用← → ↑ ↓或a  d  w  s进行操作");
	setpos(38, 12);
	printf("2.用f1进行加速,用进行f2减速");
	setpos(38, 13);
	printf("3.空格键暂停游戏");
	setpos(38, 14);
	printf("4.esc退出游戏");
	setpos(40, 20);
	system("pause");
	system("cls");
}
void creatmap()
{
	setpos(0, 0);
	for (int i = 0; i < 58; i += 2)
	{
		wprintf(L"%c", Wall);
	}
	setpos(0, 25);
	for (int i = 0; i < 58; i += 2)
	{
		wprintf(L"%c", Wall);
	}
	for (int i = 1; i < 25; i++)
	{
		setpos(0, i);
		wprintf(L"%c", Wall);
	}
	for (int i = 1; i < 25; i++)
	{
		setpos(56, i);
		wprintf(L"%c", Wall);
	}
}
void initsnake(pSnake snake)
{
	snake->_snake = NULL;
	snake->_food = NULL;
	snake->_score = 0;
	snake->dir = left;
	snake->sta = ok;
	snake->_weight = 10;
	snake->_sleeptime = 200;
}
void creatsnake(pSnake snake1,pSnake snake2)
{
	pSnakeNode p1 = (pSnakeNode)malloc(sizeof(SnakeNode));
	assert(p1);
	p1->next = NULL;
	p1->x = 24;
	p1->y = 5;
	snake1->_snake = p1;
	setpos(p1->x, p1->y);
	wprintf(L"%c", Body);
	for (int i = 1; i < 5; i++)
	{
		pSnakeNode j = (pSnakeNode)malloc(sizeof(SnakeNode));
		assert(j);
		j->x = 24 + i * 2;
		j->y = 5;
		j->next = NULL;
		setpos(j->x, j->y);
		wprintf(L"%c", Body);
		p1->next = j;
		p1 = p1->next;
	}
	pSnakeNode p2 = (pSnakeNode)malloc(sizeof(SnakeNode));
	assert(p2);
	p2->next = NULL;
	p2->x = 24;
	p2->y = 22;
	snake2->_snake = p2;
	setpos(p2->x, p2->y);
	wprintf(L"%c", Body);
	for (int i = 1; i < 5; i++)
	{
		pSnakeNode j = (pSnakeNode)malloc(sizeof(SnakeNode));
		assert(j);
		j->x = 24 + i * 2;
		j->y = 22;
		j->next = NULL;
		setpos(j->x, j->y);
		wprintf(L"%c", Body);
		p2->next = j;
		p2 = p2->next;
	}
}
void creatfood(pSnake snake1,pSnake snake2)
{
	int cout = 0;
	int x, y;
	pFood food1 = (pFood)malloc(sizeof(Food));
	assert(food1);
again:
	if (cout == 0)
	{
		do {
			x = rand() % 52 + 2;
			y = rand() % 23 + 1;
		} while (x % 2 != 0);
		pSnakeNode p1 = snake1->_snake;
		pSnakeNode p2 = snake1->_snake;
		while (p1&&p2)
		{
			if (snake2->_food == NULL)
			{
				if (p1->x == x && p1->y == y && p2->x == x && p2->y == y)
					goto again;
			}
			else if (p1->x == x && p1->y == y && p2->x == x && p2->y == y&&snake2->_food->x==x&&snake2->_food->y==y)
				goto again;
			p1 = p1->next;
			p2 = p2->next;
		}
		cout = 1;
	}
	food1->x = x;
	food1->y = y;
	snake1->_food = food1;
	setpos(food1->x, food1->y);
	wprintf(L"%c", FOOD);
}
void gameinfo(pSnake snake1,pSnake snake2)
{
	setpos(70, 6);
	printf("分数:%d", snake1->_score);
	setpos(70, 7);
	printf("奖励:%d", snake1->_weight);
	setpos(70, 10);
	printf("分数:%d", snake2->_score);
	setpos(70, 11);
	printf("奖励:%d", snake2->_weight);
	setpos(62, 15);
	printf("游戏规则如下:");
	setpos(62, 16);
	printf("1.用← → ↑ ↓或a  d  w  s进行操作");
	setpos(62, 17);
	printf("2.用f1进行加速,用进行f2减速");
	setpos(62, 18);
	printf("3.空格键暂停游戏");
	setpos(62, 19);
	printf("4.esc退出游戏");
	setpos(62, 20);
	printf("5.咬到蛇身或撞墙都会死");
}
void pause()
{
	while (1)
	{
		if (Key_state(VK_SPACE))
			return;
		Sleep(1000);
	}
}
void kill_self(pSnake snake1,pSnake snake2)
{
	pSnakeNode p1 = snake1->_snake->next;
	pSnakeNode p2 = snake2->_snake->next;
	if (snake1->_snake->x == snake2->_snake->x && snake1->_snake->y == snake2->_snake->y)
	{
		snake1->sta = by_headpush;
		snake2->sta = by_headpush;
		return;
	}
	while (p1&&p2)
	{
		if (snake1->_snake->x == p2->x && snake1->_snake->y == p2->y)
		{
			snake1->sta = by_body;
			return;
		}
		if (snake1->_snake->x == p1->x && snake1->_snake->y == p1->y)
		{
			snake1->sta = by_self;
			return;
		}
		p1 = p1->next;
		p2 = p2->next;
	}
}
void snakemove(pSnake snake1,pSnake snake2)
{
	pSnakeNode i = (pSnakeNode)malloc(sizeof(SnakeNode));
	assert(i);
	i->next = snake1->_snake;
	if (snake1->dir == up)
	{
		i->x = snake1->_snake->x;
		i->y = snake1->_snake->y - 1;
	}
	else if (snake1->dir == down)
	{
		i->x = snake1->_snake->x;
		i->y = snake1->_snake->y + 1;
	}
	else if (snake1->dir == left)
	{
		i->x = snake1->_snake->x - 2;
		i->y = snake1->_snake->y;
	}
	else if (snake1->dir == right)
	{
		i->x = snake1->_snake->x + 2;
		i->y = snake1->_snake->y;
	}
	else if (snake1->sta == by_end)
	{
		return;
	}
	snake1->_snake = i;
	pSnakeNode p = snake1->_snake;
	if ((i->x == snake1->_food->x && i->y == snake1->_food->y)|| (i->x == snake2->_food->x && i->y == snake2->_food->y))
	{
		(snake1->_score) += snake1->_weight;
		creatfood(snake1,snake2);
	}
	else
	{
		pSnakeNode pre = NULL;
		while (p->next != NULL)
		{
			pre = p;
			p = p->next;
		}
		pre->next = NULL;
		setpos(p->x, p->y);
		printf("  ");
		free(p);
	}
	if (snake1->_snake->x == 0 || snake1->_snake->x == 56 || snake1->_snake->y == 0 || snake1->_snake->y == 25)
	{
		snake1->sta = by_wall;
		return;
	}
	kill_self(snake1,snake2);
	p = i;
	while (p)
	{
		setpos(p->x, p->y);
		wprintf(L"%c", Body);
		p = p->next;
	}
}
void gamestart(pSnake snake1,pSnake snake2)
{
	assert(snake1&&snake2);
	welcome();
	creatmap();
	initsnake(snake1);
	initsnake(snake2);
	creatsnake(snake1,snake2);
	creatfood(snake1,snake2);
	creatfood(snake2, snake1);
	gameinfo(snake1,snake2);
}
void gamerun(pSnake snake1,pSnake snake2)
{
	do {
		if (Key_state(0x57) && snake1->dir != down)
		{
			snake1->dir = up;
		}
		else if (Key_state(0x53) && snake1->dir != up)
		{
			snake1->dir = down;
		}
		else if (Key_state(0x41) && snake1->dir != right)
		{
			snake1->dir = left;
		}
		else if (Key_state(0x44) && snake1->dir != left)
		{
			snake1->dir = right;
		}
		else if (Key_state(VK_SPACE))
		{
			pause();
		}
		else if (Key_state(VK_ESCAPE))
		{
			snake1->sta = by_end;
			snake2->sta = by_end;
		}
		else if (Key_state(VK_F1))
		{
			if (snake1->_sleeptime >= 80)
			{
				(snake1->_sleeptime) -= 20;
				(snake1->_weight) += 2;
			}
			setpos(70, 7);
			printf("奖励:%d", snake1->_weight);
			if (snake2->_sleeptime >= 80)
			{
				(snake2->_sleeptime) -= 20;
				(snake2->_weight) += 2;
			}
			setpos(70, 11);
			printf("奖励:%d", snake2->_weight);
		}
		else if (Key_state(VK_F2))
		{
			if (snake1->_sleeptime <= 280)
			{
				(snake1->_sleeptime) += 20;
				(snake1->_weight) -= 2;
			}
			setpos(70, 7);
			printf("奖励:%d", snake1->_weight);
			if (snake2->_sleeptime <= 280)
			{
				(snake2->_sleeptime) += 20;
				(snake2->_weight) -= 2;
			}
			setpos(70, 11);
			printf("奖励:%d", snake2->_weight);
		}else if (Key_state(VK_UP) && snake2->dir != down)
		{
			snake2->dir = up;
		}
		else if (Key_state(VK_DOWN) && snake2->dir != up)
		{
			snake2->dir = down;
		}
		else if (Key_state(VK_LEFT) && snake2->dir != right)
		{
			snake2->dir = left;
		}
		else if (Key_state(VK_RIGHT) && snake2->dir != left)
		{
			snake2->dir = right;
		}
		snakemove(snake1,snake2);
		snakemove(snake2, snake1);
		setpos(70, 6);
		printf("分数:%d", snake1->_score);
		setpos(70, 7);
		printf("奖励:%d", snake1->_weight);
		setpos(70, 10);
		printf("分数:%d", snake2->_score);
		setpos(70, 11);
		printf("奖励:%d", snake2->_weight);
		Sleep(snake1->_sleeptime);
	} while (snake1->sta == ok&&snake2->sta==ok);
}
void gameend(pSnake snake1,pSnake snake2)
{
	/*if (snake1->sta == by_end)
	{
		system("cls");
		setpos(48, 15);
		printf("游戏结束!");
		setpos(42, 22);
		system("pause");
	}
	else*/
	if (snake1->sta != snake2->sta)
	{
		if (snake1->sta == by_body)
		{
			system("cls");
			setpos(42, 15);
			printf("玩家1咬到对方蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake1->sta == by_wall)
		{
			system("cls");
			setpos(38, 15);
			printf("玩家1撞到墙了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}/*else if (snake2->sta == by_end)
		{
			system("cls");
			setpos(48, 15);
			printf("游戏结束!");
			setpos(42, 22);
			system("pause");
		}*/
		else if (snake2->sta == by_body)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家2咬到对方蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake2->sta == by_wall)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家2撞到墙了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake1->sta == by_self)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家1咬到自己蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake2->sta == by_self)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家2咬到自己蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
	}
	else
	{
		if (snake1->sta == by_end)
		{
			system("cls");
			setpos(48, 15);
			printf("游戏结束!");
			setpos(42, 22);
			system("pause");
		}
		else if (snake1->sta == by_body)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家1和玩家2都咬到对方蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake1->sta == by_wall)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家1和玩家2撞到墙了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake1->sta == by_headpush)
		{
			system("cls");
			setpos(34, 15);
			printf("两位玩家相撞了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
		else if (snake1->sta == by_self)
		{
			system("cls");
			setpos(34, 15);
			printf("玩家1和玩家2都咬到自己蛇身了,游戏结束!");
			setpos(40, 22);
			system("pause");
		}
	}
	free(snake1->_food);
	pSnakeNode p = snake1->_snake;
	pSnakeNode pre = NULL;
	while (p)
	{
		pre = p;
		p = p->next;
		pre->next = NULL;
		free(pre);
	}
	free(snake1);
	free(snake2->_food);
	p = snake2->_snake;
	pre = NULL;
	while (p)
	{
		pre = p;
		p = p->next;
		pre->next = NULL;
		free(pre);
	}
	free(snake2);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/570219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于模糊控制的纯跟踪横向控制在倒车中的应用及实现

文章目录 1. 引言2. Pure Pursuit在倒车场景的推导3. 模糊控制器的设计3.1 基础知识3.2 预瞄距离系数k的模糊控制器设计 4. 算法和仿真实现 1. 引言 Pure Pursuit是一种几何跟踪控制算法&#xff0c;也被称为纯跟踪控制算法。他的思想就是基于当前车辆的后轮中心的位置&#x…

Axure RP 9 for Mac/win:打造极致交互体验的原型设计神器

在数字化浪潮席卷全球的今天&#xff0c;原型设计作为产品开发的关键环节&#xff0c;其重要性不言而喻。Axure RP 9&#xff0c;作为一款专为设计师和开发者打造的原型设计软件&#xff0c;以其出色的交互设计能力和高效的协作体验&#xff0c;赢得了广大用户的青睐。 Axure …

【JavaScript】axios

基础使用 <script src"https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.min.js"></script> <script>axios.get(https://study.duyiedu.com/api/herolist).then(res> {console.log(res.data)}) </script>get - params <script s…

U盘乱码频发,原因与解决方案大揭秘

在日常的工作和生活中&#xff0c;U盘因其便携性和大容量成为了我们不可或缺的存储设备。然而&#xff0c;有时候我们会遭遇U盘乱码的问题&#xff0c;这让我们无法正确读取和使用其中的文件。那么&#xff0c;U盘乱码究竟是何原因导致的呢&#xff1f;又该如何解决这一问题呢&…

Python自学之路--002:Python 如何生成exe可执行文件

目录 1、概述 2、安装pyinstall 3、终端指令 1、概述 大部分时候&#xff0c;执行的仅仅是一个Python解释器出来的文件&#xff0c;至于怎么将文件生成exe的可执行文件呢&#xff1f;Python有对应的库&#xff0c;也就是pyinstall。安装之后产生dist文件夹&#xff0c;里面就…

UE5 GAS开发P34 游戏效果理论

GameplayEffects Attributes&#xff08;属性&#xff09;和Gameplay Tags&#xff08;游戏标签&#xff09;分别代表游戏中实体的特性和标识。 Attributes&#xff08;属性&#xff09;&#xff1a;Attributes是用来表示游戏中实体的特性或属性的值&#xff0c;例如生命值、…

ffmpeg的安装以及使用

1.FFmpeg 的主要功能和特性&#xff1a; 格式转换&#xff1a;FFmpeg 可以将一个媒体文件从一种格式转换为另一种格式&#xff0c;支持几乎所有常见的音频和视频格式&#xff0c;包括 MP4、AVI、MKV、MOV、FLV、MP3、AAC 等。视频处理&#xff1a;FFmpeg 可以进行视频编码、解…

书生·浦语大模型开源体系(四)作业

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

云计算技术架构及发展

云计算是指一种将可伸缩、弹性、共享的物理和虚拟资源池以按需自服务的方式供应和管理&#xff0c;并提供网络访问的模式。 云计算服务商利用分布式计算和虚拟资源管理等技术&#xff0c;通过网络将分散的ICT资源集中起来形成共享的资源池&#xff0c;并以动态按需和可度量的方…

基于若依和flowable7.0.1的ruoyi-nbcio-plus流程管理系统正式发布

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

皮带机巡检解决方案

在化工行业中、皮带机人工巡检存在的疲劳安全、巡检质量、数据分析等问题&#xff0c;通过以智能巡检机器人为中心的设备生命周期运维管理系统&#xff0c;完成对皮带机的巡检巡逻和排查预警&#xff0c;有效降低人员和设备的安全隐患&#xff0c;更助力企业运维水平和智能化作…

人脸识别 ArcFace人脸识别

文章目录 损失函数的设计思路 损失函数的设计思路

电子温度计不准需要怎么处理?

电子温度计不准需要怎么处理&#xff1f; 首选将温度计完全浸入温度为0℃左右的水中&#xff0c;使温度计指示值与0℃相等&#xff0c;拿出测量待测物的温度。其次将温度计完全浸入温度为100℃左右的水中&#xff0c;使温度计指示值与100℃相等&#xff0c;拿出测量待测物的温…

【InternLM实战营---第六节课笔记】

一、本期课程内容概述 本节课的主讲老师是【樊奇】。教学内容主要包括以下三个部分&#xff1a; 1.大模型智能体的背景及介绍 2. Lagent&AgentLego框架介绍 3.Lagent&AgentLego框架实战 二、学习收获 智能体出现的背景 智能体的引入旨在克服大模型在应对复杂、动态任…

redis单线程模型

工作原理 在Redis中&#xff0c;当两个客户端同时发送相同的请求时&#xff0c;Redis采用单线程模型来处理所有的客户端请求&#xff0c;会依次处理这些请求&#xff0c;每个请求都会按照先后顺序被执行&#xff0c;不会同时处理多个请求。使得Redis能够避免多线程并发访问数据…

【无标题】w

import requests , sys , edge _ tts , os , asyncio from pydub import AudioSegment , playback url http://localhost:8080/v1/chat/ completions ’ def send _ message ( message ): headers {" Content - Type “:” application / json "} data { " mode…

【MySQL 数据宝典】【磁盘结构】- InnoDb 数据文件-Page结构、行记录格式

一、 数据文件 1.1 表空间文件结构 InnoDB表空间文件结构主要包括&#xff1a;Tablespace&#xff08;表空间&#xff09;、Segment&#xff08;段&#xff09;、Extent&#xff08;区&#xff09;、Page&#xff08;页&#xff09;、Row&#xff08;行&#xff09;。 Tables…

SAP DMS创建文档操作简介

前面的博文中我们创建了根目录的文档类型,下面我们需要创建我们后台已经配置到的文档类型 1、事务代码CV01N 框出的部分表示是用什么界面进行维护 当我们选择浏览器就 会变成一下界面 因为我们配置的是内部给号所以输入文档类型即可。 输入文档的描述。回车后输入状态的描…

【电路笔记】-Hartley振荡器

Hartley振荡器 文章目录 Hartley振荡器1、概述2、Hartley振荡器电路3、并联Hartley振荡器电路4、示例5、使用运算放大器的Hartley振荡器6、总结1、概述 Hartley振荡器设计使用两个电感线圈与一个并联电容器串联,形成产生正弦振荡的谐振储能电路。 与Hartley振荡器不同,我们…

第一讲 - Java入门

第一讲 - Java入门 文章目录 第一讲 - Java入门1. 人机交互1.1 什么是cmd&#xff1f;1.2 如何打开CMD窗口&#xff1f;1.3 常用CMD命令1.4 CMD练习1.5 环境变量 2. Java概述1.1 Java是什么&#xff1f;1.2下载和安装1.2.1 下载1.2.2 安装1.2.3 JDK的安装目录介绍 1.3 HelloWor…
最新文章