游戏中处理地图生成,绘制,单击处理等主要逻辑的类 map.h,所有格子放在20*20=400的一个数组中,即最大支持长宽各20个格子。每个格子用一个字节表示,低四位0表示空格,1~8表示数字,9表示雷。高四位bit7~bit4: bit7表示该格子是否打开,bit6表示是否已经标记,bit5表示是否要打叉(点错或标记错的时候)。说明:除了动画以外,所有的图形元素都是32*32像素。
简单的动画:与每个格子一一对应,也用20*20=400的一个数组表示,仍是一个字节表示。低四位表示帧数,bit7表示是否播放。时间间隔150ms,在窗口的初始化InitInstance()函数中启动定时器。图形大小48*48像素。
图形处理:先用一张黑白图与DC相“与”,将要显示的地方清零,再用图案相“或”。这个方法叫“去背”。这是动画效果中用的图:

附: map.h map.cpp
//
扫雷地图
#ifndef __MAP_H
#define
__MAP_H

#include
"
queue.h
"

#define
G_NULL 0
//
游戏未开始
#define
G_END 1
//
游戏结束

//
表示雷
#define
BOMB 9

//
判断坐标是否合法的宏
#define
INCLOSEAREA(x,y,xsize,ysize) ((x)>=0 && (x)<=(xsize) && (y)>=0 && (y)<=(ysize))
#define
INCLOSEINDEX(x,y) ((x)>=0 && (x)<=(iWidth-1) && (y)>=0 && (y)<=(iHeight-1))

//
取低4位
#define
LOWBYTE(x) ((x) & 0x0f)
//
取第i个bit(0~7)
#define
GETBIT(x,i) ((x) & (1<<(i)))
#define
IS1TO9(x) ((x)>=1 && (x)<=9)

class
GameMap

...
{
public:
GameMap();
~GameMap();

void Draw(void);
void DrawButton(void);
void SetMine(int iNum);
void SetMapSize(int w,int h);
int CreateMap(void);
int CountBombNum(int x,int y);
int OpenGrid(int x,int y);//x,y是方格坐标0-8
int Open8around(int x,int y);
void SetFlag(int x,int y);//作标记
void ClearFlag(int x,int y);//作标记
void SearchBlank(int x,int y);
int CheckWin(void);

//排行榜
void LoadScore(void);
int IsNewScore(int l,int score);
void WriteScore(int l,char *pname, int time);
void GetName(int l, char *pname);
int GetTimeValue(int l);
void ResetScore(int iLevel);

void SetMap(int w,int h,int num);
void SetDC(HDC h);
void SetHWND(HWND h);
void SetHINSTANCE(HINSTANCE h);
void SetLevel(int l);
void LoadPic(void);

int GetHeight(void);
int GetWidth(void);
int GetNum(void);
int GetLevel(void);

//动画效果
void DrawAnimation(void);

public:
int iState;
int iSubState;
POINT button_pos;
int iFaceState;

private:
int iWidth;
int iHeight;
int iMineNum;
char pData[400];//map
char pGray[400];
int nowx;
int nowy;
char info[100];
int iLevel;
HDC hdc;
HDC hdcmem;
HWND hwnd;
HINSTANCE hinstance;
HBITMAP hbm_earth;
HBITMAP hbm_number;
HBITMAP hbm_bomb;
HBITMAP hbm_mask;
HBITMAP hbm_sign;
HBITMAP hbm_face;
HBITMAP hbm_flag;
HBITMAP hbm_gray;
HBITMAP hbm_back;

BITMAP strBm;

//队列
GQUEUE queue;

//score
char scorename[80];
int scoretime[3];
}
;

#endif


/**/
//// map.cpp

#include
"
stdafx.h
"
#include
"
resource.h
"

#include
"
map.h
"

//
使用队列
#include
"
queue.h
"

#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
time.h
>

GameMap::GameMap()

...
{
iWidth=9;
iHeight=9;
iMineNum=0;

iState=G_NULL;//游戏没有开始

iFaceState=0;//NORMAL 笑脸标志

nowx=0;//鼠标坐标索引
nowy=0;

button_pos.x= 32*iWidth+50;//定义笑脸按钮的坐标
button_pos.y= 20;

//提示字符串
memset(info,0,sizeof(info));

//初始化等级
iLevel=0;

//初始化成绩
memset(scorename,0,sizeof(scorename));
memset(scoretime,-1,sizeof(scoretime));
}

GameMap::
~
GameMap()

