一步一步实现扫雷游戏(C语言实现)(四)

此项目相关博文链接

一步一步实现扫雷游戏(C语言实现)(一)

一步一步实现扫雷游戏(C语言实现)(二)

一步一步实现扫雷游戏(C语言实现)(三)

一步一步实现扫雷游戏(C语言实现)(四)

唉!说来就惭愧啊!几星期前我就开始使用win32写着个程序,但是到了中途碰到了许多钉子。以前我写了几篇博客记录以前写的代码,但是那是不成功的代码。今天我把扫雷游戏的基本过程实现了,下面一一介绍:

思维导图:



一步一步实现扫雷游戏(C语言实现)(四)

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]。

类的实现(不做介绍,请看注释):
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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]中:
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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消息是用来载入初始状态下的格子图的(如下图)

一步一步实现扫雷游戏(C语言实现)(四)

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()。实现代码如下:
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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 ()
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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,则程序自动点开一片区域

函数实现分别如下:
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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);

}

}

}

}
一步一步实现扫雷游戏(C语言实现)(四)一步一步实现扫雷游戏(C语言实现)(四)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

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 上午2:35
下一篇 2023年2月8日 上午2:36

相关推荐