俄罗斯方块(控制台版)

前言

听了老师的思路,自己实现了一个. 没有参考任何工程,  分层不是很明显.

本来应该分成4个层 : UI + 逻辑 + 算法 + 基础函数集. 中间调试花了很长时间,将自己玩的不轻.

现在大概分为 : UI + 逻辑 + 键盘处理. 没有做优化, 该做其他事情了.

效果图

俄罗斯方块(控制台版)_第1张图片

工程下载 srcRussiaBlocks.zip

控制台版俄罗斯方块. 编译环境: vs6, 纯c工程. 代码预览: http://blog.csdn.net/lostspeed/article/details/49556369

实现思路

/// @file exam_1_1\doc\readme.txt
/// @brief 游戏设计文档

工程名称 Russia blocks (俄罗斯方块 控制台版)

子目录定义
./ui 界面绘制
./logic 业务逻辑
./alg 算法
./doc 文档
./lib 第三方库

中文的显示

画墙 2维数组

初始化墙 墙在数组内用1表示
	墙在UI上用输入法输入符号"■"表示
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 0000000000 00000000000 1
1 1111111111 11111111111 1

砖头用2维数组表示, 1表示砖头, 0表示砖头图案中的空白
	砖头在UI上用输入法输入符号"□"表示

横着的砖头
1111
0000
0000
0000

砖头属性: 位置, 类型
	类型: 大类型,小类型
	大类型: 横转, 竖砖 etc.
	小类型: 旋转后的类型(一样是横转,旋转的角度不同,显示的图案不同)

随机方法
	srand
	rand() % n; ///< 0 ~ (n - 1)

砖头的显示
	将砖头画到UI上(将砖头数据画到UI中), 砖头用2表示

接受控制
	_kbhit() 是否有键按下
	getchar()

键处理
	上下左右 + 旋转(单方向)

建立当前砖头
消除砖头(固定砖头, 清掉当前砖头数据)
固定砖头(将当前砖头数据和UI溶为一体)
消行
产生新砖头

优化: 只更新需要显示的数据
记时: clock_t ts = clock() 单位(ms)
分数的显示

游戏循环的流程

自动下落
判断游戏结束
	胜利的判断(得分超过多少)
	失败的判断(无法显示新砖头)




工程结构

俄罗斯方块(控制台版)_第2张图片

代码预览

/// @file \lesson\2015_1030\exam_1_1\main.c
/// @brief Russia blocks

#include "gameControl.h"

int main(int argc, char** argv)
{
    startGame();

    return 0;
}



/// @file \2015_1030\exam_1_1\ui\const_define_ui.h
/// @brief UI常量的定义

#ifndef __CONST_DEFINE_UI_H__
#define __CONST_DEFINE_UI_H__

/// 墙的size
#define WALL_ROW 20
#define WALL_COL 16

extern unsigned char g_cGameUI[WALL_ROW][WALL_COL];

/// 砖头的size 
#define BRICK_ROW 4
#define BRICK_COL 4

/// 砖头起始位置
#define BRICK_FIRST_POSITION_ROW 0
#define BRICK_FIRST_POSITION_COL ((int)(WALL_COL / 2) - BRICK_COL)

#define BRICK_BIG_TYPE_CNT 5 ///< (横砖, 竖砖, 三角形, L型, Z型)
#define BRICK_SMALL_TYPE_CNT 4 ///< (旋转: 0度, 90度,180度, 270度)

#define BRICK_ROW_ALL (BRICK_BIG_TYPE_CNT * BRICK_SMALL_TYPE_CNT * BRICK_ROW)

/// 墙的厚度
#define WALL_WIDTH 2 ///< 左右的墙厚度
#define WALL_HEIGHT 2 ///< 下面的墙厚度

#define DATA_EMPTY 0
#define DATA_WALL 1
#define DATA_BRICK 2

/// 图形符号

/// 为了使UI居中,在左面打印一行空格
#define GRAPH_OFFSET_LINE "          "

/// 使用说明
#define GRAPH_USAGE_LINE1 "左箭头 => 向左, 右箭头 => 向右, 上箭头 => 向上"
#define GRAPH_USAGE_LINE2 "下箭头 => 向下, W键 => 旋转, Q键 => 退出"

