基于51单片机的贪吃蛇小程序(8*8LED点阵实现)by_jy

**

基于51单片机的贪吃蛇小程序(8*8LED点阵实现)by_jy

**

一直很想写一个贪吃蛇的小程序,这两天终于抽空完成了,这里把我的思路分享给大家,仅供参考!

代码如下:
先放段主函数压压惊

void main()
{	
	x_s[0] = 2;			   //初始化三个点
	y_s[0] = 3;
	x_s[1] = 3;
	y_s[1] = 3;
	x_s[2] = 4;
	y_s[2] = 3;
	Timer0Init();
	suijishu();
	while(1)
	{
	   appear();
		   if(flag_a)		//蛇身移动位
		   {
				   if(!flag_s)	  //蛇身死亡位
				    {
						auto_move();
						HC_595(0x00);     //消隐
						P0 = 0xFF;		 //消隐	
						shensi();
						chiguo();
						delay(1000);
						flag_a = 0;
					}
			}				
	
	}
}

/*
本程序核心程序:
appear函数,显示蛇身
auto_move函数,控制蛇身惯性移动 ,同时里面附了判断是否穿墙的一段代码
direction函数,控制蛇的移动方向
suijishu(随机数)函数,随机生成果子
chiguo(吃果)函数,判断是否吃到果子
shensi(身死)函数,碰到自己身体则死亡

*/

#include<reg51.h>
#include<intrins.h>
#include<stdlib.h>
#define M 12		//难度系数,12中等,越小越难	
typedef unsigned char uchar;
typedef unsigned int uint;

sbit SRCLK = P3^6;		  //595芯片
sbit RCLK = P3^5;
sbit SER = P3^4;

sbit LSA=P2^2;			 //138译码器
sbit LSB=P2^3;
sbit LSC=P2^4;

uchar code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
							0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

					//定义方向键
sbit K1 = P3^1;		//上 
sbit K2 = P3^0;		//下 
sbit K3 = P3^2;		//左
sbit K4 = P3^3;		//右 

uchar m;			//储存果子的X坐标
uchar n;			//储存果子的Y坐标

uchar snakelong = 3;	   //初始化蛇长
char flag_x = 1;		   //初始化运动方向
char flag_y = 0;		   //初始化运动方向
uchar flag_gg = 0;		   //生成果子的重复性标志
uchar flag_c = 0, sheshen = 0;	  //吃果标志
uchar flag_a = 0;		//移动标志
uchar flag_s = 0;		//死亡标志
uchar x_s[32] = {0},y_s[32] = {0};	   //定义蛇的最大长度
uchar code X_[8] = {0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE};	 
uchar code Y_[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};	   

/*  
	y1				//定义x,y坐标
	y2			  //如,我想让(4,2)亮,只需要P0 = X_[4];HC_595 = Y_[2];
	y3
	y3
	y5
	y6
	y7
	y8
	  x1    x2   x3   x4  x5   x6   7   x8
*/

一个小的延时函数:


void suijishu();				   //提前定义随机数生成函数
void delay(uint i)				   //简单的delay函数,对于51单片机,delay(1)约等于9微秒
{
	while(i--);
}

定时器初始化:


void Timer0Init()				  //定时器初始化函数	   模式1,16位定时器
{
	TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

	TH0=0XFC;	//给定时器赋初值,定时1ms
	TL0=0X18;	
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器			
}

两个显示函数,分别控制点阵和数码管;

void HC_595(uchar date)				 //595例程,这里就不详细解释了
{	
	uchar a;
	SRCLK = 0;
	RCLK = 0;

	for(a=0;a<8;a++)
	{
		SER = date>>7;
		date<<=1;
		SRCLK = 1;
		_nop_();
		_nop_();
		SRCLK = 0;
	}
	RCLK = 1;
		_nop_();
		_nop_();
	RCLK = 0;
}


void display()						//数码管显示,用来显示蛇长
{
	uchar i,b=2;
	HC_595(0x00);     //消隐
	_nop_();
	P0=0x00;		  //因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意。
	_nop_();
		for(i=0;i<2;i++)
	{	
		while(b) {		   //这里循环两次是为了让数码管显示更清楚一点。
			switch(i)	 //位选,选择点亮的数码管
			{
				case(0):
					LSA=0;LSB=0;LSC=0; P0=smgduan[(snakelong%10)];delay(10);P0=0x00;break;	
				case(1):
					LSA=1;LSB=0;LSC=0; P0=smgduan[(snakelong/10)];delay(10);P0=0x00;break;
			}	
				_nop_();
				b--;
				}		
	}
	LSA=1;LSB=1;LSC=1; P0=0xFF;	 //还是为了消隐。以一个数码管常亮为代价,以求不影响点阵和数码管显示蛇长      
}

