C语言贪吃蛇(光标版)(me)

//第二天:把墙打出来了。横向一个字符跨度为2,因为C语言一个汉字char 占2字节。完善剩下的函数 
/*********************** 
*** 2020贪吃蛇********** 
***作者:Tjhon9********* 
***时间:2020.02.24***** 
************************/ 
#include <stdio.h>
#include <time.h> 
#include <windows.h>//设置光标,控制台必不可少。 
#include <stdlib.h> 
#include <conio.h>//控制台 

#define W 100//游戏界面宽度是100 
#define H 40 //游戏界面高度是40 
#define UPLEVEL 5//游戏等级提升所需食物个数是5 
#define OUTLINE 102 //界外菜单信息。 
#define LEN sizeof(struct score)//结构体的大小 
const char *FILE_BIN_PATH="Data.bin";//二进制文件的路径 

struct snake
{
	int X[105];		//蛇身的横坐标
	int Y[105];     //蛇身的纵坐标
	int length;     //蛇的长度
	int count;		//蛇吃到食物个数
	int speed;     //蛇移动速度
	 
};
struct food
{
	int X;	//食物横坐标
	int Y;	//食物纵坐标
};
struct score
{
	int score;
	struct score *next;
	//char name[20];
};
//全局变量
int i,j,k,max;			 //i,j,k为循环变量,max作为记录历史最高分。 
int key,directionkey;	//按键,与记录上一个方向按键。 
struct snake s,*ps=&s;	//定义蛇的结构体指针并指向蛇的结构体
struct food f,*pf=&f;	//定义食物结构体指针并指向食物结构体
struct score *head=NULL,*pr,*p;

//有关文件、链表的3个函数 
/*从文件读取数据到链表*/ 
void loadFromFile();
/*将链表数据储存到文件中*/ 
void saveToBfile(); 
/*获取历史最高分*/
int GetMaxScore(struct score *head);  

/*设置控制台标题与其大小*/
void setTitle();

/*设置控制台颜色*/
void setColor(int colorchoose); 

/*设置光标位置*/
void setPos(int x,int y); 

/*设置游戏的界面,四周的围墙*/
void Table();

/*显示提示和菜单*/
void showMenu();
 
/*得到用户输入*/
void getKey();

/*初始化蛇身*/
void initSnake();

/*移动蛇身,刷新蛇身信息*/
void moveSnake();

/*显示蛇身*/
void printSnake();

/*产生食物*/
void produceFood();

/*吃掉食物,变长*/
void eatFood();

/*穿墙*/
void crossWall();

/*死亡*/
void die();

/*游戏结束*/
void gameOver();

/*游戏开始*/
void gameStar(); 

/*主函数*/ 
int main()
{
     gameStar(); 
	return 0;
}
/*设置控制台标题与其大小*/
void setTitle()
{
	 //设置控制台窗口大小
    system("mode con cols=150 lines=45");
    //设置控制台标题
    SetConsoleTitle("2020贪吃蛇");
}
/*设置光标位置*/
  void setPos(int x,int y)
{
	HANDLE a;//句柄HANDLE 
  	COORD c;//COORD是个结构体 ,有两个成员int X与int Y 
	  c.X=x;
	  c.Y=y;
	  a=GetStdHandle(STD_OUTPUT_HANDLE);
	  SetConsoleCursorPosition(a,c);
}
/*设置控制台颜色*/
void setColor(int colorchoose)
{/*10,12,14绿红黄*/
	HANDLE a;
	a=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(a,colorchoose);//具体用法:CSDN搜索:SetConsoleTextAttribute函数用法 作者 花狗Fdog。 
} 
/*设置游戏的界面,四周的围墙*/
void Table()
{
	for(i=0;i<H;i++)
	{
	
			if(i==0||i==H-1)//上墙与下墙的纵坐标分别是0与H-1 
			{	for(k=0;k<W;k+=2)//k是横坐标 //左右墙的横坐标分别为0~1 与98~99 
				{
				setPos(k,i);//k,i,原来是i,j。 
				printf("卍"); //因为一个字符卍占据两个位置,方块■也是,所以横坐标一次性+2. 
				}  
			}	
			else 
			{	for(j=0;j<W;j+=2)//j是横坐标。 
			   {
			   	  if(j==0||j==W-2)//j==98
			   	  {
			   	  		setPos(j,i);//j,i,原来是i,j 
						printf("卍"); 
				  }
				}	
				
			}	
			
	} 
} 

