C语言——贪吃蛇游戏

news2024/9/28 19:26:04

用c语言在window环境的控制台中模拟写出贪吃蛇小游戏。

一.要实现的基本的功能

如下:

        贪吃蛇的地图绘制

        贪吃蛇吃食物的功能(方向控制蛇的动作)

        贪吃蛇撞墙死亡判断

        贪吃蛇撞自己死亡判断

        计算得分

        贪吃蛇的速度

        暂停游戏

 二 贪吃蛇的铺垫准备:

        1.控制台的窗口大小控制

                   system("mode con cols=250 lines=80");   cols (列) lines(行);

        2.控制台屏幕上的坐标COORD

            一般来说,控制台的输出一般是从左上角开始的,但是贪吃蛇的食物应该设置成随机出现,那我们需要先了解一下控制台屏幕上的坐标COORD:

        COORD是windowsAPI中的定义的一个结构体,表示一个字符在控制台屏幕缓冲区上的坐标,坐标系(0,0)的原点位于缓冲区的顶部左侧单元格。

COORD类型的声明:
typedef struct _COORD{
    SHORT X;
    SHORT Y;
  }COORD,_PCOORD;

给坐标赋值:

COORD pos={x,y};

        3.控制台光标隐藏

        在一般的情况下,程序结束后,控制台上总有一个光标在闪烁

        我们需要把它给隐藏掉。

//光标影藏掉
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标的操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

 这段代码的功能是为了把光标给隐藏掉。

        4.获取按键GetAsynKeyState

        这个函数是获取按键的情况。(用与获取贪吃蛇的方向控制)

        GetAsynKeyState的函数原型如下:

SHORT GetAsynKeyState(
        int vKey
  );

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

GetAsynKeyState的返回值是short类型,在上一次调用GetAsynKeyState函数后,如果返回的16位short数据中,最高位是1,说明按键的状态按下,如果是0,说明按键的状态是抬起;如果最低位置为1则说明该按键被安国,否则为0。

为了代码的简易性,这里把该函数给定义成宏来使用更简便:

# define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

 这里的意思是说,当键盘是按过状态的时候,返回1,没按下过的状态返回值为0

 这里用一个例子来简单说明一下:

如图:当我按下键盘1的时候,第1个if判断返回值为0,所以为假,跳转到第二个if条件判断

     第2个if里的判断为真,,所以打印了1.。。。

          当我再按下键盘2的时候,第1个和第2个if条件判断都为假,跳转到第三个if条件判断才为真,打印2.。。。

          当按下除了0;1;2;3这四个键之外的所有键,均为判断为假,不输出任何信息;

        5.设置程序适应本地环境

                setlocale(LC_ALL, "");      //点击跳转至setlocale函数讲解。

        6.食物刷新的随机坐标

                srand((unsigned int)time(NULL));  //点击跳转至srand函数讲解。

三.贪吃蛇游戏的理解

          1.贪吃蛇在c语言中可以理解为一条链表,通过不断的移动链表的方向与位置,把某个坐标食物“吃”掉,成为链表的一份子,在链表中的结构如下:

