c语言:贪吃蛇的实现

目录

贪吃蛇实现的技术前提:

[Win32 API介绍](#Win32 API介绍)

控制台程序(console)

控制台屏幕上的坐标

GetStdHandle

GetConsoleCursorInfo

CONSOLE_CURSOR_INFO

SetConsoleCursorInfo

SetConsoleCursorPosition

GetAsyncKeyState

宽字符的打印

贪吃蛇的主要功能

游戏的初始化和适应本地环境

打印欢迎界面

打印墙

创建蛇身

创建打印食物

控制移动

吃到食物

没吃到食物

撞墙

撞到蛇身

结束界面

完整的代码

snake.h

snake.c

test.c

效果展示:


贪吃蛇实现的技术前提:

c语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。

Win32 API介绍

Win32 API(Windows API)是指微软公司为Windows操作系统提供的应用程序编程接口(Application Programming Interface)。它是一组函数、数据结构、常量和其他定义,用于开发Windows平台上的应用程序。

Win32 API提供了许多功能,涵盖了操作系统的各个方面,包括图形界面、输入输出、文件系统、网络通信、多线程、安全性等。开发者可以使用这些API来创建各种类型的应用程序,包括桌面应用程序、游戏、驱动程序、系统工具等。

Win32 API基于C语言编写,提供了一种与操作系统交互的标准接口。开发者可以通过调用API函数来访问操作系统的功能和资源,实现各种操作,例如创建窗口、绘制图形、处理用户输入、读写文件、访问网络等。

使用Win32 API进行开发需要一定的编程知识和理解操作系统的工作原理。开发者需要了解API函数的参数、返回值和使用方法,以及如何处理错误和异常情况。此外,开发者还可以利用各种开发工具和框架,简化Win32 API的使用过程,提高开发效率。

总之,Win32 API是Windows平台上开发应用程序的基础,通过使用这些API,开发者可以充分利用操作系统的功能,创建出各种强大、高效的应用程序。

控制台程序(console)

在cmd里面输入mode con cols=100 lines=30就可以控制命令提示符界面的大小

title 名字可以改命令符操作名字


控制台屏幕上的坐标

COORD是Windows API中定义的一个结构体,表示一个字符在控制台屏幕上的坐标。

cpp 复制代码
typedef struct _COORD 
{
 SHORT X;//横坐标
 SHORT Y;//纵坐标
} COORD, *PCOORD;

给坐标赋值:

cpp 复制代码
COORD pos={ 40,10 };

GetStdHandle

GetStdHandle是⼀个Windows API函数。它用于从⼀个特定的标准设备(标准输入、标准输出或标准错误)中取得⼀个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备。

cpp 复制代码
HANDLE GetStdHandle(DWORD nStdHandle);

实例:

cpp 复制代码
HANDLE hOutput = NULL;

//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

GetConsoleCursorInfo

检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息。

cpp 复制代码
BOOL WINAPI GetConsoleCursorInfo
(
HANDLE hConsoleO
 PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

实例:

cpp 复制代码
HANDLE hOutput = NULL;

//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;

GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 

CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台光标的信息

cpp 复制代码
typedef struct _CONSOLE_CURSOR_INFO 
{
 DWORD dwSize;
 BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
  • dwSize,由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完 全填充单元格到单元底部的水平线条。
  • bVisible,游标的可见性。如果光标可见,则此成员为TRUE。
cpp 复制代码
1 CursorInfo.bVisible = false; //隐藏控制台光标 

SetConsoleCursorInfo

设置指定控制台屏幕缓冲区的光标的大小和可见性。

cpp 复制代码
BOOL WINAPI SetConsoleCursorInfo
(
 HANDLE hConsoleOutput,
 const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

实例:

cpp 复制代码
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

//影藏光标操作 
CONSOLE_CURSOR_INFO CursorInfo;

GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 
CursorInfo.bVisible = false; //隐藏控制台光标 

SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 

SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。

cpp 复制代码
BOOL WINAPI SetConsoleCursorPosition(
 HANDLE hConsoleOutput,
 COORD pos
);

实例:

cpp 复制代码
COORD pos = { 10, 5};
 HANDLE hOutput = NULL;
 //获取标准输出的句柄(⽤来标识不同设备的数值) 
 hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
 //设置标准输出上光标的位置为pos 
 SetConsoleCursorPosition(hOutput, pos);

SetPos:封装⼀个设置光标位置的函数

cpp 复制代码
//设置光标的坐标 

void SetPos(short x, short y)
{
 COORD pos = { x, y };
 HANDLE hOutput = NULL;
 //获取标准输出的句柄(⽤来标识不同设备的数值) 
 hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}

GetAsyncKeyState

获取按键情况,GetAsyncKeyState的函数原型如下:

cpp 复制代码
SHORT GetAsyncKeyState(
 int vKey
);

将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最高是0,说明按键的状态是抬 起;如果最低位被置为1则说明,该按键被按过,否则为0。 如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1。

cpp 复制代码
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

宽字符的打印

cpp 复制代码
#include <stdio.h>

#include<locale.h>

int main() {
 setlocale(LC_ALL, "");
 wchar_t ch1 = L'●'; 
wchar_t ch2 = L'中';
 wchar_t ch3 = L'国';
 wchar_t ch4 = L'★';
 printf("%c%c\n", 'a', 'b');
 
 wprintf(L"%c\n", ch1);
 wprintf(L"%c\n", ch2);
 wprintf(L"%c\n", ch3);
 wprintf(L"%c\n", ch4);
 return 0;
}

贪吃蛇的主要功能

  • 贪吃蛇游戏的主要功能可以包括以下几个方面:

  • 游戏界面:游戏界面应包括蛇、食物、得分和游戏结束提示的显示,以及其他可能的游戏元素。

  • 控制方式:提供玩家通过方向键、触屏或其他方式来控制蛇的移动方向。

  • 单人模式:提供单人模式,玩家可以独自进行游戏。

  • 分数统计:记录游戏分数,并在游戏结束时进行展示。

  • 游戏结束提示:当蛇碰到边界或者自己的身体时,游戏应该结束,并给出相应的提示。

  • 界面友好:提供简洁易懂、美观友好的游戏界面,方便玩家使用。

游戏的初始化和适应本地环境

cpp 复制代码
void GameStart(pSnake ps)
{
    //设置控制台的信息
    system("mode con cols=100 lines=30");
    system("title 贪吃蛇");


    //隐藏光标
    HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
    //获取控制台光标信息
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(handle, &CursorInfo);
    CursorInfo.bVisible = false;
    SetConsoleCursorInfo(handle, &CursorInfo);

    //欢迎界面
    WelcomeToGame();

    //绘制地图
    CreateMap();

    //初始化蛇
    InitSnake(ps);

    //创建食物
    CreateFood(ps);
}
cpp 复制代码
//设置光标的坐标 
void SetPos(int x, int y)
{
    //获得设备句柄
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    //根据句柄设置光标的位置
    COORD pos = { x, y };
    SetConsoleCursorPosition(handle, pos);
}
cpp 复制代码
void test()
{
	int ch = 0;
	do
	{
		//创建贪吃蛇
		Snake snake = { 0 };
		GameStart(&snake);
		GameRun(&snake);
		GameEnd(&snake);
		SetPos(20, 15);
		printf("再来一局吗?(Y/N)");
		ch=getchar();
		getchar();
	} while (ch=='Y'||ch=='y');
}

int main()
{
	//修改适配本地中文环境
	setlocale(LC_ALL,"");

	test();
	SetPos(0,27);
	return 0;
}

打印欢迎界面

cpp 复制代码
void WelcomeToGame()
{

    SetPos(38, 15);
    printf("欢迎来到贪吃蛇小游戏\n");
    SetPos(39, 23);
    system("pause");
    system("cls");

    SetPos(23, 10);
    printf("用↑. ↓. ←. →来控制蛇的移动,F3是加速,F4是减速\n");
    SetPos(23, 11);
    printf("加速能得到更高的分数");
    SetPos(39, 23);
    system("pause");
    system("cls");
}

打印墙

cpp 复制代码
void CreateMap()
{
    //上
    SetPos(0, 0);
    for (int i = 0; i <= 56; i += 2)
    {
        wprintf(L"%lc",L'□');
    }
    //下
    SetPos(0, 26);
    for (int i = 0; i <= 56; i += 2)
    {
        wprintf(L"%lc", L'□');
    }
    //左
    for (int i = 1; i <= 25; i ++)
    {
        SetPos(0, i);
        wprintf(L"%lc", L'□');
    }
    //右
    for (int i = 1; i <= 25; i++)
    {
        SetPos(56, i);
        wprintf(L"%lc", L'□');
    }
}

创建蛇身

cpp 复制代码
void InitSnake(pSnake ps)
{
    //创建5个蛇身的节点
    pSnakeNode cur = NULL;
    for (int i = 0; i < 5; i++)
    {
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake():malloc()");
            return;
        }
        cur->x = POS_X + 2 * i;
        cur->y = POS_Y;
        cur->next = NULL;

        if (ps->pSnake == NULL)
        {
            ps->pSnake = cur;
        }
        else
        {
            cur->next = ps->pSnake;
            ps->pSnake = cur;
        }
    }

    //打印蛇身
    cur = ps->pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    //贪吃蛇的其他信息初始化
    ps->dir = RIGHT;
    ps->foodweight = 10;
    ps->pFood = NULL;
    ps->Score = 0;
    ps->SleepTime = 200;
    ps->status = OK;
  
}

创建打印食物

cpp 复制代码
void CreateFood(pSnake ps)
{
    int x = 0;
    int y = 0;

 again:
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 24 + 1;
    } while (x % 2 != 0);

    //坐标和蛇的身体的每个节点的坐标比较
    pSnakeNode cur = ps->pSnake;
    while (cur)
    {
        if (x == cur->x && y == cur->y)
        {
            goto again;
        }
        cur = cur->next;
    }

    //创建食物
    pSnakeNode pFood=(pSnakeNode)malloc(sizeof(SnakeNode));
    if (pFood == NULL)
    {
        perror("CreateFood()::mallloc()");
        return;
    }
    
    pFood->x = x;
    pFood->y = y;

    ps->pFood = pFood;
    SetPos(x, y);
    wprintf(L"%lc", L'¥');

}

控制移动

cpp 复制代码
void SnakeMove(pSnake ps)
{
    pSnakeNode pNext=malloc(sizeof(SnakeNode));
    if (pNext == NULL)
    {
        perror("SnakeMove()::malloc()");
        return;
    }
    pNext->next = NULL;

    switch (ps->dir)
    {
    case UP:

        pNext->x = ps->pSnake->x;
        pNext->y = ps->pSnake->y - 1;
        break;

    case DOWN:

        pNext->x = ps->pSnake->x;
        pNext->y = ps->pSnake->y + 1;
        break;

    case LEFT:

        pNext->x = ps->pSnake->x - 2;
        pNext->y = ps->pSnake->y;
        break;

    case RIGHT:

        pNext->x = ps->pSnake->x + 2;
        pNext->y = ps->pSnake->y;
        break;
    }
        //下一个坐标是否是食物
        if (NextIsFood(ps, pNext))
        {
            //是食物就吃掉
            EatFood(ps,pNext);
        }
        else
        {
            //不是食物就正常走一步
            NotEatFood(ps, pNext);
        }

        //检测撞墙
        KillByWall(ps);

        //检测撞到自己
        KillBySelf(ps);
    }
cpp 复制代码
void GameRun(pSnake ps)
{
    //打印帮助信息
    PrintHelpInfo();

    //检测按键
    do
    {
        //当前的分数情况
        SetPos(65, 12);
        printf("总分:%5d\n", ps->Score);
        SetPos(65, 13);
        printf("食物的分数:%02d\n", ps->foodweight);
        //检测按键
        //上下左右,ESC,空格,F3,F4
        if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
        {
            ps->dir = UP;
        }
        else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
        {
            ps->dir = DOWN;
        }
        else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
        {
            ps->dir = LEFT;
        }
        else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
        {
            ps->dir = RIGHT;
        }
        else if (KEY_PRESS(VK_ESCAPE))
        {
            ps->status = ESC;
            break;
        }
        else if (KEY_PRESS(VK_SPACE))
        {
            //游戏暂停
            pause();
        }
        else if (KEY_PRESS(VK_F3))
        {
            if (ps->SleepTime >= 80)
            {
                ps->SleepTime -= 30;
                ps->foodweight += 2;
            }
        }
        else if (KEY_PRESS(VK_F4))
        {
            if (ps->SleepTime <= 200)
            {
                ps->SleepTime += 30;
                ps->foodweight -= 2;
            }
        }
        //走一下
        SnakeMove(ps);
        //睡眠一下
        Sleep(ps->SleepTime);
    } while(ps->status==OK);
}

吃到食物

cpp 复制代码
void EatFood(pSnake ps, pSnakeNode pNext)
{
    pNext->next = ps->pSnake;
    ps->pSnake = pNext;

    //打印蛇
    pSnakeNode cur = ps->pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    ps->Score += ps->foodweight;

    //释放旧的食物
    free(ps->pFood);

    //新建食物
    CreateFood(ps);
}

没吃到食物

cpp 复制代码
void NotEatFood(pSnake ps, pSnakeNode pNext)
{
    pNext->next = ps->pSnake;
    ps->pSnake = pNext;

    pSnakeNode cur = ps->pSnake;
    while (cur->next->next )
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }
    
    //将尾节点的位置打印成空白字符
    SetPos(cur->next->x, cur->next->y);
    printf("  ");

    free(cur->next);
    cur->next = NULL;

}

撞墙

cpp 复制代码
//检测撞墙
void KillByWall(pSnake ps)
{
    if (ps->pSnake->x == 0 || ps->pSnake->x == 56||ps->pSnake->y==0||ps->pSnake->y==26)
    {
        ps->status = KILL_BY_WALL;
    }
}

撞到蛇身

cpp 复制代码
//检测撞到自己
void KillBySelf(pSnake ps)
{
    pSnakeNode cur = ps->pSnake->next;
    while (cur)
    {
        if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
        {
            ps->status = KILL_BY_SELF;
            return;
        }
        cur = cur->next;
    }
}

结束界面

cpp 复制代码
void GameEnd(pSnake ps)
{
    SetPos(20, 15);
    switch (ps->status)
    {
    case ESC:
        printf("主动退出游戏,正常退出\n");
        break;
    case KILL_BY_WALL:
        printf("很遗憾,撞墙了,游戏结束\n");
        break;
    case KILL_BY_SELF:
        printf("很遗憾,撞到自己了,游戏结束\n");
        break;
    }
    //释放资源
    pSnakeNode cur = ps->pSnake;
    pSnakeNode del = ps->pSnake;
    while (cur)
    {
        del = cur;
        cur = cur->next;
        free(del);
    }

    free(ps->pFood);
    ps->pSnake = NULL;
}

完整的代码

snake.h

cpp 复制代码
#pragma once

#include<locale.h>
#include<stdlib.h>
#include<stdio.h>
#include<windows.h>
#include<stdbool.h>


//蛇默认的起始坐标
#define POS_X 24
#define POS_Y 5

#define KEY_PRESS(VK)   ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
enum GAME_STAYUS
{
	OK=1,	
	ESC,
	KILL_BY_WALL,
	KILL_BY_SELF
};

enum DIRECTION
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};