/*显示提示和菜单*/
void showMenu()
{

	setPos(OUTLINE,4);
	setColor(12);
	printf("欢迎来到贪吃蛇");
	
	setPos(OUTLINE,8);
	printf("↑↓←→ 控制蛇头方向");
	
	setPos(OUTLINE,12);
	printf("按+怎加蛇移动速度,按-减少蛇移动速度");
	
	setPos(OUTLINE,16);
	printf("Esc 退出游戏");
	
	setPos(OUTLINE,20);
	printf("按 空格  暂停游戏");
	
	setPos(OUTLINE,24);
	printf("您的当前得分是: %d",ps->count*100);
	
	setPos(OUTLINE,28);
	printf("贪吃蛇移动速度: %d MS移动一下",ps->speed);
	
	setPos(OUTLINE,32);
	printf("您当前游戏等级是 %d",ps->count/UPLEVEL+1);
}

/*初始化蛇身*/
void initSnake()
{
	ps->length=3;//蛇的长度为3 
	ps->count=0;//吃的食物为0;
	ps->speed=300;//初始速度300ms
	ps->X[0]=W/2;//蛇尾起步位置 
	ps->Y[0]=H/2; //蛇尾起步位置。
	for(i=1;i<ps->length;i++)
	{
		ps->X[i]=ps->X[i-1]+2;//加2是因为一节蛇身,蛇头或蛇尾都占两个字节 ,如果加1的话,就用#@x做蛇身体各个部位 ,而不用▲做蛇部位(只不过看起来与上下移动不协调)。 
		ps->Y[i]=ps->Y[i-1];//蛇开始是横着的,因为上面+1了,所以蛇开始是横着的,所以纵坐标相同。 
	 } 
}

/*显示蛇身*/
void printSnake()
{
	for(i=0;i<ps->length;i++)
	{
		setPos(ps->X[i],ps->Y[i]);
		if(i==0)//打印蛇尾 
		{
			printf("蛇");
		}
		else if(i==ps->length-1)
		{
			printf("赖");//打印蛇头 
		}
		else
		{
			printf("皮");//打印蛇身。 
		}
	}
}

/*移动蛇身,刷新蛇身信息*/
void moveSnake() 
{
	setPos(ps->X[0],ps->Y[0]);
	printf(" ");
	for(i=1;i<ps->length;i++)
	{
		ps->X[i-1]=ps->X[i];
		ps->Y[i-1]=ps->Y[i];	
	}	
}

/*得到用户输入*/
//内含蛇移动的 关键! 
void getKey()
{
	//key=getch();
	switch(key)
	{
		//Esc
		case 27:gameOver(); 
				break;
		//空格
		case 32:setPos(OUTLINE,36);
				setColor(5);
				Sleep(0);
				printf("游戏暂停,按任意方向键继续\n"); 
				setPos(0,H);
				break;
		
		//+
		case 43:if(ps->speed<=20)
				{
					break;
		 		} 
		 		else
		 		{
		 		ps->speed-=20;
				break;
				 }
		
		//-
		case 45:
				ps->speed+=20;
				break;
		
		//↑
		case 72:if(directionkey==80)//防止开回马车。 
				{
					key=80;
					break;
				}
				moveSnake();
				ps->Y[ps->length-1]-=1; 
				break;
		
		// ↓
		case 80:if(directionkey==72)
				{
					key=72;
					break;
				}
				moveSnake();
				ps->Y[ps->length-1]+=1; 
				break;
		
		// ←
		case 75:if(directionkey==77)
				{
					key=77;
					break;
				}
				 moveSnake();
				ps->X[ps->length-1]-=2; 
				break;
		
		// →
		case 77:if(directionkey==75) 
				{
					key=75;
					break;
				}
				moveSnake();//后面部位先移动位置把头覆盖住,然后头再移位,然后再打印出来。 
				ps->X[ps->length-1]+=2; 
				break;
	}
}

/*产生食物*/
void produceFood()
{
	srand(time(NULL));
	while(1)
	{	//食物横坐标范围:2~98所有偶数(因为食物是汉字打出来的,踩得是偶数 占据的偶数与奇数的宽度);因为左右墙是0~1与98~99,所以)。 即为(1~48)*2 
		pf->X=(rand() % 48+1)*2;
		//食物横坐标范围:1~38所有的数。(因为上下墙是0与39) 
		pf->Y=(rand() % 39+1); 
		for(i=0;i<ps->length;i++)
		{
			if(ps->X[i]==pf->X&&ps->Y[i]==pf->Y)
			break;
		}
		if(i==ps->length)//确保食物不在蛇身上,然后把食物表示出来之后就不循环了。 
		{
			setPos(pf->X,pf->Y);
			//setColor(8);
			printf("★"); 
			break; 
		}
	} 
}

/*吃掉食物,变长*/
void eatFood()
{
	if(ps->X[ps->length-1]==pf->X && ps->Y[ps->length-1]==pf->Y)
	{
		ps->length++;
	
	//长度增加后,新坐标就等于原来贪吃蛇的头坐标,然后依次向后缩,缩到尾巴被覆盖。下一步移动时,蛇身体就拉伸了。 
	for(i=ps->length-1;i>=1;i--)
	{
		ps->X[i]=ps->X[i-1];
		ps->Y[i]=ps->Y[i-1];
	}
	produceFood();
	ps->count++;
    	if(ps->count%UPLEVEL==0)
    	{
		if(ps->speed>=20)
			ps->speed-=20;
		else
		{
			ps->speed=5;
			printf("速度不会变快了"); 
		 } 
    	}
	}//end of if
	
}

