大作业之俄罗斯方块样例

#include <windows.h>
#include <stdio.h>
#include <time.h>

#define CELL 20
#define ROWS 25
#define COLS 15
//升级所需分数值
#define SCORE_LEVEL_INC 80
#define ID_TIMER 1

/////////////////全局变量/////////////////////////////
HWND hwnd;                  //保存窗口句柄

int score = 0;                //分数
int level = 0;                //级数
int interval_unit = 25;       //随级数递增的时间间隔增量
int interval_base = 300;      //时间间隔基量
int old_interval;           //保存当前的时间间隔,用于加速操作

int cur_left, cur_top;       //记录方块当前的位置
int width_block, height_block;   //方块的宽带和高度

bool isPause = false;             //暂停标识
UINT timer_id = 0;                //保存计时器ID

static byte* block = NULL;        //方块,方块为随机大小,采用动态分配内存方式,所以这里是指针变量
byte g_panel[ROWS][COLS] = { 0 };
////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawPanel(HDC hdc);     //绘制表格
void RefreshPanel(HDC hdc);      //刷新面板
void DoDownShift(HDC hdc);       //下移
void DoLeftShift(HDC hdc);       //左移
void DoRightShift(HDC hdc);      //右移
void DoAccelerate(HDC hdc);      //加速
void DoRedirection(HDC hdc); //改变方向
void ClearRow(HDC hdc);          //消行
bool ExportBlock();     //输出方块,
//该函数会直接修改全局变量block,width_block,height_block,
//cur_left和cur_top
bool IsTouchBottom(HDC hdc);         //判断是否到达底部

int main()
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    TCHAR szAppName[] = TEXT("teris");
    MSG msg;
    WNDCLASS wc;

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;
    if (!RegisterClass(&wc))
    {
        printf("RegisterClass occur errors!");
        return 0;
    }
    hwnd = CreateWindow(szAppName, TEXT("Teris Demo"),
        WS_OVERLAPPEDWINDOW,
        0, 0, 0, 0,
        NULL,
        NULL,
        hInstance,
        NULL);
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

void DrawPanel(HDC hdc)          //绘制游戏面板
{
    int x, y;
    RECT rect;

    for (y = 0; y < ROWS; y++)
    {
        for (x = 0; x < COLS; x++)
        {
            //计算方块的边框范围
            rect.top = y * CELL + 1;
            rect.bottom = (y + 1) * CELL - 1;
            rect.left = x * CELL + 1;
            rect.right = (x + 1) * CELL - 1;
            FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
        }
    }
}

void DoDownShift(HDC hdc)        //下移
{
    if (NULL == block) return;

    //判断是否到达底部
    if (IsTouchBottom(hdc))    //到底部
    {
        //消行处理
        ClearRow(hdc);
        ExportBlock();      //输出下一个方块
    }

    cur_top++;
    RefreshPanel(hdc);
}

void DoLeftShift(HDC hdc)        //左移
{
    int x, y;
    if (NULL == block) return;

    if (0 == cur_left) return;
    if (cur_top < 0) return; //方块没有完整显示前,不能左移
    for (y = 0; y < height_block; y++)
    {
        for (x = 0; x < width_block; x++)          //从左边开始扫描,获取该行最左边的实心方格块
        {
            if (*(block + y * width_block + x))
            {
                //判断当前方格在面板上面左边一个方格是否为实心,是就代表不能再左移
                if (g_panel[cur_top + y][cur_left + x - 1]) return;

                break;      //只判断最左边的一个实心方格,之后直接扫描下一行
            }
        }
    }
    cur_left--;
    RefreshPanel(hdc);
}

void DoRightShift(HDC hdc)       //右移
{
    int x, y;
    if (NULL == block) return;

    if (COLS - width_block == cur_left) return;
    if (cur_top < 0) return;     //方块完整显示前不能右移
    for (y = 0; y < height_block; y++)
    {
        for (x = width_block - 1; x >= 0; x--)   //从右边开始扫描,获取该行最右边的实心方格块
        {
            if (*(block + y * width_block + x))
            {
                //判断当前方格在面板上右边一个方格是否为实心,是就代表不能再右移
                if (g_panel[cur_top + y][cur_left + x + 1]) return;

                break;      //只判断最右边的一个实心方格
            }
        }
    }
    cur_left++;
    RefreshPanel(hdc);
}

