**
**
一直很想写一个贪吃蛇的小程序,这两天终于抽空完成了,这里把我的思路分享给大家,仅供参考!
代码如下:
先放段主函数压压惊
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,正在上升/下降时,不能下降上升
正在左移/右移时,不能右移/左移 进行了加入以及优化
第二次对数码管显示问题,吃到果子延时增长问题进行了解决,同时对死亡时的效果进行了优化
调试经验:
建议初学者首先构思好程序框架,想清楚程序需要哪几个必要模块(因此建议程序书写模块化),然后
对几个必要模块进行相对独立的调试,进而进行整合,确保基础功能的实现。
在此之后,在对程序出现的问题进行解决和优化。
*/