/*穿墙
进墙蛇头刚好等于墙,出墙蛇头不要等于墙,刚好出墙为好。 
上下墙纵坐标分别为 0 与 H-1,左右墙横坐标分别为 0~1 与 98~99,横向蛇头踩偶数*/ 
void crossWall()
{
	//穿右墙 ,用key保证蛇不碰到墙 
	if( key==77 && ps->X[ps->length-1]==98)
	{
		ps->X[ps->length-1]=2;//原来错在这里==双等号,没有赋值操作 
	}
	//穿左墙 
     if(ps->X[ps->length-1]==0 && key==75)
	{
	   ps->X[ps->length-1]=96;// 
	}
	//穿上墙
	 if(ps->Y[ps->length-1]==0 && key==72)
	{
		ps->Y[ps->length-1]=H-2;
	} 
	//穿下墙 
	 if(ps->Y[ps->length-1]==H-1 && key==80)
	{
		ps->Y[ps->length-1]=1;
	}
} 

/*从文件读取数据到链表*/ 
void loadFromFile()
{
	FILE *fp;
	int flag=2;
	fp=fopen(FILE_BIN_PATH,"rb");
	if(fp==NULL)
	{
		printf("没有找到文件!\n");
		//exit(-1);
		return;
	}
	while(1)
	{
		p=(struct score*)malloc(LEN);
		flag=fread(p,LEN,1,fp);
		if(flag==0)
		break;
		
		if(head==NULL)
		{
			head=pr=p;
			pr->next=NULL;	
		} 
		else
		{
			pr->next=p;
			pr=p;
			pr->next=NULL;
		}
		 
	}

	fclose(fp);
}

/*将链表数据储存到文件中*/ 
void saveToBfile()
{
	FILE *fp;
	int cnt=0;
	fp=fopen(FILE_BIN_PATH,"wb");
	if(fp==NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	if(head==NULL)
	{
		printf("没有需要写入文件的数据\n");
		return ;	
	} 
	else
	{
		for(p=head;p!=NULL;p=p->next)
		{
			fwrite(p,LEN,1,fp);
			cnt++;
		}
	}

	fclose(fp);
} 

/*获取历史最高分*/
int GetMaxScore(struct score *head)
{
	
	if(head==NULL) return NULL;
	else
	{
		max=head->score;
		for(p=head->next;p!=NULL;p=p->next)
		{
			if(max<p->score)
			{
				max=p->score;
			 } 
		}
	}
	return max;
}

/*游戏结束*/
void gameOver()
{
	
    system("cls");
    setPos(W/2,H/2);
    	if(head==NULL)
	{
		p=(struct score*)malloc(sizeof(struct score));
		pr=head=p;
		pr->score=ps->count*100;
		pr->next=NULL;
	}
	else
	{
		p=(struct score*)malloc(sizeof(struct score));
		p->score=ps->count*100;
		pr->next=p;
		pr=p;
		pr->next=NULL;
	}
	max=GetMaxScore(head);
	setPos(W/2,H/2);
	printf("游戏结束,你的得分是%d,历史最高分是%d\n",ps->count*100,max);
	saveToBfile();
    showMenu();
    setPos(0,H);
    exit(0);
    

}

/* 判断蛇的死亡 */
void die()
{
    for(i=0;i<ps->length-1;i++)   //i遍历的就是蛇身,从0开始则包括了蛇尾
    {   /* 当蛇头碰到蛇身,蛇死亡。 */
        if(ps->X[ps->length-1]==ps->X[i]&&ps->Y[ps->length-1]==ps->Y[i])//
        {
            gameOver();
        }
    }
}

/*游戏开始*/
void gameStar()
{
		setTitle();
	setColor(14);
	Table();
	
	initSnake();
	printSnake();
	key=77;
	produceFood();
	while(2) 
	{
		if(key==72||key==75||key==77||key==80)
		{
			directionkey=key;
		}
		j=0; 
		if(kbhit())//没有它蛇会停在原地等待接收key. 
		{
			key=getch();
			if(kbhit())//库函数,如果长按键盘上的值 
			{
				Sleep(20);
				j=1;	
			} 
		}
		getKey(); //让蛇自己移动需要 一个key值,一个getKey函数,然后放在while(1)中 
		if(key==43||key==45)
		{
			key=directionkey;
		}
		eatFood();
		crossWall();
		printSnake();	
		showMenu(); 
		die();
		if(j==1) Sleep(10);//Sleep函数控制蛇的移动速度。 
		else Sleep(ps->speed);
	}
} 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值