void DoRedirection(HDC hdc)      //改变方向
{
    int i, j;
    byte* temp = NULL;
    if (NULL == block) return;
    if (cur_top < 0) return;     //方块完整显示前不能转向

    temp = (byte*)malloc(sizeof(byte) * width_block * height_block);
    for (i = 0; i < width_block; i++)
    {
        for (j = 0; j < height_block; j++)
        {
            //temp[i][j]=block[height_block-j-1][i];
            *(temp + i * height_block + j) = *(block + (height_block - j - 1) * width_block + i);
        }
    }

    //给方块重新定位
    int incHeight = width_block - height_block;
    int incWidth = height_block - width_block;
    int temp_cur_top = cur_top - incHeight / 2;
    int temp_cur_left = cur_left - incWidth / 2;

    //system("cls");
    //printf("temp_top=%d, temp_left=%d",temp_cur_top,temp_cur_left);

    //判断当前空间是否足够让方块改变方向
    int max_len = max(width_block, height_block);
    //防止下标访问越界
    if (temp_cur_top + max_len - 1 >= ROWS || temp_cur_left < 0 || temp_cur_left + max_len - 1 >= COLS)
    {
        free(temp);      //退出前必须先释放内存
        return;
    }
    for (i = 0; i < max_len; i++)
    {
        for (j = 0; j < max_len; j++)
        {
            //转向所需的空间内有已被占用的实心方格存在,即转向失败
            if (g_panel[temp_cur_top + i][temp_cur_left + j])
            {
                free(temp);      //退出前必须先释放内存
                return;
            }
        }
    }

    //把临时变量的值赋给block,只能赋值,而不能赋指针值
    for (i = 0; i < height_block; i++)
    {
        for (j = 0; j < width_block; j++)
        {
            //block[i][j]=temp[i][j];
            *(block + i * width_block + j) = *(temp + i * width_block + j);
        }
    }

    //全局变量重新被赋值
    cur_top = temp_cur_top;
    cur_left = temp_cur_left;
    //交换
    i = width_block;
    width_block = height_block;
    height_block = i;

    free(temp);      //释放为临时变量分配的内存
    RefreshPanel(hdc);
}

void DoAccelerate(HDC hdc)       //加速
{
    if (NULL == block) return;

    if (IsTouchBottom(hdc))
    {
        //消行处理
        ClearRow(hdc);
        ExportBlock();
    }
    cur_top++;
    RefreshPanel(hdc);
}

bool IsTouchBottom(HDC hdc)
{
    int x, y;
    int i, j;

    if (NULL == block) return false;
    if (ROWS == cur_top + height_block)
    {
        //固定方块
        for (i = 0; i < height_block; i++)
        {
            for (j = 0; j < width_block; j++)
            {
                if (*(block + i * width_block + j)) g_panel[cur_top + i][cur_left + j] = 1;
            }
        }
        return true;
    }
    for (y = height_block - 1; y >= 0; y--)          //从底行开始扫描
    {
        //判断第一个实心方块在面板上邻接的下方方格是否为实心,是就代表已经到达底部
        for (x = 0; x < width_block; x++)
        {
            if (*(block + y * width_block + x))
            {
                if (cur_top + y + 1 < 0) return false;
                if (g_panel[cur_top + y + 1][cur_left + x])
                {
                    //判断是否gameover
                    if (cur_top <= 0)
                    {
                        if (timer_id)
                        {
                            KillTimer(hwnd, ID_TIMER);
                            timer_id = 0;
                        }
                        MessageBox(hwnd, TEXT("游戏结束"), TEXT("MSG"), MB_OK | MB_ICONEXCLAMATION);
                        SendMessage(hwnd, WM_CLOSE, 0, 0);
                    }
                    //
                    //固定方块
                    for (i = 0; i < height_block; i++)
                    {
                        for (j = 0; j < width_block; j++)
                        {
                            if (*(block + i * width_block + j)) g_panel[cur_top + i][cur_left + j] = 1;
                        }
                    }
                    return true;
                }
            }
        }
    }
    return false;
}

void ClearRow(HDC hdc)               //消行
{
    int i, j, k;
    int count = 0;        //消行次数
    bool isFilled;
    //消行处理
    for (i = ROWS - 1; i >= 0; i--)
    {
        isFilled = true;
        for (j = 0; j < COLS; j++)
        {
            if (!g_panel[i][j])
            {
                isFilled = false;
                break;
            }
        }
        if (isFilled)
        {
            for (j = 0; j < COLS; j++)
            {
                g_panel[i][j] = 0;
            }
            //所有方块往下移
            for (k = i - 1; k >= 0; k--)
            {
                for (j = 0; j < COLS; j++)
                {
                    g_panel[k + 1][j] = g_panel[k][j];
                }
            }
            i = i + 1;
            count++;
        }
    }

    //最高级别为9级,所以分数极限为(9+1)*SCORE_LEVEL_INC-1
    if (score >= 10 * SCORE_LEVEL_INC - 1) return;

    //加分规则:消除行数,1行加10分,2行加15分,3行加20分,4行加30分
    switch (count)
    {
    case 1:
        score += 10;
        break;
    case 2:
        score += 15;
        break;
    case 3:
        score += 20;
        break;
    case 4:
        score += 30;
        break;
    }

    int temp_level = score / SCORE_LEVEL_INC;
    if (temp_level > level)
    {
        level = temp_level;
        //撤销当前计时器,然后重设
        if (timer_id) KillTimer(hwnd, ID_TIMER);
        timer_id = SetTimer(hwnd, ID_TIMER, interval_base - level * interval_unit, NULL);
    }

    system("cls");
    printf("score: %d, level: %d ", score, level);
}

