c语言俄罗斯方块编程

来源:互联网 发布:sql select between 编辑:程序博客网 时间:2024/05/01 04:46

俄罗斯方块最详解

    (由于写这个文档的排板与预览的效果不一样,故到现在才有空改成原来的样子,请大家不要整个页面文字

复制,而是请整个页面另存为,这样的排板便于阅读理解,又或者QQ:42595947(黑伯爵)联系我(那个qq

提问的答案是13457371117),我给排板好的.txt文件你(好像是68k大)。在最后附有完整的源代码细心、慢慢地看吧,有不妥之处还请高手请教)

    做每一件事前,都会有一个粗略的构想。编程更应该这样,现在先说一些大的、粗略的东西。(过程中有例子,均通过了检验)
****************************************************************************************
****************************************************************************************
目录:
    ●屏幕的划分
    ●图形显示
    ●三种坐标。 绝对坐标、相对坐标、左上角坐标
    ●方块的构造
    ●动画效果 (有例子)
    ●键盘控制 (有例子)
    ●判断方块碰撞
    ●消行
    ●变形
    ●关于菜单的制作 (有例子)
    ●附录(完整的源程序)
****************************************************************************************
****************************************************************************************
1、屏幕的划分
    将整个屏幕划分成四部分:a、一个没盖的杯子;b、一个不断下落4*4数组的盒子;c、一个给
预览下一个方块4*4数组的盒子;d、提示信息。由于提示信息比较简单,这里只讨论前三样。

没盖的杯子:      
    即平时说玩这款游戏时,下落方块不可超出的那个边界,下落的方块从这个“杯口”的上方往下下落,方块只在“杯子”里移动、变形、停止。  
    游戏空间指的是整个游戏主要的界面(呵呵,其实就是所说的“杯子”)。实际上是一个宽10格子、高20格子的  游戏板。用一个全局数组GameSpace[22][12]表示。表示的时候:GameSpace[x][y]为1时表示游戏板上(x,y)这个位置上已经有方块占着了,GameSpace[x][y]为0表示游戏板上这位置还空着。为了便于判断形状的移动是否到边、到底,初始的时候在游戏板的两边各加一列,在游戏板的下面加一行,全 部填上1,表示不能移出界。即GameSpace[x][0],GameSpace[x][11](其中x从0到20)初始都为1,GameSpace[20][y](其中y从0到11)初始都为1。 
 
   0 1 2 3 4 5 6 7 8 910 
00■□□□□□□□□□□■ 
01■□□□□□□□□□□■ 
02■□□□□□□□□□□■ 
03■□□□□□□□□□□■ 
04■□□□□□□□□□□■ 
05■□□□□□□□□□□■ 
06■□□□□□□□□□□■ 
07■□□□□□□□□□□■ 
08■□□□□□□□□□□■
09■□□□□□□□□□□■ 
10■□□□□□□□□□□■ 
11■□□□□□□□□□□■ 
12■□□□□□□□□□□■ 
13■□□□□□□□□□□■ 
14■□□□□□□□□□□■ 
15■□□□□□□□□□□■ 
16■□□□□□□□□□□■ 
17■□□□□□□□□□□■ 
18■□□□□□□□□□□■ 
19■□□□□□□□□□□■
20■■■■■■■■■■■■
 
下落的4*4盒子:
    即7种标准的方块形状,如我认为比较经典的测试方块“7字形”
可看作:
  0 1 2 3
 0□□□□                      {{0,0,0,0},
 1□■■□  用数组表示则是   {0,1,1,0},
 2□□■□                       {0,0,1,0},
 3□□■□                       {0,0,1,0}}

预览4*4数组盒子:
    即玩这款游戏时,给看下一个方块是什么样的窗口。
    这三样东西可以这样联系起来:一个不断下落的盒子由杯子的上方下落到杯子底部,之后将预览盒子的东西放到下落的4*4盒子中,如此循环反复……


2、图形显示 
  Tc2.0中有两种显示模式,一种是我们所熟知的字符模式,另一种是图形模式。在字符模式下只能显式字符,如ASCII字符。一般是显示25 
行,每行80个字符。程序缺省的是字符模式。在字符模式下不能显式图形和进行绘图操作。要想进行图形显示和绘图操作,必须切换到图形模 
式下。 
  Tc2.0中用initgraph()函数可以切换到图形模式,用closegraph()可以从图形模式切换回字符模式。initgraph()和closegraph()都是图形
函数,使用图形函数必须包括头文件"graphics.h"。
  void far initgraph(int far *graphdriver,int far *graphmode,char far *pathtodriver);graphdriver是上涨指向图形驱动序号变量的指针;graphmode是在graphdriver选定后,指向图形显示模式序号变量的指针。pathtodriver表示存放图形驱动文件的路径。 特别值得一提的是驱动文件路径的写法,由于有转义字符,如bgi文件夹(一般这个文件夹就在Tc文件夹目录下,这个说明文档黙认bgi在c:/,后面举的例子亦然)在c:/,写的时候很多人写成"c:/bgi",少了个/,应写成"c://bgi",切记。
    此外,我还强烈建议借一本叫《c作图与c汉字技术》的书,这是我见过介绍c 绘图方面最全面的书了,为了节省时间,可以直接看这几个函数,这些函数均包含在头文件#include <graphics.h>中
void line( int x,int y,int xx,int yy); /*从(x,y)到(xx,yy)处以setcolor()决定的颜色画直线*/

void setlinestyle(int linestyle , unsigned upattern ,int thickness );     
                                     /*linestyle 变化范围为 0 ~ 4,也可以是大写的英文*/
                                     /*分别表示实线,点线,中心线,点画线,用户自定义线*/
                                     /*upattern处一般写0*/
                              /*thickness只有1、3两个值,分别表示一个像素宽,三个像素宽*/
                                      
void setcolor (int color );   /*决定前景颜色,color 变化范围为 0 ~ 15也可以是大写的英文*/

void setbkcolor(int color);   /*决定背景颜色,color 变化范围为 0 ~ 15也可以是大写的英文*/

void rectangle(int x,int y,int xx,int yy);/*以(x,y)为左上角,以(xx,yy)为右下角画矩形*/

void bar(int x,int y,int xx,int yy);   /*以(x,y)为左上角,以(xx,yy)为右下角画条形*/

void bar3d(int x,int y,int xx,int yy,int depth,int topflag);
                                       /*以(x,y)为左上角,以(xx,yy)为右下角画立体条形*/
                                       /*depth决定了三维直方图的长度*/
                                       /*当topflag非0时,画出三维顶,否则不画出三维顶*/

void setfillstyle(int pattern,int color);/*pattern变化范围为 0 ~ 12也可以是大写的英文*/
                                         /*color 变化范围为 0 ~ 15也可以是大写的英文*/

char* itoa(int n,char* str,int radix);  /*n是要转的整型变量 */
     /*str用来存的字符串*/
     /*radix是按什么进制转化*/
                                        /*能将整型数字转成字符型数字,结合outtextxy()*/
                                        /*可将得分、菜单上的数值等简单地显示在屏幕上*/

void settextjustify(int horiz,int vert);
/*horiz变化范围为 0 ~ 2,也可以是大写的英文,分别代表左对齐,字符串中心对齐,右对齐*/
/*vert 变化范围为 0 ~ 2,也可以是大写的英文,分别代表底部对齐,中心对齐,顶部对齐*/

void settextstyle(int font,int direction,int charsize);
/*font变化范围为 0 ~ 12也可以是大写的英文*/
/*direction只能是0、1,也可以是大写的英文,0、1分别代表水平输出,垂直输出*/
/*charsize变化范围为 1 ~ 10也可以是大写的英文,数值越大,字体越大*/

void outtextxy(int x,int y,char* str);    /*按settextjustify()决定的对齐方式*/
                                        /*以setcolor()决定的颜色,在(x,y)附近输出字符串*/
(在第10点的小程序中有以上的大部分函数的实现)


3、三种坐标。 绝对坐标、相对坐标、左上角坐标
绝对坐标:
    图形屏幕其实是个y轴的正方向朝下,原点在屏幕最左上角的直角坐标系,vga模式下的一般分辨率为640*480,这样就把屏幕横向、纵向分别平分640等分、480等分,横、纵坐标变化范围分别为0 ~ 639,0 ~ 479,每个像素点占用一个1的长度。我们说的绝对坐标就是以屏幕原点为相对点的切切实实的坐标。

相对坐标:
    即以屏幕上某个绝对坐标为相对点(或把它看成原点),在这个基础上再计算的坐标。
    如以(0,0)为相对点,(1,1)的绝对坐标为(1,1),相对坐标为(1,1);以(2,2)相对点,(1,1)的绝对坐标为(1,1),相对坐标为(-1,-1)。