...
{
//清除绘图句柄
DeleteObject(hbm_earth);
DeleteObject(hbm_number);
DeleteObject( hbm_bomb);
DeleteObject( hbm_mask);
DeleteObject( hbm_sign);
DeleteObject( hbm_face);
DeleteObject( hbm_flag);
DeleteObject( hbm_gray);
DeleteObject( hbm_back);
}
void
GameMap::Draw(
void
)

...
{
int iLeft=0;
int ivisible=0;
int xStart=0;
int yStart=0;
int i=0;
int j=0;

char temp[50]=...{0};
char igrid=0;
RECT rect;
iLeft=iMineNum;//当前雷的数量

yStart=0;//绘图坐标
for(j=0;j<iHeight;j++)

...{
xStart=0;
for(i=0;i<iWidth;i++)

...{
igrid=pData[j*iWidth+i];
//图形显示,字节(bit7~bit0)的bit7,1,已打开,0,没有打开
if(GETBIT(igrid,7))

...{
//先画地面背景
SelectObject(hdcmem,hbm_earth);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,0,SRCCOPY);

//bit3~bit0 表示周围雷的数量(1~8),9表示雷
if(BOMB==LOWBYTE(igrid) )

...{
//雷
SelectObject(hdcmem,hbm_bomb);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,32,SRCAND);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,0,SRCPAINT);
}
else if(IS1TO9(LOWBYTE(igrid)))

...{
//数字
SelectObject(hdcmem,hbm_number);
BitBlt(hdc,xStart+8,yStart,16,32,hdcmem,LOWBYTE(igrid)*16,32,SRCAND);
BitBlt(hdc,xStart+8,yStart,16,32,hdcmem,LOWBYTE(igrid)*16,0,SRCPAINT);
}
else

...{
//空格,什么都不画
}

}
else

...{
//画默认的遮盖图像
SelectObject(hdcmem,hbm_mask);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,0,SRCCOPY);

//bit6表示是否标记过
if(GETBIT(igrid,6))

...{
//显示标记旗帜
SelectObject(hdcmem,hbm_flag);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,0,SRCCOPY);

iLeft--;
}
}
if(G_END == iState && GETBIT(igrid,5))

...{
//显示叉
SelectObject(hdcmem,hbm_sign);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,32,SRCAND);
BitBlt(hdc,xStart,yStart,32,32,hdcmem,0,0,SRCPAINT);
}

xStart+=32;
}
yStart+=32;
}

sprintf(temp,"剩余: %d 个 ",iLeft);
TextOut(hdc,32*iWidth+40,150,temp, strlen(temp));

sprintf(info,"w:%d,h: %d, num:%d",iWidth,iHeight,iMineNum);
TextOut(hdc,0,32*iHeight+10,info, strlen(info));
}

void
GameMap::SetMine(
int
iNum)

...
{
iMineNum=iNum;
}

void
GameMap::SetMapSize(
int
w,
int
h)

...
{
if(w<=0 || h<=0)

...{
return;
}

iWidth=w;
iHeight=h;
}

//
返回0表示失败,非0表示成功
int
GameMap::CreateMap(
void
)

...
{
int i=0;
int x=0;
int y=0;
int flag=0;

char temp[20]=...{0};

//根据地图大小,雷的个数生成一张地图
if(iWidth<=1 || iHeight<=1)

...{
return 0;
}

if(iMineNum<=1)

...{
return 0;
}

memset(pData,0xff,iWidth*iHeight);

//在地图中放置雷
srand((unsigned int)time(NULL));
for(i=0;i<iMineNum;i++)

...{
flag=0;//当前没有布雷
while(0==flag)

...{
//随机生成x,y坐标
x=rand()%iWidth;
y=rand()%iHeight;
if(pData[y*iWidth+x]!=BOMB)

...{
pData[y*iWidth+x]=BOMB;
flag=1;
}
else

...{
flag=0;//雷的坐标重复,继续选择坐标
}
}
}

//生成数字
for(y=0;y<iHeight;y++)

...{
for(x=0;x<iWidth;x++)

...{
if(BOMB==pData[y*iWidth+x])

...{
continue;
}
pData[y*iWidth+x]=CountBombNum(x,y);
}
}

//灰度图初始化
memset(pGray,0,400);
return 1;
}

int
GameMap::CountBombNum(
int
x,
int
y)

