【转载】88行代码实现俄罗斯方块游戏(含讲解)来源:在正式阅读本文之前,请你记得你应该用娱乐的心态来看,本代码所使用到的技巧,在工作了的人眼里会觉得很纠结,很蛋疼,很不可理喻,很丑,注意,是你蛋疼,不关我的事通常,写一个俄罗斯方块,往往动不动就几百行,甚至上千行,而这里只有88行正所谓头脑风暴,打破常规。这里将使用很多不平常的手段来减少代码以下是Win-TC可以成功编译并执行的代码(代码保证单行长度不超过80字符,如果你是Win7系统,那请看后文):程序代码:#includegraphics.h#includeconio.h#includestdlib.hintgcW=20,gcColor[]={DARKGRAY,LIGHTBLUE,LIGHTGREEN,LIGHTCYAN,LIGHTRED,LIGHTMAGENTA,MAGENTA,YELLOW};structtetris{int_pool[16][32],(*pool)[32],tmap[8][4][16];intx,y,s,st,t;}gt;voidtrsInit(){intsp[8][4]={{15,4369},{23,785,116,547},{71,275,113,802},{39,305,114,562},{54,561},{99,306},{51,51},{-1}};int*p,i,j,b;for(p=sp[0];*p=0;++p)if(*p==0)*p=p[-2];gt.pool=>._pool[4];for(j=0;j7;++j)for(i=0;i4;++i)for(b=0;b16;++b)gt.tmap[j+1][i][b]=(sp[j][i]&1)*(j+1),sp[j][i]=1;memset(gt._pool,-1,sizeof(gt._pool));for(i=0;i10;++i)memset(>.pool[i],0,sizeof(int[21]));return;}inttrsCopy(intsp[],intx,inty,intc){intm[]={0,32,64,96,1,33,65,97,2,34,66,98,3,35,67,99},i,cx,cy;for(i=0;i16;++i)if(sp[i]){cx=x+(m[i]5),cy=y+(m[i]&31);if(gt.pool[cx][cy])if(c==2)gt.pool[cx][cy]=0;elsereturn0;if(c==1)gt.pool[cx][cy]=sp[i];}return1;}inttrsScene(){intx,y=0;gt.s=random(7)+1,gt.st=gt.t=0;gt.x=4,gt.y=0;for(--gt.t;;delay(10),--gt.t){intk=0;while(kbhit()){k=getch();if(k==27)return0;if(k=='A'||k=='a'){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x-1,gt.y,0))--gt.x;}elseif(k=='D'||k=='d'){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x+1,gt.y,0))++gt.x;}elseif(k=='W'||k=='w'){if(trsCopy(gt.tmap[gt.s][(gt.st+1)%4],gt.x,gt.y,0))gt.st=(gt.st+1)%4;}}if(k=='S'||k=='s'||gt.t0){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y+1,0))++gt.y,gt.t=50;else{trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,1);for(--y;y0;--y){for(x=0;gt.pool[x][y]0;++x);if(gt.pool[x][y]0)for(k=y++;k0;--k)for(x=0;gt.pool[x][0]=0;++x)gt.pool[x][k]=gt.pool[x][k-1];}return1;}}trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,1);for(x=0;gt.pool[x][0]=0;++x){for(y=1;gt.pool[x][y]=0;++y){setfillstyle(1,gcColor[gt.pool[x][y]]);bar(201+x*gcW,1+y*gcW,200+gcW+x*gcW,gcW+y*gcW);}}trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,2);}}intmain(){intg=DETECT,m=0;initgraph(&g,&m,);randomize();trsInit();while(trsScene());return0;}如果你没有Win-TC,或者你是Win7系统,可以用这个能用VC6编译的工程包:以上是图形界面版本,显示看起来好看一些但为了能更通用,还有一份控制台版本的代码,同样是88行,直接复制到VC即可编译:程序代码:#includewindows.h#includestdio.h#includetime.h#includeconio.h#includestdlib.hchargcText[]=1LJTSZ#;structtetris{int_pool[16][32],(*pool)[32],tmap[8][4][16];intx,y,s,st,t;}gt;voidtrsInit(){intsp[8][4]={{15,4369},{23,785,116,547},{71,275,113,802},{39,305,114,562},{54,561},{99,306},{51,51},{-1}};int*p,i,j,b;for(p=sp[0];*p=0;++p)if(*p==0)*p=p[-2];gt.pool=>._pool[4];for(j=0;j7;++j)for(i=0;i4;++i)for(b=0;b16;++b)gt.tmap[j+1][i][b]=(sp[j][i]&1)*(j+1),sp[j][i]=1;memset(gt._pool,-1,sizeof(gt._pool));for(i=0;i10;++i)memset(>.pool[i],0,sizeof(int[21]));return;}inttrsCopy(intsp[],intx,inty,intc){inti,cx,cy;for(i=0;i16;++i)if(sp[i]){cx=x+(i&3),cy=y+(i2);if(gt.pool[cx][cy])if(c==2)gt.pool[cx][cy]=0;elsereturn0;if(c==1)gt.pool[cx][cy]=sp[i];}return1;}inttrsScene(){intx,y=0;COORDpos={0};gt.s=rand()%7+1,gt.st=gt.t=0;gt.x=3,gt.y=0;for(--gt.t;;Sleep(1),--gt.t){intk=0;while(kbhit()){k=getch();if(k==27)return0;if(k=='A'||k=='a'){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x-1,gt.y,0))--gt.x;}elseif(k=='D'||k=='d'){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x+1,gt.y,0))++gt.x;}elseif(k=='W'||k=='w'){if(trsCopy(gt.tmap[gt.s][(gt.st+1)%4],gt.x,gt.y,0))gt.st=(gt.st+1)%4;}}if(k=='S'||k=='s'||gt.t0){if(trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y+1,0))++gt.y,gt.t=50;else{trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,1);for(--y;y0;--y){for(x=0;gt.pool[x][y]0;++x);if(gt.pool[x][y]0)for(k=y++;k0;--k)for(x=0;gt.pool[x][0]=0;++x)gt.pool[x][k]=gt.pool[x][k-1];}return1;}}trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,1);SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);for(y=1;gt.pool[0][y]=0;++y,putchar(10)){for(x=0;gt.pool[x][0]=0;++x){putchar(gcText[gt.pool[x][y]]);}}trsCopy(gt.tmap[gt.s][gt.st],gt.x,gt.y,2);}}intmain(){srand((unsigned)time(NULL));for(trsInit();trsScene(););return0;}区别仅仅是绘画用的函数不同而已,不过控制台版显示效果自然会差很多了当你玩下去,你如果堆放到最顶,输了的话,程序就会以最为华丽的方式:Crash(程序崩溃)谢幕======================================华丽的分割线========================================以下是对代码的压缩方法进行分析首先,通常我们需要准备7种方块,4个方向的形状表,相当多的俄罗斯方块程序就是在开头写了这样一个很长的数组定义,有的光这个定义就直接超100行了,这个程序是怎么实现的呢?其实这个程序,同样是使用一个7*4*16的数组来保存这个形状表,但是,它没有直接初始化,见这个数组的定义:intsp[8][4]={{15,4369},{23,785,116,547},{71,275,113,802},{39,305,114,562},{54,561},{99,306},{51,51},{-1}};这个莫名其妙的数组的值是什么意思呢?其实很好猜的,我们尝试把这些数化为二进制:15=11114369=1000100010001合理地四位四位拆开,从低位到高位,从左到右,从上到下排列一下:11110000000000001000100010001000你终于发现,这就是长条方块的两个形状后面类似然后你会发现,这个数组并不完整,有的只定义了两个形状,有的是四个形状,没定义的数会默认置0的,这个怎么解释?看这个数组定义的下面第二行:for(p=sp[0];*p=0;++p)if(*p==0)*p=p[-2];意思是找出这个数组为0的元素,用它前面的元素值填上即*p=p[-2]而数组中最后一个元素值-1起监督头的作用,用于让这个循环跳出虽然可以把这些常数全直接写在数组里,但常数太多显得不太好,就这样写了之后你看到这行代码:gt.pool=>._pool[4];为什么定义两个pool呢?因为我们需要在原来的pool的界外用-1值填充,以便后面做碰撞检测减少不必要的代码但如果直接用原来的_pool,那每次访问都要加上一个偏移常数,不美观且显得代码长,就用另一个指针直接指向开始的位置然后,后面的三重循环就是解开那个位压缩数组以初始化gt.tmap数组,这个数组就是记录7*4种形状的数组再下