资源描述
在Linux系统下基于ARM嵌入式的俄罗斯方块目录一、 摘要3二、 各种问题的具体介绍(1)图形的如何存储问题3(2)图形的染色问题3(3)游戏的屏幕显示问题3(4)方块图形的自动下移问题3(5)方块图形的左右移动问题3(6)方块图形的如何翻转问题3(7)图形移动时的自动消行问题3(8)图形移动翻转时的边界判断问题3(9)如何实现一键到底的问题3(10)各种移动的用键问题3(11)游戏时的自动冒行问题3(12)游戏时的作弊消行问题3(13)游戏时作弊方块出现问题3(14)游戏时的作弊炸弹使用问题4三、流程图 流程图5四、 实习总结 实习总结6五、附件(1)程序源代码6(2)操作截图18六、参考文献21一、摘要在Linux系统下使用vim编辑器实现如手机上的游戏俄罗斯方块,利用所学知识实现以下的各个问题。二、各种问题的具体介绍(1)图形的存储问题:每个方块采用一个4 * 4的小数组存储,不同的方块给对应的数组赋不同的值,从17,以便打印是染不同的颜色,实现每种方块都有不同的颜色。(2)图形的染色问题:使用VT控制码对不同的图形涂不同的颜色。(3)游戏屏幕的显示问题:采用一个20 * 12的大数组,数组元素全部赋值为0,在终端打印空格,形成一片矩形区域,每个俄罗斯方块(以下简称方块)占4 * 4个空格的区域,赋值给大数组即可在终端打印出方块。在大数组的上方再打印一个4 * 4的小数组,以显示下一个将出现的方块,并打印出得分,等级,计时等。(4)方块图形的自动下移问题:有一个变量记录方块下移的行数,并像左右移动一样打印向下移动的方块,设置一个信号,每一秒发送一个信号,进程每收到一次信号变量加一实现自动下移。(5)方块图形的左右移动问题:有一个变量记录方块第一行一列的坐标,左移时变量减一,右移时变量加一,把小数组原来在大数组的位置清零,在把小数组赋值到大数组相应坐标的位置即可打印出移动后的方块的位置,实现方块的左右移动。(6)方块图形的如何翻转问题:为了实现方块不同方向的转动,要新建一个4 * 4的新的小数组new,将存放方块的小数组按逆向旋转90度的方向存进new,把小数组清空,并把new的值重新赋值给小数组,大数组里方块原来的位置置零,把旋转后的小数组复制给大数组,即可打印出旋转后的方块。(7)图形移动时的自动消行问题:当大数组的某一行全部不为零时,既是达到了消行的条件,将大数组这样的行置零,其上的所有行下移一行(或多行),以实现消行的操作。系统会根据消行的行数打印出鼓励语句,例如come on, good,perect等。(8)图形移动翻转时的边界判断问题:方块左右移动或向下运动,当遇到左右边界或到底时应不再继续左移,右移或下移,此时要判断边界。找出存放方块的小数组中所有不为0元素的元素在大数组的位置,判断大数组相应位置是否不为0,是否为边界,若满足两者之一即为到达边界(9)如何实现一键到底的问题:实现按一下x或向下键使方块从当前位置一下移到最后一行,先将方块当前位置清零,把小数组的值赋给大数组的最底,即可打印出移到最底的状态,即可实现一键到底。(10)各种移动的用键问题:左移使用d键或者向左的箭头键,右移使用a键或者向右的箭头键,翻转使用w键或者向下的箭头键,一键到底使用x键或者向下的箭头键。(11)游戏时的自动冒行问题:当玩家每累计得20分时会从底部随机冒出一行一增加难度。大数组所有行向上移动一行,最后一行随机赋07的值。(12)游戏时的作弊消行问题:按e消去最后一行,当玩家希望减少行数时可使用此种方法。将大数组的最后一行置零,并将除第一行的所有行下移一行,此时屏幕的最后一行被消除。(13)游戏时作弊方块出现问题:按m在按数字键17可以出玩家希望得到的方块。数字17分别对应7种方块,当进程得到相应方块的命令时会把相应方块赋值给下一个将出现的方块小数组,打印到终端即可得到。(14)游戏时的作弊炸弹使用问题:按z键可在当前方块到底时炸掉它周围一些方块。当方块到底时将其周围一些大数组置零。三、流程图按操作键a或?x或向下键? e? 左移 右移一键到底作弊消行是是是是停止下移 出方块 下移?否 自动下移是 消行? 炸弹? 期望方块? 输了?消行,计算得分使用炸弹得到期望的图形游戏结束否 开始否是是是是四、实习总结 在Linux下使用红帽系统做出俄罗斯方块,最一开始接到这个项目时只知道游戏怎么玩,但是却不知道该怎么用所学的知识实现它,当时是如此的纠结。我们的项目是要求以组为单位的,每个小组由4个人组成,以便训练我们与人的协同合作能力,所以接到项目以后我们小组四个人就聚在一起开始讨论,大概讨论了一下午才确定了项目的基本结构和大体流程,然后简单的分派了一下任务,把几个模块分派给不同的人来做,然后再把每个人写的组合在一起。在写图形的翻转问题时遇到了好多问题,开始时我想了一种方法,可是写着写着突然发现写不下去了,如果用这种方法会出现好多连带的问题,可是我的思路已经固定在那了,再想其他的方法也会不知不觉的绕道以前带的问题上去,后来没办法,只能求助小组其他成员,因为我们是在协同作战,最后经过我们的一起努力完成的图形的翻转。等游戏功能基本完成以后,我们又加上了一些自己的创意,比如作弊键。完成以后再往前回顾后发现,有了思路以后项目是很容易完成的,最主要的是开始的思路混乱问题,那段时间时那么的心酸纠结,但是我的收获却也很多,例如,完成后的那种成就感是什么也没法替代的,再就是我们小组成员之间的合作也是相当值得骄傲的,这次的项目不仅使我们的知识更加牢固,也是我们小组成员间的交流更加顺畅,赢得了知识也赢得了友谊。五、附件1.程序源代码#include #include #include #include #include #define M 20#define N 12int a144 = 0,1,1,0,/方块模型0,0,1,0,0,0,1,0;int a244 = 0,2,2,0,0,2,0,0,0,2,0,0;int a344 = 0,3,0,0,3,3,3,0,0,0,0,0;int a444 = 4,4,0,0,4,4,0,0,0,0,0,0;int a544 = 5,5,5,5;int a644 = 0,6,0,0,0,6,6,0,0,0,6,0;int a744 = 0,0,7,0,0,7,7,0,0,7,0,0;int aMN = 0;/控制屏幕显示int aa44 = 0;/存放旋转后的方块模型int new44 = 0;/存放下一个将要出现的方块模型int blow = 0;/计向下移动,行坐标int leftright = 0;/计左右移动int sum = 0;/记录消行总数int score = 0;/记录总分数char rank = A;/等级int second = 0;/记录开始游戏的时间int mflag = 0,v; /作弊出任意想要的方块模型的标志,V对应相应的方块模型int zflag = 0; struct termios termios_p1/*指针*/,termios_pb1/*副本*/;/键盘控制void output(int n,int s)/若s为1输出大数组,s为0输出next方块switch(n)case 0:if(s)printf(* );elseprintf( );break;case 1:printf(3340m 330m);break;case 2:printf(3341m 330m);break;case 3:printf(3342m 330m);break;case 4:printf(3343m 330m);break;case 5:printf(3344m 330m);break;case 6:printf(3345m 330m);break;case 7:printf(3346m 330m);break;void configure()/计算得分,等级score = sum*10;rank = A+score/50;void show()/显示屏幕int i,j;configure();printf(t=n);printf(t$3331m334m331mScore:%3dtRank:%cttTime:%3d330m$n,score,rank,second);printf(t$tt3331m334m331mNext:330m $n);for(i = 0;i 4;i+)printf(t$tt);for(j = 0;j 4;j+)output(newij,0);/输出next方块printf( $n);printf(t$t- $n);for(i = 0;i M;i+)printf(t$t|);for(j = 0;j N;j+)output(aij,1);/输出大数组组成的屏幕printf(|t$n);printf(t$t- $n);printf(t=n);void copy(int des44,int sor44)/小数组复制到另一个小数组int i,j;for(i = 0;i 4;i+)for(j = 0;j = 0;i-)for(j = 3;j = 0;j-)if(aaji & flag)am+jn+i = aaji;else if(aaji & !flag)am+jn+i = 0;int isbottom()/判断是否到底及模型下是否有其他方块int m = blow;int n = N/2-1+leftright;int i,j,k;int l42= -1,-1,-1,-1,-1,-1,-1,-1;for(i = 0,k = 0;i 4;i+,k+)for(j = 0;j 4;j+)if(aaji)lk0=j+m;/每列最后一个元素的横坐标lk1=i+n;/每列最后一个元素的纵坐标for(i=0;i=M|ali0+1li1)/最后一行是否到底,下一行是否有其他元素return 1;return 0;int isleft()/判断是否到左边界int m = blow;int n = N/2-1+leftright;int i,j,k = 0;int l42= -1,-1,-1,-1,-1,-1,-1,-1;for(i = 0,k = 0;i =0;j-)if(aaij)lk0=i+m;/每行最左边元素的横坐标lk1=j+n;/每行最左边元素的纵坐标for(i=0;ik ;i+ )if(li0 = -1)continue;if(li1-1 0|ali0li1-1)return 1;return 0;int isright()/判断是否到右边界int m = blow;int n = N/2-1+leftright;int i,j,k = 0;int l42= -1,-1,-1,-1,-1,-1,-1,-1;for(i = 0,k = 0;i 4;i+,k+)for(j = 0;j 4;j+)if(aaij)lk0=i+m;/每行最右边元素的横坐标lk1=j+n;/每行最右边元素的纵坐标for(i=0;i= N|ali0li1+1)return 1;return 0;void move(int z)/大数组里的元素下移一行int k,j;for(k=z;k0;k-)for(j=0;jN;j+)akj=ak-1j;for(j=0;jN;j+)a0j=0;int erase()/一行满了消行int i,j,k;int flag,count=0;for(i=0;iM;i+)flag=1;for(j=0;jN;j+)if(!aij)flag=0;break;if(flag)move(i);count+;return count;void cheaterase()/作弊消去最后一行int i,j;move(M-1);blow+;void copyof(int (*aa)4,int n)switch (n)case 0:copy(aa,a1);break;case 1:copy(aa,a2);break;case 2:copy(aa,a3);break;case 3:copy(aa,a4);break;case 4:copy(aa,a5);break;case 5:copy(aa,a6);break;case 6:copy(aa,a7);break;int islose()if(isbottom(a,aa)show(a);printf(tt3331m334m331mGame over!330mn);return 1;return 0;void clear(int (*aa)4)int i,j;for(i = 0;i 4;i+)for(j = 0;j 4;j+)aaij = 0;int findfirstline(int(*bb)4)int k,h,flag;for(k=0;k4;k+)flag=0;for(h=0;h4;h+)if(bbkh)flag=1;if(flag)return k;int findfirstrow(int (*bb)4)int k,h,flag;for(k=0;k4;k+)flag=0;for(h=0;h4;h+)if(bbhk)flag=1;if(flag)return k;void change()int bb44 =0;int i,j,k,h,p,q;for(i = 0;i 4;i+)for(j = 0;j 4;j+)bbji = aai3-j;clear(aa);i=findfirstline(bb);j=findfirstrow(bb);for(k = i,p=0;k4;k+,p+)for(h=j,q=0;h4;h+,q+)aapq=bbkh;void cheatbomb()int i,j,n;n = N / 2 - 1 + leftright;for(i = blow; i blow + 3; i+)for(j = n - 1; j n + 4; j+)aij = 0;void down()if(isbottom()if(zflag)cheatbomb();zflag = 0;bottomoperate();elsepaint(0);blow+;paint(1);second+;show();alarm(1);void downbottom()paint(0);while(!isbottom()blow+;paint(1);void left()if(!isleft()paint(0);leftright-;paint(1);void right()if(!isright()paint(0);leftright+;paint(1);void turn()if(!isbottom() & !isleft() & !isright()paint(0);change();paint(1);void print(int count)switch(count)case 0:printf(ttt3336m331mCome on!330mn);break;case 1:printf(ttt3331m331mGood!330mn);break;case 2:printf(ttt3333m331mPretty!330mn);break;case 3:printf(ttt3334m331mWonderful!330mn);break;case 4:printf(ttt3335m331m334mPerfect_!330mn);break;int cheatmodel()scanf(%d,&v);getchar();return v;void appear()int i,j,u;for(i=0;iM-1;i+)for(j=0;jN;j+)aij=ai+1j;for(j=0;jc_lflag &= (ICANON|ECHO);termios_p-c_ccVMIN = 1;termios_p-c_ccVTIME = 0;/给原终端改变设置tcsetattr(0,TCSANOW,termios_p);char c,y;int n = rand()%7;/随机产生一个图案copyof(aa,n);/把图案赋给模板aaint m = rand()%7;/随机产生一个图案copyof(new,m);/把图案赋给另一个模板newpaint(1);/把aa模板里有数字的方块赋给大屏幕show();/显示大屏幕里的内容,并且给大屏幕里有数字的地方涂色signal(SIGALRM,down);alarm(1);/产生信号,图案自动往下走while(1)fflush(NULL);c=getchar();if(c = q)/q表示退出tcsetattr(0,TCSANOW,termios_pb);exit(0);else if(c = a)/a是向左移left();else if(c = d)/向右移right();else if(c = x)/一键到底downbottom();else if(c = w)/旋转turn();else if(c = 0x5b)/用方向键完成左移,右移,旋转和一键到底y = getchar();switch(y)case 0x41:turn();break;case 0x42:downbottom();break;case 0x43:right();break;case 0x44:left();break;else if(c = e)/作弊键,消除最后一行cheaterase();else if(c = m)/按几出现哪个模型mflag = 1;cheatmodel();else if(c = z)/炸弹zflag = 1;show(a);int main()srand(time(NULL);play();return 0;2.程序截图(1)实现自动下移: (2)实现一键到底(使用向下的键或“x”键): (3)实现左移(用向左的键或“a”键): (4)实现右移(用向右的键或“d”键): (5)实现翻转(用向上的键或“w”键): 六、参考文献(1)、ANSI控制码的说明摘自百度例如:echo -ne 3332m 可以将字符的显示颜色改为绿色echo -ne 333;1H 可以将光标移到第3行第1列处具体的摘抄一些如下:330m 关闭所有属性331m 设置高亮度334m 下划线335m 闪烁337m 反显338m 消隐3330m - 3337m 设置前景色3340m - 3347m 设置背景色33nA 光标上移n行33nB 光标下移n行33nC 光标右移n行33nD 光标左移n行33y;xH设置光标位置332J 清屏33K 清除从光标到行尾的内容33s 保存光标位置33u 恢复光标位置33?25l 隐藏光标33?25h 显示光标 m 设置显示属性. 同样的序列可以设置一个或多个属性.par 作用0 所有属性重设为默认值1 设置边框2 设置亮度减半(用一种颜色模拟另一种颜色)4 设置底纹(用一种颜色模拟另一种颜色)(用于模拟的颜色由using ESC .设置)5 设置闪烁7 设置反转视频(reverse video)10 重设选定映像,显示控制标记,反转元素标记.11 选择空映像,设置显示控制标记,重设反转标记.12 选择空映像,设置显示控制标记,重设反转标记.(在完成映像表转换之前反转标记引起每一字节的高位反转.)21 设置正常亮度(和 ECMA-48 不兼容)22 设置正常亮度24 关闭下划线25 不闪烁27 反转视频关闭30 黑色背景31 红色前景32 绿色前景33 棕色前景34 蓝色前景35 品红前景36 孔雀蓝前景37 白色前景38 打开下划线,设置默认前景色39 关闭下划线,设置默认前景色40 黑色背景41 红色背景42 绿色背景43 棕色背景44 蓝色背景45 品红背景46 孔雀蓝背景47 白色背景49 设置默认背景色(2)、linux中的信号处理(signal和alarm) (摘自百度)信号是unix中所使用的进程通信的一种最古老的方法.系统使用它来同志一个或多个进程异步事件的发生.linux系统库bits/signum.h对信号名作了定义:linux的大多数信号是提供给内核的,仅有少数几种信号可以在信号间发送.SIGHUP 当终止一个终端时,内核就把这种信号发送给该终端所控制的所有进程.SIGINT 当一个用户按下中断键(ctrl+c)后,内核就向该终端用关联的所有进程发送这个信号.SIGQUIT 当用户按下(ctrl+),内核就向该终端用关联的所有进程发送这个信号.SIGILL 当一个进程企图执行一条非法指令时,内核就发送这个信号.SIGFPE 当产生浮点错误时,内核就发送这个信号.SIGKILL 这是一个非常特殊的信号,他可以从一个进程发送到另一个进程,使接收到该信号的进程终止.内核偶然也发送这种信号.SIGALRM 当一个定时器到时的时候,内核就发送这个信号.SIGSTOP 子进程结束信号.UNIX用它来实现系统调用exit(),wait();信号的处理unix的系统调用signal()用于接受一个指定的信号,并可以指定相应的处理方法.在linux系统库signal.h中,它的说明如下:signal(int sig,sighandler_t handler);sig用于指定信号类型.handle是用于处理该信号的函数.handle还可以是:.SIG_IGN 忽略这个信号.SIG_DFL 恢复对这个信号的默认处理当用户按下ctrl+c时,进程被中断,catch()被执行.中断处理函数处理完毕后,转回断点执行下面的指令.当编写自己的中断处理函数时,注意下面两点:1.信号不能打断系统调用.2.信号不能打断信号处理函数. alarm(设置信号传送闹钟)定义函数unsigned int alarm(unsigned int seconds);函数说明alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。如果参数seconds 为0,则之前设置的闹钟会被取消,并将剩下的时间返回。返回值返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0。23
展开阅读全文