VC++ 6.0 MFC 俄罗斯方块 自动求解 代码 源程序
发布时间:2024-11-18
发布时间:2024-11-18
F1开启/关闭自动求解。
#include <windows.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define tDown 1 //方块下落定时器的标识(编号)
#define tPaint 2 //重绘定时器的标识(编号)
#define tDownTime 500 //方块下落一行位置的时间间隔
#define tPaintTime 50 //窗口重绘的时间间隔
#define ROW 24 //地图的行数目(第23行不用)
#define COL 14 //地图的列数目(第0列和第13列不用)
#define MAX_CLASS 7 //方块形状数目
#define LEN 20 //每个方格大小为20×20像素
#define StartY -1 * LEN + 5 //-15,绘制俄罗斯方块地图时的边界起始位置 #define StartX -1 * LEN + 5 //-15
int iDeleteRows = 0; //总共清除的行
int iTotalNum = 0; //总得分
char WindowTxt[100] = "俄罗斯方块游戏 自动求解已关闭"; //窗口标题
char s1[] = "关闭", s2[] = "启动"; //用于启动/关闭自动求解功能时显示不同的标题
F1开启/关闭自动求解。
bool bAuto; //是否自动求解的标志
bool Pause; //是否暂停的标志
int Map[ROW][COL]; //俄罗斯方块的地图(被占据的方格为1,否则为0)
int CurrentBox[4][4]; //当前落下的方块
int CurrentY, CurrentX; //当前落下方块的当前位置(指左上角位置)
int NextBox[4][4]; //下一个将落下的方块
int Box[MAX_CLASS][4][4] = //7种方块形状
{
{
{0,0,0,0},
{1,1,1,1},
{0,0,0,0},
{0,0,0,0}
},
{
{0,0,0,0},
{0,1,0,0},
{1,1,1,0},
{0,0,0,0}
},
{
{0,0,0,0},
{1,1,0,0},
{0,1,1,0},
{0,0,0,0}
},
{
{0,0,0,0},
{0,1,1,0},
{1,1,0,0},
{0,0,0,0}
},
{
{0,1,1,0},
{0,0,1,0},
{0,0,1,0},
{0,0,0,0}
},
{
{0,1,1,0},
{0,1,0,0},
{0,1,0,0},
{0,0,0,0}
F1开启/关闭自动求解。
{
{0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0}
}
};
void InitMap( ); //初始化地图
int NewFall( ); //新的方块落下
void BuildNextBox( ); //产生下一个随机的方块
int Test( int y, int x, int box[4][4] ); //测试在(y,x)位置是否能放置方块box,能放置返回1,否则返回0
int Drop( ); //定时时间到,当前方块下降一行位置
void PutBox( ); //放置当前方块
int Move( int Right ); //(通过方向键)移动方块,参数right为1表示向右移动,为0表示向左移动
void Clear( ); //清除满足条件的行
int Rotate( ); //测试旋转是否可行,如果可行则旋转当前方块
int RotateTest( int Box1[4][4], int Box2[4][4] ); //旋转当前方块
int count1( int y, int x, int box[4][4] ); //新增函数
int BestStartX( ); //新增函数
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); //窗口处理函数声明
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow ) //入口函数
{
static TCHAR szAppName[ ] = TEXT ("Russion");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
F1开启/关闭自动求解。
if( !RegisterClass( &wndclass ) )
{
MessageBox( NULL, TEXT ("Program requires Windows NT!" ),
szAppName, MB_ICONERROR );
return 0;
}
hwnd = CreateWindow( szAppName, WindowTxt,
WS_OVERLAPPED | WS_SYSMENU | WS_BORDER,
CW_USEDEFAULT, CW_USEDEFAULT,
(COL + 4) * LEN, //窗口宽度:(14+4)×20=360像素
ROW * LEN, //窗口高度:24×20=480像素(包括标题栏部分)
NULL, NULL, hInstance, NULL );
ShowWindow( hwnd, iCmdShow );
UpdateWindow( hwnd );
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
//初始化地图(将第0列和第13列、第23行设置为1(被占据),其他位置设置为0) void InitMap( )
{
int y, x;
for( y = 0; y < ROW; y++ )
{
for( x = 0; x < COL; x++ )
{
//第0列、第13列、第23行设置为1(被占据)
if( x < 1 || x > COL - 2 || y > ROW - 2 )
Map[y][x] = 1;
else Map[y][x] = 0;
}
}
}
//计算在(y,x)位置(左上角位置)上放置方块box后空出的方格数
F1开启/关闭自动求解。
int count1( int y, int x, int box[4][4] )
{
if( !Test(y,x,box) ) return 100; //不能在(y,x)位置放置box,返回∞
if( Test(y+1,x,box) ) return 100; //如果box还能下降,也返回∞
int tmpy, tmpx;
int c = 0; //空出的方格数
for( tmpx = 0; tmpx < 4; tmpx++ ) //考虑第0~3列
{
for( tmpy = 3; tmpy >= 0; tmpy-- )
{
if( box[tmpy][tmpx] ) break;
}tmpy++;
if( tmpy>0 )
{
for( ; tmpy<4; tmpy++ )
{
if( tmpy+y<0 || tmpy+y>=ROW || tmpx+x<0 || tmpx+x>=COL ) continue; if(!Map[tmpy+y][tmpx+x]) c++; //空出的方格
}
}
}
return c;
}
//当启动自动求解功能时,求下一个方块的最佳下降位置
//策略1为:放置后,空出的方格数最少,为MinC1,此时能达到最高位置为MaxY1,该位置为BestX1;
//策略2为:放置后,能下降的位置最高,为MaxY2,此时空出的方格数为MinC2,该位置为BestX2;
//必有MaxY1<=MaxY2, MinC1>=MinC2
//取二者的折衷,策略为:优先采取策略1,但如果MinC2==MinC1或MaxY2>=MaxY1+2,则取策略2
int BestStartX( )
{
int X, Y, tmpx, tmpy; //循环变量
int BestX = 0, MaxY = 0, MinC = 100;//最终所求的最佳下降位置
int BestX1, MaxY1, MinC1; //策略1:MinC:最佳位置处放置方块时,空出的空格最少,BestX:最佳下降位置,MaxY:能下降到的位置
int BestX2, MaxY2, MinC2; //策略2:最佳位置为能下降的位置最高
int c; //以上策略求最值时用到的辅助变量
int tBox1[4][4], tBox2[4][4]; //tBox2为实现旋转时用到的临时变量
int Rotates = 0, Rotates1 = 0, Rotates2 = 0;//找到最佳位置后,当前方块需要旋转的次数 int Last1, Last2; //Last1为下一个方块未旋转时最下一个方格所在的行,Last2为旋转后最下一个方格所在的行
F1开启/关闭自动求解。
memcpy(tBox1, NextBox, sizeof(tBox1)); memcpy(tBox2, NextBox, sizeof(tBox1)); for( tmpy=3; tmpy>=0; tmpy-- ) //统计tBox1中最下一个方格所在的行 { for( tmpx=0; tmpx<4; tmpx++ ) if(tBox1[tmpy][tmpx]) break; if(tmpx<4) break; } Last1 = tmpy; BestX1 = 0, MaxY1 = 0, MinC1 = 100; BestX2 = 0, MaxY2 = 0, MinC2 = 100; //枚举从第0~COL-4列下落 for( X=0; X<=COL-4; X++ ) { for( Y=0; Y<=ROW-1; Y++ ) if( !Test( Y, X, tBox1 ) ) break; Y--; c = count1(Y,X,tBox1); if( c<MinC1 || c==MinC1 && Y+Last1>MaxY1 ) MinC1 = c, BestX1 = X, MaxY1 = Y+Last1; if( Y+Last1>MaxY2 || Y+Last1==MaxY2 && c<MinC2 ) MinC2 = c, BestX2 = X, MaxY2 = Y+Last1; } //第1次旋转,旋转后为tBox1 Last2 = RotateTest(tBox1, tBox2); memcpy(tBox1, tBox2, sizeof(tBox1)); for( X=0; X<=COL-4; X++ ) { for( Y=0; Y<=ROW-1; Y++ ) if( !Test( Y, X, tBox1 ) ) break; Y--; c = count1(Y,X,tBox1); if( c<MinC1 || c==MinC1 && Y+Last2>MaxY1 ) MinC1 = c, BestX1 = X, MaxY1 = Y+Last2, Rotates1 = 1; if( Y+Last2>MaxY2 || Y+Last2==MaxY2 && c<MinC2 ) MinC2 = c, BestX2 = X, MaxY2 = Y+Last2, Rotates2 = 1; } //第2次旋转,旋转后为tBox1 Last2 = RotateTest(tBox1, tBox2); memcpy(tBox1, tBox2, sizeof(tBox1)); for( X=0; X<=COL-4; X++ ) { for( Y=0; Y<=ROW-1; Y++ ) if( !Test( Y, X, tBox1 ) ) break; Y--; c = count1(Y,X,tBox1); if( c<MinC1 || c==MinC1 && Y+Last2>MaxY1 ) MinC1 = c, BestX1 = X, MaxY1 = Y+Last2, Rotates1 = 2; if( Y+Last2>MaxY2 || Y+Last2==MaxY2 && c<MinC2 )
F1开启/关闭自动求解。
MinC2 = c, BestX2 = X, MaxY2 = Y+Last2, Rotates2 = 2;
}
//第3次旋转,旋转后为tBox1
Last2 = RotateTest(tBox1, tBox2); memcpy(tBox1, tBox2, sizeof(tBox1));
for( X=0; X<=COL-4; X++ )
{
for( Y=0; Y<=ROW-1; Y++ )
if( !Test( Y, X, tBox1 ) ) break;
Y--;
c = count1(Y,X,tBox1);
if( c<MinC1 || c==MinC1 && Y+Last2>MaxY1 )
MinC1 = c, BestX1 = X, MaxY1 = Y+Last2, Rotates1 = 3;
if( Y+Last2>MaxY2 || Y+Last2==MaxY2 && c<MinC2 )
MinC2 = c, BestX2 = X, MaxY2 = Y+Last2, Rotates2 = 3;
}
MinC = MinC1, BestX = BestX1, MaxY = MaxY1, Rotates = Rotates1;
if( MinC2==MinC1 || MaxY2>=MaxY1+2 )
MinC = MinC2, BestX = BestX2, MaxY = MaxY2, Rotates = Rotates2; if( Rotates>0 )
{
for( int i=0; i<Rotates; i++ )
{
RotateTest(NextBox, tBox1);
memcpy(NextBox, tBox1, sizeof(tBox1));
}
}
return BestX;
}
int NewFall( ) //新的方块落下(如果能落下返回1,否则不能落下返回0(程序就该结束了)) {
int y, x;
CurrentY = 0; //当前方块的当前位置是指该方块(4×4大小)的左上角所在位置 if(bAuto) CurrentX = BestStartX( );
else CurrentX = COL / 2 - 2; //初始为(0,5)
for( y = 0; y < 4; y++ )
{
for( x = 0; x < 4; x++ )
CurrentBox[y][x] = NextBox[y][x];
}
BuildNextBox( ); //产生下一个随机的方块
return Test( CurrentY, CurrentX, CurrentBox );
}
F1开启/关闭自动求解。
int no[400] = {3,3,5,1};
void BuildNextBox( ) //产生下一个随机的方块
{
static int j=0;
int i, y, x;
i = rand()%MAX_CLASS; //随机生成0~6的整数
for( y = 0; y < 4; y++ )
{
for( x = 0; x < 4; x++ )
NextBox[y][x] = Box[i][y][x];
}
}
//测试在(y,x)位置(左上角位置)是否能放置方块box,能放置返回1,否则返回0 int Test( int y, int x, int box[4][4] )
{
int tmpy, tmpx;
for( tmpy = 0; tmpy < 4; tmpy++ )
{
for( tmpx = 0; tmpx < 4; tmpx++ )
{
if( Map[tmpy + y][tmpx + x] && box[tmpy][tmpx] )
return 0;
}
}
return 1;
}
int Drop( ) //定时时间到,当前方块下降一行位置(如果能下降返回1,否则返回0) {
int NewY;
NewY = CurrentY + 1;
if( Test( NewY, CurrentX, CurrentBox ) )
{
CurrentY = NewY;
return 1;
}
return 0;
}
void PutBox( ) //在当前位置(CurrentY,CurrentX)放置当前方块(此时当前方块已经不能下降了)
{
int y, x;
F1开启/关闭自动求解。
for( y = 0; y < 4; y++ )
{
for( x = 0; x < 4; x++ )
{
if( CurrentBox[y][x] )
Map[CurrentY + y][CurrentX + x] = CurrentBox[y][x];
}
}
}
int Move( int Right ) //(通过方向键)移动方块,参数right为1表示向右移动,为0表示向左移动
{
int x;
if( Right ) x = CurrentX + 1; //向右移动一列位置
else x = CurrentX - 1; //向左移动一列位置
if( Test( CurrentY, x, CurrentBox ) )
{
CurrentX = x;
return 1;
}
return 0;
}
void Clear( ) //清除满足条件的行
{
int y, x; //循环变量
int DelY, DelX; //循环变量
int Full; //一行是否满的标志
for( y = 0; y < ROW - 1; y++ ) //检查第0~22行
{
Full = 1;
for( x = 1; x < COL - 1; x++ ) //检查每行的第1~12列
{
if( !Map[y][x] )
{
Full = 0; break;
}
}
if( Full ) //第y行满了,删除该行,该行以上的其他行下移一行
{
iDeleteRows++; iTotalNum = iDeleteRows*100; //更新得分
for( DelY = y; DelY > 0; DelY-- )
{
F1开启/关闭自动求解。
for( DelX = 1; DelX < COL - 1; DelX++ )
Map[DelY][DelX] = Map[DelY-1][DelX];
}
for( DelX = 1; DelX < COL - 1; DelX++ ) //第0行置为0
Map[0][DelX] = 0;
}
}
}
int Rotate( ) //测试旋转是否可行,如果可行则旋转当前方块
{
int y, x;
int TmpBox[4][4];
RotateTest( CurrentBox, TmpBox );
if( Test( CurrentY, CurrentX, TmpBox ) )
{
for( y = 0; y < 4; y++ )
{
for( x = 0; x < 4; x++ )
CurrentBox[y][x] = TmpBox[y][x];
}
return 1;
}
else return 0;
}
/*
0000 0000 0000 //旋转规律是:Box3[y][x] = Box1[y][3-x] -> 方块绕竖直方向对称变换
0100-> 0010-> 0010 //Box2[x][y] = Box3[y][x] -> 沿着主对角线对称变换(相当于矩阵转置)
1110 0111 0110
0000 0000 0010 */
int RotateTest( int Box1[4][4], int Box2[4][4] ) //旋转当前方块
{ //新增返回值为:旋转后的Box2中最下一个方格所在的行
int y, x;
for( y = 0; y < 4; y++ )
{
for( x = 3; x >=0; x-- )
Box2[x][y] = Box1[y][3 - x];
}
for( y=3; y>=0; y-- ) //统计Box2中最下一个方格所在的行
{
for( x=0; x<4; x++ )
F1开启/关闭自动求解。
{
if(Box2[y][x]) break;
}
if(x<4) break;
}
return y;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, //窗口处理函数 WPARAM wParam, LPARAM lParam )
{
HDC hdc, hdcMem;
int y, x;
PAINTSTRUCT ps;
HBITMAP hBitMap;
HPEN hPen;
HBRUSH hBrush;
static int cxClient, cyClient; //窗口客户区宽度和高度
char str[20]; //用于显示得分的变量
switch( message )
{
case WM_CREATE:
SetTimer( hwnd, tDown, tDownTime, NULL ); //开启两个定时器 SetTimer( hwnd, tPaint, tPaintTime, NULL );
srand( (unsigned)time( NULL ) );
bAuto = false;
Pause = false;
InitMap( );
BuildNextBox( ); //先随机产生一个方块
NewFall( ); //方块落下并随机产生下一个方块
sprintf( str, " 得分:%d", iTotalNum ); strcat( WindowTxt, str ); SetWindowText(hwnd,WindowTxt);
return 0;
case WM_SIZE:
cxClient = LOWORD( lParam ); //取得窗口客户区宽度和高度 cyClient = HIWORD( lParam );
return 0;
case WM_TIMER:
switch( wParam )
{
case tDown: //下降定时器
if( !Drop( ) ) //如果不能下降则放置当前方块
{
PutBox( );
F1开启/关闭自动求解。
MessageBeep( -1 ); Clear( ); //清除 //刷新得分 sprintf( str, "%d", iTotalNum ); WindowTxt[36] = 0; strcat( WindowTxt, str ); SetWindowText(hwnd,WindowTxt); if( !NewFall( ) ) //如果新的方块不能落下,则程序结束 { KillTimer(hwnd, tDown ); KillTimer(hwnd, tPaint ); //PostMessage( hwnd, WM_CLOSE, NULL, NULL ); } } break; case tPaint: //重绘定时器 InvalidateRect(hwnd, NULL, FALSE); //强制重绘窗口工作区 break; } case WM_KEYDOWN: switch( wParam ) { case VK_LEFT: //"向左"方向键 if(bAuto) break; Move(0); break; case VK_RIGHT: //"向右"方向键 if(bAuto) break; Move(1); break; case VK_UP: //"向上"方向键:旋转 if(bAuto) break; Rotate( ); break; case VK_DOWN: //"向下"方向键:当前方块下移一行位置 if(bAuto) break; MessageBeep( -1 ); Drop( ); break; case VK_RETURN: //回车键:暂停 Pause = !Pause; if( Pause ) //暂停、自动求解时也可以暂停 KillTimer( hwnd, tDown ); else //启动 { if(bAuto) SetTimer( hwnd, tDown, tDownTime/5, NULL ); else SetTimer( hwnd, tDown, tDownTime, NULL ); } break; case VK_SPACE: if(bAuto) break;
F1开启/关闭自动求解。
while( 1 ) //使用永真循环,使得当前方块一直下降到不能下降为止 { if( !Drop( ) ) { PutBox( ); Clear( ); sprintf( str, "%d", iTotalNum ); WindowTxt[36] = 0; strcat( WindowTxt, str ); SetWindowText(hwnd,WindowTxt); if( !NewFall( ) ) //如果新的方块不能落下,则程序结束 { KillTimer(hwnd, tDown ); KillTimer(hwnd, tPaint ); //PostMessage( hwnd, WM_CLOSE, NULL, NULL ); } break; } } break; case VK_F1: bAuto = !bAuto; if(bAuto) //自动求解 { KillTimer(hwnd, tDown ); SetTimer( hwnd, tDown, tDownTime/5, NULL ); memcpy( WindowTxt+25, s2, strlen(s2) ); //修改标题 } else { KillTimer(hwnd, tDown ); SetTimer( hwnd, tDown, tDownTime, NULL ); memcpy( WindowTxt+25, s1, strlen(s1) ); //修改标题 } SetWindowText(hwnd,WindowTxt); break; } case WM_PAINT: //重绘窗口工作区 hdc = BeginPaint( hwnd, &ps ); hdcMem = CreateCompatibleDC( hdc ); hBitMap = CreateCompatibleBitmap( hdc, cxClient, cyClient ); SelectObject( hdcMem, hBitMap ); //画地图最外面的矩形(4, 4, 246, 446) Rectangle( hdcMem, StartX + LEN * 1 - 1,StartY + LEN * 1 - 1, StartX + LEN * (COL - 1) + 1, StartY + LEN * (ROW - 1) + 1 ); hPen = CreatePen( PS_SOLID, 1, RGB(180, 180, 180) ); SelectObject( hdcMem, hPen );
F1开启/关闭自动求解。
hBrush = CreateSolidBrush( RGB(250, 250, 250) ); SelectObject( hdcMem, hBrush ); for( y = 1; y < ROW - 1; y++ ) //画地图中的每一格 { for( x = 1; x < COL - 1; x++ ) { Rectangle( hdcMem, StartX + LEN * x, StartY + LEN * y, StartX + LEN * (x + 1), StartY + LEN * (y + 1) ); } } DeleteObject( hPen ); DeleteObject( hBrush ); hPen = CreatePen( PS_SOLID, 1, RGB(180, 180, 180) ); SelectObject( hdcMem, hPen ); hBrush = CreateSolidBrush( RGB(255, 100, 100) ); SelectObject(hdcMem, hBrush); for( y = 1; y < ROW - 1; y++ ) //画出地图中每个被占据的方格 { for( x = 1; x < COL - 1; x++ ) { if( Map[y][x] ) { Rectangle( hdcMem, StartX + LEN * x, StartY + LEN * y, StartX +LEN * (x + 1), StartY + LEN * (y + 1) ); } } } for( y = 0; y < 4; y++ ) //画当前方块 { for( x = 0; x < 4; x++ ) { if( CurrentBox[y][x] ) { if( y + CurrentY > 0 ) { Rectangle( hdcMem, (x + CurrentX) * LEN + StartX, (y + CurrentY) * LEN +StartY, (x + CurrentX + 1) * LEN + StartX, (y + CurrentY + 1) * LEN + StartY ); } } } } for( y = 0; y < 4; y++ )//在窗口右边区域画下一个方块
F1开启/关闭自动求解。
{
for( x = 0; x < 4; x++ )
{
if( NextBox[y][x] )
{
Rectangle( hdcMem, (x + COL) * LEN + StartX,
(y + 2) * LEN + StartY,
(x+ COL + 1) * LEN + StartX, (y + 3) * LEN + StartY ); }
}
}
DeleteObject( hPen );
DeleteObject( hBrush );
DeleteObject( hBitMap );
BitBlt( hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY );
DeleteDC( hdcMem );
EndPaint( hwnd, &ps );
return 0;
case WM_DESTROY:
KillTimer(hwnd, tDown );
KillTimer(hwnd, tPaint );
PostQuitMessage( 0 );
return 0;
}//end of switch( message )
return DefWindowProc( hwnd, message, wParam, lParam );
}