#define GRAPH_EMPTY "  "
#define GRAPH_WALL "■"
#define GRAPH_BRICK "□"

#endif

/// @file \2015_1030\exam_1_1\ui\ui.h
/// @brief UI显示

#ifndef __UI_H__
#define __UI_H__

void updateUI();

void clearScreen();
void DrawUI();

#endif //  #ifndef __UI_H__


/// @file \exam_1_1\ui\ui.c
/// @brief

#include "common.h"

unsigned char g_cGameUI[WALL_ROW][WALL_COL] = {0}; ///< 游戏显示区
unsigned char g_cBrickData[BRICK_ROW][BRICK_COL] = {0}; ///< 当前单块砖头数据

/// 从指定的行列取的砖头数据大小的墙数据, 用于是否能将砖块挪动到这个位置
unsigned char g_cUiWallDataByPos[BRICK_ROW][BRICK_COL] = {0};

/// 砖头图案库
unsigned char g_cBrickDataLib[BRICK_ROW_ALL][BRICK_COL] = 
{
    /// 逆时针旋转

    /// ========================================
    /// 横砖头 大类型0
    /// ========================================

    /// 横砖头 - 0度
    1, 1, 1, 1,
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,

    /// 横砖头 - 90度
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 0, 0, 0,

    /// 横砖头 - 180度
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,
    1, 1, 1, 1,

    /// 横砖头 - 270度
    0, 0, 0, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,

    /// ========================================
    /// 竖砖头 大类型1
    /// ========================================

    /// 竖砖头 - 0度
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 0, 0, 0,

    /// 竖砖头 - 90度
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,
    1, 1, 1, 1,

    /// 竖砖头 - 180度
    0, 0, 0, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,

    /// 竖砖头 - 270度
    1, 1, 1, 1,
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,

    /// ========================================
    /// 三角型砖头 大类型2
    /// ========================================

    /// 三角型砖头 - 0度
    1, 0, 0, 0,
    1, 1, 0, 0,
    1, 0, 0, 0,
    0, 0, 0, 0,

    /// 三角型砖头 - 90度
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 1, 0, 0,
    1, 1, 1, 0,

    /// 三角型砖头 - 180度
    0, 0, 0, 0,
    0, 0, 0, 1,
    0, 0, 1, 1,
    0, 0, 0, 1,

    /// 三角型砖头 - 270度
    0, 1, 1, 1,
    0, 0, 1, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,

    /// ========================================
    /// L型砖头 大类型3
    /// ========================================

    /// L型砖头 - 0度
    1, 1, 1, 1,
    1, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,

    /// L型砖头 - 90度
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 0, 0, 0,
    1, 1, 0, 0,

    /// L型砖头 - 180度
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 1,
    1, 1, 1, 1,

    /// L型砖头 - 270度
    0, 0, 1, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,
    0, 0, 0, 1,

    /// ========================================
    /// Z型砖头 大类型4
    /// ========================================

    /// L型砖头 - 0度
    0, 0, 0, 1,
    1, 1, 1, 1,
    1, 0, 0, 0,
    0, 0, 0, 0,

    /// Z型砖头 - 90度
    1, 1, 0, 0,
    0, 1, 0, 0,
    0, 1, 0, 0,
    0, 1, 1, 0,

    /// Z型砖头 - 180度
    0, 0, 0, 0,
    0, 0, 0, 1,
    1, 1, 1, 1,
    1, 0, 0, 0,

    /// Z型砖头 - 270度
    0, 1, 1, 0,
    0, 0, 1, 0,
    0, 0, 1, 0,
    0, 0, 1, 1,
};

void updateUI()
{
    if (IsDataDrity())
    {
        mergeBrickDataToUi();
        clearScreen();
        DrawUI();
        SetDataDirty(FALSE);
    }
}