//贪吃蛇结点的描述
typedef struct SnakeNode
{
	//坐标
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, * pSnakeNode;

          2.游戏中我们还需要时刻通过键盘的按键来控制蛇的走向,所以这个时候,还需要一个变量状态来控制着蛇的方向,所以要枚举一个方向DIRECTION

enum DIRECTION
{
	UP = 1,    //上
	DOWN,      //下
	LEFT,      //左
	RIGHT      //右
};

          3.在这个过程中,我们需要绘制一个范围地图,比如:墙,或者障碍体,蛇每一次移动,都需要判断链表的头结点(贪吃蛇的头部)是否越界,或者触碰到障碍体与蛇头触碰到自己的身体而死掉,还要枚举一个GAME_STATUS来记录游戏输掉与结束的原因。

enum GAME_STATUS
{
	OK,//正常运行
	END_NORMAL,//按ESC退出
	KILL_BY_WALL,//触碰到墙体
	KILL_BY_SELF //触碰到身体
};

          4.如果想功能丰富些,还可以自定义添加得分机制,和蛇的移动速度自定义调节之类,添加闯关功能,添加障碍体等等。

通过上面分析,我们可以得知贪吃蛇大致的结构:

//贪吃蛇的结构
typedef struct Snake
{
	pSnakeNode _pSnake;//指向贪吃蛇头结点的指针
	pSnakeNode _pFood;//指向食物结点的指针
	int _Score;//贪吃蛇累计的总分
	int _FoodWeight;//一个食物的分数
	int _SleepTime;//每走一步休息的时间,时间越短,速度越快,时间越长,速度越慢
	enum DIRECTION _Dir;//描述蛇的方向
	enum GAME_STATUS _Status;//游戏的状态:正常、退出、撞墙、吃到自己
}Snake, * pSnake;

四.贪吃蛇的代码:

        接下来就是关于游戏的代码写法了。

void test()
{
	int ch = 0;
	do
	{
		Snake snake = { 0 };//创建了贪吃蛇

		//1. 游戏开始 - 初始化游戏
		GameStart(&snake);

		//2. 游戏运行 - 游戏的正常运行过程
		GameRun(&snake);

		//3. 游戏结束 - 游戏善后(释放资源)
		GameEnd(&snake);

		SetPos(20, 18);
		printf("再来一局吗?(Y/N):");
		ch = getchar();
		getchar();// 清理掉\n
	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 27);
}

int main()
{
	//设置程序适应本地环境
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	test();	
	return 0;
}

先来一个大概的游戏入口test函数

test()函数里包含了贪吃蛇的游戏内容

总体分为三部分:

                //1. 游戏开始 - 初始化游戏
                   GameStart(&snake);

                //2. 游戏运行 - 游戏的正常运行过程
                   GameRun(&snake);

                //3. 游戏结束 - 游戏善后(释放资源)
                   GameEnd(&snake);

这三个函数里面各分别还需要实现一些小功能的函数

这里先实现一下 GameStart(&snake)这个函数的各个功能并讲解:

1. GameStart(&snake) :

void GameStart(pSnake ps)
{
	//控制台窗口的设置
	system("mode con cols=250 lines=80");  //控制台的窗口大小
	system("title 梁丶贪吃蛇");             //控制台上面的名字命名

	//光标影藏掉
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//影藏光标操作
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false; //隐藏控制台光标
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

	//打印游戏玩法界面
	WelComeToGame();
	//创建地图
	CreateMap();
	//初始化贪食蛇
	InitSnake(ps);
	//创建食物
	CreateFood(ps);
}

1.1 //打印游戏玩法界面——WelComeToGame();

void WelComeToGame()
{
	//定位光标
	SetPos(115, 35);
	printf("欢迎来到贪吃蛇小游戏");
	SetPos(115, 36);
	system("pause");//pause是暂停
	system("cls");
	SetPos(100, 35);
	printf("使用 ↑ . ↓ . ← . → . 分别控制蛇的移动, F3是加速,F4是减速");
	SetPos(115, 36);
	system("pause");
	system("cls");
}

这里的函数功能实现的效果如下:

SetPos()为自己自定义的函数,他的功能是指定输出的位置坐标,

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

简单来说就是在指定的位置输出信息。

1.2 //打印墙体——void CreateMap();

如图红色标记的就是所谓的墙体,这里的墙体显示是使用多个 ”口“ 符号来简单设置的。

要在控制台上打印该”墙体“需要用到之前的setpos()函数,

简单的理解就是使用for循环在每个坐标的位置打印一个”口“;

//打印地图
void CreateMap()
{                        
	//上
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i <= 200; i += 2)  //这里的墙体大小随意改,不一定要200,合适就好
	{
		wprintf(L"%lc", WALL); //注:#define WALL L'□'
	}                          
	//下
	SetPos(0, 70);
	for (i = 0; i <= 200; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (i = 1; i < 70; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (i = 1; i < 70; i++)
	{
		SetPos(200, i);
		wprintf(L"%lc", WALL);
	}
}

1.3 //初始化贪食蛇——InitSnake(ps);

设置完地图,现在要打造一下蛇的身体了

//初始化贪吃蛇
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;   //游戏开始的时候,我们默认一下蛇的身体有五个节点
	for (int i = 0; i < 5; i++)    //在此循环创建五个节点,并相连成链表
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake():malloc err...");
			return;
		}
		cur->x = POS_X + i * 2;      //pos_x和pos_y为贪吃蛇头的节点位置,
		cur->y = POS_Y;              //在头文件里会define一下
		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"%1c", BODY);      //注:#define BODY L'●'
		cur = cur->next;
	}

	ps->_Status = OK;        //游戏的状态:正常、退出、撞墙、吃到自己
	ps->_Score = 0;          //贪吃蛇累计的总分
	ps->_pFood = NULL;       //指向食物结点的指针
	ps->_SleepTime = 200;    //每走一步休息的时间,时间越短,速度越快,时间越长,速度越慢
	ps->_FoodWeight = 10;    //一个食物的分数
	ps->_Dir = RIGHT;        //描述蛇的方向
}

 这里是把游戏的初期给初始化