左上角坐标:
    特别提出这个,是因为在俄罗斯方块中解决数组与屏幕连接的关健所在。如画个小正方形时用函数rectangle(x,y,x+单位长度,y+单位长度)
(其中(x,y)为这个正方形的左上角坐标,单位长度可以是10,16,我就取了16,这样在640*480中便变成了 (640/16)*(480/16) = 40*30 ),又如前面说的GameSpace[21][12]可以这样化散为整成GameSpace[(x-相对原点的x)/单位长度][(y-相对原点的y)/单位长度]。


4、方块的构造
    为了简单处理,我选择了数组构造方块,而不是坐标描点,但这样浪费空间。在要画的地方赋1,不画的地方赋0,如长条可表示为
  0 1 2 3
 0□■□□                      {{0,1,0,0},      到了这里,我们应     ____
 1□■□□  用数组表示则是   {0,1,0,0},      已深入的将各种方    |__  |          ■■
 2□■□□                       {0,1,0,0},      块看成由四个小正        | |   看成     ■
 3□■□□                       {0,1,0,0}}      形组成,如               |_|             ■

为突出立体感,可以在画了一个小正方形后,再用line()函数在这个小正方形的左上角坐标附近画横、纵两条白线,再在小正方形的右侧画一条黒线,如图是放大的小正方形
   ________   
  | ______ |  (其中,里面的横线为白,里面左侧的纵线为白,里面右侧的纵线为黒)
  ||         ||
  ||         ||
  ||         ||       
  |_______|
  
   在我的程序里就没搞立体感,只为体现算法而已。
   现在,整个屏幕在我们眼里应达到这样理性的看法:
 
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□
□□□□□□■□□□□□□□□□□■□□□■■■■■■□□□□
□□□□□□■□□□□□■■□□□■□□□■□□□□■□□□□
□□□□□□■□□□□■■□□□□■□□□■□■■□■□□□□
□□□□□□■□□□□□□□□□□■□□□■□■■□■□□□□ 其中每个小正方形放大看为
□□□□□□■□□□□□□□□□□■□□□■□□□□■□□□□
□□□□□□■□□□□□□□□□□■□□□■■■■■■□□□□          (x,y)______
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□             |           |
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□             |           |
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□             |           |
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□             |_______|   
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□     (x,y)为左上角坐标
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□□□□■□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□■□□■□□□□□□□□□□□□□
□□□□□□■□□□□□□□■□□■□□□□□□□□□□□□□ 
□□□□□□■□□□□□□□■■□■□□□□□□□□□□□□□ 
□□□□□□■■■■■■■■■■■■□□□□□□□□□□□□□ 
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 


5、动画效果
    说穿了就是先画一个图形显示出来,延时一小段时间认人看清楚后,再将显示的图形在原来的位置涂黒(或涂成背景颜色)。其中延时函数为delay (数值),包含在头文件#include <dos.h>中。
括号中的数值越大,延时越长,数值最大为65535,可配合for 循环让delay()超过65535这个数,下面给出的小程序是熟悉这个函数的.功能是一个红色的正方形斜斜地移动
/*animation.c*/
#include <graphics.h> 
#include <dos.h>       /*delay()函数的头文件*/

void main()
{
    int gr = DETECT , gm = 0;
    int x = 50 , y = 50;
    int i , j;
 initgraph( &gr , &gm , "c://bgi" ) ;
 for( i = 0 ; i < 8 ; ++ i , x += 50 , y += 50)
 { 
  setfillstyle( SOLID_FILL , RED );
  bar( x , y , x + 50 , y + 50 );
  for (j = 0 ; j < 30 ; ++ j)
   delay( 10000 );       
                      /*不同的编译器显示的快慢不同,10000这个数值可根据不同的编译器改*/
  setfillstyle( SOLID_FILL , BLACK );
  bar( x , y , x + 50 , y + 50 );
 }
}

 

6、键盘控制
    键盘上的每个键都有自己的键盘码,为了得到想要的,下面介绍一个函数
在Tc2.0中有一个处理键盘输入的函数bioskey(); 
int bioskey(int cmd); 
  当cmd为1时,bioskey()检测是否有键按下。没有键按下时返回0;有键按下时返回键盘码(任何键盘码都不为0),但此时并不将检测到的按  键码从键盘缓冲队列中清除。 
  当cmd为0时,bioskey()返回键盘缓冲队列中的键盘码,并将此按键码从键盘缓冲队列中清除。如果键盘缓冲队列为空,则一直等到有键按下,才将得到的键盘码返回。 

  Escape键的按键码为0x11b,下面的小程序可以获取按键的键盘码 
/*keyboard_code.c*/
#include <stdio.h>
#include <bios.h>