void DrawUI()
{
    int i = 0;
    int j = 0;

    for (i = 0; i < WALL_ROW; i++)
    {
        printf("%s", GRAPH_OFFSET_LINE);
        for (j = 0; j < WALL_COL; j++)
        {
            switch(g_cGameUI[i][j])
            {
                case DATA_WALL:
                    printf("%s", GRAPH_WALL);
                break;

                case DATA_BRICK:
                    printf("%s", GRAPH_BRICK);
                break;

                default:
                    printf("%s", GRAPH_EMPTY);
                break;
            }
        }

        printf("\n");
    }

    printf("%s", GRAPH_OFFSET_LINE);
    printf("%s\n", GRAPH_USAGE_LINE1);

    printf("%s", GRAPH_OFFSET_LINE);
    printf("%s\n", GRAPH_USAGE_LINE2);
}

void clearScreen()
{
    system("cls");
}

/// @file \2015_1030\exam_1_1\gameControl.h
/// @brief 游戏控制逻辑

#ifndef __GAMECONTROL_H__
#define __GAMECONTROL_H__

#include "common.h"
#include "logic/keyBoardControl.h"

typedef enum eBrickType
{
    eBrickType_unknown = -1,
    eBrickType_Horizontal_bar, ///< 横块
    eBrickType_Vertical_bar, ///< 竖块
    eBrickType_Triangle, ///< 三角型
    eBrickType_L, ///< L型
    eBrickType_Z, ///< Z型
}ENUM_EBRICKTYPE;

typedef enum eBrickRotateAngle
{
    eBrickRotateAngle_unknown = -1,
    eBrickRotateAngle_0, ///< 0度
    eBrickRotateAngle_90, ///< 90度
    eBrickRotateAngle_180, ///< 180度
    eBrickRotateAngle_270, ///< 270度
}ENUM_EBRICKROTATEANGLE;

void startGame();
void InitGameData();

void SetDataDirty(BOOL bDirty);
BOOL IsDataDrity();

void SetHaveBrickOnUI(BOOL bHave);
BOOL IsHaveBrickOnUI();
int GetBrickFirstPositionRow();
int GetBrickFirstPositionCol();

void SetBrickData(
    int iBrickPosRow,
    int iBrickPosCol, 
    ENUM_EBRICKTYPE BrickType, 
    ENUM_EBRICKROTATEANGLE BrickRotateAngle);

BOOL GetBrickData(
    int* piBrickPosRow,
    int* piBrickPosCol, 
    ENUM_EBRICKTYPE* pBrickType, 
    ENUM_EBRICKROTATEANGLE* pBrickRotateAngle);

void GenBrickData(BOOL bGenData);
void fillBrickData();
BOOL IsEmptyBrickData();
void mergeBrickDataToUi();
void clearBrickDataOnUiData(int iUiPosRow, int iUiPosCol);
void mergeBrickDataOnUiData();
ENUM_EBRICKTYPE GenRand_BrickType();
ENUM_EBRICKROTATEANGLE GenRand_BrickAngle();

/// 是否给定坐标在固定墙内(固定墙:程序初始化时,画出的墙)
BOOL IsFixWall(int iCoordinatesX, int iCoordinatesY);

/// 判断该点在UI上是否为墙数据
BOOL IsWallData(int iRow, int iCol, BOOL* pbIsWallData);

ENUM_EKBINPUT procKbInput(enum eKbInput KbInput);
void procKbInput_Left();
void procKbInput_Right();
void procKbInput_Up();
void procKbInput_Down();
void procKbInput_Rotate();

void Brick2Wall(); ///< 砖块数据转墙数据
void KillRows(); ///< 消行
void KillRow(int iRow); ///< 消指定行

BOOL GetWallDataOnUi(unsigned char cData[BRICK_ROW][BRICK_COL], int iRowBegin, int iColBegin);
BOOL IsSrcDataCanPutOnDst(
    unsigned char cDataSrc[BRICK_ROW][BRICK_COL], 
    unsigned char cDataDst[BRICK_ROW][BRICK_COL]);

#endif // #ifndef __GAMECONTROL_H__

/// @file \2015_1030\exam_1_1\gameControl.cpp
/// @brief

#include "common.h"

#include "gameControl.h"
#include "ui/ui.h"
#include "logic/keyBoardControl.h"

BOOL g_bDrity = FALSE;
BOOL g_bHaveBrickOnUI = FALSE;

