历史上的今天
今天是:2025年04月01日(星期二)
2020年04月01日 | 基于51单片机开发板8*8LED矩阵的贪吃蛇程序
2020-04-01 来源:eefocus
引语:过年的时候闲着无聊,恰好也刚学了点51开发板的东西,所以就想写一个贪吃蛇的小程序。在网上总结了一些关于贪吃蛇的算法,于是就有了以下的程序。
先来说以一下算法的原理:将8*8LED看作是直角坐标系,将左上角定为坐标原点。定义两个数组 snake_x[],snake_y[]用来存储蛇的x,y轴坐标。因此,我们的核心算法就是小蛇惯性保持自己的运动姿态,产生食物,以及小蛇吃到食物后长度增加。
再来看一下我的开发板,我的开发板是德飞莱51开发板,这里不再讲解其8*8LED的工作原理,直接上代码
void Hc595SendByte()
{
unsigned char a;
SRCLK=0;
RCLK=0;
for(a=0;a<8;a++)
{
SER=CHARCODE[p_now][a];
SRCLK=1;
_nop_();
_nop_();
SRCLK=0;
}
RCLK=1;
_nop_();
_nop_();
RCLK=0;
}
void Display()
{
unsigned int tab;
for(tab=0;tab<8;tab++) //扫描8列
{
Hc595SendByte_clean(0x00); //消隐
COMMONPORTS= TAB[tab]; //输出字码
Hc595SendByte(); //写入一个直字节数据
p_now++; //扫描下一行
if(p_now == 8) //扫描8行后归零
{
p_now = 0; //伪指针归零
}
}
}
这里定义了两个函数 Hc595SendByte ()和Display()两个函数,其中CHARCODE[x][y]数组是用来存放LED的状态值的。0x00代表灭,0x01代表亮。因此,在整个程序中,如果想将小蛇或食物显示在LED上,只需将相关点的坐标作为数组括号内的两个常量表达式,并将其赋值0x00或0x01即可。
在void Auto_Move()函数中,
void Auto_Move()
{
unsigned char i;
if(Time==10) //蛇500ms移动一下
{
temp_x = snake_x[0]; //蛇尾坐标临时存储
temp_y = snake_y[0];
for(i=0;i snake_x[i] = snake_x[i+1]; snake_y[i] = snake_y[i+1]; } snake_x[snake_long] = x; //蛇头坐标临时存储 snake_y[snake_long] = y; switch(direction_flag) { case 16: {//默认移动-向右即一开始适时,舍 x++; temp_direction=direction_flag; break; } case 1: {//惯性制导-向上 y--; temp_direction=direction_flag; break; } case 4 {//惯性制导-向左 x--; temp_direction=direction_flag; break; } case 6:{//惯性制导-向右 x++; temp_direction=direction_flag; break; } case 9:{//惯性制导-向下 y++; temp_direction=direction_flag; break; } default: {direction_flag=temp_direction;//按键按错时修正运动方向 switch(direction_flag) { case 1:{y--;break; //修正-向上 } case 4:{x--;break; //修正-向左 } case 6:{x++;break; //修正-向右 } case 9:{y++;break; //修正-向下 } case 16:{x++;break; //修正-默认 } } break; } } Auto_move()函数是本程序的核心,关于他具体怎么实现小蛇的移动,程序的注释已经写的很清楚。 接下来就是定义产生食物的函数。这个函数内部运用了rand()随机函数,用来产生随机的食物位置。food_x和food_y是食物的横纵坐标。 void Food() { if(food_flag>0) { food_x=rand()%7; //伪随机数-食物横坐标(0-7之间) food_y=rand()%7; //伪随机数-食物纵坐标(0-7之间) CHARCODE[food_x][food_y]=0x01;//在地图中显示食物 food_flag=-food_flag; //食物产生标志翻转 } } 接下来是小蛇长度变长的函数 void Eating() { if(food_flag<0&&snake_x[snake_long]==food_x&&snake_y[snake_long]==food_y)//小蛇的头部坐标如果等于食物的坐标,那么则说明小蛇吃到食物 { snake_long++; //蛇身加长,即增加小蛇数组常量表达式的值 switch(direction_flag) { case 1:{ snake_x[snake_long]=food_x; //将惯性运动下一个坐标点亮增加蛇长 snake_y[snake_long]=food_y-1; break; } case 4:{ snake_y[snake_long]=food_y; //将惯性运动下一个坐标点亮增加蛇长 snake_x[snake_long]=food_x-1; break; } case 6:{ snake_y[snake_long]=food_y; //将惯性运动下一个坐标点亮增加蛇长 snake_x[snake_long]=food_x+1; break; } case 9:{ snake_x[snake_long]=food_x; //将惯性运动下一个坐标点亮增加蛇长 snake_y[snake_long]=food_y+1; break; } } CHARCODE[food_x][food_y]=0x00;//清除食物 food_flag=-food_flag; //产生新的食物 } } 仅看注释已经可以明白该段程序。 下面是程序相关变量的定义以及函数的声明部分。 #ifndef _DEFINITION_H #define _DEFINITION_H #include #include #include #include #define COMMONPORTS P0 //LED位码IO口 #define GPIO_KEY P1 //矩阵键盘IO口 sbit SRCLK=P3^6; //74HC595控制端口 sbit RCLK=P3^5; sbit SER=P3^4; int x=1,y=3; //蛇头初始坐标 int temp_x=0,temp_y=0; //蛇尾初始坐标 int food_x,food_y; //食物坐标 int food_flag=1; //食物产生标志 unsigned char temp_direction; //方向矫正 unsigned char snake_long=1; //蛇身长-初始为2 unsigned char Time=0; //用于定时累计 unsigned char p_now=0; //伪指针(点阵需横向扫描8次) unsigned char KeyValue=16; //矩阵键盘键值存储 unsigned char direction_flag=16; //惯性移动方向标志 unsigned char snake_x[10]={0,1}; //蛇的横坐标 蛇的最长长度为10 unsigned char snake_y[10]={3,3}; //蛇的纵坐标 unsigned char code TAB[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//LED位码 unsigned char CHARCODE[8][8]= //地图 { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} }; #endif #ifndef _FUNCTION_H #define _FUNCTION_H void Hc595SendByte(); void Hc595SendByte_clean(unsigned char);//消隐函数,SER=0x00 void Delay10ms(); void KeyDown();//按键扫描函数,上下左右四个方向键 void WALL();//小蛇撞到墙后,从另一边出来 void Direction();//上下左右四个方向键,对应四个direction_flag void Auto_Move(); void Food(); void Eating(); void INIT();//定时器函数,用于扫描LED void Display(); void Distory();//小蛇的头部坐标等于蛇身的某处坐标时,则触发bee()函数,并停止小蛇移动 void bee();//蜂鸣器函数 #endif 好了,现在你应该可以将剩下的代码完成了。 现在这个代码还有不够完善的地方,比如在实际运行的时候,小蛇的长度达到定义的长度以后就无法继续向下移动,或者是LED矩阵显示的是乱码。
史海拾趣
|
AMD有可能在本月末宣布其奉行“轻晶圆厂”策略的大动作,包括转手其Dresden晶圆厂,接受来自一家中东财团的投资组建一家新公司。 在Austin美国发言人John Lau和Jefferies共同发表一份投资分析报道中指出,其智囊团认为AMD正寻找买主,打算抛售其在D ...… 查看全部问答> |
|
急!!!如何让我开发的WDM驱动程序在双核的机器中只运行在单核上? 我们一张PCI卡里面一块芯片的读写只支持在单核上运行,这样在双核机器上要强制让操作系统运行在单CPU模式下才可正确使用,我用的操作系统是Windows Vista. 请问我应该怎样通过编程的方式让我的驱动程序能内在的指定运行在单核上?… 查看全部问答> |
|
大虾们好!最近最一个产品,测试一个润滑脂的低液位报警,就是提醒要加料了! 装在直径200mm,高度75mm(这个很小)还有高度250mm的塑料桶里面。桶底有一个直流电机带动的刮板。 润滑脂粘度很大,类似于黄油,稍微稀一点,流动性很小。 用音叉肯 ...… 查看全部问答> |
|
城主及大家好. 请问STM32在休眠模式下怎么使用独立看门狗? 手册上说可以在休眠模式下工作,但看门狗需要喂吧?在停止模式下怎么喂呢? 应用中要用到休眠模式,但又要用到看门狗,该怎样解决呢? 另外.独立看门狗打开后能否关闭呢? 谢谢大家.… 查看全部问答> |
|
定义了全局变量,在中断函数中赋值。在主程序中全局变量的值为何没有变。请各位大侠指教,不胜感激!结果是 ser=0!!!!! #include <msp430x14x.h> unsigned char a[6]; unsigned char b[6]; unsigned char n; unsigned char pt; unsigned ...… 查看全部问答> |
|
飞思卡尔I.MX53软硬件平台 项目预算:¥ 50,000~80,000 开发周期: 90天 项目分类: android 竞标要求: 项目标签: java ANDROID ...… 查看全部问答> |




