在2KB内存的单片机上实现的的俄罗斯方块

来源:本站
导读:目前正在解读《在2KB内存的单片机上实现的的俄罗斯方块》的相关信息,《在2KB内存的单片机上实现的的俄罗斯方块》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《在2KB内存的单片机上实现的的俄罗斯方块》的详细说明。
简介:相对于五子棋,俄罗斯方块算法更是满天飞。我的代码还是相对好移植的,看起来也更清晰,方便学弟学妹们做编程小学期时移植。

一. 综述

相对于五子棋,俄罗斯方块算法更是满天飞。我的代码还是相对好移植的,看起来也更清晰,方便学弟学妹们做编程小学期时移植。不过要做就要做的有特色:它运行在软硬件由我们独立开发的平台上。以下是截图:

在2KB内存的单片机上实现的的俄罗斯方块

在2KB内存的单片机上实现的的俄罗斯方块

有如下特点:

跑在独立开发的平台上,平台为MSP430F149,内存2KB,频率8MHz 支持体感:可以通过左右倾斜来左右移动方块,还可以上下抖动改变方块形状 代码精简,方便移植 内存占用率极低 支持横屏和竖屏操作 支持等级:用户在达到一定分数后,等级会上升,从而方块下落速度变得更快 二. 系统设计

我们将问题细化为以下几个方面:

1. 方块的形状如何存储

俄罗斯方块总共有19种形状,每种形状都由四个小方块组成。如何高效存储这些方块的形状是个值得思考的问题。

在2KB内存的单片机上实现的的俄罗斯方块

上图介绍了存储方法:

我们用以下数组保存形状:

在2KB内存的单片机上实现的的俄罗斯方块

const unsigned char  BoxShape[19][9]={            { 1,0,0,1,1,1,2,1,1  },       { 1,0,1,1,2,1,1,2,2  },       { 0,0,1,0,2,0,1,1,3  },       { 1,0,0,1,1,1,1,2,0  },           { 1,0,2,0,1,1,1,2,5  },       { 0,0,1,0,2,0,2,1,6  },       { 2,0,2,1,2,2,1,2,7  },       { 0,0,0,1,1,1,2,1,4  },           { 1,0,2,0,2,1,2,2,9  },       { 2,0,0,1,1,1,2,1,10 },       { 1,0,1,1,1,2,2,2,11 },       { 0,0,1,0,2,0,0,1,8  },           { 0,0,0,1,1,1,1,2,13 },       { 1,0,2,0,0,1,1,1,12 },           { 2,0,1,1,2,1,1,2,15 },       { 0,0,1,0,1,1,2,1,14 },           { 1,0,1,1,1,2,1,3,17 },       { 0,1,1,1,2,1,3,1,16 },           { 1,0,2,0,1,1,2,1,18 } }; 

在2KB内存的单片机上实现的的俄罗斯方块

2. 如何解决各个方块的互相转换的顺序:

以上的数组已经解决了这一问题,多维数组的最后一位即该形状发生改变后的下一个形状。

3. 如何存储当前的画布信息

在2KB内存的单片机上实现的的俄罗斯方块

3. 如何解决碰撞问题:

这大概是算法当中最值得考虑的了,我们用以下四个函数解决:

所有的函数形参类似,它们的定义分别是:

XAxi,YAxi:存储当前形状的左上角的点在画布中的实际偏移量。

Data[][22]:存储的画布数据

Type:当前形状的ID(即BoxSharp的数组偏移量)

在2KB内存的单片机上实现的的俄罗斯方块

