贪吃蛇游戏(C)

设计内容及要求:
贪吃蛇游戏是一个深受人们喜欢的游戏,一条蛇在密闭的围墙内,在围墙内随机出现一个食物,通过按键盘上的四个光标键控制蛇的上下左右四个方向移动,蛇头撞到食物,则表示食物被蛇吃掉,这时蛇的身体长一节,同时记10分,接着又出现食物,等待被蛇吃掉,如果蛇在移动过程中,撞到墙壁或身体交叉蛇头撞到自己的身体则游戏结束。

设计要求:
1、游戏界面:如边框表示围墙,红色矩形框表示蛇,绿色小方块表示食物。
2、设计思路:蛇的图形和蛇的移动。
3、用2个结构体表示食物和蛇的矩形块。

设计流程:

  1. 绘制围墙
  2. 欢迎玩家进入游戏
  3. 显示成绩
  4. 初始化蛇
  5. 初始化食物(位置随机)
  6. 移动蛇
  7. 规则判定
  8. 结束游戏或重新开始

课程设计任务实现:

  1. 绘制围墙
    声明Pos函数
    {
    用coord结构体标注位置
    用标准输出设备句柄指定窗体
    定位光标函数定位窗体和位置
    }
    用for循环进行快速绘制
  2. 欢迎玩家进入游戏
    用printf语句输出欢迎语
    用while(!kbhit());不断循环kbhit函数
    实现检测键入值即按任意键开始游戏
    用getch()清空键盘缓冲区
  3. 显示成绩
    初始成绩为0,用printf语句输出
  4. 初始化蛇
    构建一个蛇节点的结构体(用于双向链表)
    用动态双向链表来组成蛇
    用Pos函数移动光标
    用printf语句输出蛇
  5. 初始化食物
    用rand函数和srand函数结合构建一个随机数
    构建一个食物的结构体(一个节点)
    用Pos函数移动光标
    用printf语句输出食物
  6. 移动蛇
    设置一个变量keycode来存储键入值
    用switch语句与keycode值结合来实现按键控制方向
    用变量antikeycode来存储当前方向的反方向的值并通过比较新键入的方向与antikeycode值是否相等,若相等则keycode不赋新键入的值
    用改变尾节点和尾节点前一个节点及头节点的值来实现蛇的移动。
    用Pos函数移动光标
    用printf语句输出蛇
  7. 规则判定
    蛇头撞边框则游戏结束即判断蛇头坐标与边框是否重合
    蛇头撞身体则游戏结束即判断蛇头与蛇身坐标是否重合
    蛇头吃食物身长加1,增加尾节点,通过比较尾节点前两个节点的坐标得出尾节点的坐标,用Pos函数移动光标用printf语句输出蛇。分数加10,设立score变量存储成绩。
    将蛇的移动和规则判定放置在while(1)语句下,实现循环,违反规则则跳出while语句。
  8. 结束游戏或重新开始
    结束游戏后,释放所有节点
    通过while(1)语句实现循环,若键入值无效,则循环,键入新值继续判断,直到键入值为q或r,来根据值进行不同的操作。

优化:
1.对蛇碰撞自身的优化
经过分析蛇头与前四个节点不会相撞所以直接与第五个节点进行比较
2.对蛇的移动的优化
优化前需要释放动态空间和重新申请新的动态空间,还需要增加一个指针变量。
优化后只需要更改链表内的存储数据即可实现。
优化前代码:
struct Snake//声明蛇的一个节点 (双向链表)
{
int x;
int y;
struct Snake *last;
struct Snake *next;
} ;
struct Snake *head,*p1,*p2; //动态链表创建三变量
struct Snake lasthead; //指向头节点前加入的一个节点
//释放尾节点,给现任尾节点赋空指向
p2=p1->last;
free(p1);
p1=p2;
p1->next=NULL;
//增加头节点
lasthead=(struct Snake
)malloc(sizeof(struct Snake));
head->last=lasthead;
lasthead->next=head;
head=lasthead;
优化后:
struct Snake//声明蛇的一个节点 (双向链表)
{
int x;
int y;
struct Snake *last;
struct Snake *next;
} ;
struct Snake *head,*p1,*p2; //动态链表创建三变量
//使原尾节点上一节指向为NULL
p1->last->next=NULL;
p2=p1->last;
//使原尾节点连接到原头节点前
p1->next=head;
head->last=p1;
head=p1;

源码:

#include<stdio.h> 
#include<stdlib.h>//调用暂停函数 
#include<windows.h>//调用光标结构体
#include<time.h>//调用time函数 
#include<conio.h>//VC6.0下调用kbhit函数


	 struct Snake//声明蛇的一个节点	(双向链表) 
	{
	int x;
	int y; 
	struct Snake *last;
	struct Snake *next; 
	} ; 
	
	 
	 struct Food//声明食物位置
	 {
	 int x;
	 int y;	
	 } ;
	 
	 