void main()
{
    int key ;
    while ( 1 ) 
    { 
         key = bioskey( 0 );        /* wait for a keystrike */ 
         printf ( "0x%x/n" , key ); 
         if ( key == 0x11b )
             break;                 /* Escape */ 
    }

常用按键的按键码如下: 

#define LEFT  0x4b00 
#define RIGHT 0x4d00 
#define DOWN  0x5000 
#define UP    0x4800 
#define HOME  0x4700 
#define END   0x4f00 
#define SPACE 0x3920 
#define ESC   0x011b 
#define ENTER 0x1c0d 


    这样只要事先用#define 这个宏定义,将要用的键盘码定义好,然后用switch语句分支各个键的功能即可。这里多加说明一个函数kbhit(),其头文件为#include <conio.h>.当没有按键时,返回0值;有按键时,返回非0值,结合while循环,就可实现键盘控制了。下面的函数功能是按上下左右键,屏幕上的正方形跟着做出相应地移动,按逃跑键退出
/*control.c*/
#include <dos.h>       /*delay()函数的头文件*/
#include <graphics.h>
#include <bios.h>
#include <conio.h>

#define LEFT  0x4b00
#define RIGHT 0x4d00
#define DOWN  0x5000
#define UP    0x4800
#define ESC   0x011b

void main()
{
    int gr = DETECT , gm = 0;
    int x = 100 , y = 100;
    int i , key;
 initgraph( &gr , &gm , "c://bgi" ) ;
 while ( 1 )
 {
  while ( !kbhit() )            /*前面刚说完,不记得了?往上翻翻*/
  { 
   setfillstyle( SOLID_FILL , RED );
   bar( x , y , x + 50 , y + 50 );
   for (i = 0 ; i < 30 ; ++ i)
    delay( 10000 );      
   setfillstyle( SOLID_FILL , BLACK );
   bar( x , y , x + 50 , y + 50 );
  }
  key = bioskey( 0 );           /*前面刚说完,不记得了?往上翻翻*/
  switch ( key )
  {
   case LEFT:
    x -= 50;
    break;
   case RIGHT:
    x += 50;
    break;
   case UP:
    y -= 50;
    break;
   case DOWN:
    y += 50;
    break;
   case ESC:
    exit( 0 );
  }
 }
}


7、判断方块碰撞(即方块是否还能下落)
    用一个带有返回值的函数,若碰撞则说明不能下落,返回1;反则说明没有碰撞,返回0.
    具体一点就是整个4*4方块数组下落一个单位长度,与游戏空间数组(即前面说的“无盖的杯子”)有重叠的1,则在当前位置4*4数组是1的地方赋值给游戏空间对应的数组元素,表示停止下落,并画有1 的地方。对于左移、右移一个单位长度有重叠的1 ,则不允许左移、右移,继续自然下落。

8、消行
    总的想法是先认为每一行都是满1的,从游戏空间的数组由上到下扫描,一旦测试到某一行中某个列元素为0,则认为这一行没满,跳出这行的扫描循环,进入下一行的扫描。
    若扫描完某一行的元素都没有发现0,则以这行以上的每一行完完整整地将上一行的元素赋值给下一行,这个过程以由下到上进行,然后将整个游空间(GameSpace)画黒,再在GameSpace[21][12]中有1的地方画小正方形,如示例

A 0 0 0 1 0 0   从A行到D行扫描                           A 0 0 0 0 0 0
B 1 1 0 1 0 1   扫描后发现C行是满的                      B 0 0 0 1 0 0
C 1 1 1 1 1 1   则从B行起B行赋给C行,A行赋给B行    C 1 1 0 1 0 1
D 0 1 1 1 1 1   最后在最行A行赋全0 ,变成               D 0 1 1 1 1 1

对应的   ↓                                                   对应的   ↓
图形     ↓                                                   图形      ↓

A□□□■□□                                              A□□□□□□
B■■□■□■                                              B□□□■□□
C■■■■■■                                              C■■□■□■
D□■■■■■                                              D□■■■■■

 


读者可能会问这样不是每次都消一行而己,若要一次消两行以上怎么办(如恰好有一长条一次能消4行)?这个简单,因为即使一次消多行也是建立在一行一行消的基础上的。把消行函数再细化成两部分,一部分为为扫描哪行满行并返回这个满行的行号的值,另一部分就消这个满行行号的行,用一个while 循环,判断条件为 “扫描函数(在我的程序里是fullrow())!= 0”,形如
while(fullrow() != 0 )
{                          其中flrw是个整型变量,用来记录哪行满行,而clearrow(flrw)
    flrw=fullrow();              则消指定的行。由于计算速度快,人眼看多次消行,就像一
    clearrow( flrw );            次搞掂的样子
}


9、极度重要的变形函数
   分两部分讲 a、怎么变 
              b、变后插入到已有方块里的处理(即卡位处理),情况如图:
                         □■□□□□□              □■□□□□□
                         □■◆□□□□              □■□□□□□
                         □■◆□□□□              □■□□□□□
                         □■◆□□□□   变形后   ◆◆◆◆□□□
                         □■◆□□□□   有可能   □■□□□□□
                         □■□□□□□              □■□□□□□
                         □■□□□□□              □■□□□□□
a、怎么变。
    其实我能想到两种方法,一种是把每个方块各种变形的样子都用结构体定义好(这样有19种),形成一个封闭的链表,变形一次指针指向下一种形状;
另一种是旋转90度,也是我选择的方法。
    后一种是一种通法,但执行效率不比前一种方法快。下面针对旋转90度详细讲解。
    网上说的旋转90度只是泛泛地说而已,经过毛概课上偷偷画方格,折纸,终于想到了一种主对角线对折,再翻来达到90度旋转的方法。请拿上一张纸,标上正面,反面,|、||,跟着图示做:
 
正面                      反面                                      正面
  ________                _______                                _______
  |/         |               |/         |               __ |__       |        /|
  |  /    |||   沿对角线  |  /  —  |  再沿中轴  |   |   |      | —   /  |
  |    /     |   对折后    |    /     |  翻过来     |   |   | 得  |     /    |
  |      /   |               |     /    |              |__|__|      |   /     |
  | |     /  |               | 〓   /  |                   |          |  /   〓 |
  |_____ /|               |_____ /|                              |/ _____|
 
  图(a)                   图(b)                            图(c)

   对比图(a),图(c),则发现原来坚的东西变横了,也实现了顺时针转了90度。概念是这样,接下来是4*4数组怎么对折,只要仔细研究发现主对角线上的元素不必动,只要
       0 1 2 3
     0■□□□        0-1号与1-0号   1-2号与2-1号   2-3与3-2号    
     1□■□□        0-2号与2-0号   1-3号与3-1号              互相交换值,
     2□□■□        0-3号与3-0号        
     3□□□■ 
    
     再沿中轴互相交换值
           |   
      0 1 |2  3       
    0□□|□□       0-0号与0-3号    0-1号与0-2号
    1□□|□□       1-0号与1-3号    1-1号与1-2号
    2□□|□□       2-0号与2-3号    2-1号与2-2号
    3□□|□□       3-0号与3-3号    3-1号与3-2号
           |
 这样就完成了第一部分。

b、变后插入到已有方块里的处理 (即卡位处理)
    整体思路是先执行上面的第一部分,但不显示出来,然后判断是不是插到了已有方块中,
    若可以左移或右移之后就可以的,我们就人为地移动它,后显示。
    若左移或右移,又插入了右侧或左侧的方块,则逆序地执行第一部分,先执行中轴交换值,再对角线交换值,变回原来的形状,认为不具备变形的条件,后原形显示。
    对于第一种情况,为分析情况,我们讨论每行的0、1、2、3号位置(注意这里的0、1、2、3号位置是4*4数组的每一行的,须要用循环一行行扫描),由于1、2号位置情况复杂且出现的机会少,故舍弃。因为(注意这里为了更好地说明,把长条■■■■变成◆◆◆◆,实际作图时还是■■■■)
   
    0 1 2 3                      0 1 2 3                            0 1 2 3
    □□◆□                     □□□□                           □□□□
    □■◆□      ■            □■□□      ■                ■□□□□■
    □■◆□      ■            ◆◆◆◆      ■                ■◆◆◆◆■
    □■◆□      ■   变形后  □■□□     ■  这时1号卡  ■□□□□■  
       ■           ■   有可能     ■          ■  右移 两位  ■          ■
       ■           ■                ■          ■                ■          ■
       ■□□□□■                ■□□□□■                ■□□□□■
又如    
    0 1 2 3                    0 1 2 3                            0 1 2 3
    □□◆□                   □□□□                           □□□□
    □■◆□   ■              □■□□   ■                ■  □□■□
    □■◆□   ■              ◆◆◆◆   ■                ■  ◆◆◆◆
    □■◆□   ■   变形后   □■□□   ■  这时1号卡  ■  □□■□   这时2号又卡了
       ■        ■   有可能      ■        ■  右移两位   ■        ■
       ■        ■                 ■        ■               ■        ■
       ■□□□■                 ■□□□■               ■□□□■

                                                        0 1 2 3
                                                        □□□□
                                                        ■□□□■
                                                        ■◆◆◆◆
                                         若右移一位  ■□□□■   这时3号又卡了
                                                        ■        ■
                                                        ■        ■
                                                        ■□□□■
由于1、2 号位的情况只会在长条(因为其它的方块长度最长为3)时出现,虽然存在移两位可行的情况,但实在是少,如要都完善的编写上,显得麻烦,故舍弃。
    现在重点讨论0、3号位置。
    0号位置由于变形后,有可能因为左侧位置不够而不能变形,只须稍微右移一个单位长度就可以了,如
  
   0 1 2 3              0 1 2 3               0 1 2 3
   □◆□□             □□□□           ■□□□□
   ■◆□□             ■□□□           ■□□□□
   ■◆□□   变形后  ◆◆◆◆  移动后  ■◆◆◆◆ 
   ■◆□□             ■□□□           ■□□□□
   ■                     ■                   ■
   ■                     ■                   ■

而人为地移动后,对应的1,2,3号位置又有可能插入了已有的方块中,如

   0 1 2 3              0 1 2 3            0 1 2 3
   □◆□□             □□□□           □□□□
   ■◆■□             ■□■□           ■□■□□
   ■◆■□   变形后  ◆◆◆◆ 右移一  ■◆◆◆◆   1号又卡了
   ■◆■□             ■□■□ 位后     ■□■□□
   ■   ■               ■   ■             ■   ■
   ■   ■               ■   ■             ■   ■

   0 1 2 3              0 1 2 3              0 1 2 3
   □◆□■             □□□□             □□□□
   ■◆□■             ■□□■             ■□□■□
   ■◆□■   变形后  ◆◆◆◆ 右移一    ■◆◆◆◆   2号又卡了
   ■◆□■             ■□□■ 位后      ■□□■□
   ■     ■             ■      ■            ■     ■
   ■     ■             ■      ■            ■     ■

   0 1 2 3                 0 1 2 3                0 1 2 3
   □◆□□■             □□□□               □□□□
   ■◆□□■             ■□□□■            ■□□□■
   ■◆□□■   变形后  ◆◆◆◆■ 右移一    ■◆◆◆◆   3号又卡了
   ■◆□□■             ■□□□■ 位后      ■□□□■
   ■        ■             ■        ■            ■        ■
   ■        ■             ■        ■            ■        ■

对于这些情况我们无奈,只能逆序变回原形。
    3号位置的情况亦然,只不过是把左移变成右移,移动后讨论的是0,1,2号位置。

10、关于菜单的制作
     我认为不会很难。概念上是把要显示的动态数值改变后,涂黒原来的地方,将新的数值在原来 的地方再显示即可。特别值得一提的是,这之中可能用到的itoa()函数,头文件#include <stdlib.h>。也许有人会问,为什么要用itoa()函数,这是因为在图形模式下,用printf()输出只能是8*8点阵,白色字体。若想搞点比较艺术的字体,输出的字体大点的,就看下面的函数是怎么实现的。
功能是按一下回车,数值增大100,按ESC退出。

/*itoa.c*/
#include <graphics.h>
#include <bios.h>
#include <conio.h>
#include <stdlib.h>

#define ESC   0x011b
#define ENTER 0x1c0D

main()
{
    int gr = DETECT , gm = 0;
    int key , value = 100 ;     /*value是要转的整型变量*/
    char str[10];               /*用来存的字符串*/
 initgraph( &gr , &gm , "c://bgi" ) ;
 setbkcolor( BLACK );  /*设置背景颜色*/
                                 /*对应的数值形式是setbkcolor( 0 )*/
       
        setfillstyle( SOLID_FILL,BLACK );/*设置bar函数填充样式,这里为实心、黑色*/
                                  /*对应的数值形式是setfillstyle( 1 , 0 )*/
       
        settextjustify( LEFT_TEXT , CENTER_TEXT );/*设置文本输出的样式,*/
                                                  /*这里是左对齐,中心对齐*/
                                               /*对应的数值形式是settextjustify( 0 , 1)*/
 
        setcolor( CYAN );                   /*设置输出文本的颜色,这里是青色*/ 
                                           /*对应的数值形式是setcolor( 3 )
       
        settextstyle ( SANS_SERIF_FONT , HORIZ_DIR , 3 );/*设置文本的字型,*/
                                        /*这里是无衬线笔画字体,水平输出,3是24*24点阵*/
                                  /*对应的数值形式是settextstyle ( 3 , 0 , 3 )
                                  
        itoa( value , str , 10 ); /*前一个是要转成字符的整型变量名,*/
                                        /*后一个是转成字符后用来存的字符串首址*/ 
                                 /*itoa中的10是按10进制转化*/
        while (1)       
 {
  while ( !kbhit() )
  {
   outtextxy(320,240,str);
  } 
  key=bioskey( 0 );
  switch ( key )
  {
   case ENTER:
    bar(300,200,400,300);
    value += 100 ;
    itoa( value , str , 10 );
    break; 
   case ESC:
    exit( 0 );
  }
 } 
}


    好了,看到这相信你对这款游戏的制作已有所把握。
    你可以先搞个没有键盘控制垂直下落的单一的测试方块,然后把向左、向右、向下加上,再把无盖的杯子画出来,让其有个范围,顺道搞掂碰撞,之后把消行功能加上,又将单一的测试方块变成7种随机的方块出现且最难搞的变形函数也完成了,补上记分功能,把菜单给做了,最后加上了自己的新功能。记住千里之行始于足下,聚沙成塔。我们有的是朩头、钉子、石头等各种零件,却不能忘记心中那宏伟的城堡!现在就一点点来吧。

                                                            桂林电子科技大学
                                                            计算机与控制学院
                                                             06 级  软件工程
                                                                 韩金林
                                                            Count. Henry Black
                                                                00:06  a.m.
                                                             July  28th   2007


附录:   Russian  Diamond 实现的源程序(菜单上好像还有小bugs,不影响大体)
/*Russian_Diamond.c*/

#include <stdio.h>
#include <graphics.h>        /*主要应用于作图*/
#include <bios.h>           /*主要应用于键盘*/
#include <conio.h>
#include <stdlib.h>         /*主要应用于产生随即数*/
#include <dos.h>            /*主要应用于延时*/
/**************************
  第一个界面的主要函数
***************************/
void hello();    /*第一个界面的入口函数*/
void interface();   /*输出不许改变的文字信息 */
void activemenu();   /*选择菜单的动态显示函数*/
void menu_up();    /*选择菜单的向上函数*/
void menu_down();   /*选择菜单的向下函数*/
void menu_left();   /*选择菜单的减少速度值,难度值的函数*/
void menu_right();   /*选择菜单的增大速度值,难度值的函数*/
void menu_enter();   /*确定选项的函数*/
void menu_escape();   /*选择退出的函数*/
/************************
  第二个界面的主要函数
************************/
void game();    /*选择开始游戏的入口函数*/
void gamestart();   /*第一次产生随机数*/
void initgamespace();  /*初始化游戏空间*/
void drawwal();    /*画边框与预览框*/
void drawsqa();    /*画下落方块*/
void showtext();   /*显示游戏过程中提示信息*/
void showvalue();   /*显示游戏过程中数值信息*/
void up();     /*游戏中变形函数*/
void down();    /*向下函数*/
void left();    /*向左函数*/
void right();    /*向右函数*/
void pause();    /*暂停函数*/
void escape();    /*显示退出菜单的函数*/
void speed_up();   /*加速函数*/
void speed_down();   /*减速函数*/
void wipe();    /*使用橡皮函数*/
void if_end();    /*判断游戏结束的函数*/
void preview(int );   /*预览框函数*/
void clearrow(int );  /*消行函数*/
void showscore(int );  /*显示分数的函数*/
void id(int );    /*为了能按照预览框的形状开始掉落而写的函数*/


int  crash();    /*判断碰撞的函数*/
int  fullrow();    /*侦测满行的函数*/
int  next();    /*产生随机数的函数*/
/************************
 退出界面
***********************/
void goodbye();     /*退出函数的入口*/
/************************
  第一个界面的主要参数值
************************/
int start_color=14;    /*说明当前选项到了确定选择的各个参数准备开始游戏的地方*/
int speed_color=3;    /*说明当前选项到了选择速度的speed的地方*/
int difficulty_color=3;   /*说明当前选项到了选择难度的difficulty的地方*/ 
int end_color=3;    /*说明当前选项到了选择退出的end的地方*/
int menu_color=0;    /*控制当前选项到了哪的重要判断标记*/
/************************
  游戏主体的各个全局变量
************************/

#define LEFT  0x4b00
#define RIGHT 0x4d00
#define DOWN  0x5000
#define UP    0x4800
#define ESC   0x011b
#define ENTER 0x1c0D
#define SPACE 0x3920
#define W     0x1177
#define S     0x1f73
#define D     0x2064
#define UNIT  16  /*单位长度*/
#define spaLeft  188    /*游戏框的左上角*/
#define spaTop 34  /*由于作品上交时间急又由于出现了bug,已改动故现在已不是真正的左上角的数值了,只能是概念上有个游戏框的左上角*/
#define staLeft  252 /*方块出现的初始地方*/
#define staTop 34  /*如上面说的那样,出现bug,而中间画图判断等用到原始的34,其实左上角y的数值为staTop-UNIT*/

int a[ 11 ][ 4 ][ 4 ]={
             /*************************
               7 字形 
             *************************/
             { {0,0,0,0},
         {0,1,0,0},
         {0,1,0,0},
               {0,1,1,0} },            
              /*************************
               反 7 字形 
             *************************/
             { {0,0,0,0},
               {0,1,1,0},
               {0,1,0,0},
               {0,1,0,0} },            
              /*************************
               t 字形 
             *************************/
             { {0,0,0,0},
               {0,1,0,0},
               {1,1,1,0},
               {0,0,0,0} },
              /*************************
               小z 字形 
             *************************/
             { {0,0,0,0},
              {1,1,0,0},
              {0,1,1,0},
              {0,0,0,0} },
             /*************************
               小反 z 字形 
             *************************/
             { {0,0,0,0},
              {0,1,1,0},
              {1,1,0,0},
              {0,0,0,0} },          
            /*************************
               长条 
             ************************/
             { {0,1,0,0},
              {0,1,0,0},
              {0,1,0,0},
              {0,1,0,0} },
             /*************************
               田 字形 
             *************************/
             { {0,0,0,0},
              {0,1,1,0},
              {0,1,1,0},
              {0,0,0,0} },
            /*******************************
              选了难度为困难时才有的图形
            ********************************/
            /********************************
              杯子形
            *********************************/
             { {1,1,1,0},
              {1,0,1,0},
              {1,0,1,0},
               {0,0,0,0} },
              /*************************
               大 Z 字形 
             *************************/
             { {1,1,0,0},
              {0,1,0,0},
              {0,1,1,0},
               {0,0,0,0} },
             /*************************
               大 Z 字形 
             *************************/
             { {0,1,1,0},
              {0,1,0,0},
              {1,1,0,0},
               {0,0,0,0} },
       /*************************
               一点 
             *************************/
             { {0,0,0,0},
              {0,1,0,0},
              {0,0,0,0},
              {0,0,0,0} },
         } ;

int speed=0;    /*速度变量*/
int difficulty=0;   /*难度变量*/
int nandu;       /*根据难度而确定产生的随机数的范围*/
int score=0;    /*记录分数的变量*/
int eraser=0;    /*记录橡皮的变量*/
int x , y;     /*游戏界面的坐标*/
int GameSpace[ 26 ] [ 12 ] ;/*游戏空间的数组,以记录坐标*//*由于出现bug而不再是26行了,,又由于上交时间紧是从第3号有效*/
int current[4][4];         /*为按照预览框的形状初始下落*/
int rec;         /*为记录下个方块的形状*/


void main()
{
 int gr = DETECT , gm = 0;
 initgraph( &gr , &gm , "c://bgi" ) ;
 hello();     /*是hello()中嵌有game(),goodbye()函数,通过选择进入不同的函数,goodbye()为出口*/
}        /*game()中也嵌有hello(),goodbye()函数,通过选择进入不同的函数,goodbye()为出口*/


/******************************
  第一个界面的具体函数
*****************************/
void hello()
{
 int key;     /*记录按键的变量*/
 char ch[1];
 setbkcolor( BLACK ) ;
 interface();    /*输出不许改变的文字信息*/
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,3);
 
 setcolor( WHITE );   
 switch ( difficulty )  
  {
   case 0:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"easy");
    break;
   case 1:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(340,390,"normal");
    break; 
   case 2:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"hard");
    break;
  }
 itoa(speed,ch,10);   /*根据难度限制speed的范围并输出*/
 outtextxy(370,360,ch);
 while (1)
 { 
  while(!kbhit())
  {
   activemenu();  /*若没按键一直根据相应的值输出*/
  }
  key=bioskey(0);   /*根据不同的按键,实现选择的功能*/
  switch (key)
  {
   case UP:
    menu_up();
    break;
   case DOWN:
    menu_down();
    break;
   case LEFT:
    menu_left();
    break;
   case RIGHT: 
    menu_right();
    break;
   case ENTER:
    
    menu_enter();
    break;
   case ESC:
    menu_escape();
    break;
   
  } 
 }
}