u8 Bottom_Anti(u8 XAxi ,u8 YAxi,u8  Data[][22],u8 Type){    u8 i;    u8 Value=1;    for(i=0;i<4;i++)       {           if(Data[XAxi+BoxShape[Type][2*i]][YAxi+BoxShape[Type][2*i+1]+1]==1)               Value=0;    }      return Value;     }u8 Right_Anti(u8 XAxi ,u8 YAxi,u8  Data[][22],u8 Type){    u8 i;    u8 Value=1;         for(i=0;i<4;i++)       {           if(Data[XAxi+BoxShape[Type][2*i]+1][YAxi+BoxShape[Type][2*i+1]]==1)               Value=0;    }                     return Value;         }u8 Lift_Anti(u8 XAxi ,u8 YAxi,u8  Data[][22],u8 Type){    u8 i;    u8 Value=1;         for(i=0;i<4;i++)       {           if(Data[XAxi+BoxShape[Type][2*i]-1][YAxi+BoxShape[Type][2*i+1]]==1)               Value=0;    }                     return Value;        }u8 Change_Anti(u8 XAxi ,u8 YAxi,u8  Data[][22],u8 *Type){    u8 i;    u8 Value=1;     for(i=0;i<4;i++)       {           if(Data[XAxi+BoxShape[BoxShape[*Type][8]][2*i]][YAxi+BoxShape[BoxShape[*Type][8]][2*i+1]]==1)               Value=0; //???????????        }       return Value;       }

在2KB内存的单片机上实现的的俄罗斯方块

算法本身很清晰,分别检测四个方块在改变后的实际位置是否已经有墙壁或方块存在。这个属性用临时变量Value存储,默认为1,如发现碰撞,则赋值为0并返回。

4. 如何判断某一行已经被填充满并计分?

下面是代码,很简单,检测该行的10个方格是不是都填满了,若填满,则加分,并重新刷新LCD

在2KB内存的单片机上实现的的俄罗斯方块

void CheckMark(u8 *mark ,u8  Data[][22],u8 dir){    u8 m,t,s;    u8 tMark=0;        for(m=20;m>2;m--)      {        tMark=0;        for(t=1;t<11;t++)        {            if(Data[t][m]==1)                tMark++;        }        if(tMark==10)        {            for(t=m;t>2;t--)            {                for(s=1;s<11;s++)                {                    Data[s][t]= Data[s][t-1];                }            }            m++;            TotalRefreshLCD(Data,dir);            delay_ms(200);            (*mark)++;        }              }}

在2KB内存的单片机上实现的的俄罗斯方块

5. 如何产生方块?并检查方块已经顶到头?

代码如下:(值得讨论的是,在单片机中如何产生随机数?)

在2KB内存的单片机上实现的的俄罗斯方块

void GenerateBox(u8 *XAxi ,u8 *YAxi,u8 *Type,u8 Data[][22],u8 *FailFlag){    u8 temp;    *XAxi=5;    *YAxi=2;    *Type=random(0,18);  //产生随机数        for(temp=2;temp<10;temp++)    {        if(Data[temp][2]==1)   //若在画布上的第3行出现方块,则失败        {            *FailFlag=2;            return;        }    }        }

在2KB内存的单片机上实现的的俄罗斯方块

好了,这些核心问题我们都讨论完了,下面讨论核心流程。

三 . 核心处理流程

若加上全部处理,包括刷新界面,显示欢迎界面,显示失败和分数界面,那么还是很冗长的。这些对于实现核心算法无益,我们也不需要关心。

下面是流程图:

在2KB内存的单片机上实现的的俄罗斯方块

主要操作代码如下,使用了状态机,请注意看OS_func_state==1的流程,这是系统在运行时的主要流程。

主要核心操作流程

在2KB内存的单片机上实现的的俄罗斯方块

u8 TerisBrick(){    u8 grade=0,mark=0;    u8 GameGUIData[12][22];    u8 KeyTemp;    u8 XAxi,YAxi,Type,OriGrade=0;        TickControlEN=1;    u8* temp[2];    u8 dir=0;        temp[0]="竖版游戏模式";    temp[1]="横版游戏模式";         if(AccControlEN==1)                        {                                                      ADXL345Init(0);                        }                  while(OS_func_state<10)    {        switch(OS_func_state)        {        case 0:            GUI_GameOpen("俄罗斯方块",&grade);                        OriGrade=grade;            dir=ListGUI(temp,"选择游戏方向",2);  //dir 表征运动方向,0:横版,1竖版            dir--;            Clear_Screen();            OS_func_state=1;                        break;        case 1:                        TerisBrickInit(GameGUIData,12,22);            _EINT();            SetPaintMode(1,COLOR_Black);            if(dir==0)            {                Rectangle(30,22,110,190,1);                            }            else                Rectangle(22,32,232,132,1);              PutString(275,70,"grade");            PutString(275,110,"mark");            Lcd_disp(2,200,"左右方向键移动,下键加速,上改变形状");              while(OS_func_state==1)            {                           back_light=back_light_set;                          FontSetTotal(COLOR_Black);                        NumberDis(275,90,grade,3,1);            NumberDis(275,130,mark,3,1);            GenerateBox(&XAxi,&YAxi,&Type,GameGUIData,&OS_func_state);            while(Bottom_Anti(XAxi,YAxi,GameGUIData,Type))            {       key_data=KEYNULL;            ShowBoxGUI(XAxi,YAxi,Type,1,dir);                            if(AccControlEN==1)                        {                          ADXL345ReadData();                          ADXL345ShowData(0);                          L3G4200DReadData();                          L3G4200DShowData();                        }            if(KeyTemp==KEYDOWN_DOWN)                delay_ms(150);            else                 delay_ms(500-70*grade);                        ShowBoxGUI(XAxi,YAxi,Type,0,dir);                YAxi++;            if(AccControlEN==1)                        {                              if(dir==0)  //横版                          {                            if(AccX>3)                              key_data=KEYLEFT_UP    ;                             else if(AccX<-3)                              key_data=KEYRIGHT_UP  ;                             if(GyroY>400||GyroY<-400)                             {                               key_data=KEYUP_UP  ;                                 delay_ms(200);                             }                                                         }                          else                          {                             if(AccY>3)                              key_data=KEYLEFT_UP    ;                             else if(AccY<-3)                              key_data=KEYRIGHT_UP  ;                               if(GyroX>400||GyroX<-400)                               {                               key_data=KEYUP_UP  ;                               delay_ms(200);                               }                                                      }                        }            switch(key_data)            {            case KEYLEFT_UP    :                if(Lift_Anti(XAxi,YAxi,GameGUIData,Type)!=0)                    XAxi--;                break;                case KEYRIGHT_UP  :                                if(Right_Anti(XAxi,YAxi,GameGUIData,Type)!=0)                    XAxi++;                                break;            case KEYUP_UP  :                if(Change_Anti(XAxi,YAxi,GameGUIData,&Type)!=0)                    Type=BoxShape[Type][8];                break;            case KEYDOWN_UP  :                                if(Bottom_Anti(XAxi,YAxi,GameGUIData,Type))                    YAxi++;                break;                            case KEYCANCEL_UP    :                if(MessageGui("提示信息","是否跳出?",1)==1)                {                                         TickControlEN=1;                    OS_func_state=2;                    return 1;                }                else                    TotalRefreshLCD(GameGUIData,dir);                break;                            }            KeyTemp=key_data;                        key_data=KEYNULL;            }                        for(u8 t=0;t<4;t++)            {                GameGUIData[XAxi+BoxShape[Type][2*t]][YAxi+BoxShape[Type][2*t+1]]=1;            }            ShowBoxGUI(XAxi,YAxi,Type,1,dir);                CheckMark(&mark,GameGUIData,dir);            grade=OriGrade+mark/10;                        }                         break;        case 2:            if(mark==0)                MessageGui("超级菜鸟","学习下游戏规则",0);            else if(mark>0&&mark<10)                MessageGui("初学者","你还要加油哦",0);            else if(mark>9&&mark<20)                MessageGui("中级水平","哥们你很牛逼",0);            else if(mark>19&&mark<30)                MessageGui("高级水平","无敌哥!膜拜",0);            else                MessageGui("超级无敌","不是一般人!!",0);            if( MessageGui("提示信息","是否继续",1)==1)                OS_func_state=0;            else             {                          TickControlEN=1;                OSTaskClose();                            }            break;                    }        }                return 1;                        }

在2KB内存的单片机上实现的的俄罗斯方块

四. 其他模块

其他模块主要包括界面显示,分数显示,方块显示和键盘输入等。考虑到和平台相关,因此不具备移植性,贴在下面仅供参考。

1. 初始化界面绘图画布数组,主要是建立“围栏”

在2KB内存的单片机上实现的的俄罗斯方块

void TerisBrickInit(u8  Data[][22],u8 i,u8 j){        u8  a,b;    for(b=0;b<i;b++)    {        for(a=0;a<j;a++)            Data[b][a]=0;    }    for(b=0;b<j;b++)    {        Data[0][b]=1;        Data[11][b]=1;    }    for(a=0;a<i;a++)    {        Data[a][0]=1;        Data[a][21]=1;    }        }

在2KB内存的单片机上实现的的俄罗斯方块

2. 绘制方块和刷新界面

绘制界面和方块

在2KB内存的单片机上实现的的俄罗斯方块

void  ControlBox(u8 x,u8 y,u8 mood,u8 dir){    //dir指出方向,0,为横版,1为竖版    if(mood==1)    {       SetPaintMode(0,COLOR_Black);    if(dir==1)         Rectangle(22+10*y,22+10*x,32+10*y,32+10*x,1);    else                 Rectangle(22+8*x,22+8*y,30+8*x,30+8*y,1);    SetPaintMode(0,COLOR_Red);    if(dir==1)         Rectangle(23+10*y,23+10*x,31+10*y,31+10*x,1);    else        Rectangle(23+8*x,23+8*y,29+8*x,29+8*y,1);    }        else    {        SetPaintMode(1,COLOR_Black);        if(dir==1)                         Rectangle(22+10*y,22+10*x,32+10*y,32+10*x,1);        else            Rectangle(22+8*x,22+8*y,30+8*x,30+8*y,1);            }    }void TotalRefreshLCD(u8  Data[][22],u8 dir){    u8 t,s;    SetPaintMode(1,COLOR_Black);    if(dir==0)                Rectangle(30,22,110,190,1);            else        Rectangle(22,32,232,132,1);              for(t=1;t<11;t++)    {        for(s=2;s<21;s++)        {            if(Data[t][s]==1)                ControlBox(t,s,1,dir);        }    }    }

在2KB内存的单片机上实现的的俄罗斯方块

五. 总结

本科一年级时候写过一个俄罗斯方块,代码冗长可读性极差,各种判断和奇怪的变量,最后还有一堆BUG。 这套代码算是对当时愚蠢的我的补偿吧,其实写的也不怎么地。比如,画布大小必须是22*12么?为什么不可变?

蛮想把我开发的这套系统拿出来给大家分享的,可惜自制硬件就是这样:给自己带来了快乐,给别人带来了开发和移植的麻烦。

说多了,呵呵

提醒:《在2KB内存的单片机上实现的的俄罗斯方块》最后刷新时间 2024-03-14 01:06:00,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《在2KB内存的单片机上实现的的俄罗斯方块》该内容的真实性请自行鉴别。