如图中的位置,贪吃蛇的位置不一定是固定的,也可自己更改调试。

1.4 //创建食物——CreateFood(ps);

把贪吃蛇初始化后,接下来就要把食物给创建出来了。

 void CreateFood(pSnake ps)
{
	int x = 0;            //创建两个坐标变量x与y;
	int y = 0;
tmp:
	do
	{
		x = rand() % 195 + 4;    //因为我的创建的墙体宽度是200,所以为了保证食物坐标在墙体内
		y = rand() % 66 + 2;     //所以需要%195,y坐标同理(要注意墙体的位置,不要与墙体重叠)
	} while (x % 2 != 0);        //这里的判断结束条件是因为单个墙体”口“的宽度是2,注意~
	//不能与蛇身重复
	pSnakeNode cur = ps->_pSnake;    //创建一个新节点赋予蛇的头结点
	while (cur)                      //要遍历一下该节点的坐标与蛇的身体坐标有没有重叠
	{                                //重叠的话就用goto语句重新生成一个食物节点坐标
		if (cur->x == x && cur->y == y)    
		{
			goto tmp;
		}
		cur = cur->next;
	}
              //当没有重叠的时候,创建一个新节点,赋予x和y的坐标值建立新节点并打印食物标志☆
	pSnakeNode set = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (set == NULL)
	{
		perror("creatrfood():err...");
		return;
	}
	set->x = x;
	set->y = y;
	ps->_pFood = set;
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);        打印食物 “☆”
}

2.GameRun(&snake);

这里就需要运用到GetAsynKeyState(文章铺垫有讲解)

void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintHelpInfo();        //这里是为了贴心点,打印一下玩法信息讲解。
	do
	{
		SetPos(205, 10);      //这些坐标位置是需要自己去选取合适的位置
		printf("得分:%05d", ps->_Score);
		SetPos(205, 12);
		printf("每个食物的分数:%2d", ps->_FoodWeight);

		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 = END_NORMAL;
			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 < 320)
			{
				ps->_SleepTime += 30;
				ps->_FoodWeight -= 2;
			}
		}

		Sleep(ps->_SleepTime);    //系统库函数sleep(),休眠毫秒数
		SnakeMove(ps);        
	} while (ps->_Status == OK);
      //循环条件为ps->status,当这里的状态为ok的时候,游戏正常继续运行,
      //当为其他状态的时候,说明游戏已经结束(输了),结束循环break。选择继续或下一把!

}

   2.1 //打印帮助信息——PrintHelpInfo(); 

文本打印的位置可以靠setpos(x,y)函数来更改合适的位置。

