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

news2024/11/16 23:44:01

一.贪吃蛇

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

二.贪吃蛇的功能

  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.coloradmin.cn/o/1620009.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript系列------1

1. JavaScript组成 ECMAScript JS 语言基础。比如&#xff1a;变量、分支语句、循环语句、对象等等。 Web APIs DOM(页面文档对象模型): 操作文档&#xff0c;比如对页面元素进行移动、大小、添加删除等操作 BOM(浏览器对象模型): 操作浏览器&#xff0c;比如页面弹窗&#x…

【前端面试常问】什么是前端工程化

&#x1f31f;【前端面试常问】前端工程化&#x1f31f; &#x1f4da; 什么是前端工程化&#xff1f; &#x1f389; 前端工程化&#xff0c;简而言之&#xff0c;是通过整合先进的工具链和最佳实践&#xff0c;将前端开发过程实现标准化、自动化和高效化的过程&#xff0c;…

如何利用美国站群服务器通过CN2线路优化中美之间的数据传输?

如何利用美国站群服务器通过CN2线路优化中美之间的数据传输? 随着全球化进程的不断推进&#xff0c;跨国企业和国际市场的拓展对数据传输速度和稳定性提出了更高的要求。特别是对于中美之间的数据传输&#xff0c;由于地理位置遥远和网络环境不同&#xff0c;优化数据传输变得…

机器人自动驾驶时间同步进阶

0. 简介 之前时间同步也写过一篇文章介绍机器人&自动驾驶中的时间同步。在最近的学习中发现一些额外需要阐述学习的内容&#xff0c;这里就再次写一些之前没写到的内容。 1. NTP NTP 是网络时间协议&#xff0c;用来同步网络中各计算机时间的协议&#xff0c;把计算机的时…

QT常量中有换行符解决方法--使用中文显示乱码或者编译报错

QT6.3常量中有换行符 方法1&#xff1a;&#xff08;不能与方法2混用&#xff09; cpp文件顶部添加&#xff1a; #pragma execution_character_set("utf-8") 方法2&#xff1a;&#xff08;不能与方法1混用&#xff09; int ret2QMessageBox::information(this,QSt…

Java | Leetcode Java题解之第45题跳跃游戏II

题目&#xff1a; 题解&#xff1a; class Solution {public int jump(int[] nums) {int length nums.length;int end 0;int maxPosition 0; int steps 0;for (int i 0; i < length - 1; i) {maxPosition Math.max(maxPosition, i nums[i]); if (i end) {end maxP…

Qt 菜单栏上手教程:QMenuBar QMenu QToolbar

引言 在Qt框架中&#xff0c;QMenuBar、QMenu、QToolbar和QAction都是用于构建应用程序界面中的用户交互元素。 QMenuBar 是什么&#xff1a;QMenuBar是一个用于创建横向菜单栏的类。在桌面应用程序中&#xff0c;它通常位于窗口的顶部。应用场景&#xff1a;当您需要一个包含…

清华大学灵境智能技术交叉创新群体博士后,综合年收入30-60万

清华大学灵境智能技术交叉创新群体现面向全球招聘优秀博士后研究人员&#xff0c;以进一步推动三维生成、三维重建、多模态智能交互、控制交互演化、知识工程及情感计算等领域的研究。我们诚邀有志于从事交叉学科研究的青年才俊加入我们的团队&#xff0c;共同探索智能技术的前…

智慧中药煎配系统:自动化煎药管理,传统中医焕发现代魅力-亿发

传统中医药服务迎来了全新的发展契机。亿发中药煎配系统&#xff0c;利用现代通信技术和自动控制技术&#xff0c;结合智能制造、大数据和物联网等现代化信息技术&#xff0c;革新中药煎制的方式&#xff0c;实现了100%数字化和全流程信息追溯&#xff0c;从根本上保障了药效和…

【Git系列】rebase的使用场景

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

解密 Grupo MasMovil 使用 ClickHouse 监控无线网络

