VC++ 6.0 MFC 俄罗斯方块 自动求解 代码 源程序

发布时间: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 );

}

VC++ 6.0 MFC 俄罗斯方块 自动求解 代码 源程序.doc 将本文的Word文档下载到电脑

    精彩图片

    热门精选

    大家正在看

    × 游客快捷下载通道(下载后可以自由复制和排版)

    限时特价:7 元/份 原价:20元

    支付方式:

    开通VIP包月会员 特价:29元/月

    注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
    微信:fanwen365 QQ:370150219