牛骨文教育服务平台(让学习变的简单)

C语言图形编程--俄罗斯方块制作(二)源代码

所有源代码文件,此为本人2年前所作,设计上还有些缺陷。希望大家不吝指正。

设计详解点击这里

下面是头文件head.h

/************(C) COPYRIGHT 2013 yang_yulei ************
* File Name          : head.h
* Author             : yang_yulei
* Date First Issued  : 12/18/2013
* Description        : 
*
*
*****************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef _HEAD_H_
#define _HEAD_H_ 

/* Includes ------------------------------------------------------------------*/
#include <graphics.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>

/* Macro ---------------------------------------------------------------------*/
#define TRUE  1
#define FALSE 0

//GUI游戏界面相关的参数
#define GUI_WALL_SQUARE_WIDTH 10 //外围围墙小方格的宽度(单位:像素)
#define GUI_xWALL_SQUARE_NUM  30 //横向(x轴方向)围墙小方格的数量(必须是偶数)
#define GUI_yWALL_SQUARE_NUM  46 //纵向(y轴方向)围墙小方格的数量(必须是偶数)
#define GUI_WALL_WIDTH_PIX    (GUI_WALL_SQUARE_WIDTH*GUI_xWALL_SQUARE_NUM) 
#define GUI_WALL_HIGH_PIX     (GUI_WALL_SQUARE_WIDTH*GUI_yWALL_SQUARE_NUM) 

#define WINDOW_WIDTH  480     //窗口的宽度
#define WINDOW_HIGH   GUI_WALL_HIGH_PIX //窗口高度

//俄罗斯方块相关的参数
//移动的方向
#define DIRECT_UP    3
#define DIRECT_DOWN  2    
#define DIRECT_LEFT  -1
#define DIRECT_RIGHT 1    

//每一个小方块的大小(是围墙小方格宽度的2倍)
#define ROCK_SQUARE_WIDTH  (2*GUI_WALL_SQUARE_WIDTH)
//横向能容纳小方格的数量
#define X_ROCK_SQUARE_NUM  ((GUI_xWALL_SQUARE_NUM-2)/2)  
//纵向能容纳小方格的数量
#define Y_ROCK_SQUARE_NUM  ((GUI_yWALL_SQUARE_NUM-2)/2)  

/* Exported types ------------------------------------------------------------*/
typedef int BOOL ;     //布尔值类型

/*数据结构-线性表(结构体数组)*/
typedef struct ROCK
{
    //用来表示方块的形状(每一个字节是8位,用每4位表示方块中的一行)
    unsigned int rockShapeBits ;  
    int          nextRockIndex ;  //下一个方块,在数组中的下标
} RockType ;

//方块在图形窗口中的位置(即定位4*4大块的左上角坐标)
typedef struct LOCATE
{
    int left ; 
    int top ;
} RockLocation_t ;

/* Function prototypes -------------------------------------------------------*/
//源文件play.c中
void PlayGame(void) ;

//源文件init.c中
int InitProcParameters(void) ;

//源文件GUI.c中
void DrawRock(int, const struct LOCATE *, BOOL) ;
void DrawGameGUI(void) ;
void UpdataScore(void) ;
void UpdataGrade(int) ;

#endif /* _HEAD_H_ */

/*************(C) COPYRIGHT 2013 yang_yulei *****END OF FILE**/

下面是源文件main.cpp

/************(C) COPYRIGHT 2013 yang_yulei ************
* File Name          : main.cpp
* Author             : yang_yulei
* Date First Issued  : 1/16/2012
* Description        : 开发环境 VC++ 6.0 含EasyX图形库(http://www.easyx.cn)
*                   俄罗斯方块
*                   
*                   
****************************************
* History:
*  1/16/2012 : V0.1
* 12/18/2013 : V0.2
****************************************
* 
*****************************************/
/* Includes ------------------------------------------------------------------*/
#include "head.h"
#include <windows.h>
#include <dos.h>

/* Typedef -------------------------------------------------------------------*/
/* Variables -----------------------------------------------------------------*/
//全局变量-游戏板的状态描述(即表示当前界面哪些位置有方块)
//0表示没有,1表示有(多加了两行和两列,形成一个围墙,便于判断方块是否能够移动)
char g_gameBoard[Y_ROCK_SQUARE_NUM+2][X_ROCK_SQUARE_NUM+2] = {0} ;
//统计分数
int  g_score = 0 ; 
//等级
int  g_grade = 0 ;

