程序代码:
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#define W 14 //游戏区域宽度
#define H 26 //游戏区域高度
#define W1 6 //右边状态栏宽度
#define BSIZE 25 //游戏方格边长
#define Y1 6 //放置照片的纵坐标
#define Y2 11 //分数显示栏顶端纵坐标
#define Y3 14 //等级显示栏顶端纵坐标
#define Y4 17 //帮助栏顶端纵坐标
#define Cur_x W/2-1 //游戏方块初始状态左上角横坐标
#define Cur_y 1 //初始状态左上角纵坐标
#define BgColor RGB(0xF5,0xF5,0xDC) //米色
#define FgColor RGB(255,153,204) //淡红色
#define RED RGB(255,0,0)
#define ORANGE RGB(250,128,10)
#define YELLOW RGB(255,255,0)
#define GREEN RGB(0,255,0)
#define CYAN RGB(0,255,255)
#define LIGHT_BLUE RGB(0xA6,0xCA,0xF0) //天蓝色
#define PURPLE RGB(255,0,255)
#define PINK RGB(233,45,143)
#define BLUE RGB(79,195,242)
#define DARKGREEN RGB(0,94,9)
#define GRAY RGB(60,60,60)
#define LIGHTGREEN RGB(114,249,184)
#define MAX_BOX 33 //方块总数
#define MS_NEWBLOCK WM_USER+1 // 消息ID,产生新的【方块】
#define IDI_ICON 100
int score=0,level=0,level_step=100; //分数\等级以及每等级所需分数的定义及初始化
int top=H-1; //最顶端的纵坐标
int x,y; //方块当前位置的横坐标及纵坐标
int cur_boxnum,next_boxnum; //cur_boxnum是当前方块编号,next_boxnum是下一个方块编号
int flag_fullrow=0; //满行标志,初始化为0
int flag_speed_increase=0; //速度增加的标志
int Pause=0; //暂停标志
struct BOARD
{
int var; //状态,1代表已被占用,0代表未被占用
int color; //颜色
}board[H][W]; //定义游戏主板,H行W列
struct BLOCK
{
int a[8][2]; //定义方块形状的数组
int color; //方块颜色
int next; //下一个方块的号码
};
struct BLOCK block[MAX_BOX]=
{ //初始化各个游戏方块,-1并不是真正坐标,而是起标识符作用
{{1,1,1,2,1,3,2,3,-1,-1},RED,1},
{{0,2,1,2,2,2,0,3,-1,-1},RED,2},
{{0,1,1,1,1,2,1,3,-1,-1},RED,3},
{{2,1,0,2,1,2,2,2,-1,-1},RED,0},
{{1,1,1,2,0,3,1,3,-1,-1},ORANGE,5},
{{0,1,0,2,1,2,2,2,-1,-1},ORANGE,6},
{{1,1,2,1,1,2,1,3,-1,-1},ORANGE,7},
{{0,2,1,2,2,2,2,3,-1,-1},ORANGE,4},
{{1,1,0,2,1,2,2,2,-1,-1},LIGHT_BLUE,9},
{{1,1,1,2,2,2,1,3,-1,-1},LIGHT_BLUE,10},
{{0,2,1,2,2,2,1,3,-1,-1},LIGHT_BLUE,11},
{{1,1,0,2,1,2,1,3,-1,-1},LIGHT_BLUE,8},
{{1,1,1,2,2,2,2,3,-1,-1},GREEN,13},
{{1,2,2,2,0,3,1,3,-1,-1},GREEN,12},
{{2,1,1,2,2,2,1,3,-1,-1},CYAN,15},
{{0,2,1,2,1,3,2,3,-1,-1},CYAN,14},
{{1,0,1,1,1,2,1,3,-1,-1},PURPLE,17},
{{0,2,1,2,2,2,3,2,-1,-1},PURPLE,16},
{{1,0,1,1,1,2,1,3,-1,-1},PURPLE,19},
{{0,2,1,2,2,2,3,2,-1,-1},PURPLE,18},
{{1,1,2,1,1,2,2,2,-1,-1},YELLOW,20},
{{0,1,2,1,0,2,1,2,2,2,-1,-1},PINK,22},
{{1,1,2,1,1,2,1,3,2,3,-1,-1},PINK,23},
{{0,2,1,2,2,2,0,3,2,3,-1,-1},PINK,24},
{{0,1,1,1,1,2,0,3,1,3,-1,-1},PINK,21},
{{0,1,1,1,1,2,1,3,2,3,-1,-1},BLUE,26},
{{2,1,0,2,1,2,2,2,0,3,-1,-1},BLUE,25},
{{1,1,2,1,1,2,0,3,1,3,-1,-1},DARKGREEN,28},
{{0,1,0,2,1,2,2,2,2,3,-1,-1},DARKGREEN,27},
{{1,1,0,2,2,2,1,3,-1,-1},LIGHTGREEN,29},
{{0,0,3,0,1,1,2,1,1,2,2,2,0,3,3,3},GRAY,30},//这三个均为炸弹方块
{{0,0,3,0,1,1,2,1,1,2,2,2,0,3,3,3},GRAY,31},
{{0,0,3,0,1,1,2,1,1,2,2,2,0,3,3,3},GRAY,32},
};
void Paint(HDC hdc) //此函数用于初始化界面
{
int i,j;
HPEN hpen,hpen1; //定义画笔,用于绘制分隔线
HBRUSH hbrush; //定义画刷
hpen=CreatePen(PS_SOLID,1,FgColor); //创建画笔,用前景色作画笔
hpen1=CreatePen(PS_DASHDOTDOT,3,FgColor); //创建画笔,颜色为前景色,线宽为3,双点划线
SelectObject(hdc,hpen1); //选择画笔
MoveToEx(hdc,W*BSIZE,0,NULL); //将光标移动到(W*BSIZE,0)处
LineTo(hdc,W*BSIZE,H*BSIZE); //从光标所在位置画线至(W*BSIZE,H*BSIZE)处
DeleteObject(hpen1); //删除之前所选用的画笔
SelectObject(hdc,hpen); //重新选择画笔
for(i=1;i<H-1;i++) //绘制游戏区域方格线
for(j=1;j<W-1;j++)
{
hbrush=CreateSolidBrush(board[i][j].color); //画刷颜色与游戏板状态颜色相同
SelectObject(hdc,hbrush);
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
DeleteObject(hbrush);
}
hbrush=GetStockObject(NULL_BRUSH);
SelectObject(hdc,hbrush);
for(i=1;i<5;i++) //绘制右边状态栏游戏预览区域方格线
for(j=W+1;j<W+W1-1;j++)
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
Rectangle(hdc,(W+1)*BSIZE,Y2*BSIZE,(W+W1-1)*BSIZE,(Y2+2)*BSIZE); //绘制分数栏方格线
Rectangle(hdc,(W+1)*BSIZE,Y3*BSIZE,(W+W1-1)*BSIZE,(Y3+2)*BSIZE); //绘制等级栏方格线
Rectangle(hdc,(W+1)*BSIZE,Y4*BSIZE,(W+W1-1)*BSIZE,(Y4+5)*BSIZE); //绘制帮助栏方格线
TextOut(hdc,(W+2)*BSIZE,(int)((Y2+0.2)*BSIZE),"分 数",8); //输出文字
TextOut(hdc,(W+2)*BSIZE,(int)((Y3+0.2)*BSIZE),"等 级",8); //同上
TextOut(hdc,(W+1)*BSIZE,(H-3)*BSIZE,"制作人:袁峰",strlen("制作人:袁峰"));
TextOut(hdc,(W+1)*BSIZE,(H-2)*BSIZE,"QQ:775141406",strlen("QQ:775141406"));
DeleteObject(hpen); //删除画笔
DeleteObject(hbrush); //删除画刷
}
void ShowScore(HDC hdc) //显示分数的函数
{
char score_str[5]; //定义字符串用于保存分数值
SetBkColor(hdc,BgColor); //设置文本背景色为窗口背景色
wsprintf(score_str,"%4d",score); //将数字score转换成字符串后保存到score_str之中
TextOut(hdc,(int)((W+2.2)*BSIZE),(int)((Y2+1.2)*BSIZE),score_str,4); //在右边状态栏上显示分数
}
void ShowLevel(HDC hdc) //显示等级的,具体同上
{
char level_str[3];
SetBkColor(hdc,BgColor);
wsprintf(level_str,"%2d",level);
TextOut(hdc,(int)((W+2.7)*BSIZE),(int)((Y3+1.2)*BSIZE),level_str,2);
}
void ShowHelp(HDC hdc) //显示帮助的,该函数只在初始化界面时调用
{
char help1[]="↑ - 旋转",
help2[]="↓ - 下移",
help3[]="← - 左移",
help4[]="→ - 右移",
help5[]="空格-暂停";
TextOut(hdc,(int)((W+1.8)*BSIZE),(int)((Y4+0.2)*BSIZE),help1,9);
TextOut(hdc,(int)((W+1.8)*BSIZE),(int)((Y4+1.2)*BSIZE),help2,9);
TextOut(hdc,(int)((W+1.8)*BSIZE),(int)((Y4+2.2)*BSIZE),help3,9);
TextOut(hdc,(int)((W+1.8)*BSIZE),(int)((Y4+3.2)*BSIZE),help4,9);
TextOut(hdc,(int)((W+1.8)*BSIZE),(int)((Y4+4.2)*BSIZE),help5,9);
}
void EraseBox(HDC hdc,int x,int y,int num) //清除(x,y)处编号为num的方块
{
int i;
HPEN hpen=CreatePen(PS_SOLID,1,FgColor);
HBRUSH hbrush=CreateSolidBrush(BgColor);
SelectObject(hdc,hpen);
SelectObject(hdc,hbrush);
for(i=0;i<8;i++) //用背景色填充方块所在区域,使方块隐藏
{
if(block[num].a[i][0]<0) break;
Rectangle(hdc,(x+block[num].a[i][0])*BSIZE,(y+block[num].a[i][1])*BSIZE,
(x+block[num].a[i][0]+1)*BSIZE,(y+block[num].a[i][1]+1)*BSIZE);
}
DeleteObject(hpen);
DeleteObject(hbrush);
}
void ShowBox(HDC hdc,int x,int y,int num) //显示(x,y)处编号为num的方块
{
int i;
HPEN hpen=CreatePen(PS_SOLID,1,FgColor);
HBRUSH hbrush=CreateSolidBrush(block[num].color); //创建画刷,颜色和方块颜色相同
SelectObject(hdc,hpen);
SelectObject(hdc,hbrush);
for(i=0;i<8;i++) //显示方块的过程
{
if(block[num].a[i][0]<0) break;
Rectangle(hdc,(x+block[num].a[i][0])*BSIZE,(y+block[num].a[i][1])*BSIZE,
(x+block[num].a[i][0]+1)*BSIZE,(y+block[num].a[i][1]+1)*BSIZE);
}
DeleteObject(hpen);
DeleteObject(hbrush);
}
void RePaint(HDC hdc,int org_top)
{
int i,j;
HBRUSH hbrush;
HPEN hpen;
hpen=CreatePen(PS_SOLID,1,FgColor);
SelectObject(hdc,hpen);
if(flag_fullrow) //如果有满行,则重绘主板
{
for(i=org_top;i<y+4;i++) //org_top为之前的最顶端
{
if(i<=0||i>=H-1) continue; //越界了,就跳出本次循环
for(j=1;j<W-1;j++)
{ //注意这里绘制主板时,每次都要选择不同的画刷,用完后一定要删除
hbrush=CreateSolidBrush(board[i][j].color);
SelectObject(hdc,hbrush);
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
DeleteObject(hbrush);
}
}
flag_fullrow=0;
}
else if(cur_boxnum==MAX_BOX-1||cur_boxnum==MAX_BOX-2||cur_boxnum==MAX_BOX-3) //如果为炸弹方块
{
hbrush=CreateSolidBrush(BgColor); //背景色作画刷
SelectObject(hdc,hbrush);
for(i=y-2;i<y+6;i++) //炸弹作用区域为以炸弹为中心的8*8的矩形
{
if(i<=0||i>=H-1) continue;
for(j=x-2;j<x+6;j++)
{
if(j<=0||j>=W-1) continue;
Rectangle(hdc,j*BSIZE,i*BSIZE,(j+1)*BSIZE,(i+1)*BSIZE);
}
}
DeleteObject(hbrush);
}
DeleteObject(hpen);
}
void SetFullRow(HDC hdc) //满行处理函数,重置游戏板状态并重绘主板,通过调用Repaint函数重绘
{
int i,ii,j;
int k=0;
int org_top=top; //保存最顶端
if(cur_boxnum!=MAX_BOX-1&&cur_boxnum!=MAX_BOX-2&&cur_boxnum!=MAX_BOX-3) //如果不是炸弹方块,则执行以下过程
{
for(i=y;i<y+4;i++) //从y行开始,从上到下遍历游戏区域
{
if(i<=0||i>=H-1) continue; //越界了,就跳出本次循环
for(j=1;j<W-1;j++)
if(!board[i][j].var) break; //一旦该行有一个为空,即跳出
if(j==W-1) //找到满行了
{
for(ii=i;ii>=top;ii--) //重置游戏区域各个方格的状态,top为最顶端,i为找到的满行
for(j=1;j<W-1;j++)
board[ii][j]=board[ii-1][j];
top++; //消去1行后,Top加1
k++; //找到的满行数,每找到一满行,k就加1
flag_fullrow=1; //满行标志符
}
}
if(k!=0)
{//以下为增加分数的具体过程
if(k==1) score+=10; //满行数为1,加10分
else if(k==2) score+=30; //满行为2加30分
else if(k==3) score+=50;
else if(k==4) score+=80;
ShowScore(hdc); //更新分数
}
if(level!=score/level_step) //这里是程序优化部分,也可省略
{
level=score/level_step;
flag_speed_increase=1; //分数增加的标志置1
ShowLevel(hdc); //更新等级
}
}
RePaint(hdc,org_top); //重绘游戏主板
}
void ChangeVar(void) //改变游戏主板的状态
{
int i,j;
if(cur_boxnum==MAX_BOX-1||cur_boxnum==MAX_BOX-2||cur_boxnum==MAX_BOX-3) //如果该方块为炸弹方块
{
for(i=y-2;i<y+6;i++)
{
if(i<=0||i>=H-1) continue; //越界则跳出本次循环
for(j=x-2;j<x+6;j++)
{
if(j<=0||j>=W-1) continue; //越界
board[i][j].color=BgColor; //状态置0,颜色恢复为背景色
board[i][j].var=0;
}
}
}
else
{
for(i=0;i<8;i++)
{
if(block[cur_boxnum].a[i][0]<0) break;
board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].var=1; //游戏板该位置状态置1
board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].color=block[cur_boxnum].color; //游戏板颜色改为方块颜色
}
}
}
BOOL CanMove(void) //通过判断新位置是否有方块填充来判断来判断方块是否能够移动
{
int i;
for(i=0;i<8;i++)
{
if(block[cur_boxnum].a[i][0]<0) break;
if(board[y+block[cur_boxnum].a[i][1]][x+block[cur_boxnum].a[i][0]].var) //如果该位置有方块填充,则不能移动
return FALSE;
}
return TRUE;
}
BITMAPFILEHEADER * DibLoadImage (PTSTR pstrFileName) //加载DIB图像到内存之中
{
BOOL bSuccess ;
DWORD dwFileSize,dwHighSize,dwBytesRead;
HANDLE hFile;
BITMAPFILEHEADER *pbmfh;
hFile=CreateFile(pstrFileName,GENERIC_READ,FILE_SHARE_READ,NULL,
OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if(hFile==INVALID_HANDLE_VALUE)
return NULL;
dwFileSize=GetFileSize(hFile,&dwHighSize);
if(dwHighSize)
{
CloseHandle(hFile);
return NULL ;
}
pbmfh=malloc(dwFileSize) ;
if(!pbmfh)
{
CloseHandle(hFile);
return NULL ;
}
bSuccess=ReadFile(hFile,pbmfh,dwFileSize,&dwBytesRead,NULL) ;
CloseHandle(hFile);
if(!bSuccess||(dwBytesRead!=dwFileSize)
||(pbmfh->bfType!=*(WORD *)"BM")
||(pbmfh->bfSize!=dwFileSize))
{
free(pbmfh);
return NULL;
}
return pbmfh;
}