/***********************
 游戏主体部分
************************/

void game()
{
 int i , j ;
 int key ;  /*记录按键*/
 int flrw;  /*记录满行的所在行的行数*/
 int times;  /*记录在一次消行循环中消了多少行*/
 setbkcolor( BLACK ) ;
 drawwal() ;  /*画边界且画预览框的边界*/
 initgamespace() ; /*初始化游戏空间*/
 showtext();   /*显示提示信息*/
 showvalue();  /*根据玩家所选的速度,难度输出信息*/
 
 if (difficulty==2) /*根据难度而确定产生的随机数的范围*/
  nandu=11;
 else
  nandu=7;
 
 gamestart();  /*主要是第一次产生随机数*/
 while(1)
 { 
   randomize();
  while( ! kbhit() )       /*若有按键返回一个非零值*/
  {    
   if_end();   /*每次都判断游戏是否结束*/
   if ( crash() == 1 )  /*若方块不再下落则坐标复位,又产生一个随机数*/
   {
    x = staLeft;
    y = staTop-UNIT;
    id( rec );
    rec =next();
       preview( rec );
   }  
   
   if ( crash() == 0)      /*若方块还可以下落,y坐标加一个单位长度*/
    y += UNIT;
    
   for(times=0;fullrow()!=0;++times)  /*消行部分,实质上是每次只消一行*/
   {
    flrw=fullrow();
    clearrow( flrw );  
      }
   if (times!=0)      /*若有消行,记录分数*/
    showscore ( times );
   
   drawsqa( RED );      /*画红色方块*/
   for ( i = 0; i < 25 - speed*4 ; ++i )
   {
    if ( kbhit() )    /*为消除看起来很卡的感觉*/
    {
     break;
    }
    delay( 5000 ) ;    /*与画黑色色方块一起,达到动画效果*/
   }
   drawsqa( BLACK ) ;  /*与延时一起,达到动画效果*/
  }
   
    key=bioskey(0);   /*根据不同的按键,实现不同的功能*/
  switch ( key )
  {
   case DOWN:
    down();
    break;
   case LEFT:
    left();
    break;
   case RIGHT:
    right();
    break;
   case UP:
    up();
    break;
   case SPACE:
    pause();
    break;
   case ESC:
    escape();
    break;
   case W:      
    wipe ();
    break;   
   case S:
    speed_up();
    break;
   case D:
    speed_down();
    break;
  }
 }
}