/// 如果屏幕上还没有砖头, 砖头的数据如下
int g_iUiBrickPosRow = -1;
int g_iUiBrickPosCol = -1;
ENUM_EBRICKTYPE g_BrickType = eBrickType_unknown;
ENUM_EBRICKROTATEANGLE g_BrickRotateAngle = eBrickRotateAngle_0;

extern unsigned char g_cBrickData[BRICK_ROW][BRICK_COL];
extern unsigned char g_cBrickDataLib[BRICK_ROW_ALL][BRICK_COL];

void startGame()
{
    InitGameData();

    do 
    {
        if (!IsHaveBrickOnUI())
        {
            GenBrickData(TRUE);
        }

        updateUI();

        if (_kbhit() > 0)
        {
            if (eKbInput_quit ==  procKbInput(GetKbInput()))
            {
                break;
            }
        }
    } while (1);
}

int GetBrickFirstPositionRow()
{
    return BRICK_FIRST_POSITION_ROW;
}

int GetBrickFirstPositionCol()
{
    return BRICK_FIRST_POSITION_COL;
}

void SetHaveBrickOnUI(BOOL bHave)
{
    if (!bHave)
    {
        _ASSERT(1);
    }
    g_bHaveBrickOnUI = bHave;
}

BOOL IsHaveBrickOnUI()
{
    return g_bHaveBrickOnUI;
}

void SetBrickData(
    int iBrickPosRow,
    int iBrickPosCol, 
    ENUM_EBRICKTYPE BrickType, 
    ENUM_EBRICKROTATEANGLE BrickRotateAngle)
{
    g_iUiBrickPosRow = iBrickPosRow;
    g_iUiBrickPosCol = iBrickPosCol;

    g_BrickType = BrickType;
    g_BrickRotateAngle = BrickRotateAngle;
}

BOOL GetBrickData(
    int* piBrickPosRow,
    int* piBrickPosCol, 
    ENUM_EBRICKTYPE* pBrickType, 
    ENUM_EBRICKROTATEANGLE* pBrickRotateAngle)
{
    BOOL bRc = FALSE;

    do 
    {
        if ((NULL == piBrickPosRow)
            || (NULL == piBrickPosCol)
            || (NULL == pBrickType)
            || (NULL == pBrickRotateAngle))
        {
            _ASSERT(0);
            break;
        }

        *piBrickPosRow = g_iUiBrickPosRow;
        *piBrickPosCol = g_iUiBrickPosCol;
        *pBrickType = g_BrickType;
        *pBrickRotateAngle = g_BrickRotateAngle;
        bRc = TRUE;
    } while (0);

    return bRc;
}

ENUM_EBRICKTYPE GenRand_BrickType()
{
    BOOL bRc = FALSE;
    int iCnt = (eBrickType_Z - eBrickType_Horizontal_bar) + 1;
    int iIndex = rand() % iCnt;

    bRc = ((iIndex > eBrickType_unknown) && (iIndex <= eBrickType_Z));
    if (!bRc)
    {
        _ASSERT(0);
    }

    return (ENUM_EBRICKTYPE)iIndex;
}

ENUM_EBRICKROTATEANGLE GenRand_BrickAngle()
{
    BOOL bRc = FALSE;
    int iCnt = (eBrickRotateAngle_270 - eBrickRotateAngle_0) + 1;
    int iIndex = rand() % iCnt;

    bRc = ((iIndex > eBrickRotateAngle_unknown) && (iIndex <= eBrickRotateAngle_270));
    if (!bRc)
    {
        _ASSERT(0);
    }

    return (ENUM_EBRICKTYPE)iIndex;
}

void GenBrickData(BOOL bGenData)
{
    if (bGenData)
    {
        SetBrickData(
            GetBrickFirstPositionRow(),
            GetBrickFirstPositionCol(), 
            GenRand_BrickType(), 
            GenRand_BrickAngle());
    }

    fillBrickData();
    SetHaveBrickOnUI(TRUE);
    SetDataDirty(TRUE);
}