显示蛇身,核心函数之一:

void appear()			// viod 蛇身出现   		 
{	
/*	x_s[0] = 2;			 //初始化三个点
	y_s[0] = 3;
	x_s[1] = 3;
	y_s[1] = 3;
	x_s[2] = 4;
	y_s[2] = 3;	 */
							     
//uint t = 150;	 //t越小越难,越大越简单		 //这里也可以控制难度,经测试也有效。
//while(t)
//{	
	uchar i;
	for(i=0;i<snakelong;i++)					 //snakelong 是用来控制数组的全局关键变量
	{
		if((x_s[i]!=0)&&(y_s[i]!=0))
		{
			P0 = X_[x_s[i]-1];
			HC_595(Y_[y_s[i]-1]);
			delay(100);		   //延时
			P0 = 0xFF;		 //消隐	
			HC_595(0x00);     //消隐
		}
	}
//	t--;
//}

}

自动惯性移位函数:

void auto_move()		// viod 自动移动 		  核心程序之一
{	
	uchar i;					
	for(i=0;i<(snakelong-1);i++)
	{
		x_s[i] = x_s[i+1];
		y_s[i] = y_s[i+1];
	}
		x_s[snakelong-1] += flag_x;
		y_s[snakelong-1] += flag_y;

			if(x_s[snakelong-1]>8)			// void 穿墙判断  
				x_s[snakelong-1] = 1;
				else if(x_s[snakelong-1]<1)
					x_s[snakelong-1] = 8;
				
			if(y_s[snakelong-1]>8)
				y_s[snakelong-1] = 1;
				else if(y_s[snakelong-1]<1)
					y_s[snakelong-1] = 8;
	
}

键盘扫描(独立键盘),控制蛇的移动方向:

void direction()		// void 方向控制 
{
	if(!K1) 
	{
		delay(1000);
		if(!K1)
		{
			if(flag_y == 1)	   //是否正在下降	 ,若是,则点击上升无用。
			{flag_y = 1;}
		   else
		    {
				flag_x = 0;
				flag_y = -1;	//shang
			}
		}
	}
	else if(!K2) 
	{
		delay(1000);
		if(!K2)
		{
			if(flag_y == -1)	   //是否正在上升
				{flag_y = -1;}
			else
			{
				flag_x = 0;
				flag_y = 1;		//xia
			}
		}
	}
	else if(!K3) 
	{
		delay(1000);
		if(!K3)
		{
			if(flag_x == 1)	   //是否正在右移
			{flag_x = 1;}
			else
			{
				flag_x = -1;	//zuo
				flag_y = 0;	
			}
		}
	}
	else if(!K4) 
	{
		delay(1000);
		if(!K4)
		{
			if(flag_x == -1)	   //是否正在左移
			{flag_x = -1;}
			else
			{
				flag_x = 1;		//you
				flag_y = 0;	
			}
		}
	}			
}

吃果函数,吃到果子蛇头增长

void chiguo()			// void 吃果判断
{
	if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  )
					{ 	flag_c = 1;		}
		else if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  ) //重复两次防止程序bug——穿过果子但不去吃     
					{ 	flag_c = 1;		}
			else if(((x_s[snakelong-2]+flag_x)==m)&&((y_s[snakelong-2]+flag_y)==n)  )	 //放宽吃果判定,以防止出现bug——穿过果子但不去吃               
							{	flag_c = 1;	sheshen = 1;    }
				else if(((x_s[snakelong-3]+flag_x)==m)&&((y_s[snakelong-3]+flag_y)==n)  )
								{	flag_c = 1;	sheshen = 2;    }			
   
	if(flag_c)
	{
		flag_c = 0;
		TR0 = 0;//关闭定时器
		snakelong++;
		if(sheshen == 1)
		{
			x_s[snakelong-1] = m+flag_x;
			y_s[snakelong-1] = n+flag_y;
			sheshen = 0;	
		}
		else if(sheshen == 2)
		{
			x_s[snakelong-1] = m+2*flag_x;
			y_s[snakelong-1] = n+2*flag_y;
			sheshen = 0;	
		}
		else
		{							 //这才是吃果的正常情况
		   	x_s[snakelong-1] = m;
			y_s[snakelong-1] = n;
			sheshen = 0;
		}
        suijishu();		  //再生成一个果子
		P0 = 0xFF;		 //消隐	
		HC_595(0x00);     //消隐
		appear();
		appear();
		P0 = 0xFF;		 //消隐	
		HC_595(0x00);     //消隐
		TR0=1;//打开定时器
	}
}

