贪吃蛇小游戏的实现代码

一. 头文件

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<stdbool.h>
#include<locale.h>
#include<time.h>
#define Pos_x 24
#define Pos_y 5
#define WALL L'□'

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)//判断按键是按过还是没按过
//蛇的方向
enum DIRECTION
{
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
};

//蛇的状态
//正常,撞墙,撞到自己,正常退出
enum STATUS
{
    OK,//正常,继续游戏
    KILL_BY_WALL,//撞墙
    KILL_BY_SELF,//撞到自己
    END_NORMAL//正常退出
};

//蛇身的节点类型
typedef struct SnakeNode
{
    //坐标
    int x;
    int y;
    //指向下一个节点的指针
    struct SnakeNode* next;
}SnakeNode, * pSnakeNode;

//贪吃蛇
typedef struct Snake
{
    pSnakeNode _pSnake;//指向蛇头的指针
    pSnakeNode _pFood;//指向食物节点的指针
    enum DIRECTION _dir;//蛇的方向
    enum STATUS _status;//游戏的状态

    int _food_weight;//一个食物的分数
    int _score;//总成绩
    int _sleep_time;//休息时间,时间越短,速度越快,时间越长,速度越慢
}Snake, * pSnake;

//函数的声明

//定位光标位置

void SetPos(short x, short y);

//游戏的初始化
void GameStart(pSnake snake);

//欢迎界面的打印
void Welcome();

//创建地图
void CreateMap();

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

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

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

//查看节点是否是食物
int JubgeNextIsFood(pSnakeNode pn, pSnake ps);

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

//游戏结束的处理
void GameEnd(pSnake ps);

//检测蛇是否撞到墙
void KillWall(pSnake ps);

//检测蛇是否撞到自己
void KillSelf(pSnake ps);

二. 实现蛇功能

#include"snake.h"
void SetPos(short x, short y)
{
    //获得标准输出设备的句柄
    HANDLE houtput = NULL;
    houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    //定位光标的位置
    COORD pos = { x,y };

    SetConsoleCursorPosition(houtput, pos);
    //设置指定控制台屏幕缓冲区中光标的位置
    //参数:1. 控制台屏幕缓冲区的句柄
    //2. 指定新光标的位置的 COORD结构
}
int JubgeNextIsFood(pSnakeNode pn, pSnake ps)//判断下个节点是否是食物
{
    if (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y)
        return 1;
    else
        return 0;
}
void EatFood(pSnakeNode pn, pSnake ps)
{
    //头插法
    ps->_pFood->next = ps->_pSnake;//将食物的下一个变为头
    ps->_pSnake = ps->_pFood;//将食物定义为头

    free(pn);//不需要pn记录头的位置了,就销毁
    pn = NULL;

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

    //重新创建食物
    CreateFood(ps);//因为从thc.h里已经声明了,所以不用再上面声明
}
void NoFood(pSnakeNode pn, pSnake ps)
{
    pn->next = ps->_pSnake;//下一步的位置之后链接上头结点
    ps->_pSnake = pn;//将头变为下一步的位置

    pSnakeNode cur = pn->next, qrev = pn;

    while (cur->next)
    {
        cur = cur->next;
        qrev = qrev->next;
    }
    SetPos(cur->x, cur->y);
    printf("  ");
    free(cur);
    cur = NULL;
    qrev->next = NULL;

    //打印蛇身
    cur = ps->_pSnake;
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", L'◆');
    cur = cur->next;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }
}
void Pause()//暂停函数
{
    while (1)//无限暂停直到再按一次空格键
    { 
        Sleep(200);
        if (KEY_PRESS(VK_SPACE))
        {
            break;
        }
    }
}
void KillWall(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 KillSelf(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;
            break;
        }
        cur = cur->next;
    }
}

void Welcome()
{
    SetPos(40, 14);
    wprintf(L"欢迎来到贪吃蛇小游戏\n");

    SetPos(42, 20);
    system("pause");//暂停
    system("cls");//清空屏幕

    SetPos(25, 14);
    wprintf(L"用↑↓←→来操控蛇的移动方向,按F3加速,按F4减速\n");

    SetPos(25, 15);
    wprintf(L"加速能得到更高的分数\n");

    SetPos(42, 20);
    system("pause");
    system("cls");
}

//蛇的移动
void SnakeMove(pSnake ps)
{
    SnakeNode* pNextNode = (SnakeNode*)malloc(sizeof(SnakeNode));
    if (pNextNode == NULL)
    {
        perror("SnakeMove()::malloc()");
        return;
    }
    switch (ps->_dir)
    {
    case UP:
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y - 1;
        break;
    case DOWN:
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y + 1;
        break;

    case RIGHT:
        pNextNode->x = ps->_pSnake->x + 2;
        pNextNode->y = ps->_pSnake->y;
        break;
    case LEFT:

        pNextNode->x = ps->_pSnake->x - 2;
        pNextNode->y = ps->_pSnake->y;
        break;
    }
    if (JubgeNextIsFood(pNextNode, ps))//检测下一个坐标是否是食物
    {
        EatFood(pNextNode, ps);//第一个参数记录头的位置,要在函数里销毁
    }
    else
    {
        NoFood(pNextNode, ps);
    }
    //撞墙死亡
    KillWall(ps); 
    //撞到自己死亡
    KillSelf(ps);
}


