俄罗斯方块实验报告.docx
程序设计实践报告(2012/2013学年第2学期)题目:俄罗斯方块嬉戏设计专业学生姓名班级学号指导教师指导单位软件工程系日.期2013.03.27成果辨定叁考标准I程序设计实践环节评分为五级制,即,优秀、良好、中等、与格、不与格.依据程序设计实践过程中学生以下表现评定:学习看法是否端正、试验课前打算是否充分、是否实现课题要求的功能、算法设计是否合理、程序设计语言运用是否娴熟、用户界面设计是否科学、程序设计实践报告完成状况(包括:内容是否详实、文字表达是否流畅、格式是否符合规范、程序注狎是否详细)、答辩表现、考勤等.筒短评语老师签名,2013年3月27日俄罗斯方块嬉戏设计一、课题内容和要求本程序的主要任务就是编写简洁的俄罗斯方块嬉戏,要求设计比较美观和健全的嬉戏界面,可以实现方块预览、方块的限制、显示更新、分数更新以与帮助等基本功能,削减程序木身的错误,增加嬉戏的可操作性。程序的设计将结合些有关C语言图形界面设计的内容,该部分是之前没有接触过的,要求利用这次机会,比较简约地J'解相关内容与其简洁应用。本程序的实现是选用WindowsXP/7操作系统以与MicrosoftVisua1.Studio2008C+为编译器,用C语言完成程序设计的实践。运用Win32限制台应用程序,最终在DoS界面卜形成程序的主界面。二、概要设计struct/此结构体数表是打印各个方块的依据/是该程序设计的灵魂intvaryx4;intvary_y4:vary=U0,2.4,6),(0,0,0,0,(0,0,0.0,0,-1.t-2,-3),0,2,2,0),0,0,-1.,-1.,0,-2,-2,-4,0t0,-,-,U0,0,2,2),(0,-1.,-1.,-2),(0,2,2,4,0,0,-1.,-1.,0,0,-2,-2,0,-1.,-1.,-2,0,0,2,4,0,-1.,0,0),0,0,0,2,(0,-1.,-2,-2,0,0,-2,-4),0,-1,-1,-1),0,2,2,2),O,O,-1.,-2tO,2,4,4,O,O,O,-1.,O,-2,-2,-2fO,0,-1.,-2,O,O,2,4),0,-1,-1,-1),(0,O,0,-2)f(O,-1,-2,-2,0,2,4,2),0,O,0,-1.),0,O,2,OJO,-1.,-1.,-2,(0,-2,O,2),0,-1,-1,-1),0,O,-2,0,(O,-1,-1,-2),本程序中关于方块的颜色、运动的速度都采纳的数组常於的形式,数组的编号分别对应方块形态的编号,这样就使得“方块形态一一方块颜色一一运动速度”一一对应起来,更直观和便利地实现动态管理。constintco1.=1.1.,15,12,12,10,10,9);限制方块的颜色constintspeed=0,12,9,6,3,1);限制方块的卜落速度constintnunber=(0,2,3,5,7,11,15):/对应方块的类型编号本程序的主体部分是由多个函数的循环多层调用来实现的,主要由以下的九个函数构成: voidPrintNext(intkind)该函数限制”下一个图形预览”中的方块样式,是整个程序的“引导者”、“开拓者二将引导程序的动态实现,将确定者下面几个函数的调用 voidStraightFa1.1(intdepth)该函数限制方块的着陆,是在整个程序中发挥着至关重要的作用,是整个动态过程的“纽带”将详细分功能的实现连串起来。VoidChoiceDirection(int*rev-count)该函数实现键盘对小方块的限制,在小方块着陆的过程中要时刻“监视”,因此,在函数中,每当些变量发生变更时.,即要调用该函数voidRevo1.ve(int*prev_count)该函数限制方块的旋转。该函数必需依附于,即每次有键盘的按动时,都要检验是否按r“t”假如按r,马上执行该函数,实行方块的翻转VOid1.eve1.MoveO该函数限制方块的移动方向。该函数类似于第四个函数,也是依附于voidGetDepth(int*pdepth)该函数可以得到小方块已累积的高度,从而推断小方块是否已经落下。该函数是个在多处都须要调用的函数VOidCheckBoundary0该函数可以限制、防止方块越过边界。一旦可能越过边界,则之前的方向限制无效,在方块左右移动、变更形态时都须要调用该函数VoidCheckFu1.1(int*pscore)检验是否有一行完全被覆盖,若有一行全部被排满的,即刻整体下移。在函数的最终,马上调用该函数voidGameOverO即当depth为。时,嬉戏结束,显示动态结束画面。在函数的最起先,马上调用该函数程序流程图大致如下:说明:该程序的流程图略显困难,但其实质,照旧是依次、选择、循环的运用,唯一困难的就是循环的主体不再是简洁的程序语句而是比较困难的函数,这样就会使得程序的结构比较冗杂,但假如把流程图画出来,就会清楚地看到问题照旧是很简洁。四、关健源码for(i=0;i<4;i+)(gotoxy(x+varypkind.vary_xi,y+varypkind.vary_yi);puts(''");该循环是用来依据上述数表打印各种不同的小方块,是木程序中很基本的语句块voidGameOverO画效果(co1.or(391);for(inti=31;i>=0:i")的打印for(intj=0;j<17;j+)(gotoxy(36-j*2,3+i);PUIS("”);S1.cep(5);该函数是用来实现嬉戏结束后的动用两层循环来实现主界面方块/延时函数的调用,使得最终的程序界面更有动感)co1.or(back);for(intj=0:j<31;j+)for(inti=0;i<17;i+)(boardij.having=0;gotoxy(4+2*i,j+3);PUtS("");S1.eep(5):)1voidRevo1.ve(int*prev_COUnt)限制方块的旋转,该函数是整个程序中最难的一部分代码(inti;before,x=current.x;before.current,y;(*prev_count)+:(*pevcount)Vconnectionprikind,sum;current,x=current.x+connectionpri_kind.connection_x*prev_count:current,y=current.y+connectionfprikind1.connectiony*prevcount;CheckBoundaryO:调用检验边界函数,防止方块在旋转过程中越出程序界面for(i=0;i<4;i+)int11=(currenI.x-4+varynumberpri_kind+*prev-count.vary_xi)2;intn=current.y+varynumberpri_kind+*prevcontJ.vary_yi-3;if(boardmn.having)m>16m<0n>31)(current,x=current.-connectionpri-kind.connection_x*prev_count;current,y=current.y-connection1.pri_kind.connection_y*prev_count;revo1.ve=0:(*prevcount);before,x=current.x;before,y=current.y;return:)co1.or(back);用背兔色把旋转前的方块覆盖for(i=0;i<4;i+)if(before,y+varykind.vary-yi>2)gotoxy(before,x+vary1.kindJ.vary_xi,before,y+varykind.vary_yi);puts(”);)kind-numberpri_kind+*prev_count:co1.or(co1.pri_kind):/打印旋转后的新的方块for(i=0:i<4;i+)(if(current.y+varykind.vary-yi>2)(gotoxy(current,x+varykind.vary_xi,current,y+varykind.vary_yi):PUtSe'"):)S1.eep(speedrank);依据等级来确定旋转、卜落的速度before,x=current.x;before.y=crrent.y;intmain()主函数是将一切子函数连串在一起的工具(srand(unsigned1.ong)time(0):gotoxy(5,5);Printfr一切打算就绪,嬉戏是否起先?n");gotoxy(4,6);Printf("起先嬉戏请按S键,否则按随意其他键退出。n"):charc;c=getchar();if(c!='S')exit(0);system(*,C1.Sa,):/检验systcm(*co1.or24*);设置背景颜色intSeorC=0;文件是否能够胜利打开FI1.E*fp=fopen("Ue1.s.txt","r");ifCfp=NU1.D(fp=fopen(*Ce1.s.txt*,*w*);fprintf(fp,飞d”,score);fc1.ose(fp):)PrintGarphyO;框架以与其中的文字kind=rand()%7;块的形态intdepth;whi1e(1)巾熨若嬉戏的过程(intpkind=rand()%7;PrintNex1.(pkind);current.x=20;始出现的位置current.y=2;Ge1.Dep1.h(&depth);if(depth=O)(GameOverO;为O时,嬉戏结束score=0;rank=1.:/打印程序的边界和基本随机函数来随机确定方是一个无限循环,不断地方块初当剩余的高度pri_kind=kind;if(kind=6)kind=15,current.-=2;e1.seif(kind=5)e1.seif(kind=4)e1.seif(kind=3)e1.seif(kind=2)e1.seif(kind=1.)kind=1.1.:kind=7;kind=5;kind=3,current.x+=2kind=2;e1.sekind=O,current,x-=2:StraightFa1.1.(depth):/调用方块下落函数for(inti=0;i<4:i+)(intm=(current,-4+varykind.vary_xi)/2;intn=cun-ent.y-3+varykind.vary_yi;boardmn.having1.;boardinn.co1.or=co1.pri_kind;)CheckFu1.K&score):/检验是否有满行的出现,若有,整体卜.呀,并加分kind=pkind;)return0:五、测试数据与其结果分析就1患翦黑铤游巍雕黯他键退出.程序正式运行前所给的提示信息程序主界面运行过程中的程序界而这是嬉戏结束时,动态界面的一瞬间截图六、程序设计实践中的关健技术难题以与解决方法【问题1】在左右方向变更方块的位置时,若始终按住左键或右键,会出现方块部分或整体越过程序界面的现象。【分析】在程序中层已经定义和调用了函数VoidGetdePtho用来计尊已经累计的高度,并由depth的值是否为0来推断是否嬉戏结束,是否方块已经着陆。因此,在程序中还应当定义另个函数,用来推断是否方块越界,并在其变换和左右移动时调用,以防止方块的越界。【解决方法】在主程序中定义,个新的函数voidCheckBoundary0,其定义如F:voidCheckBoundary()inti,n,m;for(i=0;i<4;i+)(m=current.x+varykind.vary_xi:if(11<4m>36)(current,x+三(tn<4?2:-2):i=0;)for(i=0;i<4;i+)m=(current,x+varykind.vary_xi4)/2;n=current.y+varykind.vary_yi-3;if(n<O!n>31boardmn.having)(if(Ieve1.=I)current,x+=2;e1.seif(1.eve1.=2)current,-=2;Ieve1.=O;break;)【问题2】在程序的调用中,发觉程序每次运行,“最高分”都会从0起先记。即使我把HighScore设为全局变量,照旧是出现该问题。【分析】程序的每一次运行,都会把每个变量初始化。尽管全局变量的作用域是从变量:定一起先始终到程序结束,然而,照旧不是“永久”,随着程序的结束而覆灭。因而,解决的方法就是,与时的把最高分写到计算机的文件中,来求得“永久”的保存。【解决方法】在程序中添加以下代码,一旦有新的最高分生成,则马上写入文件1.e1.s.1.x1.FI1.E*fp=fopen(*C:e1.s.txt",-r*)fscanf(fp,w%d*,&1.);fc1.ose(fp);if(*pscore>t)fp=fopen(*C:e1.s.txt*,w);Iprintf(fp,%d*f*pscore);fc1.ose(fp);co1.or(43);gotoxy(51,16);Prin1.f(飞d”,*pscore):)fc1.ose(fp);七、总结与展望两周的编程实践,对于自身编程水平是个不小的提高.实践是相识的来源、实践是相识发展的动力,第一次接触实践课,让我深刻感受到了实践出真知的道理。因此,在以后的学习中,要树立实践的意识,提高把理论学问上升为实践的实力。做成事情的前提是有一个合理可行的规划.在本次程序设计实践的起先阶段,由于编译器的选择问题(即ViSUa1.C和TUrboC),项目几乎停滞不前,导致很多时间白白奢侈。因此在以后的实践中,要在任务IE式开展之前,就要做好充分的规划,避开手忙脚乱。适当的借鉴和对案例的充分分析加之对书本、互联网等工具的充分运用,才能把一项比较浩大的工程做好。在这次的程序设计中,我在互联网上找到了至少30个有关俄罗斯方块的例子,其中能够无差错运行的少之乂少。本人花费了很大的功夫细致地探讨了那六七个案例,体悟它们程序实现的方法,找到之间的区分和相同之处。在阅读每一个程序时,多多少少都会有不完全理解甚至是根本看不懂的地方,这个时候,书本网络就是一个极好的工具。在两周的时间里,除r思索和面对电脑,剩余的时间几乎就在图书馆里找资料,在追求学问的过程中,也有了很多新的发觉。独立的思索是一切胜利的基础。把一切的感知和灵感都要归咎与自己的思索。每个案例的分析都是我独立思索的结果,以后的学习生活中都要擅长思索、勤于思索。分部编写各个函数,并对其逐进行测试,化大为小,是本次实践的大收获,是编写较困难程序的有效方法。这样有利于条理的清楚,避开错误。合作与沟通是胜利的捷径。与同课题的同学合作甚至是与不同课题同学的沟通,都会让我产生思想的火花,在困惑自己许久的问题上带来突破。在程序中,本人很想再编写个计时器,用来计量嬉戏已经运行的时间,几经尝试都没有胜利。初步猜想,可能须要多线程的限制,下一步在这个问题上力求有所突破。源代码:/*Postsand* Copyright(C)2013,ZhangXu,NanjingIniversiIyTe1.ecommunications* A1.1.rightsreserved.* 作者:ZhangXu* 完成日期:2013年3月29日* /Sinc1.udewindowsinc1.ude<ctime>Sinc1.ude<conio.h>inc1.ude<cstdio>constintback=34:constinico1.=1.1.,15,12,12,10,10,9):/用数组记录不同方块的颜色speed=(0,12,9,6,3,1);rank-1;constint用数组记录不同的着陆速度constintnumber=(0,2,3,5,7,11,15);intkind:intpri_kind;int等级状态标记,由此确定下落的速度intrevoIve-O;intIeve1.=O;struct(intx;inty:!current,before;struct(intx:inty:intco1.or:boo1.having;board1732:structintvary_x4:intvary_y4;vary=0,2,4,6,O,O,O,O,O,O,O,O),O,-1,-2,-3),(O,2t2,O,(O,O,-1.,-1.,0,-2,-2,-4),O,O,-1.,-1.),(O,O,2,2,0,-1.,-1.,-2,O,2,2,4,O,O,-1.,-1.,(O,0,-2,-2),0,-1.,-1.,-2B,O,O,2,4),O,-1,O,O),(O,O,O,2,0,-1.,-2,-2),O,0,-2,-4),0,-1,-1,-1),(0,2,2,2,(O,0,-1.,-2,0,2,4,4),0,O,0,-1.),0,-2,-2,-2,0,0,-1.,-2,0,O,2,4,0,-1,-1,-1),(0,O,0,-2,(O,-1,-2,-2,0,2,4,2),0,O,0,-1.),0,O,2,0,0,-1,-1,-2),0,-2,O,2),0,-1,-1,-1),(0,0,-2,0,(O,-1,-1,-2),);struct(intsum;intconnection_x5j;intconnection_y5;Jconnection=1,0,O,2,2,-2,O,O),2,-2,2,O,Of4,-2,0,4,-2,O,O,-1.,1.4,-4,2.-2,4),O,O,-1.,1,4,-2,2,0.0,(0,0,0,0),):voidgotoxy(intx,inty)光标位置确定函数COORDpos:pos.X=x;pos.Y=y:SetConso1.eCursorPosition(GetStdHandIe(STD_0UTPUT.HAND1.E),pos);)voidco1.or(intb)该函数的调用可以确定输出内容的颜色(HAND1.EhConso1.e=GetStd1.1.and1e(STD_OUTPCT_1IAND1.E):Setconso1.eTextAttribute(hConso1.e,b);)voidPrintGarphyOco1.or(1593);初始化限制台窗口,获得窗口句柄,设置边框颜色inti,j;gotoxy(2,2);/确定光标从其次行其次列起先,以下起先打印边框for(i=0:i<28;i+)Printf("8T);gotoxy(2,35):for(i=0;i<28:i+)PrintfS");for(i=0;i<33;i+)(gotoxy(2,3+i);Printf("冢);)for(i=0;i<33;i+)(gotoxy(38,3+i);if(i=7i=18)prntf(皴教遨癌檄癌檄廉逐羲);e1.sePrintf("8T);for(i=0;i<33;i+)gotoxy(56,3+i);PrintfCfi);)至此,完成主框架的打印intt:FI1.E*fp=fOPen("C:e1.s.txt","r");fscanf(fp,w%d*,&1.);fc1.ose(fp);co1.or(45):以卜起先打印框架内的文字gotoxy(40.4);Printf(”下一个图形预览:n");gotoxy(40,11);co1.or(43);PrinIr("最高纪录:n*):co1.or(44);gotoxy(42,13):Printf("%d*,t):co1.or(46):gotoxy(40,16);co1.or(44):gotoxy(40,18);Prin1.f("等级:n*);co1.or(39);gotoxy(40,23):puts("请运用键盘的”);gotoxy(40,24):puts("以卜按键来限制:”);gotoxy(40,26):puts(tI-*");gotoxy(10,28):puts按空格河停”);gotoxy(40,29):PUtsC按ESC退出”);gotoxy(40,34);puts(*版本号:VI.2.);co1.or(back);/使嬉戏区域全为背景颜色,初始化嬉戏界面for(i=0;i<17;i+)(for(j=0:j<32:j+)boardij.y=3+j;boardij.having=0;gotoxy(boardiCj.x,boardij.y);puts(,");)voidPrintNext(intpkind)/限制“下一个图形预览'中的方块inti:intx=46,y=7:co1.or(back);for(i=0;i<4;i+)(gotoxy(-3,y+i-2);puts(*");pri_kind=pkind;if(pkind=6)pkind=15,x-:调用航机函数来确定下一个出现的方块的形态e1.seif(pkind=5)pkin(i=1.1,x-;e1.seif(pkind=4)pkind=7,x-;e1.seif(pkind=3)pkind=5,x-=2;e1.seif(pkind=2)pkind=3,x+=2;e1.seif(pkind=1.)pkind=2,x一;e1.sepkind-O,-=3;co1.or(co1.pri_kind);for(i=0;i<4;i+)(gotoxy(x+varypkind.vary-xi,y+varypkind.vary_yi);PU1.S("");)voidGetDepth(int*pdepth)统计方块已经累积后,所剩余的高度(*pt1.ep1.h=31;for(intj=0J<4J+)(intSUm=0;intn=(current.x+varykind.vary_xj-4)/2;intm=current.y+varykind.vary_yj-3:if(m<0)tn=O;for(m+;m<32;m+)if(boardnm.having=0&&n>=0&&n<17&&ni>=0&&m<32)sum+;e1.sebreak;if(sum<(*pdepth)(*pdep1.h)=su11:)GameOverOvoid嬉戏结束时调用的函数,显示简易的小动Mico1.or(391);for(inti=31;i>=0;i)for(intj=0;j<17;j+)(gotoxy(36-j*2,3+i);PUtS("”):S1.eep(5):)co1.or(back);for(intj=0J<32J+)for(inti=0;i<17;i+)boardij.having=0;gotoxy(4+2*i,j+3);PUtS("");S1.eep(5):)gotoxy(45,18):whi1.e(rank)PrinIf("");co1.or(46);gotoxy(50,16):puts(*);/清空分数鼠示)voidCheckBoundary()限制、防止方块越过边界。旦可能越过边界,则之前的方向限制无效(inti,n,m;for(i=0:i<4;i+)(m=current.x+varykind.vary_xi;if(m<4m>36)(current,x+=(m<4?2:-2);i=0:)for(i=0;i<4;i+)(m=(current,x+varykind.vary_xi4)/2:n=crrent.y+varykind.vary_yi-3;if(n<0In>31boardmr.having)(if(1.eve1.=1.)移的纭原current,x+=2;e1.seif(1.eve1.=2)右移的更原current,-=2;Ieve1.=O;break;)void由IeVe1.的值来限制方块的移动方向左1.eve1.MoveOCheckBoundaryO;if(Ieve1.=O)reIurn:inti;co1.or(back);for(i=0;i<4;i+)(if(current,y+varykind.vary_yi>2)(gotoxy(before,x+varykind.vary_xi,before,y+varykind.vary_yi);puts(*");)co1.or(co1.pri_kind);for(i=0;i<4;i+)(if(current,y+varykind.vary_yi>2)(gotoxy(current,x+varykind.Varycurrent,y+varykind.vary_yi);PUISeW);before,x=curren1.x;voidRevo1.ve(int*prev_count)限制方块的旋转(inti;before,x=current.x;before,y"current.y;(*prev_count)+;(*prev_count)%=connectionpri_kind.SUn;current,x=curren1.x+conrectionpri_kind.connection_x*prev_count;current,y=current.y+connectionpri_kind.connection_y*prev_count;CheckBoundaryO;for(i=0;i<4;i+)确定变换的位置(intm=(current,-4+varynumberpri_kind*prev_count.vary_xij)2;intn=curren1.y+varynumberpriind1+*prev_count.vary_yi-3;if(boardmn.havingim>161im<On>31)current,x=current.x-connectionCpri_kind.connecIion_x*prev_couriI;current,y=current.y-connectionpri_kind.connection_y*prev_count;revo1.ve=0;(*prev_count);before,x=current.x;before,y=current.y;return;)co1.or(back);for(i=0;i<4;i+)/将待变换的区域设为背毋色(if(before,y+varykind.vary_yi>2)(gotoxy(before,x+varykind.vary_xi,before,y+varykind.vary_yi);puts(,");)kind=nunberpri-kind1*prev_count;co1.or(co1.pri_kin(i);for(i=0;i<4;i+)重新打印新的方块if(current,y+varykind.vary_yi>2)(gotoxy(current,x+varykind.Varycurrent,y+varykind.vary_yi):PUtS("");)S1.eep(speedrank):由嬉戏等级确定方块变更速度before,x=cuent.x;before,y=current.y;)voidChoiceDirection(int*prev-cont)时刻监视是否触动键盘,并对不同的触动做处理(intt=50rank;whi1.e(t一)if(kbhit()charc=getch();swi1.ch(c)(case0x48:/uprevoIve=I;Revo1.ve(&prev_count);break;case0x50:/downIeve1.=0;return;case0x4b:/1.eftbefore,x=current.x;current,-=2;Ieve1.=I;1.eve1.MoveO;break;case0x4d:/rightbefore,x=current.x;current,x+=2;Ieve1=2;1.eve1.MoveO;break;case',:_getchO;break;case27:co1.or(back):exit(1.);defau1.t:break:)S1.eep(speedrankJ);)voidStraightFa1.1.(intdepth)限制方块着陆的函数,是本程序的重中之重(inti;intrev_coun1.=0;before,x=crrent.x;before,y=current.y;whiIe(GetDepth(Adepth),depth)ChoiceDirection(&rev_count);GetDepth(&depth);if(depth=O)(revo1.ve=O:before,x=current.x;before,y=curren1.y;break;)co1.or(back):for(i=0;i<4;i+)(if(before,y+varykind.vary_yi>2)(gotoxy(before,x+vary1.kindJ.vary_xi,before,y+varykind.vary_yi);PU1.S("");)current,y+;co1.or(co1.pri_kind);for(i=0;i<4:i+)if(current,y+varykind.vary_yi>2)(gotoxy(current,x+varykind.Varycurrent,y+varykind.vary_yi):PUtS("");)revo1.ve=0;before,x=current.x;before,y=current.y;)ChoiceDirection(&rev_count);if(1eve1irevo1.ve)(Ieve1.=O;revo1.ve=0;ChoiceDirection(&rev_count);StraightFa1.1(depth);voidCheckFu1.1.(ini*pscore)/检验是否有满行,假如有,消行、加分(inti,j:intSUin=O:ints=0,1,3,6,10);for(i=0;i<32;i+)(boo1.mark=1.:for(j=0J