void RefreshPanel(HDC hdc)           //刷新面板
{
    int x, y;
    RECT rect;
    HBRUSH h_bSolid = (HBRUSH)GetStockObject(GRAY_BRUSH),
        h_bEmpty = (HBRUSH)GetStockObject(WHITE_BRUSH);
    if (NULL == block) return;

    //先刷屏
    for (y = 0; y < ROWS; y++)
    {
        for (x = 0; x < COLS; x++)
        {
            //为避免刷掉方块的边框,rect范围必须比边框范围小1
            rect.top = y * CELL + 2;
            rect.bottom = (y + 1) * CELL - 2;
            rect.left = x * CELL + 2;
            rect.right = (x + 1) * CELL - 2;
            if (g_panel[y][x])
                FillRect(hdc, &rect, h_bSolid);
            else
                FillRect(hdc, &rect, h_bEmpty);
        }
    }
    //再定位方块
    for (y = 0; y < height_block; y++)
    {
        for (x = 0; x < width_block; x++)
        {
            if (*(block + y * width_block + x))          //实心
            {
                rect.top = (y + cur_top) * CELL + 2;
                rect.bottom = (y + cur_top + 1) * CELL - 2;
                rect.left = (x + cur_left) * CELL + 2;
                rect.right = (x + cur_left + 1) * CELL - 2;
                FillRect(hdc, &rect, h_bSolid);
            }
        }
    }
}