//打印帮助信息
void PrintHelpInfo()
{
	SetPos(205, 35);
	printf("1.不能撞墙,不能咬到自己");
	SetPos(205, 37);
	printf("2.使用 ↑.↓.←.→ 分别控制蛇的移动");
	SetPos(205, 39);
	printf("3.F3加速,F4减速");
	SetPos(205, 41);
	printf("4.ESC-退出, 空格-暂停游戏");

	SetPos(205, 43);
	printf("凉凉~");
}

2.2 //解除暂停状态——void Pause() 

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

这里使用一个死循环,当再次按下空格键的时候,便结束循环。 

2.2 //控制蛇的方向并判断食物——void SnakeMove(pSnake ps)

void SnakeMove(pSnake ps)
{
	pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (cur == NULL)
	{
		perror("snakemove( ):err...");
		return;
	}
	cur->next = NULL;    //创建一个新节点,用于存储蛇的下一个方向移动的信息。
	switch (ps->_Dir)    //根据蛇的方向信息判断下一个节点蛇的位置从而更改蛇节点的坐标x和y
	{
	case UP:
		cur->x = ps->_pSnake->x;
		cur->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		cur->x = ps->_pSnake->x;
		cur->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		cur->x = ps->_pSnake->x - 2;
		cur->y = ps->_pSnake->y;
		break;
	case RIGHT:
		cur->x = ps->_pSnake->x + 2;
		cur->y = ps->_pSnake->y;
		break;
	}

	//判断蛇头到达的坐标处是否是食物
	if (NextIsFood(ps, cur))
	{
		//吃掉食物
		EatFood(ps, cur);
		//CreateFood(ps);
	}
	else
	{
		//不吃食物
		NoFood(ps, cur);
	}
	//蛇是否撞墙
	KillByWall(ps);

	//蛇是否自杀
	KillBySelf(ps);
}
2.2.1 //判断蛇头到达的坐标处是否是食物——NextIsFood(ps, cur)
int NextIsFood(pSnake ps, pSnakeNode pnext)
{
	if (ps->_pFood->x == pnext->x && ps->_pFood->y == pnext->y)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

用食物节点与新节点cur比较。

这里分两种情况:

        1,当坐标的x与y相等时,这时候就说明贪吃蛇”吃到“食物为真

              返回 1 .并执行下一步EatFood( )

        吃到食物——EatFood()
//吃掉食物
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"%1c", BODY);
		cur = cur->next;
	}
	//free掉食物节点;
	free(ps->_pFood);
	ps->_Score += ps->_FoodWeight;
	//CreateFood(ps);
	CreateFood(ps);
}

        2. 反之没有”吃到“食物为假,并返回 0 不执行EatFood(),代表这个位置没吃到食物,继续往下走。

        没有吃到食物——void NoFood ( )
//不吃食物
void NoFood(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"%1c", BODY);
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//pSnakeNode fp = cur->next;
	//cur->next = NULL;
	free(cur->next);
	cur->next = NULL;
}

判断完是否吃到食物后,就需要判断贪吃蛇本体是否撞墙,或者撞到自己的身体部分了

        判断蛇是否撞墙——KillByWall(ps);
//蛇是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnake->x == 0 ||
		ps->_pSnake->x == 200 ||
		ps->_pSnake->y == 0 ||
		ps->_pSnake->y == 69)
		ps->_Status = KILL_BY_WALL; //当if为真的时候,把status状态更改为kill_by——wall
                                    //打印游戏结束后的判断条件
}
        蛇是否自杀——void KillBySelf(pSnake ps)
//蛇是否自杀
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y)
		{
			ps->_Status = KILL_BY_SELF;
		}
		cur = cur->next;    //遍历节点,当与身体相撞时更改status状态;
	}
}

3.GameEnd(pSnake ps)

当编程走到这说明游戏已经结束。

现在要打印一下输赢状态与获取分数等数据信息并释放一下蛇的节点内存