int  g_rockTypeNum = 0 ; //共有多少种俄罗斯方块
RockType rockArray[50] = {(0,0)} ;

/*****************************************
* Function Name  : main
* Description    : Main program
* Input          : None
* Output         : None
* Return         : None
*****************************************/
int
main(void)
{
    //画出游戏界面
    initgraph(WINDOW_WIDTH, WINDOW_HIGH) ; //初始化图形窗口
    cleardevice() ;
    DrawGameGUI() ; 

    //使用 API 函数修改窗口名称
    HWND hWnd = GetHWnd();
    SetWindowText(hWnd, "俄罗斯方块");

    //初始化参数
    InitProcParameters() ;

    //游戏过程
    PlayGame()    ;
    
    closegraph()    ;
    return 0 ;
}

下面是源文件init.cpp---游戏运行前 初始化的一些方法

/************(C) COPYRIGHT 2013 yang_yulei ************
* File Name          : init.cpp
* Author             : yang_yulei
* Date First Issued  : 12/18/2013
* Description        : 
*
****************************************
* 
*****************************************/
/* Includes ------------------------------------------------------------------*/
#include "head.h"

/* Variables -----------------------------------------------------------------*/
extern char g_gameBoard[][X_ROCK_SQUARE_NUM+2] ;
extern int  g_rockTypeNum ; 
extern RockType rockArray[] ;

/* Function prototypes -------------------------------------------------------*/
static int ReadRockShape(void) ;
static unsigned int ShapeStr2uInt(char* const);

/*****************************************
* Function Name  : InitProcParameters
* Description    : 在正式开始运行游戏前,初始化一些参数:g_gameBoard
                   从配置文件中读取系统中俄罗斯方块的形状
* Be called      : main
* Input          : None
* Output         : g_gameBoard rockArray
* Return         : None
*****************************************/
//初始化程序参数
int
InitProcParameters(void)
{
    int  i ;
    
    //初始化游戏板(把这个二维数组的四周置1,当作围墙,用于判断边界)
    for (i = 0; i < X_ROCK_SQUARE_NUM+2; i++)
    {
        g_gameBoard[0][i] = 1 ;
        g_gameBoard[Y_ROCK_SQUARE_NUM+1][i]= 1 ;
    }
    for (i = 0; i < Y_ROCK_SQUARE_NUM+2; i++)
    {
        g_gameBoard[i][0] = 1 ;
        g_gameBoard[i][X_ROCK_SQUARE_NUM+1]= 1 ;
    }

    //从配置文件中读取游戏中所有方块的形状点阵
    ReadRockShape() ;

    return 0 ;
}