void CreateMap()//创造墙
{
    //上
    for (int i = 0; i < 29; i++)
        wprintf(L"%lc", WALL);
    //下
    SetPos(0, 26);
    for (int i = 0; i < 29; i++)
        wprintf(L"%lc", WALL);
    //左

    for (int i = 1; i <= 25; i++)
    {
        SetPos(0, i);
        wprintf(L"%lc", WALL);
    }
    //右
    for (int i = 1; i <= 25; i++)
    {
        SetPos(56, i);
        wprintf(L"%lc", WALL);
    }
    //getchar();
}

void InitSnake(pSnake ps)//初始化蛇
{
    int i = 0;
    pSnakeNode cur = NULL;
    for (int i = 0; i < 5; i++)
    {
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake()::malloc()");
            return;
        }
        cur->next = NULL;
        cur->x = Pos_x + 2 * i;
        cur->y = Pos_y;

        //头插法插入链表
        if (ps->_pSnake == NULL)//空链表
        {
            ps->_pSnake = cur;
        }
        else //非空
        {
            cur->next = ps->_pSnake;
            ps->_pSnake = cur;
        }
    }
    cur = ps->_pSnake;
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", L'◆');
    cur = cur->next;
    int tmp = 0;

    while (cur)
    {
        tmp++;
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", L'●');
        cur = cur->next;
    }
    //printf("%d", tmp);
    //设置贪吃蛇的属性
    ps->_dir = RIGHT;//默认向右
    ps->_score = 0;//总成绩初始为0
    ps->_food_weight = 10;//食物的奖励分数
    ps->_sleep_time = 200;//蛇多久动一次,单位是毫秒
    ps->_status = OK;//蛇的状态是正常的
}

void CreateFood(pSnake ps)//随机生成食物
{
    int x = 0;
    int y = 0;

    //生成x是2的倍数
    //x: 2至54
    //y: 1至25
again:
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 25 + 1;

    } while (x % 2 != 0);
    //x和y的坐标不能和蛇的身体坐标冲突
    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()::malloc()");
        return;
    }

    pfood->x = x;
    pfood->y = y;
    pfood->next = NULL;
    SetPos(x, y);//定位食物位置
    wprintf(L"%lc", L'★');

    ps->_pFood = pfood;

}

void PrintHelpInfo()
{
    SetPos(64, 10);
    wprintf(L"%ls", L"不能穿墙,不能咬到自己");
    SetPos(64, 11);
    wprintf(L"%ls", L"用↑↓←→来控制蛇");
    SetPos(64, 12);
    wprintf(L"%ls", L"按F5加速,F6减速");
    SetPos(64, 13);
    wprintf(L"%ls", L"按ESC退出游戏,空格暂停游戏");
}
void GameRun(pSnake ps)
{
    //打印帮助信息
    PrintHelpInfo();
    do
    {
        //打印总分数和食物的分值
        SetPos(64, 10);
        printf("总分数:%d\n", ps->_score);//总分数
        SetPos(64, 11);
        printf("当前食物的分值:%2d\n", ps->_food_weight);//食物的分值,%2d防止分数减少后面的0不去除

        //蛇只能向当前方向的左右转向,不能朝后转向
        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_SPACE))
        {
            Pause();
        }
        else if (KEY_PRESS(VK_ESCAPE))
        {
            ps->_status = END_NORMAL;//游戏状态设置为退出
        }
        else if (KEY_PRESS(VK_F5))//加速
        {
            if (ps->_sleep_time > 80)
            {
                ps->_sleep_time -= 30;
                ps->_food_weight += 2;//速度增加食物分数增加
            }
        }
        else if (KEY_PRESS(VK_F6))//减速
        {
            if (ps->_sleep_time < 320)
            {
                ps->_sleep_time += 30;
                ps->_food_weight -= 2;
            }
        }

        SnakeMove(ps);//蛇的移动

        Sleep(ps->_sleep_time);

    } while (ps->_status == OK);

}
void GameEnd(pSnake ps)
{
    SetPos(60, 5);
    switch (ps->_status)
    {
    case END_NORMAL:
        printf("主动退出游戏\n");
        break;
    case KILL_BY_WALL:
        printf("撞到墙啦o(╥﹏╥)o\n");
        break;
    case KILL_BY_SELF:
        printf("撞到自己啦┭┮﹏┭┮\n");
        break;
    }
    //释放蛇身的链表
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        pSnakeNode tmp = cur;
        cur = cur->next;
        free(tmp);
        tmp = NULL;
    }


}

void GameStart(pSnake ps)
{
    //0. 设置窗口大小,隐藏光标
    system("mode con cols=100 lines=30");
    system("title 贪吃蛇");
    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    //隐藏光标操作 
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标状态
    CursorInfo.bVisible = false;//隐藏控制台光标
    SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态


    //1. 打印环境界面
    Welcome();
    //2. 绘制地图 
    CreateMap();
    //3. 创建蛇
    InitSnake(ps);
    //4. 创建食物
    CreateFood(ps);
}

三. 测试游戏文件

#include"snake.h"

//完成游戏测试逻辑
void test()
{
	int f = 1;
	srand((unsigned int)time(NULL));
	
	do
	{
		system("cls");
		//创建贪吃蛇
		Snake  snake = { 0 };

		//初始化游戏
		GameStart(&snake);
		//玩游戏
		GameRun(&snake);
		//结束游戏
		GameEnd(&snake);
		system("cls");
		SetPos(35, 14);
		printf("是否再来一局(Y/N):");
		while(1)
		{
			if (KEY_PRESS(78))
			{
				f = 0;
				break;
			}
			if (KEY_PRESS(89))
			{
				break;
			}
		}
	} while (f);
	SetPos(0, 27);//结束游戏的文字打印到下方
}

int main()
{
	setlocale(LC_ALL, "");
	test();
	return 0;
}

我好水............

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值