/*****************************
 退出界面的函数
*****************************/
void goodbye()      /*为达到动态效果,写得稍微烦了点*/   
{
 static int color=9;
 int i,j,tag=0;     /*tag为标记变量*/
 settextjustify( CENTER_TEXT,CENTER_TEXT );
 settextstyle (0,0,7);
 while(1)
  {
   if (tag==1)    /*标记变量若为1,跳出循环(即有按键则退出)*/
    break;
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())  /*若有按键,标记变量赋1,跳出循环,以下每个for 循环同样 */
    { 
     tag=1;
     break;
    } 
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 50,110,"T");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 110,110,"H");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 170,110,"A");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 230,110,"N");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 290,110,"K");
    delay(10000);  
   }
   
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 430,110,"Y");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 490,110,"O");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 550,110,"U");
    delay(10000);  
   }
   
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 240,190,"F");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 300,190,"O");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 360,190,"R");
    delay(10000);  
   }
   
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 110,270,"P");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 170,270,"L");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 230,270,"A");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 290,270,"Y");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 350,270,"I");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 410,270,"N");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
   { 
    if (tag==1)
     break;
    if (kbhit())
    { 
     tag=1;
     break;
    }
    setcolor ( color );
    if ( color==14 )
     color=9;
    outtextxy( 470,270,"G");
    delay(10000);  
   }
   for (j=0;j<1;++j,++color)
    { 
     if (tag==1)
      break;
     if (kbhit())
     { 
      tag=1;
      break;
     }
     setcolor ( color );
     if ( color==14 )
      color=9;
     outtextxy( 530,270,"!");
     delay(10000);  
    }
  }
 getch();
 exit(0);

/***************************************
 以下是开始菜单中各个函数的具体实现
**************************************/

void interface()
{
/*********************
   画主要的线
*********************/
 setlinestyle(0,0,THICK_WIDTH);
 setcolor(RED);
 line(80,0,80,479);
 line(0,285,639,285);
 setcolor(GREEN);
 line(87,0,87,479);
 line(0,292,639,292);
 setcolor(BLUE);
 line(94,0,94,479);
 line(0,299,639,299);
/***************************
   显示相关的信息
****************************/ 
 
 setcolor ( BROWN );
 settextjustify( CENTER_TEXT,CENTER_TEXT );
 settextstyle (0,0,2);
 outtextxy( 325,60," Welcome to ");
 settextstyle (0,0,5);
 setcolor ( LIGHTCYAN );
 outtextxy( 325,120,"RUSSIAN");
 setcolor ( LIGHTMAGENTA );
 outtextxy( 325,170,"DIAMOND");
 setcolor ( LIGHTGRAY );
 settextstyle (0,0,1);
 settextjustify( CENTER_TEXT,CENTER_TEXT );
 outtextxy( 430,230,"by henry_black");
 outtextxy( 430,250,"Jun 2nd 07");
/****************************
  显示菜单的文字
*************************/ 
 
 setcolor ( CYAN );
 settextjustify( RIGHT_TEXT,CENTER_TEXT );
 settextstyle (1,0,3);
 outtextxy( 325,330,"GAME ");
 outtextxy( 317,360,"GAME SPEED");
 outtextxy( 317,390,"DIFFICULTY");
 outtextxy( 325,420,"GAME ");
}

void activemenu()
{
 switch ( menu_color )  /*根据不同menu_color确定不同选项的颜色,达到选择的动态感*/
 {
  case 0:
   start_color=14; 
   speed_color=3;
   difficulty_color=3;
   end_color=3;
   break;
  case 1:
   start_color=3; 
   speed_color=14;
   difficulty_color=3;
   end_color=3;
   break;
  case 2:
   start_color=3; 
   speed_color=3;
   difficulty_color=14;
   end_color=3;
   break;
  case 3:
   start_color=3; 
   speed_color=3;
   difficulty_color=3;
   end_color=14;
   break; 
 }
 /*以下是根据确定不同选项的颜色值,输出各选项*/
 
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,3); 
 setcolor ( start_color );
 outtextxy( 350,330,"START");
 setcolor ( end_color );
 outtextxy( 350,420," END ");
 
 setcolor ( speed_color );   /*画箭头*/
 line(325,365,335,360);
 line(325,365,335,370);
 line(335,360,335,370);
 
 line(435,365,425,360);
 line(435,365,425,370);
 line(425,360,425,370);
 
 setcolor ( difficulty_color );
 line(325,395,335,390);
 line(325,395,335,400);
 line(335,390,335,400);
 
 line(435,395,425,390);
 line(435,395,425,400);
 line(425,390,425,400);
}

void menu_up()
{
 if (menu_color==0)  /*若选到了最顶,再按就下到最下边的选项*/
  menu_color=4;
 --menu_color;   /*实现向上移动一个位置*/
  
}

void menu_down()   
{
 if (menu_color==3)  /*若选到了最底,再按就下到最上边的选项*/
  menu_color=-1;
 ++menu_color;   /*实现向下移动一个位置*/
}

void menu_left()   /*该函数为减小speed,difficulty的值*/
{
 char ch[1];    /*为把数值转字符输出*/
 if (menu_color == 1 ) /*说明当前选项为速度*/
 {
  if (speed==0 && difficulty!=0)  /*根据难度,确定范围并从最小值变成最大值*/
   speed=6;
   
  if (speed==0 && difficulty==0) 
   speed=4;
   
  --speed;
  
  setfillstyle(0,BLACK);   /*先画黑,再转字符输出*/
  bar(345,355,385,375);
  setcolor( WHITE );
  settextjustify( LEFT_TEXT,CENTER_TEXT );
  settextstyle (1,0,3);
  itoa(speed,ch,10);
  outtextxy(370,360,ch);
 }
 if (menu_color == 2)   /*说明当前选项为难度*/
 {
  if (difficulty==0)   /*最小值变成最大值*/
   difficulty=3;
  --difficulty;
  switch ( difficulty )  /*根据difficulty的值,先画黑,再输出相应的信息*/
  {
   case 0:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"easy");
    break;
   case 1:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(340,390,"normal");
    break; 
   case 2:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"hard");
    break;
  }
  
  
 }
}