/*****************************************
* Function Name  : ReadRockShape
* Description    : 从配置文件中读取系统中俄罗斯方块的形状 把它记录在rockArray中
* Be called      : InitProcParameters
* Input          : rockshape.ini
* Output         : rockArray
* Return         : 成功返回0 失败返回1
*****************************************/
int
ReadRockShape(void)
{
    FILE* fp ;
    int  i = 0 ;
    int  len = 0 ;
    int  rockArrayIdx = 0 ;
    int  shapeNumPerRock = 0 ; //一种方块的形态数目(用于计算方块的nextRockIndex)
    
    char rdBuf[128] ;
    char rockShapeBitsStr[128] = {0};
    
    unsigned int  shapeBits = 0 ;

    g_rockTypeNum  = 0 ;

    //打开配置文件 从中读取方块的形状
    fp = fopen(".
ockshape.ini", "r") ;
    if (fp == NULL)
    {
        perror("open file error!
") ;
        return 1 ;
    }

    while (fgets(rdBuf, 128, fp) != NULL)
    {
        len = strlen(rdBuf) ;
        rdBuf[len-1] = "" ;

        switch (rdBuf[0])
        {
        case "@": case "#":
            strcat(rockShapeBitsStr, rdBuf) ;
        break ;

        case 0 : //一个方块读取结束
            shapeBits = ShapeStr2uInt(rockShapeBitsStr) ;
            rockShapeBitsStr[0] = 0 ;
            shapeNumPerRock++ ;
            rockArray[rockArrayIdx].rockShapeBits = shapeBits ;
            rockArray[rockArrayIdx].nextRockIndex = rockArrayIdx + 1 ;
            rockArrayIdx++ ;
            g_rockTypeNum++ ; //记录方块数量的全局变量+1
        break ;

        case "-"://一种方块读取结束(更新其nextRockIndex值)
            rockArray[rockArrayIdx-1].nextRockIndex = rockArrayIdx - shapeNumPerRock ;
            shapeNumPerRock = 0 ;
        break ;

        default :
        break ;
        }
    }//while()

    return 0 ;
}

/*****************************************
* Function Name  : ShapeStr2uInt
* Description    : 把配置文件中的描述方块形状的字符串 转化为 unsigned int型
* Be called      : 
* Input          : shapeStr 描述方块形状的字符串(从文件中读取的)
* Output         : None
* Return         : unsigned int型的方块形状点阵(用其低16位表示)
*****************************************/
unsigned int
ShapeStr2uInt(char* const shapeStr)
{
    unsigned int  shapeBitsRet = 0 ;
    char* p = shapeStr ;

    for (p += 15; p >= shapeStr; p--)
    {
        if (*p == "@")
        {
            shapeBitsRet |= ((unsigned int)1 << (&shapeStr[15]-p)) ;
        }
    }

    return shapeBitsRet ;
}

下面是源文件GUI.cpp---一些关于在界面上画出界面的一些方法

/************(C) COPYRIGHT 2013 yang_yulei ************
* File Name          : GUI.cpp
* Author             : yang_yulei
* Date First Issued  : 12/18/2013
* Description        : 
*
****************************************
* 
*****************************************/
/* Includes ------------------------------------------------------------------*/
#include "head.h"

/* Variables -----------------------------------------------------------------*/
//预览区位置
RockLocation_t previewLocation = {GUI_WALL_SQUARE_WIDTH*GUI_xWALL_SQUARE_NUM+70, 50} ; 

extern RockType rockArray[] ;

/*****************************************
* Function Name  : DrawRock
* Description    : 在游戏区画出编号为rockIndex的方块
* Be called      : PlayGame()
* Input          : rockIndex       :
                   currentLocatePtr: 此方块的位置
                   displayed       : 此方块是否显示
* Output         : None
* Return         : None
*****************************************/
void
DrawRock(int rockIndex, const struct LOCATE * currentLocatePtr, BOOL displayed)
{
    int i ;
    int mask  ;
    int rockX ;     //俄罗斯方块的4*4模型的左上角点x轴的坐标
    int rockY ;     //俄罗斯方块的4*4模型的左上角点y轴的坐标
    int spaceFlag ; //占位标记(用于g_gameBoard,1表示某处有方块 0表示此处无方块)
    int color ;     //画出的方块的颜色
    
    //若此方块是用于显示的,则设置其颜色为白色,其占位标记设为1
    //否则设置其颜色为黑色(背景色),占位标记设为0
    displayed ? (color = WHITE,spaceFlag = 1) 
              : (color = BLACK,spaceFlag = 0) ;

    setcolor(color) ;                 //设置画笔颜色
    setlinestyle(PS_SOLID, NULL, 2) ; //设置线形为1像素的实线
    rockX = currentLocatePtr->left ;
    rockY = currentLocatePtr->top ;
    
    //逐位扫描由unsigned int的低2字节
    //16个位组成的俄罗斯方块形状点阵(其代表4*4的方块形状)
    mask = (unsigned int)1 << 15 ;
    for (i=1; i<=16; i++)
    {
        //与掩码相与为1的 即为方块上的点
        if ((rockArray[rockIndex].rockShapeBits & mask) != 0)
        {
            //在屏幕上画出此方块
            rectangle(rockX+2, 
                      rockY+2, 
                      rockX+ROCK_SQUARE_WIDTH-2, 
                      rockY+ROCK_SQUARE_WIDTH-2) ; 
        }

        //每4次 换行 转到下一行继续画
        i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left)
                 :  rockX += ROCK_SQUARE_WIDTH ;

        mask >>= 1 ;
    }
}

/*****************************************
* Function Name  : DrawGameGUI
* Description    : 画出游戏界面
* Be called      : main()
* Input          : None
* Output         : None
* Return         : None
*****************************************/
void
DrawGameGUI(void)
{
    int i = 0 ;
    int wallHigh = GUI_yWALL_SQUARE_NUM * GUI_WALL_SQUARE_WIDTH ;//围墙的高度(像素)
    
    setcolor(RED) ;                      //设置围墙的颜色
    setlinestyle(PS_SOLID, NULL, 0)    ; //设置围墙方格的线形(1像素的实线)

    //画出围墙(画矩形是 先确定左上顶点的坐标,再确定右下顶点坐标)
    //先画出上下墙
    for (i = GUI_WALL_SQUARE_WIDTH; 
         i <= GUI_WALL_WIDTH_PIX; 
         i += GUI_WALL_SQUARE_WIDTH)
    {
        rectangle(i-GUI_WALL_SQUARE_WIDTH,
                  0,
                  i,
                  GUI_WALL_SQUARE_WIDTH) ; //上墙

        rectangle(i-GUI_WALL_SQUARE_WIDTH,
                  wallHigh-GUI_WALL_SQUARE_WIDTH, 
                  i,
                  wallHigh) ; //下墙
    }

    //再画出左右墙
    for (i = 2*GUI_WALL_SQUARE_WIDTH; 
         i <= wallHigh-GUI_WALL_SQUARE_WIDTH; 
         i += GUI_WALL_SQUARE_WIDTH)
    {
        rectangle(0,
                  i-GUI_WALL_SQUARE_WIDTH, 
                  GUI_WALL_SQUARE_WIDTH,
                  i) ; //左墙

        rectangle(GUI_WALL_WIDTH_PIX-GUI_WALL_SQUARE_WIDTH,
                  i-GUI_WALL_SQUARE_WIDTH, 
                  GUI_WALL_WIDTH_PIX,
                  i) ; //右墙
    }

    //画分隔线
    setcolor(WHITE) ;                                              //设置画笔颜色
    setlinestyle(PS_DASH, NULL, 2) ;                               //设置线形为2像素的虚线
    line(GUI_WALL_WIDTH_PIX+20,0,GUI_WALL_WIDTH_PIX+20,wallHigh) ; //在偏移右围墙的20处画线

    //画右边统计分数及版权信息栏
    //先设置字体
    LOGFONT   f ;                       //定义字体属性结构体
    getfont(&f) ;                       //获得当前字体
    f.lfHeight = 18 ;                   //设置字体高度为 38(包含行距)
    strcpy(f.lfFaceName, "黑体") ;      //设置字体为“黑体”
    f.lfQuality = ANTIALIASED_QUALITY ; //设置输出效果为抗锯齿  
    setfont(&f) ;                       //设置字体样式

    //1,显示预览
    outtextxy(GUI_WALL_WIDTH_PIX+80 , 20 , "预览") ;
    //2,显示等级栏
    outtextxy(GUI_WALL_WIDTH_PIX+80 , 140 , "等级") ;
    //3,显示得分栏
    outtextxy(GUI_WALL_WIDTH_PIX+80 , 190 , "得分") ;

    //4,显示操作说明    
    outtextxy(GUI_WALL_WIDTH_PIX+65 , 255 , "操作说明") ;
    getfont(&f) ;
    strcpy(f.lfFaceName, "宋体") ;
    f.lfHeight = 15 ;    
    setfont(&f) ;
    outtextxy(GUI_WALL_WIDTH_PIX+45 , 290 , "w.a.s.d控制方向") ;
    outtextxy(GUI_WALL_WIDTH_PIX+45 , 313 , "回车键 暂停") ;
    outtextxy(GUI_WALL_WIDTH_PIX+45 , 336 , "空格键 快速下落") ;

    //5.版权信息
    line(GUI_WALL_WIDTH_PIX+20 , wallHigh-65 , WINDOW_WIDTH , wallHigh-65) ;
    outtextxy(GUI_WALL_WIDTH_PIX+40 , wallHigh-50 , "  杨溢之  作品") ;
    outtextxy(GUI_WALL_WIDTH_PIX+40 , wallHigh-30 , "  QQ:702080167") ;

    //显示等级,得分信息
    setcolor(RED) ; 
    outtextxy(GUI_WALL_WIDTH_PIX+90 , 163 , "1") ;
    outtextxy(GUI_WALL_WIDTH_PIX+90 , 223 , "0") ;
}

/*****************************************
* Function Name  : UpdataScore
* Description    : 增加一次得分,并把游戏界面的得分区显示 更新
* Be called      : ProcessFullRow()
* Input          : None
* Output         : None
* Return         : None
*****************************************/
void
UpdataScore(void)
{
    char scoreStr[5] ; //用字符串的形式存储得分
    extern int g_score ;
    extern int g_grade ;

    //分数的增长的单位是10
    g_score += 10 ;
    //得分是100的倍数,则等级加1 (等级在5级以上的就 保持不变)
    if (g_score == (g_score/100)*100 && g_grade < 5)
        UpdataGrade(++g_grade) ;
    
    //删除原先信息
    setfillstyle(BLACK) ; 
    bar(GUI_WALL_WIDTH_PIX+90,220,GUI_WALL_WIDTH_PIX+99,229) ;
    
    //显示信息
    setcolor(RED) ; 
    sprintf(scoreStr , "%d" , g_score) ;
    outtextxy(GUI_WALL_WIDTH_PIX+90 , 223 , scoreStr)  ;
}

/*****************************************
* Function Name  : UpdataGrade
* Description    : 增加一次等级,并把游戏界面的等级区显示 更新
* Be called      : 
* Input          : grade :新的等级值
* Output         : None
* Return         : None
*****************************************/
void
UpdataGrade(int grade)
{
    char gradeStr[5] ; 

    //删除原先信息
    setfillstyle(BLACK) ; 
    bar(GUI_WALL_WIDTH_PIX+90,160,GUI_WALL_WIDTH_PIX+99,169) ;
    
    //显示信息
    setcolor(RED)    ; 
    sprintf(gradeStr , "%d" , grade) ;
    outtextxy(GUI_WALL_WIDTH_PIX+90 , 163 , gradeStr) ;
}

下面是源文件play.cpp---控制游戏的重要方法

/************(C) COPYRIGHT 2013 yang_yulei ************
* File Name          : play.cpp
* Author             : yang_yulei
* Date First Issued  : 12/18/2013
* Description        : 
*
****************************************
* 
*****************************************/
/* Includes ------------------------------------------------------------------*/
#include "head.h"

/* Variables -----------------------------------------------------------------*/
extern char     g_gameBoard[][X_ROCK_SQUARE_NUM+2] ;
extern int      g_rockTypeNum ; 
extern RockType rockArray[] ;

/* Function prototypes -------------------------------------------------------*/
static BOOL MoveAble(int, const struct LOCATE *, int) ;
static void SetOccupyFlag(int, const struct LOCATE *) ;
static void ProcessFullRow(void) ;
static BOOL isGameOver() ;
static void ProccessUserHit(int, int*, struct LOCATE*) ;
static void FastFall(int, struct LOCATE *, struct LOCATE *) ;
static void DelFullRow(int f_row) ;

/*****************************************
* Function Name  : PlayGame
* Description    : 此程序的主要设计逻辑
* Be called      : main
* Input          : None
* Output         : None
* Return         : None
*****************************************/
void
PlayGame(void)
{
    int   userHitChar ;      //用户敲击键盘的字符
    int   currentRockIndex ; //当前方块在rockArray数组中下标
    int   nextRockIndex ;    //准备的下个方块的下标
    BOOL  moveAbled = FALSE ;//记录方块能否落下
    DWORD oldtime = 0;
    extern int g_grade ;
    
    //当前方块位置
    RockLocation_t currentRockLocation ; 
    //初始方块位置(由当中开始下落)
    RockLocation_t initRockLocation = {(GUI_xWALL_SQUARE_NUM/2-4)*GUI_WALL_SQUARE_WIDTH, 
                                        GUI_WALL_SQUARE_WIDTH}; 
    //预览区位置
    extern RockLocation_t previewLocation ;

    //为第一次下落,初始化参数
    //随机选择当前的俄罗斯方块形状 和下一个俄罗斯方块形状
    srand(time(NULL)) ;
    currentRockIndex = rand()%g_rockTypeNum ;
    nextRockIndex = rand()%g_rockTypeNum ;
    currentRockLocation.left = initRockLocation.left ;
    currentRockLocation.top = initRockLocation.top ;

    while(1)
    {    
        DrawRock(currentRockIndex, ¤tRockLocation, TRUE) ; 
        FlushBatchDraw();    //用批绘图功能,可以消除闪烁

        //判断能否下落
        moveAbled = MoveAble(currentRockIndex, ¤tRockLocation, DIRECT_DOWN) ;

        //如果不能下落则生成新的方块
        if (!moveAbled) 
        {    
            //设置占位符(此时方块已落定)
            SetOccupyFlag(currentRockIndex, ¤tRockLocation) ;
            //擦除预览
            DrawRock( nextRockIndex, &previewLocation, FALSE) ; 
            //生成新的方块
            currentRockIndex = nextRockIndex ;
            nextRockIndex = rand()%g_rockTypeNum ;
            currentRockLocation.left = initRockLocation.left ;
            currentRockLocation.top = initRockLocation.top ;
        }
    
        //显示预览
        DrawRock(nextRockIndex, &previewLocation, TRUE) ;

        //如果超时(且能下落),自动下落一格
        //  这个超时时间400-80*g_grade 是本人根据实验自己得出的
        //  一个速度比较适中的一个公式(g_grade不会大于等于5)
        DWORD newtime = GetTickCount();   
        if (newtime - oldtime >= (unsigned int)(400-80*g_grade) && moveAbled == TRUE)
        {
            oldtime = newtime ;
            DrawRock(currentRockIndex, ¤tRockLocation, FALSE) ; //擦除原先位置
            currentRockLocation.top += ROCK_SQUARE_WIDTH ; //下落一格
        }

        //根据当前游戏板的状况判断是否满行,并进行满行处理
        ProcessFullRow() ;

        //判断是否游戏结束
        if (isGameOver())
        {    
            MessageBox( NULL,"游戏结束", "GAME OVER", MB_OK ) ;
            exit(0) ;
        }

        //测试键盘是否被敲击
        if (kbhit())
        {    
            userHitChar = getch() ; 
            ProccessUserHit(userHitChar, ¤tRockIndex, ¤tRockLocation) ;
        }

        Sleep(20) ; //降低CPU使用率
    }//结束外层while(1)

}

/*****************************************
* Function Name  : ProccessUserHit
* Description    : 处理用户敲击键盘
* Be called      : PlayGame()
* Input          : userHitChar     用户敲击键盘的ASCII码
                   rockIndexPtr    当前俄罗斯方块在rockArray中的下标
                   rockLocationPtr 当前方块在游戏界面中的位置
                   
* Output         : rockIndexPtr    响应用户敲击后 新方块的下标
                   rockLocationPtr 响应用户敲击后 新方块的位置
* Return         : None
*****************************************/
void
ProccessUserHit(int userHitChar, int* rockIndexPtr, struct LOCATE* rockLocationPtr)
{
    switch (userHitChar)
    {
    case "w" : case "W" :  //“上”键
        //检查是否能改变方块形状 
        if (MoveAble(rockArray[*rockIndexPtr].nextRockIndex, rockLocationPtr, DIRECT_UP))
        {
            DrawRock(*rockIndexPtr, rockLocationPtr, FALSE) ;
            *rockIndexPtr = rockArray[*rockIndexPtr].nextRockIndex ;
        }
    break ;

    case "s" : case "S" : //“下”键
        DrawRock(*rockIndexPtr, rockLocationPtr, FALSE) ; //擦除原先位置
        rockLocationPtr->top += ROCK_SQUARE_WIDTH    ;                 
    break ;

    case "a" : case "A" :  //“左”键
        if (MoveAble(*rockIndexPtr, rockLocationPtr, DIRECT_LEFT))
        {
            DrawRock(*rockIndexPtr, rockLocationPtr, FALSE) ; 
            rockLocationPtr->left -= ROCK_SQUARE_WIDTH    ;
        }                    
    break ;

    case "d" : case "D" :  //“右”键
        if (MoveAble(*rockIndexPtr, rockLocationPtr, DIRECT_RIGHT))
        {    
            DrawRock(*rockIndexPtr, rockLocationPtr, FALSE) ;
            rockLocationPtr->left += ROCK_SQUARE_WIDTH    ;
        }                    
    break ;

    case " " : //空格(快速下落)
        DrawRock(*rockIndexPtr, rockLocationPtr, FALSE) ;
        FastFall(*rockIndexPtr, rockLocationPtr, rockLocationPtr) ;    
    break ;

    case 13 : //回车键(暂停)
        while(1)
        {   userHitChar = getch() ;
            if (userHitChar==13)
                break ;
        }
    break ;

    default :    
    break ;
    }

}

/*****************************************
* Function Name  : MoveAble
* Description    : 判断编号为rockIndex 在位置currentLocatePtr的方块
                   能否向direction移动
* Be called      : 
* Input          : None
* Output         : None
* Return         : TRUE  可以移动
                   FALSE 不可以移动
*****************************************/
BOOL
MoveAble(int rockIndex, const struct LOCATE* currentLocatePtr, int f_direction)
{
    int i ;
    int mask  ;    
    int rockX ;
    int rockY ;
    
    rockX = currentLocatePtr->left ;
    rockY = currentLocatePtr->top ;

    mask = (unsigned int)1 << 15 ;
    for (i=1; i<=16; i++)
    {
        //与掩码相与为1的 即为方块上的点
        if ((rockArray[rockIndex].rockShapeBits & mask) != 0)
        {
            //判断能否移动(即扫描即将移动的位置 是否与设置的围墙有重叠)
            //若是向上(即翻滚变形)
            if( f_direction == DIRECT_UP )
            {
                //因为此情况下传入的是下一个方块的形状,故我们直接判断此方块的位置是否已经被占
                if (g_gameBoard[(rockY-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1]
                               [(rockX-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1] == 1)
                    return FALSE ; 
            }
            //如果是向下方向移动
            else if( f_direction == DIRECT_DOWN )
            {
                 if (g_gameBoard[(rockY-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+2]
                                [(rockX-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1] ==1) 
                    return FALSE ;
            }
            else //如果是左右方向移动
            {   //f_direction的DIRECT_LEFT为-1,DIRECT_RIGHT为1,故直接加f_direction即可判断。
                 if (g_gameBoard[(rockY-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1]
                                [(rockX-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1+f_direction] ==1) 
                    return FALSE ;
            }
        }

        //每4次 换行 转到下一行继续
        i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left)
                 :  rockX += ROCK_SQUARE_WIDTH ;

        mask >>= 1 ;
    }

    return TRUE ;
}

/*****************************************
* Function Name  : SetOccupyFlag
* Description    : 更新游戏板状态(把一些位置设置为已占用)
* Be called      : 
* Input          : rockIndex        方块的下标(定位了方块的形状)
                   currentLocatePtr 方块的位置(用来设定已占用标识)
* Output         : None
* Return         : None
*****************************************/
void
SetOccupyFlag(int rockIndex, const struct LOCATE * currentLocatePtr)
{
    int i ;
    int mask  ;    
    int rockX ;
    int rockY ;

    rockX = currentLocatePtr->left ;
    rockY = currentLocatePtr->top  ;
    
    mask = (unsigned int)1 << 15 ;
    for (i=1; i<=16; i++)
    {
        //与掩码相与为1的 即为方块上的点
        if ((rockArray[rockIndex].rockShapeBits & mask) != 0)
        {
            g_gameBoard[(rockY-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1]
                       [(rockX-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1] = 1 ;
        }

        //每4次 换行 转到下一行继续画
        i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left)
                 :  rockX += ROCK_SQUARE_WIDTH ;

        mask >>= 1 ;
    }
}

/*****************************************
* Function Name  : ProcessFullRow
* Description    : 检查是否有满行,若有,则删除满行(并更新得分信息)
* Be called      : 
* Input          : g_gameBoard
* Output         : None
* Return         : None
*****************************************/
void
ProcessFullRow(void)
{
    int  i = 1 ;
    int  cnt = 0 ;
    
    BOOL rowFulled = TRUE ;
    int  rowIdx = Y_ROCK_SQUARE_NUM ; //从最后一行开始往上检查

    while (cnt != X_ROCK_SQUARE_NUM) //直到遇到是空行的为止
    {
        rowFulled = TRUE ;
        cnt = 0 ;

        //判断是否有满行 并消除满行
        for (i = 1; i <= X_ROCK_SQUARE_NUM; i++)
        {
            if( g_gameBoard[rowIdx][i] == 0 )
            {
                rowFulled = FALSE ;
                cnt++ ;
            }
        }
        if (rowFulled) //有满行 (并更新得分信息)
        {    
            DelFullRow(rowIdx)    ; 
            //更新得分信息
            UpdataScore() ;
            rowIdx++ ;
        }   
        rowIdx--    ;
    }
}

/*****************************************
* Function Name  : DelFullRow
* Description    : 删除游戏板的第rowIdx行
* Be called      : 
* Input          : g_gameBoard
                   rowIdx 要删除的行 在g_gameBoard中的下标
* Output         : None
* Return         : None
*****************************************/
void
DelFullRow(int rowIdx)
{
    int cnt = 0 ;
    int i ;

    //把此行擦除
    setcolor(BLACK) ;
    for (i=1; i<=X_ROCK_SQUARE_NUM; i++)
    {
        rectangle(GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-ROCK_SQUARE_WIDTH+2, 
                  GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*rowIdx-ROCK_SQUARE_WIDTH+2, 
                  GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-2, 
                  GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*rowIdx-2) ;         
    }

    //把此行之上的游戏板方块全向下移动一个单位
    while (cnt != X_ROCK_SQUARE_NUM) //直到遇到是空行的为止
    {
        cnt =0 ;    
        for (i=1; i<=X_ROCK_SQUARE_NUM; i++)
        {
            g_gameBoard[rowIdx][i] = g_gameBoard[rowIdx-1][i] ;

            //擦除上面的一行
            setcolor(BLACK) ;
            rectangle( GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-ROCK_SQUARE_WIDTH+2, 
                       GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*(rowIdx-1)-ROCK_SQUARE_WIDTH+2, 
                       GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-2, 
                       GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*(rowIdx-1)-2 ) ; 

            //显示下面的一行
            if (g_gameBoard[rowIdx][i] ==1)
            {
                setcolor(WHITE) ;
                rectangle( GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-ROCK_SQUARE_WIDTH+2,
                           GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*rowIdx-ROCK_SQUARE_WIDTH+2, 
                           GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*i-2,
                           GUI_WALL_SQUARE_WIDTH+ROCK_SQUARE_WIDTH*rowIdx-2 ) ; 
            }

            if (g_gameBoard[rowIdx][i] == 0)
                cnt++ ;            //统计一行是不是 都是空格
        }//for

        rowIdx-- ;
    }
}

/*****************************************
* Function Name  : FastFall
* Description    : 让编号为rockIndex 且初始位置在currentLocatePtr的方块 
                   快速下落到底部
* Be called      : 
* Input          : rockIndex currentLocatePtr
* Output         : endLocatePtr  下落后方块的位置
* Return         : None
*****************************************/
void
FastFall
(int rockIndex, 
 struct LOCATE * currentLocatePtr, 
 struct LOCATE * endLocatePtr)
{
    int i ;
    int mask ;  //掩码,用于判断方块的形状
    int rockX ; //方块的坐标(4*4方格的左上角点的x轴坐标)
    int rockY ; //方块的坐标(4*4方格的左上角点的y轴坐标)

    while (currentLocatePtr->top <= GUI_WALL_SQUARE_WIDTH+Y_ROCK_SQUARE_NUM*ROCK_SQUARE_WIDTH)
    {
        rockX = currentLocatePtr->left ;
        rockY = currentLocatePtr->top  ;

        mask = (unsigned int)1 << 15 ;
        for (i=1; i<=16; i++)
        {
            //与掩码相与为1的 即为方块上的点
            if ((rockArray[rockIndex].rockShapeBits & mask) != 0)
            {
                if(g_gameBoard[(rockY-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1]
                              [(rockX-GUI_WALL_SQUARE_WIDTH)/ROCK_SQUARE_WIDTH+1] == 1) //遇到底部
                {
                    endLocatePtr->top = currentLocatePtr->top-ROCK_SQUARE_WIDTH    ;
                    return ;
                }  
            }

            //每4次 换行 转到下一行继续画
            i%4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left)
                     :  rockX += ROCK_SQUARE_WIDTH ;

            mask >>= 1 ;
        }

        currentLocatePtr->top += ROCK_SQUARE_WIDTH    ;
    }//while()
}

/*****************************************
* Function Name  : isGameOver
* Description    : 判断是否游戏结束
* Be called      : 
* Input          : None
* Output         : None
* Return         : TRUE  游戏结束
                   FALSE 游戏继续
*****************************************/
BOOL
isGameOver()
{    
    int i ;
    BOOL topLineHaveRock = FALSE ;    //在界面的最高行有方块的标记
    BOOL bottomLineHaveRock = FALSE ; //在界面的最低行有方块的标记

    for (i=1; i<=X_ROCK_SQUARE_NUM; i++)
    {
        if( g_gameBoard[1][i] == 1 )
            topLineHaveRock = TRUE ;
        if( g_gameBoard[Y_ROCK_SQUARE_NUM][i] == 1 )
            bottomLineHaveRock = TRUE ;
    }

    //若底层行和顶层行都有方块 则说明在所有行都有方块,游戏结束
    if (topLineHaveRock && bottomLineHaveRock)
        return TRUE ;
    else
        return FALSE ;
}

下面是配置文件rockshape.ini

@###
@###
@@##
####

@@@#
@###
####
####

@@##
#@##
#@##
####

##@#
@@@#
####
####

-----
#@##
#@##
@@##
####

@###
@@@#
####
####

@@##
@###
@###
####

@@@#
##@#
####
####

-----
@###
@@##
#@##
####

#@@#
@@##
####
####

-----
#@##
@@##
@###
####

@@##
#@@#
####
####

-----
#@##
@@@#
####
####

@###
@@##
@###
####

@@@#
#@##
####
####

#@##
@@##
#@##
####

-----
#@##
#@##
#@##
#@##

@@@@
####
####
####

-----
####
@@##
@@##
####

-----