void fillBrickData()
{
    int iIndex = 0;
    int i = 0;
    int j = 0;

    int iBrickPosRow = 0;
    int iBrickPosCol = 0;
    ENUM_EBRICKTYPE BrickType = eBrickType_unknown;
    ENUM_EBRICKROTATEANGLE BrickRotateAngle = eBrickRotateAngle_unknown;

    GetBrickData(&iBrickPosRow, &iBrickPosCol, &BrickType, &BrickRotateAngle);

    // fill g_cBrickData from g_cBrickDataLib
    /// 砖块数据在砖块图形库中的数组位置索引, 
    iIndex = (BrickType - eBrickType_Horizontal_bar) * BRICK_SMALL_TYPE_CNT + (BrickRotateAngle - eBrickRotateAngle_0);
    _ASSERT(iIndex < BRICK_ROW_ALL);

    for (i = 0; i < BRICK_ROW; i++)
    {
        for (j = 0; j < BRICK_COL; j++)
        {
            g_cBrickData[i][j] = g_cBrickDataLib[iIndex * BRICK_ROW + i][j];
        }
    }

    if (IsEmptyBrickData())
    {
        _ASSERT(0);
    }
}

BOOL IsEmptyBrickData()
{
    BOOL bEmpty = TRUE; ///< 假设为空
    int i = 0;
    int j = 0;

    for (i = 0; i < BRICK_ROW; i++)
    {
        for (j = 0; j < BRICK_COL; j++)
        {
            if (0 != g_cBrickData[i][j])
            {
                /// not empty
                bEmpty = FALSE;
                goto LABEL_ISEMPTYBRICKDATA_END;
            }
        }
    }

LABEL_ISEMPTYBRICKDATA_END:
    return bEmpty;
}

void mergeBrickDataToUi()
{
    /// 有砖头数据, 就将砖头数据写到UI数组
    /// 砖头数据没有的标记,在消行时处理
    if (IsHaveBrickOnUI() && IsDataDrity())
    {
        /// 在UI数据中清掉"当前显示的砖头数据"
        clearBrickDataOnUiData(g_iUiBrickPosRow, g_iUiBrickPosCol);

        /// 将"砖头数据"合并到UI数据
        mergeBrickDataOnUiData();
    }
}

BOOL IsFixWall(int iCoordinatesX, int iCoordinatesY)
{
    if ((iCoordinatesY >= 0) 
        && (iCoordinatesY < WALL_WIDTH))
    {
        return TRUE;
    }
    else if ((iCoordinatesY >= (WALL_COL - WALL_WIDTH))
        && (iCoordinatesY < WALL_COL))
    {
        return TRUE;
    }
    else if ((iCoordinatesX >= (WALL_ROW - WALL_HEIGHT))
        && (iCoordinatesX < WALL_ROW))
    {
        return TRUE;
    }

    return FALSE;
}

BOOL IsWallData(int iRow, int iCol, BOOL* pbIsWallData)
{
    BOOL bRc = FALSE;

    do 
    {
        _ASSERT(NULL != pbIsWallData);
        *pbIsWallData = TRUE;

        if (((iCol < 0 ) || (iCol > (WALL_COL - 1)))
            || ((iRow < 0 ) || (iRow > (WALL_ROW - 1))))
        {
            break;
        }

        *pbIsWallData = (DATA_WALL == g_cGameUI[iRow][iCol]);
        bRc = TRUE;
    } while (0);

    return bRc;
}

void clearBrickDataOnUiData(int iUiPosRow, int iUiPosCol)
{
    int i = 0;
    int j = 0;
    BOOL bIsWallData = FALSE;

    for (i = 0; i < BRICK_ROW; i++)
    {
        for (j = 0; j < BRICK_ROW; j++)
        {
            if (IsWallData(iUiPosRow + i, iUiPosCol + j, &bIsWallData) && !bIsWallData)
            {
                g_cGameUI[iUiPosRow + i][iUiPosCol + j] = DATA_EMPTY;
            }
        }
    }
}

void mergeBrickDataOnUiData()
{
    int i = 0;
    int j = 0;
    BOOL bIsWallData = FALSE;

    for (i = 0; i < BRICK_ROW; i++)
    {
        for (j = 0; j < BRICK_COL; j++)
        {
            if (IsWallData(g_iUiBrickPosRow + i, g_iUiBrickPosCol + j, &bIsWallData) && !bIsWallData)
            {
                g_cGameUI[g_iUiBrickPosRow + i][g_iUiBrickPosCol + j] = (1 == g_cBrickData[i][j]) ? DATA_BRICK : DATA_EMPTY;
            }
        }
    }
}