void menu_right()    /*该函数为增大speed,difficulty的值*/
{
 char ch[1];
 if (menu_color == 1 )    /*说明当前选项为速度*/
 {
  if (speed==5 && difficulty!=0)  /*根据难度,确定范围并从最大值变成最小值*/
   speed=-1;
   
  if (speed==3 && difficulty==0) 
   speed=-1;
   
  ++speed;
  
  setfillstyle(0,BLACK);   /*先画黑,再转字符输出*/
  bar(345,355,385,375);
  setcolor( WHITE );
  settextjustify( LEFT_TEXT,CENTER_TEXT );
  settextstyle (1,0,3);
  itoa(speed,ch,10);
  outtextxy(370,360,ch);
 }
 if (menu_color == 2)    /*说明当前选项为难度*/
 {
  if (difficulty==2)    /*最大值变成最小值*/
   difficulty=-1;
  ++difficulty;
  switch ( difficulty )   /*根据difficulty的值,先画黑,再输出相应的信息*/
  {
   case 0:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"easy");
    break;
   case 1:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(340,390,"normal");
    break; 
   case 2:
    setfillstyle(0,BLACK);
    bar(338,385,420,410);
    setcolor( WHITE );
    settextjustify( LEFT_TEXT,CENTER_TEXT );
    settextstyle (1,0,3);
    outtextxy(355,390,"hard");
    break;
  } 
 }
}

void menu_enter()
{
 if (menu_color == 0) /*说明当前选项为开始游戏*/
 {
  cleardevice();  /*若按回车则开始*/
  game();
 }
 if (menu_color == 3) /*说明当前选项为退出游戏*/
 { 
  cleardevice();  /*若按回车则退出*/
  goodbye(); 
 }
}

void menu_escape()   /*若在选择时按esc键,直接跳到退出选项*/
{
 menu_color=3;
}

/*******************************************
 以下是game()函数中各个相关的函数的具体实现
*********************************************/
void gamestart()   /*主要是开始时第一次产生随机数*/
{
 randomize();
 score=0;
 eraser=0;
 x = staLeft;
 y = staTop-UNIT;
 rec=rand()%nandu;
 id( rec );
 rec=rand()%nandu;
 preview( rec );
}


void initgamespace()
{
 int i , j ;
 for ( i = 0 ; i < 25 ; ++ i )
  for ( j = 1 ; j < 11 ; ++ j )
   GameSpace[ i ][ j ] = 0 ;  /*中间赋0,说明没有被占有*/
 for ( i = 1 ; i < 11 ; ++ i )
  GameSpace[ 25 ][ i ] = 1 ;     /*最下边赋1,为了不让方块超出底部*/
 for ( i = 0 ; i < 2 ; ++ i )
  for( j = 0 ; j < 26 ; ++ j )  
   GameSpace[ j ][ i*11 ] = 1 ; /*两侧赋1,为了不让方块超出边界*/
}


void drawwal()
{
 int i , j ;
  /**************
   画预览框
  *************/
  setfillstyle( SOLID_FILL , YELLOW ) ;     /*先画大黄框*/
 bar( 465 , 115 , 555 , 205 ) ;
 setfillstyle( SOLID_FILL , BLACK ) ;  /*再中间画小黑框*/ 
 bar( 473 , 123 , 547 , 197 ) ;          
 
 /***************
 画游戏空间的边界
 ****************/           
 setfillstyle( SOLID_FILL , YELLOW ) ;
 x = 204 ; 
 y = 34 ;  
 setcolor( 0 ) ;
 setlinestyle(0,0,1);
 for ( j = 0 ; j < 10 ; ++ j )
 {
  bar3d( x+j*UNIT , y +48 , x+j*UNIT+UNIT , y+64 , 0 , 1 ) ;
  bar3d( x+j*UNIT , y+400 , x+j*UNIT+UNIT , y+416 , 0 , 1 ) ;
 }
 x = 188 ;
 y = 34 ;
 for ( i = 0 ; i < 2 ; ++ i )
  for ( j = 3 ; j < 26 ; ++ j )
  bar3d ( x+i*176 , y+j*UNIT , x+i*176+UNIT , y+j*UNIT+UNIT , 0 , 1 ) ; 
}

void drawsqa( int color )  /*根据传进来的颜色画红的方块,黑的方块*/
{
 int i , j;
 setfillstyle( SOLID_FILL , color );
 for ( i = 0 ; i < 4 ; ++ i )
  for( j=0 ; j<4 ; ++ j )
  {                /*画的时候只画方块数组中有1的地方*/ 
   if ( (y + i * UNIT > staTop+48) && current[i][j] == 1)  /*方块其实是从最顶还要上的地方开始掉落,为好看*/
       bar3d( x + j * UNIT , y + i * UNIT , x + j * UNIT + UNIT , y + i * UNIT + UNIT , 0 , 0 ) ;
  }
}


void showtext()     /*为显示游戏中的提示信息*/
{
 int point1 [10]={15,40,180,40,180,240,15,240,15,40};
 int point2 [18]={60,260,140,260,180,305,180,388,140,432,60,432,20,388,20,305,60,260};
 
 setlinestyle(0,0,3) ;
 /*******************
      左上角
 ******************/
 setcolor( LIGHTBLUE );
 drawpoly(5,point1);
 setcolor( BROWN );
 settextjustify(RIGHT_TEXT,TOP_TEXT);
 settextstyle(0,0,0);
 outtextxy(130,70,"TURN AROUND : ");
 outtextxy(130,85,"FALL DOWN : ");
 outtextxy(130,100,"LEFTWARD : ");
 outtextxy(130,115,"RIGHTWARD : ");
 outtextxy(130,130,"SPEED UP : ");
 outtextxy(130,145,"SPEED DOWN : ");
 outtextxy(130,160,"WIPE OFF : ");
 outtextxy(130,175,"PAUSE : ");
 outtextxy(130,190,"CONFIRM : ");
 outtextxy(130,205,"EXIT : ");
 setcolor( LIGHTGRAY );
 settextjustify(LEFT_TEXT,TOP_TEXT);
 outtextxy(130,70,"UP");
 outtextxy(130,85,"DOWN");
 outtextxy(130,100,"LEFT");
 outtextxy(130,115,"RIGHT");
 outtextxy(130,130,"S");
 outtextxy(130,145,"D");
 outtextxy(130,160,"W");
 outtextxy(130,175,"SPACE");
 outtextxy(130,190,"ENTER");
 outtextxy(130,205,"ESCAPE"); 
 /**************
  左下角
 **************/
 setcolor( LIGHTCYAN );
 drawpoly(9,point2);
 settextstyle(3,HORIZ_DIR,1);
 settextjustify(RIGHT_TEXT,TOP_TEXT);
 setcolor( LIGHTMAGENTA );
 outtextxy(100,300,"score :");
 outtextxy(100,320,"speed :");
 outtextxy(100,340,"eraser :");
 outtextxy(120,360,"difficulty :");
 
 /***********************
   游戏框中上
 ************************/
 settextstyle(0,HORIZ_DIR,2);
 settextjustify(LEFT_TEXT,TOP_TEXT);
 setcolor( GREEN );
 outtextxy(225,55,"ENJOY IT");
 /************
  右下角 
 **************/
 setcolor( MAGENTA );
 settextstyle(0,HORIZ_DIR,0);
 settextjustify(CENTER_TEXT,CENTER_TEXT);
 outtextxy(510,230,"GuiLin  University   of ");
 outtextxy(510,245,"Electronica Technology");
 setcolor( YELLOW );
 outtextxy(510,270,"Department of Computer");
 setcolor( BLUE);
 outtextxy(510,295,"Major of SoftWare");
 setcolor( LIGHTRED );
 outtextxy(570,375,"by henry_black");
 outtextxy(570,390,"Jun 2nd,07");
 setcolor( GREEN );
 settextjustify(LEFT_TEXT,CENTER_TEXT);
 outtextxy(410,320,"QQ : 42595947");
 outtextxy(410,335,"nick name : Count. Black"); 
 setcolor(BLACK);
 setlinestyle(0,0,1);
}