//蛇身节点的定义
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,* pSnakeNode;


//整个蛇
typedef struct Snake
{
	//维护这条蛇的指针
	pSnakeNode pSnake;
	//指向食物的指针
	pSnakeNode pFood;

	int Score;//累计分数
	int foodweight;//一个食物的分数
	int SleepTime;//休眠的时间,休眠的时间越短,蛇的速度越快
	enum GAME_STATUS status;//游戏当前的状态
	enum DIRECTION dir;//蛇当前走的方向
}Snake,*pSnake;

//定位控制台的光标位置
void SetPos(int x, int y);

//游戏开始前的准备
void GameStart(pSnake ps);

//打印欢迎界面
void WelcomeToGame();

//绘制地图
void CreateMap();

//初始化贪吃蛇
void InitSnake(pSnake ps);

//创建食物
void CreateFood(pSnake ps);

//游戏运行的整个逻辑
void GameRun(pSnake ps);

//打印帮助信息
void PrintHelpInfo();

//蛇移动的函数---每次走一步
void SnakeMove(pSnake ps);

//判断蛇头的下一步要走的位置处是否是食物
int NextIsFood(pSnake ps, pSnakeNode pNext);

//下一步要走的位置是食物
void EatFood(pSnake ps, pSnakeNode pNext);