...
{
//雷的计数
//0 1 2
//3 4
//5 6 7

int iNum[8]=...{0};
int iSum=0;
int i=0;

if(INCLOSEAREA(x-1,y-1,iWidth-1,iHeight-1))

...{
iNum[0]=( pData[(y-1)*iWidth+x-1]==BOMB)?1:0;
}

if(INCLOSEAREA(x,y-1,iWidth-1,iHeight-1))

...{
iNum[1]=( pData[(y-1)*iWidth+x]==BOMB)?1:0;
}

if(INCLOSEAREA(x+1,y-1,iWidth-1,iHeight-1))

...{
iNum[2]=( pData[(y-1)*iWidth+x+1]==BOMB)?1:0;
}

//第2层
if(INCLOSEAREA(x-1,y,iWidth-1,iHeight-1))

...{
iNum[3]=( pData[y*iWidth+x-1]==BOMB)?1:0;
}

if(INCLOSEAREA(x+1,y,iWidth-1,iHeight-1))

...{
iNum[4]=( pData[y*iWidth+x+1]==BOMB)?1:0;
}

//第3层
if(INCLOSEAREA(x-1,y+1,iWidth-1,iHeight-1))

...{
iNum[5]=( pData[(y+1)*iWidth+x-1]==BOMB)?1:0;
}

if(INCLOSEAREA(x,y+1,iWidth-1,iHeight-1))

...{
iNum[6]=( pData[(y+1)*iWidth+x]==BOMB)?1:0;
}

if(INCLOSEAREA(x+1,y+1,iWidth-1,iHeight-1))

...{
iNum[7]=( pData[(y+1)*iWidth+x+1]==BOMB)?1:0;
}


for(i=0;i<8;i++)

...{
iSum+=iNum[i];
}

return iSum;
}

void
GameMap::SetDC(HDC h)

...
{
hdc=h;
}

void
GameMap::SetHWND(HWND h)

...
{
hwnd=h;
}

void
GameMap::SetHINSTANCE(HINSTANCE h)

...
{
hinstance=h;
}

void
GameMap::LoadPic(
void
)

...
{
if(!hinstance)

...{
MessageBox(hwnd,"Load Picture:instance is null","bitmap",MB_OK);
}

//hbm_earth=LoadBitmap(hinstance,"pic/earth.bmp");
hbm_earth=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_EARTH));
hbm_number=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_NUMBER));
hbm_bomb=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_BOMB));
hbm_mask=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_MASK));
hbm_sign=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_SIGN));
hbm_face=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_FACE));
hbm_flag=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_FLAG));
hbm_gray=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_GRAY));
hbm_back=LoadBitmap(hinstance,MAKEINTRESOURCE(IDB_BACK));

hdc=GetDC(hwnd);
hdcmem=CreateCompatibleDC(hdc);
ReleaseDC(hwnd,hdc);
}

//
return 1: 打开了雷
int
GameMap::OpenGrid(
int
x,
int
y)

...
{
int i;
if(GETBIT(pData[y*iWidth+x] , 7))

...{
//已经打开的格子不处理
return 0;
}

if(GETBIT(pData[y*iWidth+x] , 6))

...{
//已经标记的格子不处理
return 0;
}

//bit7 置1 ,表示打开
pData[y*iWidth+x] |=0x80;
//动画效果
pGray[y*iWidth+x] |=0x80;


if(BOMB==LOWBYTE(pData[y*iWidth+x]))

...{
iState=G_END;
iFaceState=3;//笑脸标志,哭

//bit5置1,表示该雷被点击,要显示叉
pData[y*iWidth+x] |=0x20;
//点中雷的界面处理
for(i=0;i<iWidth*iHeight;i++)

...{
if(BOMB==LOWBYTE(pData[i]))

...{
if( ! GETBIT(pData[i],6))

...{
//还没有标记出的雷置为显示态
pData[i]|=0x80;
}
}
else

...{
if( GETBIT(pData[i],6) )

...{
//不是雷的格子如果有标记,则要显示叉
pData[i]|=0x20;
}
}
}
return 1;
}
else if(0==LOWBYTE(pData[y*iWidth+x]))

...{
//空白格子搜索
SearchBlank(x,y);
}
return 0;
}

void
GameMap::DrawButton(
void
)

...
{
SelectObject(hdcmem,hbm_face);
BitBlt(hdc,button_pos.x,button_pos.y,32,32,hdcmem,iFaceState*32,0,SRCCOPY);
}

void
GameMap::SearchBlank(
int
x,
int
y)