bool ExportBlock()          //输出方块
{
    int sel;
    if (block)
    {
        free(block);     //释放之前分配的内存
        block = NULL;
    }

    sel = rand() % 7;
    switch (sel)
    {
    case 0:     //水平条
        width_block = 4;
        height_block = 1;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 1;       //可以理解为*(block+0*width_block+0)=1,即第一行的第一个方格,下面同理
        *(block + 1) = 1;       //*(block+0*width_block+1)=1
        *(block + 2) = 1;       //*(block+0*width_block+2)=1
        *(block + 3) = 1;       //*(block+0*width_block+3)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 1:     //三角
        width_block = 3;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 0;       //可以理解为*(block+0*width_block+0)=0,即第一行的第一个方格,下面同理
        *(block + 1) = 1;       //*(block+0*width_block+1)=1
        *(block + 2) = 0;       //*(block+0*width_block+2)=0
        *(block + 3) = 1;       //*(block+1*width_block+0)=1,第二行开始
        *(block + 4) = 1;       //*(block+1*width_block+1)=1
        *(block + 5) = 1;       //*(block+1*width_block+2)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 2:     //左横折
        width_block = 3;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 1;       //可以理解为*(block+0*width_block+0)=1,下面同理
        *(block + 1) = 0;       //*(block+0*width_block+1)=0
        *(block + 2) = 0;       //*(block+0*width_block+2)=0
        *(block + 3) = 1;       //*(block+1*width_block+0)=1
        *(block + 4) = 1;       //*(block+1*width_block+1)=1
        *(block + 5) = 1;       //*(block+1*width_block+2)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 3:     //右横折
        width_block = 3;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 0;       //可以理解为*(block+0*width_block+0)=0,下面同理
        *(block + 1) = 0;       //*(block+0*width_block+1)=0
        *(block + 2) = 1;       //*(block+0*width_block+2)=1
        *(block + 3) = 1;       //*(block+1*width_block+0)=1
        *(block + 4) = 1;       //*(block+1*width_block+1)=1
        *(block + 5) = 1;       //*(block+1*width_block+2)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 4:     //左闪电
        width_block = 3;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 1;       //可以理解为*(block+0*width_block+0)=1,下面同理
        *(block + 1) = 1;       //*(block+0*width_block+1)=1
        *(block + 2) = 0;       //*(block+0*width_block+2)=0
        *(block + 3) = 0;       //*(block+1*width_block+0)=0
        *(block + 4) = 1;       //*(block+1*width_block+1)=1
        *(block + 5) = 1;       //*(block+1*width_block+2)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 5:     //右闪电
        width_block = 3;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 0;       //可以理解为*(block+0*width_block+0)=0,下面同理
        *(block + 1) = 1;       //*(block+0*width_block+1)=1
        *(block + 2) = 1;       //*(block+0*width_block+2)=1
        *(block + 3) = 1;       //*(block+1*width_block+0)=1
        *(block + 4) = 1;       //*(block+1*width_block+1)=1
        *(block + 5) = 0;       //*(block+1*width_block+2)=0

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    case 6:     //石头
        width_block = 2;
        height_block = 2;
        block = (byte*)malloc(sizeof(byte) * width_block * height_block);
        *(block + 0) = 1;       //可以理解为*(block+0*width_block+0)=1,下面同理
        *(block + 1) = 1;       //*(block+0*width_block+1)=1
        *(block + 2) = 1;       //*(block+1*width_block+0)=1
        *(block + 3) = 1;       //*(block+1*width_block+1)=1

        cur_top = 0 - height_block;
        cur_left = (COLS - width_block) / 2;
        break;
    }
    return block != NULL;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    //TCHAR szBuffer[1024];

    switch (message)
    {
    case WM_CREATE:
        MoveWindow(hwnd, 400, 10, CELL * COLS + 8, CELL * ROWS + 32, FALSE);      //补齐宽度和高度
        srand(time(NULL));
        ExportBlock();

        timer_id = SetTimer(hwnd, ID_TIMER, interval_base - level * interval_unit, NULL);
        return 0;
    case WM_TIMER:
        hdc = GetDC(hwnd);
        DoDownShift(hdc);
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_KEYDOWN:
        hdc = GetDC(hwnd);
        switch (wParam)
        {
        case VK_LEFT:                           //左移
            if (!isPause) DoLeftShift(hdc);
            break;
        case VK_RIGHT:                          //右移
            if (!isPause) DoRightShift(hdc);
            break;
        case VK_UP:                             //转向
            if (!isPause) DoRedirection(hdc);
            break;
        case VK_DOWN:                           //加速
            if (!isPause) DoAccelerate(hdc);
            break;
        case VK_SPACE:      //暂停
            isPause = !isPause;
            if (isPause)
            {
                if (timer_id) KillTimer(hwnd, ID_TIMER);
                timer_id = 0;
            }
            else
            {
                timer_id = SetTimer(hwnd, ID_TIMER, interval_base - level * interval_unit, FALSE);
            }
            break;
        }
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        DrawPanel(hdc);          //绘制面板
        RefreshPanel(hdc);       //刷新
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        if (block) free(block);
        if (timer_id) KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

低配版(代码行数较少)
注意:C语言实现(放.c文件里)

#include <stdio.h>
#include <Windows.h>
#include <conio.h>
#include <time.h>

#define WIDTH 10//x j
#define HIGTH 25//y i
//[y][x]  [HIGTH][WIDTH]  [i][j]  [20][10]
#define TICK 1000
#define COOLDOWN 200

typedef enum ELEMENT
{
    AIR, BLOCK, MOVING
}ELEMENT;

typedef enum FUNCTION_RESULT
{
    FUNCTION_SUSSESS, FUNCTION_FAIL
}FUNCTION_RESULT;

typedef enum SHAPE_TYPE
{
    O_SHAPE, J_SHAPE, L_SHAPE, T_SHAPE, I_SHAPE, S_SHAPE, Z_SHAPE
}SHAPE_TYPE;

char map[HIGTH][WIDTH];//显示区域为0,5到10,25 0为空 1为已下落的方块 2为正在移动的方块
COORD faller[4];
COORD next_faller[4];
ULONGLONG last;
ULONGLONG now;
int key;
SHAPE_TYPE now_shape;
int keyboard_flag;
HANDLE hdl;

//形状的初始位置,分别表示xy坐标,顺序是ojltisz
short o_shape[8] = { 4,3,5,3,4,4,5,4 };
short j_shape[8] = { 4,4,5,4,6,4,4,3 };
short l_shape[8] = { 5,4,4,4,3,4,5,3 };
short t_shape[8] = { 5,4,4,4,6,4,5,3 };
short i_shape[8] = { 4,4,5,4,3,4,6,4 };
short s_shape[8] = { 4,3,5,3,4,2,5,4 };
short z_shape[8] = { 4,3,5,3,5,2,4,4 };

short shape[7][8] =
{
    { 4,3,5,3,4,4,5,4 },
    { 4,4,5,4,6,4,4,3 },
    { 5,4,4,4,3,4,5,3 },
    { 5,4,4,4,6,4,5,3 },
    { 4,4,5,4,3,4,6,4 },
    { 4,3,5,3,4,2,5,4 },
    { 4,3,5,3,5,2,4,4 },
};

int is_legal(COORD test[4])//1为合法 0为不合法
{
    for (int i = 0; i < 4; i++)
    {
        if (test[i].X < 0 || test[i].X >= WIDTH)return 0;
        if (test[i].Y >= HIGTH)return 0;
    }
    for (int i = 0; i < 4; i++)
    {
        if (map[test[i].Y][test[i].X] == BLOCK)return 0;
    }
    return 1;
}

void update_screen()
{
    //system("CLS");
    COORD pos_start = { 0,0 };
    SetConsoleCursorPosition(hdl, pos_start);
    for (int i = 5; i < HIGTH; i++)
    {
        printf("<|");
        for (int j = 0; j < WIDTH; j++)
        {
            if (map[i][j] == AIR) printf(". ");
            else printf("■");
        }
        printf("|>\n");
    }
    printf("<---------------------->");
}

void generate()
{
    now_shape = rand() % 7;
    memcpy(&faller, shape[now_shape], 4 * sizeof(COORD));
    for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
}

FUNCTION_RESULT try_move_down()
{
    memcpy(next_faller, faller, sizeof(faller));
    for (int i = 0; i < 4; i++)
    {
        next_faller[i].Y++;
        if (next_faller[i].Y >= HIGTH)return FUNCTION_FAIL;
        if (map[next_faller[i].Y][next_faller[i].X] == BLOCK)return FUNCTION_FAIL;
    }
    for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = AIR;
    memcpy(faller, next_faller, sizeof(faller));
    for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
    return FUNCTION_SUSSESS;
}

void clear_row()
{
    COORD temp_map[HIGTH][WIDTH];
    int flag;
    for (int i = HIGTH - 1; i >= 5; i--)
    {
        flag = 1;
        for (int j = 0; j < WIDTH; j++)
        {
            if (map[i][j] != BLOCK)
            {
                flag = 0;
                break;
            }
        }
        if (flag)
        {
            memcpy(temp_map, map, i * sizeof(map[0]));
            memcpy(map + 1, temp_map, i * sizeof(map[0]));
            continue;
        }
    }
}

//向右输入1,向左输入-1
FUNCTION_RESULT try_move_horizontal(int direction)
{
    memcpy(next_faller, faller, sizeof(faller));
    for (int i = 0; i < 4; i++)
    {
        next_faller[i].X += direction;
        if (next_faller[i].X >= WIDTH || next_faller[i].X < 0) return FUNCTION_FAIL;
        if (map[next_faller[i].Y][next_faller[i].X] == BLOCK)return FUNCTION_FAIL;
    }
    keyboard_flag = 1;
    for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = AIR;
    memcpy(faller, next_faller, sizeof(faller));
    for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
    return FUNCTION_SUSSESS;
}

FUNCTION_RESULT try_fall()
{
    FUNCTION_RESULT result = FUNCTION_FAIL;
    while (try_move_down() == FUNCTION_SUSSESS)result = FUNCTION_SUSSESS;
    return result;
}

//以中心点旋转
//以所有点旋转后向下
//以其他点旋转
FUNCTION_RESULT t_spin(int direction)
{
    for (int j = 0; j < 4; j++)
    {
        next_faller[j].X = faller[0].X + direction * (faller[0].Y - faller[j].Y);
        next_faller[j].Y = faller[0].Y + direction * (faller[j].X - faller[0].X);
    }
    if (is_legal(next_faller))
    {
        for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = AIR;
        memcpy(faller, next_faller, sizeof(faller));
        for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
        return FUNCTION_SUSSESS;
    }

    for (int round = 1; round >= 0; round--)
    {
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                next_faller[j].X = faller[i].X + direction * (faller[i].Y - faller[j].Y);
                next_faller[j].Y = faller[i].Y + direction * (faller[j].X - faller[i].X) + round;
            }
            if (is_legal(next_faller))
            {
                for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = AIR;
                memcpy(faller, next_faller, sizeof(faller));
                for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
                return FUNCTION_SUSSESS;
            }
        }
    }
    return FUNCTION_FAIL;
}

