运行图:

 

源码:http://yuncode.net/code/c_503ccff5a441f15

 

 

 

 

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