//下一步要走的位置不是食物
void NotEatFood(pSnake ps, pSnakeNode pNext);

//检测撞墙
void KillByWall(pSnake ps);

//检测撞到自己
void KillBySelf(pSnake ps);

//游戏结束的资源释放
void GameEnd(pSnake ps);

snake.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"snake.h"

//设置光标的坐标 
void SetPos(int x, int y)
{
    //获得设备句柄
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    //根据句柄设置光标的位置
    COORD pos = { x, y };
    SetConsoleCursorPosition(handle, pos);
}


void WelcomeToGame()
{

    SetPos(38, 15);
    printf("欢迎来到贪吃蛇小游戏\n");
    SetPos(39, 23);
    system("pause");
    system("cls");

    SetPos(23, 10);
    printf("用↑. ↓. ←. →来控制蛇的移动,F3是加速,F4是减速\n");
    SetPos(23, 11);
    printf("加速能得到更高的分数");
    SetPos(39, 23);
    system("pause");
    system("cls");
}

void CreateMap()
{
    //上
    SetPos(0, 0);
    for (int i = 0; i <= 56; i += 2)
    {
        wprintf(L"%lc",L'□');
    }
    //下
    SetPos(0, 26);
    for (int i = 0; i <= 56; i += 2)
    {
        wprintf(L"%lc", L'□');
    }
    //左
    for (int i = 1; i <= 25; i ++)
    {
        SetPos(0, i);
        wprintf(L"%lc", L'□');
    }
    //右
    for (int i = 1; i <= 25; i++)
    {
        SetPos(56, i);
        wprintf(L"%lc", L'□');
    }
}