void showvalue()  
{
 char ch[7];  /*为把数值转字符输出*/ 
 /***************
  输出分数
 *****************/
 setcolor( WHITE );   
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,2);
 itoa(score,ch,10);
 outtextxy(105,310,ch);
 /***************
  输出速度
 ****************/
 itoa(speed,ch,10);
 outtextxy(105,330,ch);
 /***************
  输出橡皮数
 ****************/
 itoa(eraser,ch,10);
 outtextxy(105,350,ch);
 /***************
  输出难度
 ****************/
 settextjustify( CENTER_TEXT,CENTER_TEXT );
 if (difficulty==0)
 outtextxy(110,390,"easy");
 if (difficulty==1)
 outtextxy(110,390,"normal");
 if (difficulty==2)
 outtextxy(110,390,"hard");
 setcolor(BLACK);    

/*****************************
 极度核心的变形函数
*****************************/
void up()   
{          /*整体思路是先变形,后判断,若不可变形则变回;若可变形则不变,再画图形*/
 int i,j,k,l,temp,tag,lab;   /*需要两个标记变量*/    
 for ( i = 0 ; i < 3 ; ++ i )        /*主对角线对折*/    
  for ( j = i+1 ; j < 4 ; ++ j )    
  {
   temp = current[ i ][ j ];
   current[ i ][ j ] = current[ j ][ i ];
   current[ j ][ i ] = temp ;
  }
 for(  i= 0 ; i < 4 ; ++ i)   /*竖中轴对折*/
  for( j = 0 ; j < 2 ; ++ j)
  {
   temp = current[ i ][ j ] ;
   current[ i ][ j ] = current[ i ][ 3-j ] ;
   current[ i ] [3-j ] = temp ;
  }
 tag=0;        /*标记变量为跳出循环,当为1时跳出循环*/
 for(  i= 0 ; i < 4 ; ++ i)
 {   /*变形后有可能插入已为1的地方,分情况讨论,讨论每行的第0,1,2,3号位置*/
  /**************************************************************************************************
   由于第1,2号位置情况复杂,且出现的情况极少,故舍弃。一有这种情况发生则认为不能变形跳出循环
  ************************************************************************************************/  
  if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT + 1 ] == current[ i ][1]) && current[ i ] [1] == 1 )
   {
    tag=1;
    break;
   } 
  if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT + 2 ] == current[ i ][2]) && current[ i ] [2] == 1 )
   {
    tag=1;
    break;
   } 
 
  /***************************************************************************************************
   第0号位置由于变形后,有可能只是因为左侧位置不够而不能变形,只须稍微向右移动一个单位长度就可以了的
  ****************************************************************************************************/
  
  if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT] == current[ i ][0]) && current[ i ] [0] == 1 )
  {
   x+=UNIT;
   lab=0;
/*****************************************************************************************************************************
人为的移动后,可能对应的第1,23号位置有可能也已经有物体,若有则说明即使移动了一个单位长度也还是不够的两个标记位均赋1,跳出循环
******************************************************************************************************************************/ 
   for (k=0;k<4;++k) 
    for (l=1;l<4;++l)
    { 
     if (lab==1)
      break;    
     if((GameSpace [ (y - spaTop) / UNIT +k] [ (x - spaLeft) / UNIT+l] == current[k][l]) && current[k] [l] == 1)
     {
      tag=1;
      x-=UNIT;
      lab=1; 
     }
    }
  }
 
 /******************************************************************************************************
   第3号位置由于变形后,有可能只是因为右侧位置不够而不能变形,只须稍微向左移动一个单位长度就可以了的
 *********************************************************************************************************/ 
  if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT+3] == current[ i ][3]) && current[ i ] [3] == 1 )
  {
   x-=UNIT;
   lab=0;
/*****************************************************************************************************************************
人为的移动后,可能对应的第0,1,2号位置有可能也已经有物体,若有则说明即使移动了一个单位长度也还是不够的两个标记位均赋1,跳出循环
******************************************************************************************************************************/    
   for (k=0;k<4;++k) 
    for (l=0;l<3;++l)
    { 
     if (lab==1)
      break;    
     if((GameSpace [ (y - spaTop) / UNIT +k] [ (x - spaLeft) / UNIT+l] == current[k][l]) && current[k] [l] == 1)
     {
      tag=1;
      x+=UNIT;
      lab=1; 
     }
    }
  }
  
 } 
  
  
  switch ( tag )   /*若标记变量始终为0,则说明没有插到别的有一的地方*/
 {
  case 0:
   break;    
  
  case 1:     /*若标记变量始终为1,则说明插到别的有一的地方,须变回来*/
   
   for(  i= 0 ; i < 4 ; ++ i)   /*逆序,先竖中轴对折*/
    for( j = 0 ; j < 2 ; ++ j)
    {
     temp = current[ i ][ j ] ;
     current[ i ][ j ] = current[ i ][ 3-j ] ;
     current[ i ] [3-j ] = temp ;
    }
   for ( i = 0 ; i < 3 ; ++ i )            /*再主对角线对折*/         
    for ( j = i+1 ; j < 4 ; ++ j )     
    {
     temp = current[ i ][ j ];
     current[ i ][ j ] = current[ j ][ i ];
     current[ j ][ i ] = temp ;
    }
   break; 
 } 
 
}

void down()
{
    if (crash()==0)  /*向下若不碰撞,则y坐标加一个单位长度*/
 y += UNIT ;   
 
}

void left()
{
 int i , j , tag=0;  /*标记变量跳出循环体,这是一种方法*/
 for( i = 0 ; i < 4 ; ++ i )
  for( j = 0 ; j < 4 ; ++ j )
  {
  /**************************************************************************************************************
  若整个方块数组向左一个单位长度,与游戏空间有重叠的1,说明下落的方块左边以有物体,不能向左,一检测重叠有就跳出循环
  ***************************************************************************************************************/
   if ( ( GameSpace[ ( y - spaTop ) / UNIT + i ] [( x - spaLeft - UNIT ) / UNIT + j] == current[ i ][ j ] ) && current[ i ][ j ] == 1 )
    {
       if ( tag == 1)
         break;
          tag = 1 ;  
    }
  }
  if ( tag == 0 )  /*标记变量始终不改变则说明可以向左移动*/
   x -= UNIT ;
}

void right()    
{
 int i , j , tag = 0 ;
 for( i = 0 ; i < 4 ; ++ i )
  for( j = 0 ; j < 4 ; ++ j )
  {
  /**************************************************************************************************************
  若整个方块数组向右一个单位长度,与游戏空间有重叠的1,说明下落的方块右边已有物体,不能向右,一检测重叠有就跳出循环
  ***************************************************************************************************************/
   if ( ( GameSpace[ ( y - spaTop ) / UNIT + i ] [( x - spaLeft + UNIT ) / UNIT + j] == current[ i ][ j ] ) && current[ i ][ j ] == 1 )
    {
      if ( tag == 1)
         break;
         tag = 1 ; 
    }
  }
  if ( tag == 0)  /*标记变量始终不改变则说明可以向右移动*/
   x += UNIT ;
}

void pause()    
{
 settextstyle(0,HORIZ_DIR,0);
 settextjustify(LEFT_TEXT,TOP_TEXT);
 setcolor( WHITE );
 outtextxy(225,35,"press any key");
 getch();     /*主要应用getch()的性质*/
 setfillstyle( SOLID_FILL , BLACK );
 bar(225,35,350,50);
 setcolor( BLACK );
}

void escape()
{
 int key;    /*按键值的记录*/
 int esc_color=0;  /*控制当前选项到了哪的重要判断标记*/
 int try_color=9;  /*说明当前选项到了选择重玩的地方*/
 int order_color=8;  /*说明当前选项到了返回hello()界面的地方*/
 int exit_color=8;  /*说明当前选项到了选择退出的地方*/
 while(1)
 {
  while(!kbhit())  /*若不按键,整个游戏停留在选项上*/
  {
   
   switch ( esc_color )  /*根据标记显示不同的颜色,以说明到了哪个选项*/
   {
    case 0:
      try_color=9;
      order_color=8;
      exit_color=8;
     break;
    case 1:
     try_color=8;
     order_color=9;
     exit_color=8;
     break;
    case 2:
     try_color=8;
     order_color=8;
     exit_color=9;
     break;
   }   
   settextjustify( LEFT_TEXT,CENTER_TEXT );
   settextstyle (1,0,3);
   setcolor(try_color);  
   outtextxy(445,35,"TRY AGAIN ");
   
   setcolor(order_color);
   outtextxy(464,60," MENU ");
   
   setcolor(exit_color);
   outtextxy(470,85," EXIT ");
  }
  key=bioskey(0);
  switch ( key )
  {
  case UP:
   if (esc_color == 0)  /*若选到了最顶,再按就下到最下边的选项*/
    esc_color=3;
   --esc_color;
   break;
  case DOWN:
   if (esc_color == 2)  /*若选到了最底,再按就下到最上边的选项*/
     esc_color=-1;
    ++esc_color;
    break;
  case ENTER:     
   if (esc_color == 0)  /*若选到了重玩,按回车则重新游戏*/
   {
    cleardevice();
    game();  
   }
   if (esc_color == 1)  /*若选到了最开始的界面,按回车则到开始界面*/
   {
    cleardevice();
    hello();  
   }
   if (esc_color == 2)  /*若选到了退出,按回车则退出游戏*/
   {
    cleardevice();
    goodbye();  
   }
   break;
  case ESC:     /*若按esc键,则直接跳到推出选项*/
   esc_color = 2;
   break;
   }
 }
}
 
