此项目相关博文链接
唉!说来就惭愧啊!几星期前我就开始使用win32写着个程序,但是到了中途碰到了许多钉子。以前我写了几篇博客记录以前写的代码,但是那是不成功的代码。今天我把扫雷游戏的基本过程实现了,下面一一介绍:
思维导图:
1.首先说说初始状态时后台随机载入地雷分布的部分,我把这部分用类封装了:
classRandMap
{
private:
intmap[MAX_X][MAX_Y];
intm, n;
intnum_mines;
public:
//函数声明
RandMap();
voidset_data(intin_m,intin_n,intin_num_mines);//使用函数参数改变类内部数据的接口
voidget_map(intout_map[MAX_X][MAX_Y]);//获取随机地雷分布图
voidset_mines(void);//分布地雷位置
intround_num_mines(inti,intj);//计算周围地雷个数
voidrand_map(void);//生成随机分布地雷图
};
基本过程是:由主函数调用RandMap类中函数,获取地雷分布图map[i][j]。
主函数实现着过程的代码:
///////////////////////////////////////
//生成随机地雷分布图map[i][j]
RandMap RMap;
RMap.set_data(m, n, num_mines);
RMap.rand_map();
RMap.get_map(map);
///////////////////////////////////////
这样就获得了随机分布地雷的数组map[i][j]。
类的实现(不做介绍,请看注释):
View Code//randmap.cpp
#include"randmap.h"
//构造函数
RandMap::RandMap()
{
memset(map,0,sizeof(map));
}
/**********
初始化地雷分布位置和个数
函数功能:根据设置的地雷个数和分布地图(map,数组)给出分布好了地雷的数组
函数原型:void set_mines(void)
*********/
voidRandMap::set_mines(void)
{
intnum=1;
inti,j;
srand((unsigned)time(0));
while(num<=num_mines)
{
//rand()%n 取(0,n-1)的随机数
i=rand()%m+1;
j=rand()%n+1;
//如果出现相同的情况呢?,没事,再循环几次,直到有了足够的地雷为止
if(i<1||i>m||j<1||j>n||map[i][j]==MINE)
{
continue;
}
map[i][j]=MINE;
num++;//判断地雷个数
}
}
/***********
返回周围地雷个数的函数
函数原型: int round_num_mines(int i,int j);
参 数: int i, int j为当前的坐标
返回值类型: int 返回该坐标处周围的地雷数
返回值情况:(1)返回1-8代表周围有1-8个地雷;
(2)返回0代表周围没有地雷;
(3)返回MINE代表此坐标时地雷;
***********/
intRandMap::round_num_mines(inti,intj)
{
intdir[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};
intk=0, mines=0;//round_num_mines 为周围地雷个数
if(map[i][j]==MINE)
{
returnMINE;
}
for(k=0; k<8; k++)
{
if(map[i+dir[k][0]][j+dir[k][1]]==MINE)
{
mines++;
}
}
returnmines;
}
/***********
随机生成地图
函数原型: void rand_map(void)
***********/
voidRandMap::rand_map(void)
{
inti, j;
set_mines();
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
{
map[i][j]=round_num_mines(i, j);
}
}
}
/+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
设置m,n,num_mines
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++/
voidRandMap::set_data(intin_m,intin_n,intin_num_mines)
{
m=in_m;
n=in_n;
num_mines=in_num_mines;
}
/+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
获取地图
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
voidRandMap::get_map(intout_map[MAX_X][MAX_Y])
{
inti, j;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
{
out_map[i][j]=map[i][j];
}
}
}
2.画地图前的准备**,确定窗口位置和地图中重要位置的像素点的坐标,先看确定窗口位置和大小的函数:
这个函数使用的是用指针做参数来改变变量的值,在C++中也可以使用指针的引用的。
//////////////////////////////////////////////
//设置窗口位置和大小
//////////////////////////////////////////////
voidset_position_size(intp_x_position,intp_y_position,intp_x_size,intp_y_size)
{
intwidth=GetSystemMetrics(SM_CXFULLSCREEN);//读取屏幕大小
intheigh=GetSystemMetrics(SM_CYFULLSCREEN);
p_x_position=(int)width/3;
p_y_position=(int)heigh/5;
p_x_size=(int)width/3;
p_y_size=(int)width/3;
}
int * p_x_position, int * p_y_position;确定窗口位置(左上角相对于屏幕的x,y坐标)
int * p_x_size, int * p_y_size;窗口大小
再来看看怎么将地图中每个格子的像素点的位置存储到数组 int pixel_x[MAX_X]和int pixel_y[MAX_Y]中:
View Code///////////////////////////////////////////////////////
//获取画格子的像素点并存储到pixel_x[i],pixel_y[j]中
///////////////////////////////////////////////////////
voidget_pixel(HWND hwnd)
{
intx1, y1, x2, y2;//(x1,y1),(x2,y2)为窗口位置坐标
intx_position, y_position, x_size, y_size;
set_position_size(&x_position,&y_position,&x_size,&y_size);
intframe_width=GetSystemMetrics(SM_CXSIZEFRAME);//边框宽度
intcaption_width=GetSystemMetrics(SM_CYCAPTION);//标题栏宽度
intmenu_high=GetSystemMetrics(SM_CYMENU);//菜单高度
x1=caption_width;
y1=caption_width;
x2=x_size-caption_width-frame_width;
y2=y_size-2*caption_width-frame_width;
inti, j;
inttmp_x=x1;
inttmp_y=y1;
//将画竖向格子线的像素点位置存储到pixel_x[i]中
for(i=0; i<=m; i++)
{
pixel_x[i]=tmp_x;
tmp_x+=(x2-x1)/m;
}
//将画横向格子线的像素点位置存储到pixel_y[j]中
for(j=0; j<=n; j++)
{
pixel_y[j]=tmp_y;
tmp_y+=(y2-y1)/n;
}
}
代码中有注释,不在作解说。
有了地图中每个格子的像素点坐标,下面作图也就方便了
3.现在来看看消息的响应:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
intx=LOWORD(lParam);//x,y为鼠标当前的位置坐标
inty=HIWORD(lParam);
switch(message)
{
caseWM_PAINT://画图消息
paint_map(hwnd);
return0;
caseWM_LBUTTONDOWN://单击鼠标左键消息
left_key(hwnd, x, y);
return0;
caseWM_RBUTTONDOWN://单击右键
//right_key(hwnd, x, y);
return0;
caseWM_DESTROY:
PostQuitMessage (0);
return0;
}
returnDefWindowProc (hwnd, message, wParam, lParam) ;
}
WM_PAINT消息是用来载入初始状态下的格子图的(如下图)
paint_map(hwnd)这个函数是实现初始化载入地图(方格子)的函数,函数使用了
////////////////////////////////////////////////////////////
//画格子图
////////////////////////////////////////////////////////////
voidpaint_map(HWND hwnd)
{
HDC hdc;
PAINTSTRUCT ps;
hdc=BeginPaint (hwnd,&ps);
DrawGrid(hdc);//使用直线画格子的
EndPaint (hwnd,&ps);
}
初始态载入图我使用的是BeginPaint ()和BeginPaint ()
DrawGrid函数的实现使用的是API函数MoveToEx()和LineTo()。实现代码如下:
View Code///////////////////////////////
//
//画格子
//
//////////////////////////////
voidDrawGrid(HDC hdc)
{
inti, j;
POINT ptLeftTop;
//画横线
for(i=0; i<=m; i++)
{
ptLeftTop.x=pixel_x[i];
ptLeftTop.y=pixel_y[0];
MoveToEx(hdc,ptLeftTop.x,ptLeftTop.y,NULL);
ptLeftTop.x=pixel_x[i];
ptLeftTop.y=pixel_y[n];
LineTo(hdc,ptLeftTop.x,ptLeftTop.y);
}
//画竖线
for(j=0; j<=n; j++)
{
ptLeftTop.x=pixel_x[0];
ptLeftTop.y=pixel_y[j];
MoveToEx(hdc,ptLeftTop.x,ptLeftTop.y,NULL);
ptLeftTop.x=pixel_x[m];
ptLeftTop.y=pixel_y[j];
LineTo(hdc,ptLeftTop.x,ptLeftTop.y);
}
}
WM_LBUTTONDOWN 是单击鼠标左键消息,单击左键说明点开此位置。
4.下面介绍响应左键事件部分(右键标记地雷部分还没实现,但是实现比左键简单,等下次实现了再发上来)
我定义看一个数组int user_map[MAX_X][MAX_Y] = {0};来存放某位置是否点开,user_map[i][j]等于1就说明点开。left_key()函数中画图的实现是使用了API函数GetDC ()和ReleaseDC ()
View Code///////////////////////////////////////
//
//响应左键事件
//
//////////////////////////////////////
voidleft_key(HWND hwnd,intx,inty)//x,y为鼠标当前的位置坐标
{
inttmp_x=x, tmp_y=y, i, j;
POINT lpPoint;
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
for(i=0; i<=m; i++)
{
if(tmp_x>pixel_x[i]&&tmp_x<pixel_x[i+1])break;
}
for(j=0; j<=n; j++)
{
if(tmp_y>pixel_y[j]&&tmp_y<pixel_y[j+1])break;
}
hdc=GetDC (hwnd);
i=i+1;
j=j+1;
if(map[i][j]==MINE)
{
GetClientRect(hwnd,&rect);
DrawText (hdc, TEXT ("GAME OVER!"),-1,&rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
}
elseif(map[i][j]==0&&user_map[i][j]==UNOPEN)
{
search0(i, j);
}
else
{
user_map[i][j]=OPEN;
}
print_user_map(hdc);
ReleaseDC (hwnd, hdc);
}
left_key()函数中出先了下面两个函数。
void print_user_map(HDC hdc);//打印用户点击之后的地图
void search0(int i, int j);//如果用户点开的位置上地雷的个数为0,则程序自动点开一片区域
函数实现分别如下:
View Code/////////////////////////////////////////////////
//打印用户点击之后的地图
/////////////////////////////////////////////////
voidprint_user_map(HDC hdc)
{
RECT rect;
inti, j;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
{
/*******
left : 指定矩形框左上角的x坐标
top: 指定矩形框左上角的y坐标
right: 指定矩形框右下角的x坐标
bottom:指定矩形框右下角的y坐标
********/
rect.left=pixel_x[i-1];
rect.top=pixel_y[j-1];
rect.right=pixel_x[i];
rect.bottom=pixel_y[j];
if(user_map[i][j]==OPEN)
{
TCHAR str_tmp[10];
sprintf(str_tmp,"%d",map[i][j]);
DrawText (hdc, TEXT (str_tmp),-1,&rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
}
}
}
}View Code//////////////////////////////////////////////////////////////////////////
//如果用户点开的位置上地雷的个数为0,则程序自动点开一片区域
//////////////////////////////////////////////////////////////////////////
voidsearch0(inti,intj)
{
user_map[i][j]=OPEN;//点开(i,j)位置
intdi, dj, k;
for(k=0; k<8; k++)
{
di=i+dir[k][0];
dj=j+dir[k][1];
if(di<1||di>m||dj<1||dj>n)
{
continue;
}
if(map[di][dj]!=0&&map[i][j]!=MINE)
{
user_map[di][dj]=OPEN;
}
}
for(k=0; k<8; k++)
{
di=i+dir[k][0];
dj=j+dir[k][1];
if(di<1||di>m||dj<1||dj>n)
{
continue;
}
if(map[di][dj]==0&&user_map[di][dj]==UNOPEN)
{
search0(di, dj);
}
}
}
print_user_map()函数是根据user_map的值来判断是否点开某位置。
search0()函数使用了搜索算法实现点开的位置是0就会点开一片区域。
最后,基本说完了,说这个是C/C++混合编程的原因是,我封装了载入随机地雷分布图部分,而没有封装画图那部分,不知道合理不。下面是某些量的预定义
//DEF.H
/++++++++++++++++++++++++++++++++++++++
预定义
+++++++++++++++++++++++++++++++++++++++/
#ifndef DEF
#defineDEF
#defineDEF_M//默认行坐标
#defineDEF_N//默认列坐标
#defineMINE -1//表示地雷
#defineMAX_X 22//最大x方向的格子数
#defineMAX_Y 22//最大y方向的格子数
#defineUNOPEN 0//没有点开
#defineOPEN 1//点开地雷
#defineMARK 2//标记地雷
#endif/ DEF /原文链接: https://www.cnblogs.com/hanxi/archive/2011/04/28/demining.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/24716
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!