void SetDataDirty(BOOL bDirty)
{
    g_bDrity = bDirty;
}

BOOL IsDataDrity()
{
    return g_bDrity;
}

void InitGameData()
{
    int i = 0;
    int j = 0;

    srand(time(NULL));
    setlocale(LC_ALL, ".936"); ///< 设置中文代码页, 用来显示中文字符
    SetDataDirty(TRUE);
    SetHaveBrickOnUI(FALSE);

    for (i = 0; i < WALL_ROW; i++)
    {
        for (j = 0; j < WALL_COL; j++)
        {
            if (IsFixWall(i, j))
            {
                g_cGameUI[i][j] = DATA_WALL;
            }
            else
            {
                g_cGameUI[i][j] = DATA_EMPTY;
            }
        }
    }
}

ENUM_EKBINPUT procKbInput(enum eKbInput KbInput)
{
    switch (KbInput)
    {
        case eKbInput_up:
            procKbInput_Up();
            break;

        case eKbInput_down:
            procKbInput_Down();
            break;

        case eKbInput_left:
            procKbInput_Left();
            break;
     
        case eKbInput_right:
            procKbInput_Right();
            break;

        case eKbInput_rotate:
            procKbInput_Rotate();
            break;
    }

    return KbInput;
}

void procKbInput_Left()
{
    int iBrickPosRow = 0;
    int iBrickPosCol = 0;
    ENUM_EBRICKTYPE BrickType = eBrickType_unknown;
    ENUM_EBRICKROTATEANGLE BrickRotateAngle = eBrickRotateAngle_unknown;
    unsigned char cUiWallDataByPos[BRICK_ROW][BRICK_COL] = {0};

    GetBrickData(&iBrickPosRow, &iBrickPosCol, &BrickType, &BrickRotateAngle);
    do 
    {
        GetWallDataOnUi(cUiWallDataByPos, iBrickPosRow, iBrickPosCol - 1);
        clearBrickDataOnUiData(iBrickPosRow, iBrickPosCol);

        if (IsSrcDataCanPutOnDst(g_cBrickData, cUiWallDataByPos))
        {
            SetBrickData(iBrickPosRow, --iBrickPosCol, BrickType, BrickRotateAngle);
        }

        SetDataDirty(TRUE);
    } while (0);
}

void procKbInput_Right()
{
    int iBrickPosRow = 0;
    int iBrickPosCol = 0;
    ENUM_EBRICKTYPE BrickType = eBrickType_unknown;
    ENUM_EBRICKROTATEANGLE BrickRotateAngle = eBrickRotateAngle_unknown;
    unsigned char cUiWallDataByPos[BRICK_ROW][BRICK_COL] = {0};

    GetBrickData(&iBrickPosRow, &iBrickPosCol, &BrickType, &BrickRotateAngle);
    do 
    {
        GetWallDataOnUi(cUiWallDataByPos, iBrickPosRow, iBrickPosCol + 1);
        clearBrickDataOnUiData(iBrickPosRow, iBrickPosCol);

        if (IsSrcDataCanPutOnDst(g_cBrickData, cUiWallDataByPos))
        {
            SetBrickData(iBrickPosRow, ++iBrickPosCol, BrickType, BrickRotateAngle);
        }

        SetDataDirty(TRUE);
    } while (0);
}

void procKbInput_Up()
{
    int iBrickPosRow = 0;
    int iBrickPosCol = 0;
    ENUM_EBRICKTYPE BrickType = eBrickType_unknown;
    ENUM_EBRICKROTATEANGLE BrickRotateAngle = eBrickRotateAngle_unknown;
    unsigned char cUiWallDataByPos[BRICK_ROW][BRICK_COL] = {0};

    GetBrickData(&iBrickPosRow, &iBrickPosCol, &BrickType, &BrickRotateAngle);
    do 
    {
        if (iBrickPosRow <= 0)
            break;

        GetWallDataOnUi(cUiWallDataByPos, iBrickPosRow - 1, iBrickPosCol);
        clearBrickDataOnUiData(iBrickPosRow, iBrickPosCol);

        if (IsSrcDataCanPutOnDst(g_cBrickData, cUiWallDataByPos))
        {
            SetBrickData(--iBrickPosRow, iBrickPosCol, BrickType, BrickRotateAngle);
        }

        SetDataDirty(TRUE);
    } while (0);
}

