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

一.贪吃蛇

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

二.贪吃蛇的功能

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

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

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

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

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

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

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

三.贪吃蛇项目的实现

1.游戏前的准备

1.1游戏的状态

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

1.2蛇的移动方向

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

1.3蛇的节点

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

1.4食物的位置

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

1.5整个贪吃蛇游戏

  • struct Snake 结构体用于表示蛇,包括以下成员:
    • _snake:蛇的头节点指针。
    • _food:食物指针。
    • _score:蛇的得分。
    • dir:蛇的移动方向。
    • sta:当前游戏状态。
    • _weight:奖励。
    • _sleeptime:游戏循环每次暂停的时间间隔。
cpp 复制代码
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函数设置本地化环境为当前系统默认的环境。**因为环境的差异,导致我们的中文的宽字符无法被识别,所以要本地化设置。

cpp 复制代码
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',如果是则继续进行下一轮游戏。

cpp 复制代码
#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函数时,控制台光标会移动到指定的位置。

cpp 复制代码
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:显示游戏信息,可能是在控制台上显示当前游戏的状态、得分等信息。
cpp 复制代码
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.4.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")清空控制台屏幕。

cpp 复制代码
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.4.2创建地图

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

cpp 复制代码
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.4.3初始化整个贪吃蛇游戏

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

cpp 复制代码
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.4.4创建蛇的初始状态

首先创建蛇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的头结点和身体节点的字符表示。通过这些操作,创建了两条蛇的初始状态,每条蛇由一个头结点和四个身体节点组成。

cpp 复制代码
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.4.5创建食物(较重点)

首先,我们分配内存以存储食物结构,并使用assert函数确保内存分配成功。进入无限循环,用于生成随机坐标。x的取值范围为2到54(包含2和54),y的取值范围为1到25(包含1和25)。我们希望食物的x坐标为偶数,因此我们检查x是否可以被2整除。如果不满足这个条件,则继续生成随机坐标,直到满足条件为止。在满足条件的情况下,我们遍历蛇1和蛇2的身体节点,检查食物坐标是否与任何节点的坐标冲突。如果存在冲突,我们将conflict标志设置为1。此外,我们还检查食物坐标是否与蛇2的当前食物坐标冲突。如果存在冲突,也将conflict标志设置为1。如果没有冲突,则跳出循环。设置食物的坐标为生成的随机坐标。在游戏地图上显示食物,通过setpos函数将光标移动到正确的位置,并使用wprintf函数打印食物字符。最后,将食物分配给蛇1的food1指针,完成食物的放置。

cpp 复制代码
// 创建食物并放置在游戏地图上
void creatfood(pSnake snake1, pSnake snake2) {
	int x, y;

	// 分配内存以存储食物结构
	pFood food1 = (pFood)malloc(sizeof(Food));
	assert(food1 != NULL);

	while (1) {
		// 生成随机坐标
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;

		// 确保食物的 x 坐标为偶数
		if (x % 2 == 0) {
			pSnakeNode p1 = snake1->_snake;
			pSnakeNode p2 = snake2->_snake;
			int conflict = 0;

			// 检查食物坐标是否与蛇1的身体节点冲突
			while (p1 != NULL) {
				if (p1->x == x && p1->y == y) {
					conflict = 1;
					break;
				}
				p1 = p1->next;
			}

			// 检查食物坐标是否与蛇2的身体节点冲突
			while (!conflict && p2 != NULL) {
				if (p2->x == x && p2->y == y) {
					conflict = 1;
					break;
				}
				p2 = p2->next;
			}

			// 检查食物坐标是否与蛇2的当前食物坐标冲突
			if (!conflict && snake2->_food != NULL && snake2->_food->x == x && snake2->_food->y == y) {
				conflict = 1;
			}

			// 如果没有冲突,则跳出循环
			if (!conflict) {
				break;
			}
		}
	}

	// 设置食物的坐标
	food1->x = x;
	food1->y = y;

	// 在屏幕上显示食物
	setpos(food1->x, food1->y);
	wprintf(L"%c", FOOD);

	// 将食物分配给蛇1
	snake1->_food = food1;
}
2.4.6显示游戏的规则

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