void speed_up()      
{
 char ch[2];
 if ( (speed==3) && (difficulty==0) ) /*根据所选难度,加速到在一定范围的速度后不再加速*/
   ;
  else if  (speed==5)
    ;
    else
     ++speed;
 
 setfillstyle( SOLID_FILL , BLACK ) ; /*根据改变的值,输出对应的信息*/
 bar(105,325,170,343);
 setcolor( WHITE );
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,2);
 itoa(speed,ch,10);
 outtextxy(105,330,ch);
 setcolor(BLACK);

void speed_down()      /*根据所选难度,减慢到在一定范围的速度后不再减慢*/
{
 char ch[2];
 if ( (speed==0))
  ;
 else
  --speed;
 
 setfillstyle( SOLID_FILL , BLACK ) ; /*根据改变的值,输出对应的信息*/
 bar(105,325,170,343);
 setcolor( WHITE );
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,2);
 itoa(speed,ch,10);
 outtextxy(105,330,ch);
 setcolor(BLACK);

void wipe()
{
 char ch[2];
 int i;
 if ( eraser!=0 )   /*当橡皮还有时,消两次最底下那行,并橡皮减1*/
 {
  for(i=0;i<2;++i)
   clearrow(24);
  --eraser;
 }
 setfillstyle( SOLID_FILL , BLACK ) ; /*根据还剩的橡皮数,输出相应的信息*/
 bar(105,345,170,363);
 setcolor( WHITE );
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,2);
 itoa(eraser,ch,10);
 outtextxy(105,350,ch); 
 setcolor(BLACK);
 
}

void if_end()
{
 int i,tag=0;   
 for (i=1;i<11;++i)
  if ( GameSpace[3][i]==1)   /*最顶上一检测出有方块,则结束*/
  {
   tag=1;
   break; 
  } 
 if (tag==1)        /*标记变量为一说明,游戏结束,进入退出的菜单*/
 { 
  escape();
 }
}

void  preview(int for_next)
{
  int i,j;
  setfillstyle( SOLID_FILL , BLACK ) ;  /*先画黑*/
 bar( 473 , 123 , 547 , 197 ) ;
 
  setfillstyle(SOLID_FILL,RED);    /*再画方块数组中有1的地方*/
  for (i=0;i<4;++i)
   for (j=0;j<4;++j)
     if (a[ for_next ][i][j]==1)
      bar3d(480+j*UNIT,128+i*UNIT,480+j*UNIT+UNIT,128+i*UNIT+UNIT,0,0); 
 setfillstyle(SOLID_FILL,BLACK);
}

void  clearrow( int temp)
{
 int i , j ;
 /*************************************************************************
  根据传进来满行的所在行的编号,从那行起由下到上每行的上一行的值赋给这行
 ***************************************************************************/      
  for (i=temp;i>0;--i)
   for(j=1;j<11;++j)
   GameSpace[ i ][ j ] =  GameSpace[ i -1 ][ j ] ;                                   
                
 
 setfillstyle(SOLID_FILL,BLACK);  /*整个游戏空间画黑*/
 bar(spaLeft+UNIT,spaTop+64,spaLeft+UNIT+160,spaTop+UNIT+384);
 
 setfillstyle (SOLID_FILL,RED);  /*画红方块,再游戏空间中有1 的地方*/
 for (i=1;i<25;++i)  
  for (j=1;j<11;++j)
   if (GameSpace[i][j]==1)
    {
       bar3d(spaLeft+j*UNIT,spaTop+i*UNIT,spaLeft+j*UNIT+UNIT,spaTop+i*UNIT+UNIT,0,0);
    }
 setfillstyle(SOLID_FILL,BLACK);                      
 setcolor(BLACK);
}

void showscore(int t)
{
 char ch[6];
 switch( t )    /*根据传进来的一次消行循环中消了多少行,确定加多少分*/
 {
  case 0:
   break;
  case 1:
   score+=10;
   break;
  case 2:
   score+=20;
   break;
  case 3: 
   score+=40;
      break;
  case 4:
   score+=60;
   break;
  
 }
 setfillstyle( SOLID_FILL , BLACK ) ; 
 bar(105,305,170,323);
 setcolor( WHITE );
 settextjustify( LEFT_TEXT,CENTER_TEXT );
 settextstyle (1,0,2);
 itoa(score,ch,10);
 outtextxy(105,310,ch);
 
 if ( (score%1000==0) && (score!=0))    /*若分数每满1000分,速度加1*/
 {
  if ( (speed==3) && (difficulty==0) )  /*若选的难度为简单,速度最大为3*/
   ;
  else if  (speed==6)       /*别的最大为6*/
    ;
    else
     ++speed;
  setfillstyle( SOLID_FILL , BLACK ) ;  /*根据速度的值,先画黑再输出*/
  bar(105,325,170,343);
  setcolor( WHITE );
  settextjustify( LEFT_TEXT,CENTER_TEXT );
  settextstyle (1,0,2);
  itoa(speed,ch,10);
  outtextxy(105,330,ch);
 }
 
 if ( (score%500==0) && score!=0)    /*若分数每满500分,橡皮加1*/
 {
  ++eraser;
  setfillstyle( SOLID_FILL , BLACK ) ;  /*根据橡皮数,先画黑再输出*/
  bar(105,345,170,363);
  setcolor( WHITE );
  settextjustify( LEFT_TEXT,CENTER_TEXT );
  settextstyle (1,0,2);
  itoa(eraser,ch,10);
  outtextxy(105,350,ch);
 }
 setcolor(BLACK);
 

void  id(int rec)         /*为了能按照预览框的形状开始掉落*/
{
 int i , j ;
 for ( i = 0 ; i < 4 ; ++ i )
  for ( j = 0 ; j < 4 ; ++ j )
   current[ i ][ j ]=a[ rec ][ i ][ j ];
}

/************************
 极度核心的碰撞函数
************************/
int crash()
{
 int i , j , k , l;
 /****************************************************************************************************************
  若方块数组整体下移一个单位长度,与游戏空间有重叠的1,则在当前位置赋值给游戏空间,表示停止下落,并画红有1的地方
 *****************************************************************************************************************/
 
 for ( i = 0 ; i < 4; ++ i )
  for ( j = 0 ; j <4 ; ++j )
   if ( ( GameSpace[ ( y - spaTop + UNIT ) / UNIT + i ][ ( x - spaLeft ) / UNIT + j ] == current[ i ][ j ] ) && (current[ i ][ j ] == 1) )
   {
    for ( k = 0 ; k < 4 ; ++ k )
     for ( l = 0 ; l < 4 ; ++ l )
      if (current[k][l]==1)         /*只能赋下落数组中是1的地方,为解决这个错误用了三天*/
       GameSpace[ ( y - staTop ) / UNIT + k ][ ( x - spaLeft ) / UNIT + l ] = current[ k ][ l ];
    setfillstyle( SOLID_FILL , RED );
    for ( k = 3 ; k < 25 ; ++ k )
     for ( l = 1 ; l < 11 ; ++ l )
     { 
      if( GameSpace[k][l] == 1 )
       bar3d( spaLeft + l * UNIT, spaTop + k * UNIT, spaLeft + l  * UNIT + UNIT,spaTop + k * UNIT + UNIT, 0, 0);
     }
    setfillstyle( SOLID_FILL , BLACK );
    return 1;  /*若有赋值返回1说明碰撞了*/
   }
 return 0;    /*若经过了判断,则返回0说明没有碰撞*/
}

int fullrow()
{        /*若有满行,也每次只返回一行的行编号*/
 int i, j, tag;
 for (i=1;i<25;++i)
 { 
  tag=1;     /*先认为满行,标记变量赋初值为1*/
  for (j=1;j<11;++j)  
  {
   if (GameSpace[i][j]==0) /*若发现一行中一有0,说明这行没满,跳出扫下一行*/ 
   {
    tag=0;    
    break;  
   }
  } 
   if (tag == 1)    /*若当前i行满行,返回该行值,即*/
    return (i); 
 }
 if (i==25)   /*扫描了整个游戏空间,i为25说明没有满行*/
  return (0);
}

int next()
{      /*主要产生一个随机数*/
   int  i;
   randomize();
   i=rand()%nandu;
   return i;
}
 

原创粉丝点击