void InitSnake(pSnake ps)
{
    //创建5个蛇身的节点
    pSnakeNode cur = NULL;
    for (int i = 0; i < 5; i++)
    {
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake():malloc()");
            return;
        }
        cur->x = POS_X + 2 * i;
        cur->y = POS_Y;
        cur->next = NULL;

        if (ps->pSnake == NULL)
        {
            ps->pSnake = cur;
        }
        else
        {
            cur->next = ps->pSnake;
            ps->pSnake = cur;
        }
    }

    //打印蛇身
    cur = ps->pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    //贪吃蛇的其他信息初始化
    ps->dir = RIGHT;
    ps->foodweight = 10;
    ps->pFood = NULL;
    ps->Score = 0;
    ps->SleepTime = 200;
    ps->status = OK;
  
}

void CreateFood(pSnake ps)
{
    int x = 0;
    int y = 0;

 again:
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 24 + 1;
    } while (x % 2 != 0);

    //坐标和蛇的身体的每个节点的坐标比较
    pSnakeNode cur = ps->pSnake;
    while (cur)
    {
        if (x == cur->x && y == cur->y)
        {
            goto again;
        }
        cur = cur->next;
    }

    //创建食物
    pSnakeNode pFood=(pSnakeNode)malloc(sizeof(SnakeNode));
    if (pFood == NULL)
    {
        perror("CreateFood()::mallloc()");
        return;
    }
    
    pFood->x = x;
    pFood->y = y;

    ps->pFood = pFood;
    SetPos(x, y);
    wprintf(L"%lc", L'¥');

}
void GameStart(pSnake ps)
{
    //设置控制台的信息
    system("mode con cols=100 lines=30");
    system("title 贪吃蛇");


    //隐藏光标
    HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
    //获取控制台光标信息
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(handle, &CursorInfo);
    CursorInfo.bVisible = false;
    SetConsoleCursorInfo(handle, &CursorInfo);

    //欢迎界面
    WelcomeToGame();

    //绘制地图
    CreateMap();

    //初始化蛇
    InitSnake(ps);

    //创建食物
    CreateFood(ps);
}