void procKbInput_Down()
{
    int iBrickPosRow = 0;
    int iBrickPosCol = 0;
    ENUM_EBRICKTYPE BrickType = eBrickType_unknown;
    ENUM_EBRICKROTATEANGLE BrickRotateAngle = eBrickRotateAngle_unknown;
    unsigned char cUiWallDataByPos[BRICK_ROW][BRICK_COL] = {0};

    GetBrickData(&iBrickPosRow, &iBrickPosCol, &BrickType, &BrickRotateAngle);
    GetWallDataOnUi(cUiWallDataByPos, iBrickPosRow + 1, iBrickPosCol);
    clearBrickDataOnUiData(iBrickPosRow, iBrickPosCol);

    if (IsSrcDataCanPutOnDst(g_cBrickData, cUiWallDataByPos))
    {
        SetBrickData(++iBrickPosRow, iBrickPosCol, BrickType, BrickRotateAngle);
    }
    else
    {
        mergeBrickDataOnUiData();
        Brick2Wall();
        SetHaveBrickOnUI(FALSE);
        KillRows();
    }

    SetDataDirty(TRUE);
}

void Brick2Wall()
{
    int i = 0;
    int j = 0;

    for (i = 0; i < BRICK_ROW; i++)
    {
        for (j = 0; j < BRICK_COL; j++)
        {
            if (DATA_BRICK == g_cGameUI[g_iUiBrickPosRow + i][g_iUiBrickPosCol + j])
            {
                g_cGameUI[g_iUiBrickPosRow + i][g_iUiBrickPosCol + j] = DATA_WALL;
            }
        }
    }
}

void KillRows()
{
    BOOL bAllColWasNoZero = TRUE;
    int i = 0;
    int j = 0;

    do 
    {
        for (i = (WALL_ROW - WALL_HEIGHT - 1); i >= 0; i--)
        {
            bAllColWasNoZero = TRUE;
            for (j = WALL_WIDTH; j < (WALL_COL - WALL_WIDTH); j++)
            {
                if (DATA_EMPTY == g_cGameUI[i][j])
                {
                    bAllColWasNoZero = FALSE;
                    break;
                }
            }

            if (bAllColWasNoZero)
            {
                /// 消掉一行后,游戏区都下降了一行
                KillRow(i++);
            }
        }
    } while (0);
}

void KillRow(int iRow)
{
    int i = 0;
    int j = 0;

    for (i = iRow;  i >= 0; i--)
    {
        for (j = WALL_WIDTH; j < (WALL_COL - WALL_WIDTH); j++)
        {
            g_cGameUI[i][j] = g_cGameUI[i - 1][j];
        }
    }

    for (j = WALL_WIDTH; j < (WALL_COL - WALL_WIDTH); j++)
    {
        g_cGameUI[0][j] = 0;
    }
}

void procKbInput_Rotate()
{
    switch (g_BrickRotateAngle)
    {
        case eBrickRotateAngle_0:
            g_BrickRotateAngle = eBrickRotateAngle_90;
            break;

        case eBrickRotateAngle_90:
            g_BrickRotateAngle = eBrickRotateAngle_180;
            break;

        case eBrickRotateAngle_180:
            g_BrickRotateAngle = eBrickRotateAngle_270;
            break;

        case eBrickRotateAngle_270:
            g_BrickRotateAngle = eBrickRotateAngle_0;
            break;

        default:
            _ASSERT(0);
            break;
    }

    GenBrickData(FALSE);
}