...
{

int tempindex[16]=...{-1, -1, 0, -1, 1, -1,
-1 ,0, 1, 0,
-1, 1, 0, 1, 1, 1};
int i;

//bit7: visible
//0 :earth
//9: bomb
POINT temp;
POINT temparound;
//表示可见
pData[y*iWidth+x]|=0x80;
temp.x=x;
temp.y=y;
queue.CreateQueue(iWidth*iHeight);

queue.InQueue(temp);
while(!queue.IsEmpty())

...{

temp=queue.OutQueue();
if(temp.y>iWidth-1 || temp.x>iWidth-1)

...{
MessageBox(hwnd,"index error","err",MB_OK);
return;
}
//打开当前格子
pData[temp.y*iWidth+temp.x] |= 0x80;
for(i=0;i<8;i++)

...{
//判断周围8个格子
temparound.x = temp.x+tempindex[i*2];
temparound.y = temp.y+tempindex[i*2+1];

//判断坐标是否合法
if ( INCLOSEINDEX(temparound.x, temparound.y))

...{
//周围的格子是空格,并且没有打开
if( (0== LOWBYTE(pData[temparound.y*iWidth+temparound.x]) )
&& !GETBIT(pData[temparound.y*iWidth+temparound.x],7) )

...{
//该格子进队列
queue.InQueue(temparound);
}
//打开格子
pData[(temparound.y)*iWidth + temparound.x ] |= 0x80;

//动画效果
pGray[(temparound.y)*iWidth + temparound.x ] |=0x80;
}
}
}
queue.DeleteQueue();
}

void
GameMap::SetFlag(
int
x,
int
y)

...
{
//已经打开的格子不允许置标记
if( GETBIT(pData[y*iWidth+x],7) )

...{
return;
}

//bit6表示是否做标记,通过异或操作实现bit翻转
pData[y*iWidth+x] ^= 0x40;
}
void
GameMap::ClearFlag(
int
x,
int
y)

...
{
//清除标记
pData[y*iWidth+x] &= (~0x40);
}

int
GameMap::CheckWin(
void
)

...
{
int i;

for(i=0;i<iWidth*iHeight;i++)

...{
if( BOMB ==LOWBYTE(pData[i]))

...{
if(!GETBIT(pData[i],6))

...{
//有一个雷未被标记
return 0;
}
}
else if(!GETBIT(pData[i],7))

...{
//不是雷的格子没有打开
return 0;
}
}

//游戏结束
iState=G_END;

return 1;
}

void
GameMap::SetMap(
int
w,
int
h,
int
num)

...
{
iWidth=w;
iHeight=h;
iMineNum=num;
//1,2,3表示初级,中级,高级,4为无效值
iLevel=4;

button_pos.x= 32*iWidth+50;
button_pos.y= 20;
}

int
GameMap::GetHeight(
void
)

...
{
return iHeight;
}

int
GameMap::GetWidth(
void
)

...
{
return iWidth;
}
int
GameMap::GetNum(
void
)

...
{
return iMineNum;
}

//
左右键同时单击某个格子
int
GameMap::Open8around(
int
x,
int
y)

...
{

char temp[20]=...{0};
int value=0;
int sign_num=0;

int tempindex[16]=...{-1, -1, 0, -1, 1, -1,
-1 ,0, 1, 0,
-1, 1, 0, 1, 1, 1};
int i;

memset(info,0,sizeof(info));

if( ! GETBIT(pData[y*iWidth+x],7))

...{
strcpy(info,"not open");
//没有打开
return 0;
}

value=LOWBYTE(pData[y*iWidth+x]);
if(value ==0 || value ==BOMB )

...{
strcpy(info,"not number");
//不是数字
return 0;
}

//周围的雷是否都已标记
for(i=0;i<8;i++)

...{
//下标合法
if( INCLOSEINDEX(x+tempindex[i*2], y+tempindex[i*2+1]) )

...{
//bit6是否标记出
if( GETBIT(pData[(y+tempindex[i*2+1])*iWidth + x+tempindex[i*2]],6))

...{
sprintf(temp,"(%d)%d,%d:%x; ", i,
x+tempindex[i*2],y+tempindex[i*2+1],
pData[(y+tempindex[i*2+1])*iWidth + x+tempindex[i*2]]);
strcat(info,temp);
sign_num++;
}
}
}

//雷的个数和该格子的数字不等,没有标记出周围所有的雷
if(sign_num != value)

...{
//sprintf(info,"not equal ",sign_num, value);
return 0;
}

//打开周围的格子
for(i=0;i<8;i++)

...{
//下标合法
if( INCLOSEINDEX(x+tempindex[i*2], y+tempindex[i*2+1]) )

...{
//没有打开的格子
if(! GETBIT(pData[(y+tempindex[i*2+1])*iWidth + x+tempindex[i*2]], 7 ))

...{
//没有标记的格子
if(! GETBIT(pData[(y+tempindex[i*2+1])*iWidth + x+tempindex[i*2]], 6 ))

...{
//pData[(y+tempindex[i*2+1])*iWidth + x+tempindex[i*2]] |= 0x80;
OpenGrid(x+tempindex[i*2], y+tempindex[i*2+1]);
}
}
}
}

return 1;
}