void PrintHelpInfo()
{
    SetPos(65, 15);
    printf("不能穿墙,不能咬到自己");
    SetPos(65, 16);
    printf("用↑. ↓. ←. →来控制蛇的移动");
    SetPos(65, 17);
    printf("F3是加速,F4是减速");

    SetPos(65, 20);
    printf("版权@lanll");
}

void pause()
{
    while (1)
    {
        Sleep(100);
        if (KEY_PRESS(VK_SPACE))
        {
            break;
        }
    }
}

int NextIsFood(pSnake ps, pSnakeNode pNext)
{
    if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
        return 1;//下一个坐标初是食物
    else
        return 0;
}

void EatFood(pSnake ps, pSnakeNode pNext)
{
    pNext->next = ps->pSnake;
    ps->pSnake = pNext;

    //打印蛇
    pSnakeNode cur = ps->pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }

    ps->Score += ps->foodweight;

    //释放旧的食物
    free(ps->pFood);

    //新建食物
    CreateFood(ps);
}

void NotEatFood(pSnake ps, pSnakeNode pNext)
{
    pNext->next = ps->pSnake;
    ps->pSnake = pNext;

    pSnakeNode cur = ps->pSnake;
    while (cur->next->next )
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }
    
    //将尾节点的位置打印成空白字符
    SetPos(cur->next->x, cur->next->y);
    printf("  ");

    free(cur->next);
    cur->next = NULL;

}