int i;//for循环计数 
int k;//for循环计数 
int keycode;//蛇移动的方向 
struct Snake *head,*p1,*p2; //标准动态链表三变量 
struct Food food;//食物节点 
int score;//记录成绩
char want='r';//用户意愿 
int antikeycode;//当前方向的反方向的值 
int judge;//当前键入值 


int Pos(int x, int y)//光标位置函数
	{
	COORD Pos;
	HANDLE hOutput;
	Pos.X = x*2; 
	Pos.Y = y;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄
	SetConsoleCursorPosition(hOutput, Pos);//定位光标位置的函数,两个参数分别是指定哪个窗体,具体位置 
	return 0; 
	}

	
int color(short x) //自定义函数根据参数改变颜色   
		{  
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);    //参数x改变字体颜色    2为绿色  4为红色  7为白色   
		return 0;
		}  	



int Welcome()//欢迎界面 
	{
 	//输出边框及欢迎文字 
	for(i=10;i<33;i++)
	  	{
	  	Pos(i,10);//上边框 
	  	printf("█");
		Pos(i,32);//下边框 
		printf("█");
		Pos(10,i);//左边框                
		printf("█");
		Pos(32,i);//右边框
		printf("█");   
		}
	  Pos(17,20);
	  printf("欢迎进入贪吃蛇游戏"); 
	  Pos(17,22);
	  printf("请按任意键开始游戏");
	  //等待按键,开始游戏 
	  while(!kbhit()) ;//kbhit函数检测键入值,即时返回,非阻塞函数,无键入返回0。 
	  while(kbhit()==1) 
			getch();//	清空键盘输入缓冲区 
	  return 0; 
	} 
	 
	

	 