void GameEnd(pSnake ps)
{
	SetPos(20, 12);
	switch (ps->_Status)
	{
	case END_NORMAL:
		printf("您主动退出游戏\n");
		break;
	case KILL_BY_SELF:
		printf("自杀了,游戏结束\n");
		break;
	case KILL_BY_WALL:
		printf("撞墙了,游戏结束\n");
		break;
	}
	//释放蛇身的结点
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	ps->_pSnake = NULL;

}

贪吃蛇小游戏就结束了!

游戏比较简单,还可以添加许多玩法。

增加障碍物

循环地图

添加生命次数

等。。。。。。。

 以下为全代码:

//#pragma once

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


#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'

#define POS_X 24
#define POS_Y 5

#define KEY_PRESS(VK)  ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

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

enum GAME_STATUS
{
	OK,//正常运行
	END_NORMAL,//按ESC退出
	KILL_BY_WALL,
	KILL_BY_SELF
};


//贪吃蛇结点的描述
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 DIRECTION _Dir;//描述蛇的方向
	enum GAME_STATUS _Status;//游戏的状态:正常、退出、撞墙、吃到自己
}Snake, * pSnake;


//游戏开始 - 完成游戏的初始化动作		1
void GameStart(pSnake ps);

//定位坐标
void SetPos(short x, short y);

//游戏开始的欢迎界面					1
void WelComeToGame();

//打印地图                               1
void CreateMap();

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

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

//游戏的正常运行						1
void GameRun(pSnake ps);

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


//游戏暂定和恢复						1
void Pause();

//蛇的移动								1
void SnakeMove(pSnake ps);

//判断蛇头到达的坐标处是否是食物		1
int NextIsFood(pSnake ps, pSnakeNode pnext);


//吃掉食物							1
void EatFood(pSnake ps, pSnakeNode pnext);

//不吃食物						1
void NoFood(pSnake ps, pSnakeNode pnext);

//蛇是否撞墙
void KillByWall(pSnake ps);

//蛇是否自杀
void KillBySelf(pSnake ps);

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

#define _CRT_SECURE_NO_WARNINGS 1

#include "sneke.h"

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

void WelComeToGame()
{
	//定位光标
	SetPos(115, 35);
	printf("欢迎来到贪吃蛇小游戏");
	SetPos(115, 36);
	system("pause");//pause是暂停
	system("cls");
	SetPos(100, 35);
	printf("使用 ↑ . ↓ . ← . → . 分别控制蛇的移动, F3是加速,F4是减速");
	SetPos(115, 36);
	system("pause");
	system("cls");
}


//打印地图
void CreateMap()
{
	//上
	SetPos(0, 0);
	int i = 0;
	for (i = 0; i <= 200; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 70);
	for (i = 0; i <= 200; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (i = 1; i < 70; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (i = 1; i < 70; i++)
	{
		SetPos(200, i);
		wprintf(L"%lc", WALL);
	}
}


//初始化贪吃蛇
void InitSnake(pSnake ps)
{
	//pSnake cur = (pSnake)malloc(sizeof(Snake));
	pSnakeNode cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake():malloc err...");
			return;
		}
		cur->x = POS_X + i * 2;
		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"%1c", BODY);
		cur = cur->next;
	}
	ps->_Status = OK;
	ps->_Score = 0;
	ps->_pFood = NULL;
	ps->_SleepTime = 200;
	ps->_FoodWeight = 10;
	ps->_Dir = RIGHT;
}

void CreateFood(pSnake ps)
{
	int x = 0;
	int y = 0;
tmp:
	do
	{
		x = rand() % 195 + 4;
		y = rand() % 66 + 2;
	} while (x % 2 != 0);
	//不能与蛇身重复
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto tmp;
		}
		cur = cur->next;
	}

	pSnakeNode set = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (set == NULL)
	{
		perror("creatrfood():err...");
		return;
	}
	set->x = x;
	set->y = y;
	ps->_pFood = set;
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