cpp 复制代码
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的状态不再为正常(游戏结束)。

cpp 复制代码
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秒钟,然后再次检测空格键的状态。这样循环会一直执行,直到空格键被按下并函数返回。这样就实现了游戏的暂停功能。

cpp 复制代码
void pause()
{
    while (1)
    {
        if (Key_state(VK_SPACE))
            return;
        Sleep(1000);
    }
}

3.3 蛇的移动及一系列的判断(重点)

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

    i->next = snake1->_snake;  // 将i节点的下一个节点指向snake1的当前头节点

    // 根据snake1的方向更新i节点的坐标
    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的状态为by_end,则直接返回
    }

    snake1->_snake = i;  // 更新snake1的头节点为i

    pSnakeNode p = snake1->_snake;

    // 检查i节点的坐标是否与snake1或snake2的食物坐标匹配
    if ((i->x == snake1->_food->x) && (i->y == snake1->_food->y) || (i->x == snake2->_food->x) && (i->y == snake2->_food->y))
    {
        if ((i->x == snake1->_food->x) && (i->y == snake1->_food->y))
        {
            (snake1->_score) += snake1->_weight;  // 增加snake1的分数
            free(snake1->_food);  // 释放snake1的食物节点
            creatfood(snake1, snake2);  // 创建新的snake1食物
        }
        else
        {
            (snake1->_score) += snake1->_weight;  // 增加snake1的分数
            free(snake2->_food);  // 释放snake2的食物节点
            creatfood(snake2, 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);
    }

    // 检查snake1的头节点是否触碰到边界
    if (snake1->_snake->x == 0 || snake1->_snake->x == 56 || snake1->_snake->y == 0 || snake1->_snake->y == 25)
    {
        snake1->sta = by_wall;  // 设置snake1的状态为by_wall
        return;
    }

    kill_self(snake1, snake2);  // 检查snake1是否与snake2相撞

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

该函数包含了以下功能:

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

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

cpp 复制代码
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)所占用的内存。

cpp 复制代码
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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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 x, y;
	pFood food1 = (pFood)malloc(sizeof(Food));
	assert(food1 != NULL);
	while (1) {
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
		if (x % 2 == 0) {
			pSnakeNode p1 = snake1->_snake;
			pSnakeNode p2 = snake2->_snake;
			int conflict = 0;
			while (p1 != NULL) {
				if (p1->x == x && p1->y == y) {
					conflict = 1;
					break;
				}
				p1 = p1->next;
			}
			while (!conflict && p2 != NULL) {
				if (p2->x == x && p2->y == y) {
					conflict = 1;
					break;
				}
				p2 = p2->next;
			}
			if (!conflict && snake2->_food != NULL && snake2->_food->x == x && snake2->_food->y == y) {
				conflict = 1;
			}
			if (!conflict) {
				break;
			}
		}
	}
	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))
	{
		if ((i->x == snake1->_food->x) && (i->y == snake1->_food->y))
		{
			(snake1->_score) += snake1->_weight;
			free(snake1->_food);
			creatfood(snake1, snake2);
		}
		else 
		{
			(snake1->_score) += snake1->_weight;
			free(snake2->_food);
			creatfood(snake2, snake1);
		}
	}
	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);
}
相关推荐
k09336 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
神奇夜光杯13 分钟前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
Themberfue16 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
plmm烟酒僧17 分钟前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
EricWang135827 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
测试界的酸菜鱼29 分钟前
Python 大数据展示屏实例
大数据·开发语言·python
我是谁??29 分钟前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
晨曦_子画38 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend1 小时前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
希言JY1 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言