//以中心点旋转
//以其他点旋转
//检查以中心点旋转后能否向下移动一格
//检查以其他点旋转后能否向下移动一格
FUNCTION_RESULT try_rotate(int direction)
{
    if (now_shape == O_SHAPE)return FUNCTION_FAIL;
    if (now_shape == T_SHAPE)return t_spin(direction);
    for (int round = 0; round <= 1; round++)
    {
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                next_faller[j].X = faller[i].X + direction * (faller[i].Y - faller[j].Y);
                next_faller[j].Y = faller[i].Y + direction * (faller[j].X - faller[i].X) + round;
            }
            if (is_legal(next_faller))
            {
                for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = AIR;
                memcpy(faller, next_faller, sizeof(faller));
                for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = MOVING;
                return FUNCTION_SUSSESS;
            }
        }
    }
    return FUNCTION_FAIL;
}


int main()
{
    //清除光标
    hdl = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cci = { 1,0 };
    SetConsoleCursorInfo(hdl, &cci);
    SetConsoleTitle("TETRIS!!!");

    //随机数种子
    srand((unsigned)time(NULL));

    //地图初始化
    memset(map, 0, WIDTH * HIGTH * sizeof(char));

    //计时开始
    last = GetTickCount64();
    keyboard_flag = 0;

    //生成第一个方块
    generate();
    update_screen();

    while (1)//主循环
    {
        now = GetTickCount64();
        //说明该向下移动了
        if (keyboard_flag)
        {
            keyboard_flag = 0;
            last = GetTickCount64() + COOLDOWN;
            update_screen();
        }
        if (now > last)
        {
            last += TICK;
            if (try_move_down() == FUNCTION_FAIL)//向下移动失败,创建一个新的形状
            {
                for (int i = 0; i < 4; i++) map[faller[i].Y][faller[i].X] = BLOCK;
                clear_row();
                generate();
            }
            update_screen();
        }

        while (_kbhit())
        {
            key = _getch();
            switch (key)
            {
            case 'E':case 'e':
                if (try_rotate(1) == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            case 'Q':case 'q':
                if (try_rotate(-1) == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            case 'S':case 's':
                if (try_move_down() == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            case 'W':case 'w':
                if (try_fall() == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            case 'D':case 'd':
                if (try_move_horizontal(1) == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            case 'A':case 'a':
                if (try_move_horizontal(-1) == FUNCTION_SUSSESS)keyboard_flag = 1;
                break;
            }
        }
    }
    return 0;
}
#include<iostream> 
#include<stdio.h>
#include<Windows.h>
#include<conio.h>
#include<time.h>
#define A1 0//A代表长条型,B为方块,C为L型,D为闪电型 
#define A2 1 
 
 
#define B 2 
 
 
#define C11 3 
#define C12 4 
#define C13 5 
#define C14 6 
 
 
#define C21 7 
#define C22 8 
#define C23 9 
#define C24 10 
 
 
#define D11 11 
#define D12 12 
 
 
#define D21 13 
#define D22 14 
using namespace std;
 
class Box 
{ 
  private: 
    int map[23][12];//画面坐标,记录有方块的点,也是游戏界面 
    int hotpoint[2];//当前活动的点,所有图形都是以此为基准绘制的 
    int top;//当前最高位置 
    int point;//分数 
    int level;//等级 
    int ID;//当前活动图形的ID号 
    int colorID;//图形的颜色ID。 
  public: 
    Box()//初始化 
    { 
      int i,j; 
      for(i=0;i<23;i++) 
        for(j=0;j<12;j++) 
          map[i][j]=0; 
      hotpoint[0]=0; 
      hotpoint[1]=5; 
      point=0; 
      level=1; 
      top=99; 
      ID=0; 
    } 
    void SetColor(int color);//颜色 
    void DrawMap();//画游戏的大界面 
    bool Judge(int x,int y);//判断当前位置能否绘制图形 
    void Welcome();//欢迎界面 
    void DrawBox(int x,int y,int num);//绘制图形 
    void Redraw(int x,int y,int num);//擦除图形 
    void Run();//运行 
    void Turn();//转动方块 
    void UpdataMap();//更新画面 
    void Pause();//暂停 
}; 

void SetPos(int i,int j)//设定光标位置 
{ 
 COORD pos={i,j}; 
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); 
} 
 
 
const int sharp[15][8]= 
{ 
{0,0,1,0,2,0,3,0},{0,0,0,1,0,2,0,3}, 
{0,0,1,0,0,1,1,1}, 
{0,0,1,0,1,1,1,2},{0,1,1,1,2,0,2,1},{0,0,0,1,0,2,1,2},{0,0,0,1,1,0,2,0}, 
{1,0,1,1,1,2,0,2},{0,0,0,1,1,1,2,1},{0,0,0,1,0,2,1,0},{0,0,1,0,2,0,2,1}, 
{0,0,0,1,1,1,1,2},{0,1,1,0,1,1,2,0}, 
{0,1,0,2,1,0,1,1},{0,0,1,0,1,1,2,1} 
};//形状点的各个坐标,先纵后横 
 
 
const int high[15]={4,1,2,2,3,2,3,2,3,2,3,2,3,2,3};//这个数组是用来保存各个形状高度的,以上面的坐标相对应 
void Box::SetColor(int colorID) 
{ 
  int n_color; 
  switch(colorID) 
  { 
    case 0: n_color = 0x08;break; 
    case 1: n_color = 0x0C;break; 
    case 2: n_color = 0x0D;break; 
    case 3: n_color = 0x0E;break; 
    case 4: n_color = 0x0A;break; 
  } 
  SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), n_color); 
} 
void Box::DrawMap()//画界面 
{ 
  int i; 
 
  SetColor(0);//界面颜色 
 
  for(i=0;i<14;i++) 
  { 
     SetPos(i*2,0); 
     cout<<"■"; 
  } 
  for(i=1;i<=24;i++) 
  { 
    SetPos(0,i); 
    cout<<"■"; 
    SetPos(13*2,i); 
    cout<<"■"; 
  } 
  for(i=0;i<14;i++) 
  { 
     SetPos(i*2,24); 
     cout<<"■"; 
  } 
   
  i=15; 
  for(i=15;i<=25;i++) 
  { 
     SetPos(i*2,0); 
     cout<<"■"; 
  } 
  for(i=1;i<=8;i++) 
  { 
    SetPos(15*2,i); 
    cout<<"■"; 
    SetPos(25*2,i); 
    cout<<"■"; 
  } 
  for(i=15;i<=25;i++) 
  { 
     SetPos(i*2,9); 
     cout<<"■"; 
  } 
 
  SetPos(16*2,16); 
  cout<<"俄罗斯方块"; 
  SetPos(16*2,17); 
  cout<<"分数:"<<point; 
  SetPos(16*2,18); 
  cout<<"级别:"<<level; 
} 
 
void Box::DrawBox(int x,int y,int num)//绘制图形 
{ 
   int i; 
   int nx,ny; 
   if (num<2)SetColor(1);//0、1是长条 
   else if(num<3) SetColor(2);//2 方块 
   else if(num<11) SetColor(3);//3、4、5、6、7、8、9、10 
   else SetColor(4); 
   for(i=0;i<4;i++) 
  {  
    nx=x+sharp[num][i*2]; 
    ny=y+sharp[num][i*2+1]; 
    SetPos((ny+1)*2,nx+1);//利用sharp数组相对于点x,y绘制形状 
    //SetColor(i+1); 
    cout<<"■"; 
  } 
} 
 
void Box::Redraw(int x,int y,int num)//擦除图形,原理同上 
{ 
   int i; 
   int nx,ny; 
   for(i=0;i<4;i++) 
  {  
    nx=x+sharp[num][i*2]; 
    ny=y+sharp[num][i*2+1]; 
    SetPos((ny+1)*2,nx+1); 
    cout<<" "; 
  } 
} 
 
void Box::Turn()//转动图形,单纯的该ID而已 
{ 
  switch(ID) 
  { 
    case A1: ID=A2; break; 
    case A2: ID=A1; break; 
 
    case B: ID=B; break; 
 
    case C11: ID=C12; break; 
    case C12: ID=C13; break; 
    case C13: ID=C14; break; 
    case C14: ID=C11; break; 
 
    case C21: ID=C22; break; 
    case C22: ID=C23; break; 
    case C23: ID=C24; break; 
    case C24: ID=C21; break; 
     
    case D11: ID=D12; break; 
    case D12: ID=D11; break; 
 
    case D21: ID=D22; break; 
    case D22: ID=D21; break; 
  } 
 
} 
 
void Box::Welcome()//欢迎界面 
{ 
  char x; 
  while(1) 
  { 
   system("cls"); 
   cout<<"■■■■■■■■■■■■■■■■■■■"<<endl; 
   cout<<"■    俄罗斯方块控制台版    ■"<<endl; 
   cout<<"■■■■■■■■■■■■■■■■■■■"<<endl; 
   cout<<"■   A,D左右移动 S向下加速    ■"<<endl; 
   cout<<"■   W 转动方块  P暂停游戏    ■"<<endl; 
   cout<<"■      Q退出游戏       ■"<<endl; 
   cout<<"■■■■■■■■■■■■■■■■■■■"<<endl; 
   cout<<"■                 ■"<<endl; 
   cout<<"■    按1-9选择等级!!     ■"<<endl; 
   cout<<"■                 ■"<<endl; 
   cout<<"■                 ■"<<endl; 
   cout<<"■■■■■■■■■■■■■■■■■■■"<<endl; 
   SetPos(16,9); 
   x=getch(); 
   if(x<='9'&&x>='1')//设置等级 
   { 
     level=x-'0'; 
     break; 
   } 
  } 
} 
 
void Box::UpdataMap()//更新画面(关键) 
{ 
   int clear; 
   int i,j,k; 
   int nx,ny; 
   int flag; 
   for(i=0;i<4;i++)//更新map数组的信息 
   {  
    nx=hotpoint[0]+sharp[ID][i*2]; 
    ny=hotpoint[1]+sharp[ID][i*2+1]; 
    map[nx][ny]=1; 
   } 
   if(hotpoint[0]<top)//如果热点高于顶点则更新顶点,这里0表示的是纵向的 
     top=hotpoint[0]; 
   clear=0;//消除的格数 
   for(i=hotpoint[0];i<hotpoint[0]+high[ID];i++) 
   { 
     flag=0; 
     for(j=0;j<12;j++)//检测是否可以消除此行 
     { 
       if(map[i][j]==0)//代表有空格,不能消除 
       { 
         flag=1;//1表示不能消除 
         break; 
       } 
     } 
     if(flag==0)//可以消除 
     { 
       for(k=i;k>=top;k--)//从当前位置向上所有的点下移一行 
       { 
         if(k==0)//最高点特殊处理 
           for(j=0;j<12;j++) 
           { 
             map[k][j]=0; 
             SetPos((j+1)*2,k+1); 
             cout<<" "; 
           } 
         else 
         { 
           for(j=0;j<12;j++) 
           { 
             map[k][j]=map[k-1][j]; 
             SetPos((j+1)*2,k+1); 
             if(map[k][j]==0) 
              cout<<" "; 
             else 
              cout<<"■"; 
           } 
         } 
       } 
       top++;//消除成功,最高点下移 
       clear++; 
       point+=clear*10*level; 
     } 
   } 
   SetColor(0); 
   SetPos(16*2,17); 
    cout<<"分数:"<<point; 
} 
 
void Box::Run()//运行游戏 
{ 
  int i=0; 
  char x; 
  int Count;//计数器 
  int nextID; 
  int temp; 
  srand((int)time(0));//将随机数的起点设置为time(0):不带秒 
  ID=rand()%15;//随机生成ID和下一个ID 
  nextID=rand()%15;//这里为了方便,其实每个形状不是等概率生成的 
  DrawBox(hotpoint[0],hotpoint[1],ID);//绘制图形 
  DrawBox(3,17,nextID); 
  Count=1000-level*100;//等级决定计数,这里是用Count控制时间,来控制下落的速度 
  while(1) 
  { 
    if(i>=Count) 
    { 
      i=0;//计数器清零 
      if(Judge(hotpoint[0]+1,hotpoint[1]))//如果下个位置无效(即到底) 
      { 
         UpdataMap();//更新画面 
         ID=nextID;//生成新ID,用原等待ID替换为当前ID 
         hotpoint[0]=0;//热点更新 
         hotpoint[1]=5; 
         Redraw(3,17,nextID); 
         nextID=rand()%15; 
         DrawBox(hotpoint[0],hotpoint[1],ID); 
         DrawBox(3,17,nextID); 
         if(Judge(hotpoint[0],hotpoint[1]))//无法绘制开始图形,游戏结束 
         { 
           //getch(); 
           system("cls"); 
           SetPos(25,15); 
           cout<<"游戏结束!!!最终得分为:"<<point<<endl; 
           system("pause");//就是在命令行上输出一行类似于“Press  any  key  to  exit” 
           exit(0);//关闭所有文件,退出正在执行的程序,返回0代表正常结束 
         } 
      } 
      else 
      { 
        Redraw(hotpoint[0],hotpoint[1],ID);//没有到底,方块下移一位 
        hotpoint[0]++;//热点下移 
        DrawBox(hotpoint[0],hotpoint[1],ID); 
      } 
    } 
    if(kbhit())//读取键盘信息 
    { 
      x=getch(); 
      if(x=='a'||x=='A')//左移 
      { 
         if(Judge(hotpoint[0],hotpoint[1]-1)==0) 
         { 
           Redraw(hotpoint[0],hotpoint[1],ID); 
           hotpoint[1]-=1; 
           DrawBox(hotpoint[0],hotpoint[1],ID); 
         } 
      } 
      if(x=='d'||x=='D')//右移 
      { 
         if(Judge(hotpoint[0],hotpoint[1]+1)==0) 
         { 
           Redraw(hotpoint[0],hotpoint[1],ID); 
           hotpoint[1]+=1; 
           DrawBox(hotpoint[0],hotpoint[1],ID); 
         } 
      } 
      if(x=='s'||x=='S')//向下加速!!!!!!!!此处可以改进,可以改进加速效果。改成+3之后,会出现BUG,最后几个无法加速 
      { 
         if(Judge(hotpoint[0]+3,hotpoint[1])==0) 
         { 
           Redraw(hotpoint[0],hotpoint[1],ID); 
           hotpoint[0]+=1; 
           DrawBox(hotpoint[0],hotpoint[1],ID); 
         } 
      } 
      if(x=='w'||x=='W')//转动方块 
      { 
        temp=ID; 
        Turn(); 
        if(!Judge(hotpoint[0],hotpoint[1])) 
         { 
           Redraw(hotpoint[0],hotpoint[1],temp); 
           DrawBox(hotpoint[0],hotpoint[1],ID); 
         } 
        else 
          ID=temp; 
      } 
      if(x=='p'||x=='P') 
      { 
        //getch(); 
        //system("cls"); 
        Pause(); 
      } 
      if(x=='q'||x=='Q') 
      { 
        system("cls"); 
        SetPos(25,15); 
        cout<<"游戏结束!!!最终得分为:"<<point<<endl; 
        system("pause"); 
        exit(0); 
      } 
      while(kbhit())//读掉剩下的键盘信息 
        getch(); 
    } 
    Sleep(1);//等待1毫秒 
    i++;//计数器加1 
  } 
} 
 
bool Box::Judge(int x,int y)//判断当前是否可以绘制方块 
{ 
  int i; 
  int nx,ny; 
  for(i=0;i<4;i++) 
  {  
    nx=x+sharp[ID][i*2]; 
    ny=y+sharp[ID][i*2+1]; 
    if(nx<0||nx>=23||ny<0||ny>=12||map[nx][ny]==1)//不能,返回1 
      return 1; 
  } 
  return 0; 
} 
void Box::Pause() 
{ 
  system("cls"); 
  while(1) 
  {   
    SetPos(30,13); 
    cout<<"暂停等待,咖啡时间^-^"<<endl; 
    if(getch()=='p'||getch()=='P') 
      break; 
  } 
  SetPos(30,13); 
  cout<<"              "<<endl; 
  DrawMap(); 
  int i ,j; 
  for(i=0;i<23;i++) 
    for(j=0;j<12;j++) 
      if(map[i][j]==1) 
      {   
        SetPos((j+1)*2,i+1); 
        cout<<"■"; 
      } 
} 
int  main()//主函数 
{ 
  Box game; 
  game.Welcome(); 
  system("cls"); 
  game.DrawMap(); 
  game.Run(); 
} 


posted @ 2020-05-25 23:09  枭魈  阅读(202)  评论(0编辑  收藏  举报