int  Startgame()//开始游戏 
	{
	//清空提示语   
	  Pos(17,20);
	  printf("                  "); 
	  Pos(17,22);
	  printf("                  "); 
	
	
	//输出提示语
	score=0; 
	Pos(18,8);
	printf("当前得分:%d",score);
	
	//初始化5个节点的蛇(动态链表)
	//赋地址值使链表连接 
	//struct Snake *head,*p1,*p2; 
	p1=(struct Snake*)malloc(sizeof(struct Snake));
	head=p1;
	for(i=1;i<5;i++)
		{	
		p2=p1;
		p1=(struct Snake*)malloc(sizeof(struct Snake));
		p1->last=p2;
		p2->next=p1;
		}
	p1->next=NULL;
	
	//赋坐标并输出 
	p1=head;
	p1->x=27;
	p1->y=21;
	while(!(p1->next==NULL))
		{
		//给下一个节点赋坐标 
		(p1->next)->x=p1->x+1;
		(p1->next)->y=p1->y;
		
		//输出值 
		Pos(p1->x,p1->y);
		color(4);
		printf("□");
		p1=p1->next;//指向下一个节点的地址 
		}
	//输出蛇尾节点 
	Pos(p1->x,p1->y);
	printf("□");

	//初始化食物位置
	srand(time(NULL));
	//struct Food food;//食物节点 
	food.x=rand()%32;
	while(food.x<11) 
	food.x=rand()%32;
	food.y=rand()%32;
	while(food.y<11) 
	food.y=rand()%32;
	Pos(food.x,food.y);
	color(2); 
	printf("█"); 
	

	//int keycode=75  左光标 
	//保持每次重开keycode=左光标; 
	keycode=75;
	while(i) //设置一个变量  将变量值传入switch 
		{
			
	
		//保证当食物刷出位置在蛇身时,不被蛇身经过时食物被意外清除 
		Pos(food.x,food.y);
		color(2); 
		printf("█"); 
		color(7); 
				
		//控制蛇的移动 
		
		 
			//覆盖尾节点的输出值使其为空
			Pos(p1->x,p1->y);
			printf("  ");
			
			
			//使原尾节点上一节指向为NULL
			p1->last->next=NULL; 
			p2=p1->last;
			
			//使原尾节点连接到原头节点前
			 p1->next=head;
			 head->last=p1;
			 head=p1;
			 
			//给蛇的头节点坐标赋值 
	 		switch(keycode) 
			{
				case 72:
					head->x=(head->next)->x;
					head->y=(head->next)->y-1;
					break;
				case 80:
					head->x=(head->next)->x;
					head->y=(head->next)->y+1;
					break;
				case 75:
					head->x=(head->next)->x-1;
					head->y=(head->next)->y;
					break;
				case 77:
					head->x=(head->next)->x+1;
					head->y=(head->next)->y;
			}
			Pos(head->x,head->y);
			color(4);
			printf("□");
			
		
		//规则判断
	    switch(1)
				{
				case 1:	
						//头撞边框游戏结束
						if(head->x==10  ||  head->x==32)
							{
							i=0;
							break;
							}
						if(head->y==10  ||  head->y==32) 
							{
							i=0;	
							break;
							}
							
						//头撞身体游戏结束 
		
							//蛇在5个节点内不会撞到身体 
							for(i=1;i<5;i++) 
							p1=p1->next;
							
						//判断是否会相撞	
					while(p1->next!=NULL)//判断到尾节点的前一个节点 
						{	
						if(head->x==p1->x  &&  head->y==p1->y)
							{
							i=0;
							break;	
							}
						p1=p1->next; 
						} 
				
						/*
						两个情况
						1.当while语句里未执行break,则下一个if语句为补充判断蛇头与蛇尾是否相撞。 
						2.当while语句里执行了break, 则下一个if语句始终为真值实际用处为跳出while循环,游戏结束。 
						*/
						if(head->x==p1->x  &&  head->y==p1->y)
							{
							i=0;		
							}
					
				} 		
	    
					
			
			
			//头吃食物   蛇尾加一节 
			if(head->x==food.x  &&  head->y==food.y) 
				{
				//计分并输出值 
				score=score+10;
				Pos(23,8);
				color(7);
				printf("%d",score);	
				
				 
				 //增加尾节点 
				p1=(struct Snake*)malloc(sizeof(struct Snake));
				p2->next=p1;
				p1->last=p2;
				
				switch(p2->last->x-p2->x) 
					{
					case -1: 
							p1->x=p2->x+1;
							break;
					case 1:
							p1->x=p2->x-1;
					case 0: p1->x=p2->x;
					}
					
				switch(p2->last->y-p2->y)
				{
					case 1:
							p1->y=p2->y-1;
							break;
					case -1:
							p1->y=p2->y+1; 
					case 0: p1->y=p2->y;
				} 	
				
				//输出蛇尾
				color(4);
				Pos(p1->x,p1->y);
				printf("□");	
				
				
				//当蛇新长的尾节点在墙里时将其释放
				if(p1->x==10  ||p1->x==32)
					{
					Pos(p1->x,p1->y);
					printf("█");
					} 
				if(p1->y==10  ||p1->y==32)
					{
					Pos(p1->x,p1->y);
					printf("█");
					} 
				
				
				//创建新的食物
				food.x=rand()%32;
				while(food.x<11) 
					food.x=rand()%32;
				 
				food.y=rand()%32;
				while(food.y<11) 
					food.y=rand()%32;
				Pos(food.x,food.y);
				color(2);
				printf("█"); 				 
				
				}
	 
		
		
		
			
		/*若传入新值  判断传入的值  
		若与原来的方向相反  则无效 即值不做改变	************
		*/ 
		//int antikeycode
		//给anticode赋值 
		switch(keycode) 
				{
				case 72:
						antikeycode=80;
						break;
				case 80:
						antikeycode=72;
						break;
				case 75:	
						antikeycode=77;
						break;
				case 77: 
						antikeycode=75;
				}
				
		judge=500;	
		//int k;	
		for(k=1;k<20000;k++)
			{
			if(kbhit()==1)
				{
				
				judge=getch();
				
			}
				switch(judge)
					{
					case 224 :
							judge=getch();
							
							switch(judge)
								{					
								case 72:
								case 80:
								case 75:
								case 77:	
									if(judge!=antikeycode)
										{ 
										k=20000; 
										break;
										} 
										
								}
					default :
						if(k!=20000)
							judge=500; 
					}
			
			} 
			
		if(judge!=500)
			keycode=judge; 
		 
		 }  
				  
	    

	
	
		return 0;

		
		}
	

int gameover()
	{
	//提示游戏结束输出最终成绩
	color(7); 
	Pos(15,8);
    printf("游戏结束,最终分数:%d",score);	
	Pos(14,9);
	printf("按q退出游戏,按r重新开始游戏"); 
	
	//游戏结束,释放所有节点 
	p1=head->next; 
	while(p1->next!=NULL)
		{
		free(p1->last);
		p1=p1->next;
		}
	free(p1);
	
	
	//按q退出,按r重新开始 
	while(1)
		{
		want=getch();	
		if(want=='q'  ||  want=='r')
			break;
		}
	
	switch(want) 
		{
		case 'q':
			break;	
		case 'r':
			system("cls");
		}
	return 0;	
 	} 
	

int main()
	{
	
	while(want=='r') 
		{
		Welcome();//欢迎界面 
		Startgame();//开始游戏 
		gameover();//游戏结束 
		}
	system("pause");
	return 0;
	}		
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值