void GameStart(pSnake ps)
{
	//控制台窗口的设置
	system("mode con cols=250 lines=80");
	system("title 梁丶贪吃蛇");

	//光标影藏掉
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//影藏光标操作
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false; //隐藏控制台光标
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

	//打印欢迎界面
	WelComeToGame();
	//创建地图
	CreateMap();
	//初始化贪食蛇
	InitSnake(ps);
	//创建食物
	CreateFood(ps);
}



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"%1c", BODY);
		cur = cur->next;
	}
	//free掉食物节点;
	free(ps->_pFood);
	ps->_Score += ps->_FoodWeight;
	//CreateFood(ps);
	CreateFood(ps);
}


//不吃食物
void NoFood(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"%1c", BODY);
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//pSnakeNode fp = cur->next;
	//cur->next = NULL;
	free(cur->next);
	cur->next = NULL;
}


//蛇是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnake->x == 0 ||
		ps->_pSnake->x == 200 ||
		ps->_pSnake->y == 0 ||
		ps->_pSnake->y == 69)
		ps->_Status = KILL_BY_WALL;
}

//蛇是否自杀
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y)
		{
			ps->_Status = KILL_BY_SELF;
		}
		cur = cur->next;
	}
}



void SnakeMove(pSnake ps)
{
	pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (cur == NULL)
	{
		perror("snakemove( ):err...");
		return;
	}
	cur->next = NULL;
	switch (ps->_Dir)
	{
	case UP:
		cur->x = ps->_pSnake->x;
		cur->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		cur->x = ps->_pSnake->x;
		cur->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		cur->x = ps->_pSnake->x - 2;
		cur->y = ps->_pSnake->y;
		break;
	case RIGHT:
		cur->x = ps->_pSnake->x + 2;
		cur->y = ps->_pSnake->y;
		break;
	}

	//判断蛇头到达的坐标处是否是食物
	if (NextIsFood(ps, cur))
	{
		//吃掉食物
		EatFood(ps, cur);
		//CreateFood(ps);
	}
	else
	{
		//不吃食物
		NoFood(ps, cur);
	}
	//蛇是否撞墙
	KillByWall(ps);

	//蛇是否自杀
	KillBySelf(ps);
}


void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintHelpInfo();
	do
	{
		SetPos(205, 10);
		printf("得分:%05d", ps->_Score);
		SetPos(205, 12);
		printf("每个食物的分数:%2d", ps->_FoodWeight);

		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 = END_NORMAL;
			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 < 320)
			{
				ps->_SleepTime += 30;
				ps->_FoodWeight -= 2;
			}
		}

		Sleep(ps->_SleepTime);
		SnakeMove(ps);
	} while (ps->_Status == OK);

}
//打印帮助信息
void PrintHelpInfo()
{
	SetPos(205, 35);
	printf("1.不能撞墙,不能咬到自己");
	SetPos(205, 37);
	printf("2.使用 ↑.↓.←.→ 分别控制蛇的移动");
	SetPos(205, 39);
	printf("3.F3加速,F4减速");
	SetPos(205, 41);
	printf("4.ESC-退出, 空格-暂停游戏");

	SetPos(205, 43);
	printf("凉凉~");
}
void GameEnd(pSnake ps)
{
	SetPos(20, 12);
	switch (ps->_Status)
	{
	case END_NORMAL:
		printf("您主动退出游戏\n");
		break;
	case KILL_BY_SELF:
		printf("自杀了,游戏结束\n");
		break;
	case KILL_BY_WALL:
		printf("撞墙了,游戏结束\n");
		break;
	}
	//释放蛇身的结点
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
	ps->_pSnake = NULL;

}

#define _CRT_SECURE_NO_WARNINGS 1

#include "sneke.h"


void test()
{
	int ch = 0;
	do
	{
		Snake snake = { 0 };//创建了贪吃蛇

		//1. 游戏开始 - 初始化游戏
		GameStart(&snake);

		//2. 游戏运行 - 游戏的正常运行过程
		GameRun(&snake);

		//3. 游戏结束 - 游戏善后(释放资源)
		GameEnd(&snake);

		SetPos(20, 18);
		printf("再来一局吗?(Y/N):");
		ch = getchar();
		getchar();// 清理掉\n
	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 27);
}

int main()
{
	//设置程序适应本地环境
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	test();
	return 0;
}





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

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

相关文章

【HTML入门】第二十二课 - 【实战】做一个注册页面

这一小节&#xff0c;我们继续纯HTML标签的使用&#xff0c;这一节我们做一个 注册页面。说到注册页面&#xff0c;其实大家的网站都大同小异&#xff0c;所以我们就不四处去找设计图了&#xff0c;我们自己想一个注册页面&#xff0c;想想注册页面都应该有哪些元素&#xff0c…

探究全局代理是什么?有什么作用?

今天&#xff0c;我们来聊聊一个经常被提及但很多人不太了解的概念——全局代理。那么&#xff0c;全局代理到底是什么&#xff1f;它有什么用&#xff1f;以及如何正确使用呢&#xff1f;接下来&#xff0c;小编就带大家一探究竟&#xff01; 什么是全局代理&#xff1f; 通俗…

360联合国内15个最强大模型推出“AI助手”碾压GPT4o!全球首部《人工智能法案》正式生效!|AI日报

文章推荐 GPT-4o版「Her」终于来了&#xff01;英伟达股价两周内下跌23%&#xff01;&#xff5c;AI日报 Apple Intelligence将在今年10月推出&#xff1f;AI毛绒玩具BubblePal回答人类幼崽十万个为什么&#xff01;&#xff5c;AI日报 今日热点 360放大招&#xff1a;联合…

Rodin来袭,AI建模再填猛将

AI建模&#xff0c;AI绘图&#xff0c;Rodin,人工智能&#xff0c;Stable Diffusion, Stable Diffusion,midjourney,Bing AI等人工智能在绘图界掀起腥风血雨&#xff0c;外界一致认为将取代原画师&#xff0c;但是真正的原画师却感觉良好&#xff0c;他们认为&#xff1a;“AI…

Unable to clear output directory

Bug信息 Exception in thread "main" java.io.IOException: Unable to clear output directory hdfs://xxx/warehouse/hive/xx.db/xxx prior to writing to itat org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.deleteMatchingPart…

“智绘未来,慧聚南京”2024南京智博会

“智慧城市”一个听起来颇具未来感的词汇&#xff0c;在如今的南京市已逐步成为现实。在5G、物联网、人工智能、大数据等新一代信息技术的支撑下&#xff0c;南京市正逐渐变成一个可感知可“思考”的智能体&#xff0c;智慧社区、智慧医疗、智慧交通、智慧政务等数字化、智能化…

Flink-StarRocks详解:第六部分-即席查询大案例解析(第56天)

系列文章目录 数仓场景&#xff1a;即席查询案例 6.1 场景介绍 6.2 方案架构 6.3 方案特点 6.4 操作流程 6.4.1 步骤一&#xff1a;创建MySQL源数据表 6.4.2 步骤二&#xff1a;创建StarRocks表 6.4.3 步骤三&#xff1a;执行Flink任务&#xff0c;启动数据流 6.4.4 步骤四&am…

gemini2相机和宇树雷达L1的使用注意点

gemini2相机&#xff1a; 官方资料:Gemini2深度相机 (yahboom.com) 目前深度这一块智能提供某一点的深度数据&#xff0c;没有提供某一点的世界坐标&#xff0c;虽然网上有文章说是可以计算 已知深度图&#xff0c;获得某个像素点的三维坐标_深度图如何知道特征点的3d坐标-CS…

Windows系统下安装mujoco环境的教程【原创】

在学习Mujoco仿真的过程中&#xff0c;我先前是在linux系统下进行的研究与学习&#xff0c;今天来试试看在windows系统中安装mujoco仿真环境。 先前在linux中的一些关于mujoco学习记录的博客&#xff1a;Mujoco仿真【xml文件的学习 3】_mujoco打开xml文件-CSDN博客 下面开始wi…

怎么防止电脑上的重要视频被录屏

加密软件防录屏是通过一系列技术手段来防止视频内容在播放过程中被非法录制和传播。 一、视频加密技术 高强度加密算法&#xff1a;使用如AES256位等高强度加密算法对视频内容进行加密&#xff0c;确保视频在传输和存储过程中的安全性。这样&#xff0c;即使视频被录制&#…

黑科技,教你同时在一个电脑上同时开启多个微信

我们右键微信图标选择属性 然后 将这个目标复制下来 然后 我们新建一个文本文件 然后 在记事本中 输入 start 后面跟着微信目标内容 但是需要注意 双引号要换一下位置 换到盘符后面 例如 我的目标是 “D:\Program Files (x86)\Tencent\WeChat\WeChat.exe” 但是 我要输入 st…

第19课 Scratch入门篇:摇骰子

摇骰子 &#x1f3b2; 故事背景&#xff1a; 有一颗神奇的骰子&#xff0c;随机变换不同的样子&#xff0c;让我们一起来设计一款属于自己的骰子&#xff0c;和自己身边的人比比&#xff0c;看看谁的数字大&#xff01; 程序原理&#xff1a; 骰子数字的变化&#xff0c;只要…

javascript 的奇技巧淫一

1 、使用 !! 转换为布尔值 // 使用双重否定快速将任何值转换为布尔值。 let a !!1; // true let b !!0; // false2 、 短 If-Else 的三元运算符\ let price 100; let message price > 200 ? "Expensive" : "Cheap";3、 默认函数参数 // 设置函数…

引领未来:埃隆·马斯克与拉里·佩奇论道企业开放合作的创新之路——张驰咨询

开放式创新是企业在产品研发和商业化活动中&#xff0c;充分整合全社会的智力、资源&#xff0c;以最大限度调动社会各界智力资源&#xff0c;从而实现互利共赢的一种创新模式。它打破了传统封闭式创新的界限&#xff0c;使企业能够像使用内部资源一样借用外部资源进行创新。 …

字体文件的引用与使用 -------css

1、字体文件的全局引用&#xff08;app.vue的<style>&#xff09; <style> font-face {font-family: "PingFangSC";src: url("./assets/fonts/PingFangSC.ttf");font-weight: normal;font-style: normal; } </style> 2、字体文件在组件…

算法力扣刷题记录 六十五【17.电话号码的字母组合】

前言 回溯章节第三篇&#xff1a;前两篇做了两道组合题目&#xff0c;练习回溯模版。 本文 记录 六十五【17.电话号码的字母组合】。 一、题目阅读 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映…

大数据应用【大数据导论】

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 目录 大数据在许多领域应用互联网领域应用生物医学…

【微服务】Spring Cloud 服务网关之Zuul

文章目录 强烈推荐引言用途使用情况使用场景基本使用示例总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站:人工智能 引言 服务网关&#xff08;AP…

手撕算法题4(附思路和源码)

算法 1.单值二叉树2.相同的树3.另一棵树的子树4.二叉树的前序遍历5.二叉树的中序遍历6.二叉树的后序遍历7.二叉树遍历8.TopK问题 1.单值二叉树 单值二叉树 思路 比较父节点和子结点&#xff0c;相同返回true&#xff0c;否则返回false&#xff0c;递归 设计程序 若结点为空返回…

本地部署 faster-whisper

本地部署 faster-whisper 1. 创建虚拟环境2. 安装依赖模块3. 创建 Web UI4. 启动 Web UI5. 访问 Web UI 1. 创建虚拟环境 conda create -n faster-whisper python3.11 -y conda activate faster-whisper2. 安装依赖模块 pip install torch2.2.2 torchvision0.17.2 torchaudio…