void
GameMap::SetLevel(
int
l)

...
{
iLevel=l;
switch(iLevel)

...{
case 0:
iWidth=9;
iHeight=9;
iMineNum=10;
break;
case 1:
iWidth=15;
iHeight=15;
iMineNum=36;
//MessageBox(hwnd,"set level 1","set",MB_OK);
break;
case 2:
iWidth=20;
iHeight=20;
iMineNum=50;
break;
default:
break;
}

button_pos.x= 32*iWidth+50;
button_pos.y= 20;
}

int
GameMap::GetLevel(
void
)

...
{
return iLevel;
}

void
GameMap::LoadScore(
void
)

...
{
FILE *f;
f=fopen("D:/score.dat","r");
if(!f)

...{
fopen("D:/score.dat","w");
return;
}

fread(scorename,sizeof(char),80,f);
fread(scoretime,sizeof(int),3,f);
fclose(f);
}

void
GameMap::GetName(
int
l,
char
*
pname)

...
{
if(l>=0 && l<=2)

...{
if( strlen(scorename+20*l) )

...{
strcpy(pname, scorename+20*l);
}
}
}

void
GameMap::WriteScore(
int
l,
char
*
pname,
int
time)

...
{
FILE *f;

if( strlen(pname)>=20)

...{
return;
}

strcpy( scorename+l*20, pname);
scoretime[l]=time;

//写到文件中
f=fopen("D:/score.dat","w");
if(!f)

...{
return;
}

fwrite(scorename,sizeof(char),80,f);
fwrite(scoretime,sizeof(int ), 3,f);
fclose(f);
}

int
GameMap::GetTimeValue(
int
l)

...
{
if(l>=0 && l<=2)

...{
if (0<=scoretime[l] && scoretime[l]<=999)

...{
return scoretime[l];
}
}
return 999;
}

void
GameMap::ResetScore(
int
l)

...
{
FILE *f;

memset(scorename+l*20, 0, 20);
scoretime[l]=999;

//写到文件中
f=fopen("D:/score.dat","w");
if(!f)

...{
return;
}
fwrite(scorename,sizeof(char),80,f);
fwrite(scoretime,sizeof(int ), 3,f);
fclose(f);
}

int
GameMap::IsNewScore(
int
l,
int
score)

...
{
if (! (l>=0 && l<=2))

...{
return 0;
}

if( (unsigned)score < (unsigned)scoretime[l] )

...{
//已经破了该等级的纪录
return 1;
}
return 0;
}

void
GameMap::DrawAnimation(
void
)

...
{
int xStart=0;
int yStart=0;
int i=0;
int j=0;

char temp[50]=...{0};
char igrid=0;
char iAnimation=0;
yStart=-8;//绘图坐标
for(j=0;j<iHeight;j++)

...{
xStart=-8;
for(i=0;i<iWidth;i++)

...{
igrid=pGray[j*iWidth+i];
//动画显示,字节(bit7~bit0)的bit7,1,显示动画,0,没有
if(GETBIT(igrid,7))

...{
iAnimation = LOWBYTE(igrid);
//先画地面背景
SelectObject(hdcmem,hbm_gray);
BitBlt(hdc,xStart,yStart,48,48,hdcmem,iAnimation*48,48,SRCAND);
BitBlt(hdc,xStart,yStart,48,48,hdcmem,iAnimation*48,0,SRCPAINT);

iAnimation++;
if(iAnimation>2)

...{
//清除播放标志bit7,恢复成0
pGray[j*iWidth+i]=0;
}
else

...{
//改变动画帧,置播放标志
pGray[j*iWidth+i]=iAnimation;
pGray[j*iWidth+i] |=0x80;
}
}
xStart+=32;
}
yStart+=32;
}
}