1. 程式人生 > >C語言圖形程式設計--俄羅斯方塊製作(二)原始碼

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(".\\rockshape.ini", "r") ;
    if (fp == NULL)
    {
        perror("open file error!\n") ;
        return 1 ;
    }

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

        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
@###
@###
@@##
####

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-----