本文字数&#xff1a;4151&#xff1b;估计阅读时间&#xff1a;11 分钟 作者&#xff1a;Rodrigo Aguirregabiria Herrero, Grupo MasMovil 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 我们很高兴与大家分享来自西班牙最大的电信…

跟裤裤一起探索结构体的奥秘!

目录 1.结构体类型的声明 2.结构体变量的创建和初始化 2.1结构体成员的直接访问 2.2结构体成员的间接访问 2.3结构体变量的创建和初始化 3.结构的自引用 4.结构体内存对齐 4.1对齐规则 ​4.2为什么存在内存对齐&#xff1f; 4.3修改默认对齐数 5.结构体传参 6.结构…

GUI测试首推!TestComplete 帮助有效缩短 40-50% 测试时长!

TestComplete 是一款自动化UI测试工具&#xff0c;这款工具目前在全球范围内被广泛应用于进行桌面、移动和Web应用的自动化测试。 TestComplete 集成了一种精心设计的自动化引擎&#xff0c;可以自动记录和回放用户的操作&#xff0c;方便用户进行UI&#xff08;用户界面&…

Pyside6:多行按钮点击判断序号

在Pyside开发过程中会遇到这么个问题&#xff1a;当多个按钮在很多行中&#xff0c;需要在点击槽函数中确认按钮的行。 普通的按钮点击信号如下&#xff1a; clicked() 该信号并未有任何参数&#xff0c;无法得到有效的信息&#xff0c;那么如何完成点击哪个确定是哪个按钮呢…

分类预测 | Matlab实现RIME-BP霜冰优化BP神经网络多特征分类预测

分类预测 | Matlab实现RIME-BP霜冰优化BP神经网络多特征分类预测 目录 分类预测 | Matlab实现RIME-BP霜冰优化BP神经网络多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.RIME-BP霜冰优化BP神经网络多特征分类预测&#xff08;Matlab实现完整源码和数据&a…

Linux SDIO-WiFi 协议栈

Linux SDIO-WiFi 协议栈 1. 简介2. BCMDHD2.1 WiFi模组2.2 驱动初始化&#xff08;dhd_module_init&#xff09; 3. Broadcom fullmac WLAN 1. 简介 2. BCMDHD BCMDHD&#xff1a;Broadcom Dongle Host DriverSIP&#xff1a;System In Package 2.1 WiFi模组 2.2 驱动初始化…

条件生成对抗网络(cGAN)在AI去衣技术中的应用探索

随着深度学习技术的飞速发展&#xff0c;生成对抗网络&#xff08;GAN&#xff09;作为其中的一个重要分支&#xff0c;在图像生成、图像修复等领域展现出了强大的能力。其中&#xff0c;条件生成对抗网络&#xff08;cGAN&#xff09;通过引入条件变量来控制生成模型的输出&am…

互联网大厂ssp面经,数据结构part3

1. 哈希表的原理是什么&#xff1f;如何解决哈希碰撞问题&#xff1f; a. 原理&#xff1a;通过哈希函数将每个键映射到一个唯一的索引位置&#xff0c;然后将值存储在对应索引位置的存储桶中。 b. 关键&#xff1a;将不同的键映射到不同的索引位置&#xff0c;以实现快速的插…

Spring Boot 集成 EasyExcel 3.x

Spring Boot 集成 EasyExcel 3.x Spring Boot 集成 EasyExcel 3.x 本章节将介绍 Spring Boot 集成 EasyExcel&#xff08;优雅实现Excel导入导出&#xff09;。 &#x1f916; Spring Boot 2.x 实践案例&#xff08;代码仓库&#xff09; 介绍 EasyExcel 是一个基于 Java 的、…

社交的神奇好处你都知道吗?

聊天交友软件是人们日常生活中不可或缺的一部分&#xff0c;有些需要花费大量金钱才能享受完整的功能&#xff0c;但也有一些不需要花费金钱的聊天交友软件&#xff0c;如微信、QQ等。这些软件提供了多种交流方式&#xff0c;让用户可以方便地结识新朋友、扩大社交圈子&#xf…