首页 > 代码库 > 一个c语言的俄罗斯方块 参考

一个c语言的俄罗斯方块 参考

本文来自开源中国 http://www.oschina.net/code/snippet_188854_13088

#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);
}

可以扩展的地方很多,例如暂停,分数,等级,提示下一个图形等。