碰到自己身体死亡函数

void shensi()			 // void 身死    碰到自己身死
{
	uchar b,x_1,y_1;
	if(snakelong>4)	
	{
	for(b=0;b<snakelong-4;b++)				   //对蛇头处的四个不用判断,也不能判断
	  {	
		if(((x_s[snakelong-1])==x_s[b])&&((y_s[snakelong-1])==y_s[b])  )  
				 {
					flag_s = 1; 
				 }
	  }
	if(flag_s == 1)
		{	
			P0 = 0x00;HC_595(0xFF);delay(2000);	   //闪烁两次
			P0 = 0xFF;HC_595(0x00);delay(2000);
			P0 = 0x00;HC_595(0xFF);delay(3000);
			P0 = 0xFF;HC_595(0x00);delay(1000);
			x_1 = x_s[snakelong-1] ;
			y_1 = y_s[snakelong-1] ;	  		   //卡在死亡状态的下一刻  
		  	snakelong++;
			x_s[snakelong-1] = x_1 + flag_x;
			y_s[snakelong-1] = y_1 + flag_y;
		}

	}
}

随机生成果子:


void suijishu()						//随机数,生成果子
{
	uchar b,m_1,n_1;
		
	do{
		flag_gg = 0;
		m_1 = (rand()%8)+1;	   				
		n_1 = (rand()%8)+1;
		for(b=0;b<snakelong;b++)
		  {	
			if((m_1==x_s[b])&&(n_1==y_s[b]))	 //如果果子和蛇身有重复,则flag_gg置1,再生成一次                    
					flag_gg = 1;		
		  }
		}while(flag_gg);		
	m = m_1;
	n = n_1;
}

主函数:

void main()
{	
	x_s[0] = 2;			   //初始化三个点
	y_s[0] = 3;
	x_s[1] = 3;
	y_s[1] = 3;
	x_s[2] = 4;
	y_s[2] = 3;
	Timer0Init();
	suijishu();
	while(1)
	{
	   appear();
		   if(flag_a)		//蛇身移动位
		   {
				   if(!flag_s)	  //蛇身死亡位
				    {
						auto_move();
						HC_595(0x00);     //消隐
						P0 = 0xFF;		 //消隐	
						shensi();
						chiguo();
						delay(1000);
						flag_a = 0;
					}
			}				
	
	}
}

/*
果子的函数放在了中断里,1ms一次进行显示,同时键盘也是1mS一次进行扫描
通过调节M的大小来实现蛇的延时移动。
*/

void guozi() interrupt 1		// void 果子出现
{	
	uint a;
	uchar i=3;
	a++;
	direction();	  //用中断去扫描键盘,1mS/次
	P0 = 0xFF;		 //消隐	
	HC_595(0x00);     //消隐
	while(i)		  //重复以增加亮度
	{
	P0 = X_[m-1];
	HC_595(Y_[n-1]);
	i--;
	}
	delay(100);
	P0 = 0xFF;		 //消隐	,因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意
	HC_595(0x00);     //消隐

		_nop_();
		P0=0x00;  //数码管消隐
		_nop_();
		display();
		delay(100);

	HC_595(0x00);     //消隐
	_nop_();				
	LSA=1;LSB=1;LSC=1; P0 = 0xFF;
			
	if (a>M)		  //蛇身移动控制位
	{
		a = 0;
		flag_a = 1;
	}
} 

/*
编写过程中共累计进行了两次优化,第一次对1,随机生成果子,2,吃果判定的放宽,3,碰到蛇身死亡,以及4,正在上升/下降时,不能下降上升
正在左移/右移时,不能右移/左移 进行了加入以及优化
第二次对数码管显示问题,吃到果子延时增长问题进行了解决,同时对死亡时的效果进行了优化

调试经验:
建议初学者首先构思好程序框架,想清楚程序需要哪几个必要模块(因此建议程序书写模块化),然后
对几个必要模块进行相对独立的调试,进而进行整合,确保基础功能的实现。
在此之后,在对程序出现的问题进行解决和优化。

*/

  • 25
    点赞
  • 129
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值