//检测撞墙
void KillByWall(pSnake ps)
{
    if (ps->pSnake->x == 0 || ps->pSnake->x == 56||ps->pSnake->y==0||ps->pSnake->y==26)
    {
        ps->status = KILL_BY_WALL;
    }
}

//检测撞到自己
void KillBySelf(pSnake ps)
{
    pSnakeNode cur = ps->pSnake->next;
    while (cur)
    {
        if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
        {
            ps->status = KILL_BY_SELF;
            return;
        }
        cur = cur->next;
    }
}


void SnakeMove(pSnake ps)
{
    pSnakeNode pNext=malloc(sizeof(SnakeNode));
    if (pNext == NULL)
    {
        perror("SnakeMove()::malloc()");
        return;
    }
    pNext->next = NULL;

    switch (ps->dir)
    {
    case UP:

        pNext->x = ps->pSnake->x;
        pNext->y = ps->pSnake->y - 1;
        break;

    case DOWN:

        pNext->x = ps->pSnake->x;
        pNext->y = ps->pSnake->y + 1;
        break;

    case LEFT:

        pNext->x = ps->pSnake->x - 2;
        pNext->y = ps->pSnake->y;
        break;

    case RIGHT:

        pNext->x = ps->pSnake->x + 2;
        pNext->y = ps->pSnake->y;
        break;
    }
        //下一个坐标是否是食物
        if (NextIsFood(ps, pNext))
        {
            //是食物就吃掉
            EatFood(ps,pNext);
        }
        else
        {
            //不是食物就正常走一步
            NotEatFood(ps, pNext);
        }

        //检测撞墙
        KillByWall(ps);

        //检测撞到自己
        KillBySelf(ps);
    }




void GameRun(pSnake ps)
{
    //打印帮助信息
    PrintHelpInfo();

    //检测按键
    do
    {
        //当前的分数情况
        SetPos(65, 12);
        printf("总分:%5d\n", ps->Score);
        SetPos(65, 13);
        printf("食物的分数:%02d\n", ps->foodweight);
        //检测按键
        //上下左右,ESC,空格,F3,F4
        if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
        {
            ps->dir = UP;
        }
        else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
        {
            ps->dir = DOWN;
        }
        else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
        {
            ps->dir = LEFT;
        }
        else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
        {
            ps->dir = RIGHT;
        }
        else if (KEY_PRESS(VK_ESCAPE))
        {
            ps->status = ESC;
            break;
        }
        else if (KEY_PRESS(VK_SPACE))
        {
            //游戏暂停
            pause();
        }
        else if (KEY_PRESS(VK_F3))
        {
            if (ps->SleepTime >= 80)
            {
                ps->SleepTime -= 30;
                ps->foodweight += 2;
            }
        }
        else if (KEY_PRESS(VK_F4))
        {
            if (ps->SleepTime <= 200)
            {
                ps->SleepTime += 30;
                ps->foodweight -= 2;
            }
        }
        //走一下
        SnakeMove(ps);
        //睡眠一下
        Sleep(ps->SleepTime);
    } while(ps->status==OK);
}

void GameEnd(pSnake ps)
{
    SetPos(20, 15);
    switch (ps->status)
    {
    case ESC:
        printf("主动退出游戏,正常退出\n");
        break;
    case KILL_BY_WALL:
        printf("很遗憾,撞墙了,游戏结束\n");
        break;
    case KILL_BY_SELF:
        printf("很遗憾,撞到自己了,游戏结束\n");
        break;
    }
    //释放资源
    pSnakeNode cur = ps->pSnake;
    pSnakeNode del = ps->pSnake;
    while (cur)
    {
        del = cur;
        cur = cur->next;
        free(del);
    }

    free(ps->pFood);
    ps->pSnake = NULL;
}

test.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"

void test()
{
	int ch = 0;
	do
	{
		//创建贪吃蛇
		Snake snake = { 0 };
		GameStart(&snake);
		GameRun(&snake);
		GameEnd(&snake);
		SetPos(20, 15);
		printf("再来一局吗?(Y/N)");
		ch=getchar();
		getchar();
	} while (ch=='Y'||ch=='y');
}

int main()
{
	//修改适配本地中文环境
	setlocale(LC_ALL,"");

	test();
	SetPos(0,27);
	return 0;
}

效果展示:

相关推荐
isyangli_blog4 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008115 小时前
FastAPI APIRouter
开发语言·python
Benszen5 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆5 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木5 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充5 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~5 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6166 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草6 小时前
反射、Tomcat执行
java·开发语言
雪的季节7 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt