数据结构大作业:纯C双链表实现贪吃蛇

本博客分享了一款18年数据结构课程的贪吃蛇大作业代码,使用C语言在Windows环境下实现,包括游戏逻辑、界面绘制、用户交互等功能。游戏设有等级积分系统,玩家可通过吃食物提升等级,同时提供了不同速度模式供玩家选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整理电脑文件资料,发现的自己18年(大一下学期)写的数据结构大作业贪吃蛇。时光流逝,转眼间2年就过去了。

老师意见:有存档功能,就能满分了。

看代码,借思路,独自实现,成长自己。

游戏效果图如下:

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
#include<time.h>

#define Wid 40
#define Hei 20
int speed;//蛇的速度
//蛇的节点 
typedef struct Snake_node{
	COORD pos;
	struct Snake_node *next;
	struct Snake_node *prev;
}Snake,*PSnake;
PSnake Head;//蛇的头指针 
PSnake Tail;//蛇的尾指针 

struct Snake_pos{
	COORD pos;
	int dir;
}HeadPos;//蛇头即将移动的方向和坐标 

COORD Food;//食物坐标
int score;//积分记录 
//光标定位 
void GetPosition(COORD pos)
{
	HANDLE hout=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hout,pos);
}
//游戏玩家积分等级输出 
void GameLevel()
{
	COORD pos_1={Wid+2,11}; 
	GetPosition(pos_1);
	if(score>=70)
	{
		printf("当前游戏等级:恭喜你登顶了"); 
	}
	else if(score>=60)
	{
		printf("当前游戏等级:最强蛇人2颗心"); 
	}
	else if(score>=55)
	{
		printf("当前游戏等级:最强蛇人1颗心"); 
	}
	else if(score>=50)
	{
		printf("当前游戏等级:一代宗师3颗心"); 
	}
	else if(score>=45)
	{
		printf("当前游戏等级:一代宗师2颗心"); 
	}
	else if(score>=40)
	{
		printf("当前游戏等级:一代宗师1颗心"); 
	}
	else if(score>=36)
	{
		printf("当前游戏等级:炉火纯青3颗心"); 
	}
	else if(score>=32)
	{
		printf("当前游戏等级:炉火纯青2颗心"); 
	}
	else if(score>=28)
	{
		printf("当前游戏等级:炉火纯青1颗心"); 
	}
	else if(score>=24)
	{
		printf("当前游戏等级:马马虎虎3颗星");
	} 
	else if(score>=19)
	{
		printf("当前游戏等级:马马虎虎2颗星"); 
	}
	else if(score>=16)
	{
		printf("当前游戏等级:马马虎虎1颗星"); 
	}
	else if(score>=13)
	{
		printf("当前游戏等级:初学乍练3颗星"); 
	}
	else if(score>=7)
	{
		printf("当前游戏等级:初学乍练2颗星"); 
	}
	else if(score>=0)
	{
		printf("当前游戏等级:初学乍练1颗星"); 
	}	
}
//添加蛇的头节点 
void AddSnakeHead(COORD pos)
{
	GetPosition(pos);//光标定位 
	printf("◆");//打印蛇头
	//双链表操作,开辟结点,储存坐标,头插法插入,考虑实际情况,头部的前指针不用置空 
	PSnake q=(PSnake)malloc(sizeof(Snake));
	q->pos=pos;
	if(Head==NULL&Tail==NULL)
	{
		Head=Tail=q;
		q->next=NULL;
	}
	else
	{
		q->next=Head;
		Head->prev=q;
		Head=q;
	}
	//输出头部的位置 
	COORD pos_1={Wid+2,2};
	GetPosition(pos_1);
	printf("              ");//涉及循环打印信息,空格覆盖,防止信息错乱 
	GetPosition(pos_1);
	printf("蛇头位置:%d行 %d列",pos.Y,pos.X/2);
}
//删掉蛇的尾节点 
void ReduceSnakeTail()
{
	GetPosition(Tail->pos);//光标定位到尾部 
	printf("  ");//输出空格覆盖 
	Tail=Tail->prev;//尾指针前 
	free(Tail->next);//释放尾部空间 
	Tail->next=NULL;//新的尾部的后指针置空 
}
//蛇的移动 
void MoveSnake(COORD pos)
{
	AddSnakeHead(pos);//增加头结点 
	ReduceSnakeTail();//删除尾结点 
}
//方向控制 
void ChangeDir()
{
	//仅有蛇头时可以向四周转换方向,当有蛇身时不能突然直接转向 
	COORD pos_1={Wid+2,4};
	int flag=0;//因为下面用的while循环,所以设置此标记,保证方向的单次改变 (如果没有会出现BUG,直接掉头)
	while(_kbhit())
	{
		switch(getch())
		{
			case 72:
				if(!(HeadPos.dir==1&&score>1)&&flag==0)//1.保证单次方向改变 2.仅有头可以四周改变方向 3.有了身体不能直接转变成相反方向 
				{
					HeadPos.dir=0;
					flag=1;
				}
				break;
			case 80:
				if(!(HeadPos.dir==0&&score>1)&&flag==0)
				{
					HeadPos.dir=1;
					flag=1;
				}
				break;
			case 75:
				if(!(HeadPos.dir==3&&score>1)&&flag==0)
				{
					HeadPos.dir=2;
					flag=1;
				}
				break;
			case 77:
				if(!(HeadPos.dir==2&&score>1)&&flag==0)
				{
					HeadPos.dir=3;
					flag=1;
				}
				break;
			case 32:
				pos_1.Y=8;
				GetPosition(pos_1);
				system("pause");
				GetPosition(pos_1);
				printf("                   ");
				GetPosition(pos_1);
				printf("按空格键暂停游戏");
				break; 
			case '5':
				speed=50;
				GetPosition(pos_1);
				printf("当前模式:噩梦");
				break; 
			case '4':
				speed=100;
				GetPosition(pos_1);
				printf("当前模式:困难");
				break;
			case '3':
				speed=150;
				GetPosition(pos_1);
				printf("当前模式:一般");
				break;
			case '2':
				speed=200;
				GetPosition(pos_1);
				printf("当前模式:简单");
				break;
			case '1':
				speed=400;
				GetPosition(pos_1);
				printf("当前模式:蜗牛");
				break;
			default:
				break;
		};
	}
	switch (HeadPos.dir)
	{
		case 0:
			HeadPos.pos.Y--;
			break;
		case 1:
			HeadPos.pos.Y++;
			break;
		case 2:
			HeadPos.pos.X-=2;
			break;
		case 3:
			HeadPos.pos.X+=2;
			break;
	}
}
//划出游戏边框 
void DrawBorder()
{
	//边框打印 
	COORD pos;
	for(pos.Y=0;pos.Y<=Hei-1;pos.Y++)
	{
		GetPosition(pos);
		for(pos.X=0;pos.X<=Wid-2;pos.X+=2)
		{
			if(pos.Y==0||pos.Y==Hei-1||pos.X==0||pos.X==Wid-2)
			{
				printf("■");
			}
			else
			{
				printf("  ");
			}
		}
		pos.X=0;
	}
	
	//边框右侧部分信息打印 
	COORD pos_1={Wid+2,4};
	GetPosition(pos_1);
	printf("模式选择提示:按键1~5分别对应傻瓜、简单、一般、困难、噩梦");
	pos_1.Y++;
	GetPosition(pos_1);
	printf("游戏过程中,模式可以随意切换,请君量力而行!");
	pos_1.Y++;
	GetPosition(pos_1);
	printf("当前模式:一般");
	speed=150;
	pos_1.Y=8;
	GetPosition(pos_1);
	printf("按空格键暂停游戏"); 
	pos_1.Y=0;
	GetPosition(pos_1);
	printf("贪吃蛇游戏");
	pos_1.Y=12; 
	GetPosition(pos_1);
	printf("想要知道最高等级是什么吗?那就继续玩游戏吧!");
	pos_1.Y=14; 
	GetPosition(pos_1);
	printf("蛇头方向控制:↑↓←→");
	pos_1.Y=15; 
	GetPosition(pos_1);
	printf("大兄弟,祝你玩的开心!O(∩_∩)O");
	pos_1.Y=Hei-3;
	GetPosition(pos_1);
	printf("开发者信息:信安1704-张鹏-学号xxxxxxxx"); 
	
}
//游戏结束判断 
int GameOver()
{
	if(HeadPos.pos.X<=0||HeadPos.pos.X>=Wid-2||HeadPos.pos.Y<=0||HeadPos.pos.Y>=Hei-1)//撞墙 
	{
		return 1;
	}
	PSnake q=Head;
	while(q!=NULL)
	{
		if(HeadPos.pos.X==q->pos.X&&HeadPos.pos.Y==q->pos.Y)//蛇食自身 
		{
			return 1;
		}
		q=q->next;
	}
	return 0;
}
//食物和积分信息输出 
void FoodInfo()
{
	COORD pos={Wid+2,1};
	GetPosition(pos);
	printf("              ");//分数多次刷新,所需要用空格刷掉前面输出的文字,防止接下来输出混乱排版 
	GetPosition(pos);
	printf("食物位置:%d行 %d列",Food.Y,Food.X/2);
	pos.Y=9;
	GetPosition(pos);
	printf("当前得分:%d",score++);
}
//初始化食物 
int InitFood()
{
	srand((unsigned) time(NULL));//利用系统时间来改变系统的种子值
	while(1)
	{
		//用取余法控制食物随机出现的位置的范围(保证出现在墙内) 
		Food.X=(rand()%(Wid/2-2)+1)*2; 
		Food.Y=rand()%(Hei-2)+1; 
		int flag=0;//flag为标志,初始化为0,表示假设食物位置不与蛇身重合 
		PSnake q=Head;
		while(q!=NULL)//从头到尾遍历检查 
		{
			if(Food.X==q->pos.X&&Food.Y==q->pos.Y)//判断随机获得的食物位置是否与蛇身重合 
			{
				flag=1;
				break; //如果重合,提前结束循环,重新获取随机食物位置 
			}
			q=q->next;
		}
		if(flag==0)//假设成立跳出循环, 
		{
			break;
		}
	}
	
	GetPosition(Food);//光标定位 
	printf("●");//打印食物 
	GameLevel();//输出玩家积分所达到的等级 
	FoodInfo();//输出食物位置信息 
}
//考虑到内存问题,链表空间释放(重新开始游戏需要用到) 
void DestroySnake()
{
	while(Tail!=Head)//释放蛇身节点 
	{
		Tail=Tail->prev;
		free(Tail->next);
	}
	free(Tail);//释放蛇头节点 
}
//游戏数据初始化 
void InitGame()
{
	system("color 0E");//控制台颜色控制 
	DrawBorder();
	//初始化蛇出现的位置(中间),默认方向向上 
	HeadPos.pos.X=Wid/2;
	HeadPos.pos.Y=Hei/2;
	HeadPos.dir=0;//0:向上 1:向下 2:向左 3:向右 
	//初始化蛇头 
	Head=NULL;
	Tail=NULL; 
	AddSnakeHead(HeadPos.pos);
	//初始化分数和食物 
	score=0;
	InitFood();
}
//游戏运行控制 
void RunGame()
{
	while(1)
	{
		Sleep(speed);//挂起函数,控制蛇的速度 
		ChangeDir();
		if(GameOver())//游戏结束 
		{
			break;
		}
		else if(HeadPos.pos.X==Food.X&&HeadPos.pos.Y==Food.Y)//吃到食物 
		{
			AddSnakeHead(HeadPos.pos);//增加蛇头 
			InitFood();//食物被吃,重新初始化打印食物 
		}
		else
		{
			MoveSnake(HeadPos.pos);//蛇移动 
		}
	}
	//蛇死亡后 
	COORD pos_1={0,Hei};
	GetPosition(pos_1);
	printf("很遗憾,你撞墙了或者自食自己,游戏结束!\n");
}
//游戏重新开始 
int RestartGame()
{
	int restar; 
	printf("你是最棒的!是否重新挑战游戏?\n");
	printf("0.否 1.是\n");
	printf("请选择(按回车键确认):");
	scanf("%d",&restar);
	if(restar==1)
	{
		system("cls");
		DestroySnake();
		return 1;
	}
	else if(restar==0)
	{
		printf("游戏退出!\n");
		return 0;
	}
	else
	{

		printf("选择无效!默认退出!\n");
		return 0;
	}
}


int  main()
{
	while(1)
	{
		InitGame();
		RunGame(); 
		if(RestartGame())
		{
			continue;
		}
		break;
	}
	return 0;
}

游戏下载:https://download.csdn.net/download/Boy_z/12367809

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值