BOOL GetWallDataOnUi(unsigned char cData[BRICK_ROW][BRICK_COL], int iRowBegin, int iColBegin)
{
    BOOL bRc = FALSE;
    BOOL bError = FALSE;
    BOOL bIsWall = FALSE;
    int i = 0;
    int j = 0;

    do 
    {
        for (i = 0; i < BRICK_ROW; i++)
        {
            for (j = 0; j < BRICK_COL; j++)
            {
                if (((iRowBegin + i) >= 0) 
                    && ((iRowBegin + i) < WALL_ROW)
                    && ((iColBegin + j) >= 0) 
                    && ((iColBegin + j) < WALL_COL))
                {
                    bIsWall = (DATA_WALL == g_cGameUI[iRowBegin + i][iColBegin + j]);
                    cData[i][j] = bIsWall ? DATA_WALL : DATA_EMPTY;
                }
                else
                {
                    cData[i][j] = DATA_EMPTY;
                }
            }
        }

        bRc = TRUE;
    } while (0);

    return bRc;
}

BOOL IsSrcDataCanPutOnDst(
    unsigned char cDataSrc[BRICK_ROW][BRICK_COL],
    unsigned char cDataDst[BRICK_ROW][BRICK_COL])
{
    BOOL bRc = FALSE;
    BOOL bCanPut = TRUE;
    int i = 0;
    int j = 0;

    do 
    {
        for (i = 0; i < BRICK_ROW; i++)
        {
            for (j = 0; j < BRICK_COL; j++)
            {
                if ((cDataSrc[i][j] != 0)
                    && (cDataDst[i][j] != 0))
                {
                    bCanPut = FALSE;
                    goto LABLE_ISSRCDATACANPUTONDST_END;
                }
            }
        }

        bRc = TRUE;
    } while (0);

LABLE_ISSRCDATACANPUTONDST_END:
    return bRc;
}

/// @file \exam_1_1\logic\keyBoardControl.h
/// @brief 键盘控制

#ifndef __KEYBOARDCONTROL_H__
#define __KEYBOARDCONTROL_H__

typedef enum eKbInput
{
    eKbInput_unknown = -1,
    eKbInput_left,
    eKbInput_right,
    eKbInput_up,
    eKbInput_down,
    eKbInput_rotate,
    eKbInput_quit,
}ENUM_EKBINPUT;

/// 键盘接收到的值
#define KB_VAL_ARROW_UP 72 ///< 上
#define KB_VAL_ARROW_DOWN 80 ///< 下
#define KB_VAL_ARROW_LEFT 75 ///< 左
#define KB_VAL_ARROW_RIGHT 77 ///< 右
#define KB_VAL_CHAR_ROTATE 119 ///< 旋转 W键
#define KB_VAL_CHAR_QUIT 113 ///< 退出 Q键

ENUM_EKBINPUT GetKbInput();

#endif

/// @file \exam_1_1\logic\keyBoardControl.c
/// @brief 

#include "keyBoardControl.h"
#include "common.h"

ENUM_EKBINPUT GetKbInput()
{
    int iRc = 0;
    ENUM_EKBINPUT KbInput = eKbInput_unknown;

    iRc = _getch();
    switch (iRc)
    {
        case KB_VAL_ARROW_UP:
            KbInput = eKbInput_up;
            break;
             
        case KB_VAL_ARROW_DOWN:
            KbInput = eKbInput_down;
            break;

        case KB_VAL_ARROW_LEFT:
            KbInput = eKbInput_left;
            break;

        case KB_VAL_ARROW_RIGHT:
            KbInput = eKbInput_right;
            break;

        case KB_VAL_CHAR_ROTATE:
            KbInput = eKbInput_rotate;
            break;

        case KB_VAL_CHAR_QUIT:
            KbInput = eKbInput_quit;
            break;

        default:
            break;
    }

    return KbInput;
};

/// @file \2015_1030\exam_1_1\common.h
/// @brief 公用头文件

#ifndef __COMMON_H__
#define __COMMON_H__

#include 
#include 
#include 
#include 
#include 
#include 

#ifndef BOOL
#define BOOL int
#define TRUE 1
#define FALSE 0
#endif

#include "./ui/const_define_ui.h"
#include "./ui/ui.h"
#include "./logic/gameControl.h"
#include "./logic/keyBoardControl.h"

#endif // #ifndef __COMMON_H__



你可能感兴趣的:(俄罗斯方块(控制台版))