从零学做简易扫雷(win32-非MFC-C语言版)

从零学做简易扫雷(win32-非MFC-C语言版)

起因

因转专业前后课程不一样,所以现在大二要补大一的程序设计实践课程,需要做一个小程序交作业,老师要求要想得高分就必须从这些题目里面选:***,***,。。。

因为我比较喜欢玩扫雷,于是就选了扫雷。又不甘心写纯黑的控制台程序,又要求只能用C语言,就打算借此机会从零学一下win32编程。废话不多说,开搞!

成品展示

项目压缩包:https://download.csdn.net/download/weixin_42856843/13131565(仅需5积分)

初始界面:
从零学做简易扫雷(win32-非MFC-C语言版)_第1张图片
游戏中:
从零学做简易扫雷(win32-非MFC-C语言版)_第2张图片
获胜:
从零学做简易扫雷(win32-非MFC-C语言版)_第3张图片
失败:
从零学做简易扫雷(win32-非MFC-C语言版)_第4张图片

流程图:

从零学做简易扫雷(win32-非MFC-C语言版)_第5张图片

制(学)作(习)过程

(点击看大图,刚点开后是一片白,因为视角在图片的左上角,请移动滚顶条调整视角)

2020/06/01更新:修复了移动后局部绘图丢失的BUG

代码

// saolei.cpp : 定义应用程序的入口点。
//
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "User32.lib")
#include 
#include 
#include 
#include 

///
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 使用者 saolei.rc

#define MAX_LOADSTRING			100
#define IDS_APP_TITLE			103
#define IDR_MAINFRAME			128
#define IDR_GAMEFRAME			123524427
#define IDD_SAOLEI_DIALOG   	102
#define IDD_ABOUTBOX			103
#define IDM_ABOUT				104
#define IDM_EXIT				105
#define IDI_SAOLEI  			107
#define IDI_SMALL				108
#define IDC_SAOLEI	    		109
#define IDC_MYICON				2
#ifndef IDC_STATIC
#define IDC_STATIC				-1
#endif
#define IDB_TEXT				3300
#define IDB_ONE					3301	//简单
#define IDB_TWO					3302	//中等
#define IDB_THREE				3303	//困难
#define IDB_STA1				3401	//难度选择描述
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC					130
#define _APS_NEXT_RESOURCE_VALUE	129
#define _APS_NEXT_COMMAND_VALUE		32771
#define _APS_NEXT_CONTROL_VALUE		1000
#define _APS_NEXT_SYMED_VALUE		110
#endif
#endif
//

typedef struct {
	int x, y;
}poi;

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

const wchar_t AppName[] = L"扫雷小游戏";
int Difficulty = 2;								//游戏难度
const int Butten_Size = 35;						//地图按钮的尺寸
const int WidthAndHeight[3] = { 10, 17, 25 };	//不同难度对应的地图大小
const int NumberOfBomb[3] = { 10, 30, 80 };		//不同按难度对应的地雷数量
int used[50][50];								//虚拟游戏地图
int vis[50][50];
int Num;										//现在还剩的雷的数量
int num_ = 1;									//未踩的格子
int mark[50][50];								//是否标记

HWND hwnd;										//创建选择难度界面
HWND hwndGame;									//创建游戏界面
HWND TEXT_NumBomb;								//显示剩余的雷
HWND WIN;										//获胜界面
HWND FAC;										//失败界面
WNDCLASSEX wc;
int _nCmdShow;
int OnGame = 0;

// 此代码模块中包含的函数的前向声明:
poi getpos(int number);
int getnum(poi t);
int cheBox(poi t);
void dfs(poi t);
void draw(poi t);
void doBox(poi t);
void doDBox(poi t);
int init(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int);
void Start();
void Restart();
void win(HINSTANCE hInstance);
void fac(HINSTANCE hInstance);
ATOM                MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow) {
	_nCmdShow = nCmdShow;
	UNREFERENCED_PARAMETER(hPrevInstance);	/*UNREFERENCED_PARAMETER 展开传递的参数或表达式。其目的是避免编译器关于未引用参数的警告*/
	UNREFERENCED_PARAMETER(lpCmdLine);

	// TODO: 在此处放置代码。
	wc.cbSize = sizeof(WNDCLASSEX);				/*结构体的大小*/
	wc.style = 0;								/*类的式样(CS_*),不要跟窗口式样(WS_*)混淆了.这个一般设置为 0*/
	wc.lpfnWndProc = WndProc;					/*指向这个窗口类的窗口过程的指针*/
	wc.cbClsExtra = 0;							/*配置给这个类的额外內存.一般为 0*/
	wc.cbWndExtra = 0;							/*配置给这个类的每个窗口的额外內存.一般为 0*/
	wc.hInstance = hInstance;					/*应用程序实例的句柄*/
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);	/*当用戶按下 Alt+Tab 组合时候显示的大图标(一般为 32*32)*/
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);	/*在我们的窗口上显示的光标*/
	wc.hbrBackground = (HBRUSH)(6);				/*窗口背景顏色的背景刷子*/	/*1是灰色,2是黑色,3,4是淡蓝色,5是淡黄色,6是白色,7是淡黑色,8,,9是黑色*/
	wc.lpszMenuName = NULL;						/*这个类的窗口所用的菜单资源的名字*/
	wc.lpszClassName = AppName;					/*类的名字*/
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);/*在任务栏和窗口的左上角显示的小图标(一般为 16*16)*/

	if (!RegisterClassEx(&wc)) {
		MessageBox(NULL, L"窗口注册失败!", L"错误!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	if (!init(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) return 0;

	HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAOLEI));	//LoadAccelerators:调入加速键表。该函数调入指定的加速键表

	MSG msg;	//消息

	// 主消息循环:
	while (GetMessage(&msg, NULL, 0, 0)) {
		if (num_ == 0) {
			Restart();
			win(hInstance);
		}
		if (num_ < 0) {
			Restart();
			fac(hInstance);
		}
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int)msg.wParam;
}

//---------------------------------------------------------------
//---界面准备部分
//---------------------------------------------------------------
//游戏初始化
int init(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow) {

	hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,									/*扩展的窗口式样*/
		AppName,											/*类的名字*/
		L"简易扫雷",											/*标题的名字*/
		WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,/*窗口式样参数*/	//一种简单的处理方法是在调用CreateWindow函数时指定的窗口样式中去掉WS_THICKFRAME样式,如果你使用的样式中已经包含该样式,例如WS_OVERLAPPEDWINDOW,我们可以將WS_OVERLAPPEDWINDOW和WS_THICKFRAME进行按位异或运算来实现
		CW_USEDEFAULT,										/*窗口的左上角的 X 坐标*/
		CW_USEDEFAULT,										/*窗口的左上角的 Y 坐标*/
		300,												/*窗口的宽度*/
		300,												/*窗口的高度*/
		NULL, NULL, hInstance, NULL);						/*分別是父窗口的句柄,菜单句柄,应用程序实例句柄,和窗口创建数据的指针*/

	if (hwnd == NULL) {
		MessageBox(NULL, L"选择窗口创建失败!", L"错误!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	CreateWindow(L"static", L"请选择您要的难度:", WS_CHILD | WS_VISIBLE | SS_RIGHT, 70, 20, 140, 20, hwnd, (HMENU)IDB_STA1, hInstance, NULL);
	//创建三个按钮,用来选择游戏难度
	CreateWindow(L"Button", L"简 单", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 80, 60, 120, 60, hwnd, (HMENU)IDB_ONE, hInstance, NULL);
	CreateWindow(L"Button", L"中 等", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 80, 120, 120, 60, hwnd, (HMENU)IDB_TWO, hInstance, NULL);
	CreateWindow(L"Button", L"困 难", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 80, 180, 120, 60, hwnd, (HMENU)IDB_THREE, hInstance, NULL);

	hwndGame = CreateWindowEx(
		WS_EX_CLIENTEDGE,									/*扩展的窗口式样*/
		AppName,											/*类的名字*/
		L"简易扫雷",											/*标题的名字*/
		WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,/*窗口式样参数*/	//一种简单的处理方法是在调用CreateWindow函数时指定的窗口样式中去掉WS_THICKFRAME样式,如果你使用的样式中已经包含该样式,例如WS_OVERLAPPEDWINDOW,我们可以將WS_OVERLAPPEDWINDOW和WS_THICKFRAME进行按位异或运算来实现
		CW_USEDEFAULT,										/*窗口的左上角的 X 坐标*/
		CW_USEDEFAULT,										/*窗口的左上角的 Y 坐标*/
		Butten_Size * WidthAndHeight[Difficulty],			/*窗口的宽度*/
		100 + Butten_Size * WidthAndHeight[Difficulty],		/*窗口的高度*/
		NULL, /*(HMENU)IDR_GAMEFRAME*/NULL, hInstance, NULL);/*分別是父窗口的句柄,菜单句柄,应用程序实例句柄,和窗口创建数据的指针*/

	if (hwndGame == NULL) {
		MessageBox(NULL, L"游戏窗口创建失败!", L"错误!",
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	TEXT_NumBomb = CreateWindow(L"static", L"", WS_CHILD | WS_VISIBLE | SS_RIGHT, 300, 20, 20, 17, hwndGame, (HMENU)IDB_TEXT, hInstance, NULL);
	CreateWindow(L"static", L"剩余地雷数量:", WS_CHILD | WS_VISIBLE | SS_RIGHT, 194, 20, 106, 17, hwndGame, NULL, hInstance, NULL);

	CreateWindow(L"static", L"单击鼠标左键翻开方块单击鼠标右键标记地雷单击数字方块翻开一圈", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, 20, 152, 17 * 3, hwndGame, NULL, hInstance, NULL);
	return 1;
}
//游戏开始
void Start() {
	ShowWindow(hwndGame, _nCmdShow);
	Restart();
}
void Restart() {
	Num = NumberOfBomb[Difficulty];
	num_ = WidthAndHeight[Difficulty] * WidthAndHeight[Difficulty];
	wchar_t str[] = L"00";
	str[0] += NumberOfBomb[Difficulty] / 10;
	str[1] += NumberOfBomb[Difficulty] % 10;
	SetWindowText(TEXT_NumBomb, str);

	//创建游戏地图
	int num = 3305;

	MoveWindow(hwndGame,
		0, 0,
		Butten_Size * WidthAndHeight[Difficulty] + 20,
		100 + Butten_Size * WidthAndHeight[Difficulty] + 43,
		1);
	OnGame = 1;

	UpdateWindow(hwndGame);

	HDC hdc = GetDC(hwndGame);	//建立设备DC

	HPEN hPen;					//创建画笔
	HFONT hFont;				//创建字体
	HBRUSH hBrush;				//创建画刷
	LOGFONT lf = { 0 };

	wcscpy(lf.lfFaceName, L"宋体");
	lf.lfWidth = Butten_Size;
	lf.lfHeight = Butten_Size;
	lf.lfWeight = FW_NORMAL;
	lf.lfCharSet = GB2312_CHARSET;

	hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
	hFont = CreateFontIndirect(&lf);
	hBrush = CreateSolidBrush(RGB(192, 192, 192));

	//选择对象到hdc
	SelectObject(hdc, hPen);
	SelectObject(hdc, hFont);
	SelectObject(hdc, hBrush);

	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 0));

	for (int i = 0; i < WidthAndHeight[Difficulty]; ++i) {
		for (int j = 0; j < WidthAndHeight[Difficulty]; ++j) {
			Rectangle(hdc, j * Butten_Size, 100 + i * Butten_Size, (j + 1) * Butten_Size, 100 + (i + 1) * Butten_Size);
			used[i][j] = 0;
			vis[i][j] = 0;
			mark[i][j] = 0;
		}
	}

	DeleteObject(hPen);
	DeleteObject(hFont);
	DeleteObject(hBrush);

	//随机生成地雷
	srand(clock());
	for (int i = 0, x = rand() % (WidthAndHeight[Difficulty] * WidthAndHeight[Difficulty]); i < NumberOfBomb[Difficulty]; ++i) {
		for (; used[x / WidthAndHeight[Difficulty]][x % WidthAndHeight[Difficulty]] == -1; x = rand() % (WidthAndHeight[Difficulty] * WidthAndHeight[Difficulty]));
		int xx = x / WidthAndHeight[Difficulty], yy = x % WidthAndHeight[Difficulty];
		used[xx][yy] = -1;
	}

	//计算数字
	for (int i = 0; i < WidthAndHeight[Difficulty]; ++i) {
		for (int j = 0; j < WidthAndHeight[Difficulty]; ++j) {
			if (used[i][j] == -1) continue;
			for (int k = -1; k < 2; ++k) {
				for (int l = -1; l < 2; ++l) {
					if (i + k < 0 || i + k >= WidthAndHeight[Difficulty] || j + l < 0 || j + l >= WidthAndHeight[Difficulty]) continue;
					if (used[i + k][j + l] == -1) ++used[i][j];
				}
			}
		}
	}
}
void win(HINSTANCE hInstance) {
	WIN = CreateWindowEx(
		WS_EX_CLIENTEDGE,									/*扩展的窗口式样*/
		AppName,											/*类的名字*/
		L"恭喜你!",											/*标题的名字*/
		WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,/*窗口式样参数*/	//一种简单的处理方法是在调用CreateWindow函数时指定的窗口样式中去掉WS_THICKFRAME样式,如果你使用的样式中已经包含该样式,例如WS_OVERLAPPEDWINDOW,我们可以將WS_OVERLAPPEDWINDOW和WS_THICKFRAME进行按位异或运算来实现
		CW_USEDEFAULT,										/*窗口的左上角的 X 坐标*/
		CW_USEDEFAULT,										/*窗口的左上角的 Y 坐标*/
		300,												/*窗口的宽度*/
		140,												/*窗口的高度*/
		NULL, NULL, hInstance, NULL);						/*分別是父窗口的句柄,菜单句柄,应用程序实例句柄,和窗口创建数据的指针*/
	CreateWindow(L"static", L"恭喜你,你完成了挑战!", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, 50, 200, 20, WIN, (HMENU)IDB_STA1, hInstance, NULL);
	ShowWindow(WIN, _nCmdShow);
}
void fac(HINSTANCE hInstance) {
	FAC = CreateWindowEx(
		WS_EX_CLIENTEDGE,									/*扩展的窗口式样*/
		AppName,											/*类的名字*/
		L"很遗憾!",											/*标题的名字*/
		WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,/*窗口式样参数*/	//一种简单的处理方法是在调用CreateWindow函数时指定的窗口样式中去掉WS_THICKFRAME样式,如果你使用的样式中已经包含该样式,例如WS_OVERLAPPEDWINDOW,我们可以將WS_OVERLAPPEDWINDOW和WS_THICKFRAME进行按位异或运算来实现
		CW_USEDEFAULT,										/*窗口的左上角的 X 坐标*/
		CW_USEDEFAULT,										/*窗口的左上角的 Y 坐标*/
		300,												/*窗口的宽度*/
		140,												/*窗口的高度*/
		NULL, NULL, hInstance, NULL);						/*分別是父窗口的句柄,菜单句柄,应用程序实例句柄,和窗口创建数据的指针*/
	CreateWindow(L"static", L"很遗憾,挑战失败!", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, 50, 200, 20, FAC, (HMENU)IDB_STA1, hInstance, NULL);

	ShowWindow(FAC, _nCmdShow);
}
//---------------------------------------------------------------
//---界面准备部分结束
//---------------------------------------------------------------

//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance) {
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAOLEI));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAOLEI);
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassExW(&wcex);
}

//---------------------------------------------------------------
//---内部核心运算部分
//---------------------------------------------------------------
int dir[8][2] = { 0, 1, 1, 0, 0, -1, -1, 0, 1, -1, -1, 1, 1, 1, -1, -1 };

poi getpos(int number) {
	poi t;
	t.x = (number - 3305) / WidthAndHeight[Difficulty];
	t.y = (number - 3305) % WidthAndHeight[Difficulty];
	return t;
}
int getnum(poi t) {
	return 3305 + t.x * WidthAndHeight[Difficulty] + t.y;
}
//检查方块是否被翻开,返回1:已经被翻开,返回0:未被翻开
int cheBox(poi t) {
	return used[t.x][t.y] != -1;
}
void dfs(poi t) {
	if (vis[t.x][t.y]) return;
	int x = t.x;
	int y = t.y;
	draw(t);
	vis[x][y] = 1;
	for (int k = 0; k < 8; ++k) {
		t.x = x + dir[k][0];
		t.y = y + dir[k][1];
		if (t.x >= 0 && t.x < WidthAndHeight[Difficulty] && t.y >= 0 && t.y < WidthAndHeight[Difficulty] && !vis[t.x][t.y]) {
			if (used[t.x][t.y] == 0) dfs(t);
			else if (used[t.x][t.y] > 0) draw(t);
		}
	}
}
//显示相应位置的数字
void draw(poi t) {
	if (vis[t.x][t.y]) return;
	HDC hdc = GetDC(hwndGame);	//建立设备DC

	HPEN hPen;					//创建画笔
	HFONT hFont;				//创建字体
	HBRUSH hBrush;				//创建画刷
	LOGFONT lf = { 0 };

	wcscpy(lf.lfFaceName, L"宋体");
	lf.lfWidth = Butten_Size;
	lf.lfHeight = Butten_Size;
	lf.lfWeight = FW_NORMAL;
	lf.lfCharSet = GB2312_CHARSET;

	hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
	hFont = CreateFontIndirect(&lf);
	hBrush = CreateSolidBrush(RGB(192, 192, 192));

	//选择对象到hdc
	SelectObject(hdc, hPen);
	SelectObject(hdc, hFont);
	SelectObject(hdc, hBrush);

	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 0));

	if (used[t.x][t.y] > 0) {
		//周围有雷
		wchar_t str[] = L"0";
		str[0] += used[t.x][t.y];

		hBrush = CreateSolidBrush(RGB(255, 255, 255));
		SelectObject(hdc, hBrush);
		Rectangle(hdc, t.y * Butten_Size, 100 + t.x * Butten_Size, (t.y + 1) * Butten_Size, 100 + (t.x + 1) * Butten_Size);
		TextOut(hdc, t.y * Butten_Size, 100 + t.x * Butten_Size, str, lstrlen(str));

		vis[t.x][t.y] = 1;
		--num_;
	}
	else if (used[t.x][t.y] == 0) {
		hBrush = CreateSolidBrush(RGB(255, 255, 255));
		SelectObject(hdc, hBrush);
		Rectangle(hdc, t.y * Butten_Size, 100 + t.x * Butten_Size, (t.y + 1) * Butten_Size, 100 + (t.x + 1) * Butten_Size);

		vis[t.x][t.y] = 1;
		--num_;
	}
	DeleteObject(hPen);
	DeleteObject(hFont);
	DeleteObject(hBrush);
}
//标记地雷
void doMark(poi t) {
	HDC hdc = GetDC(hwndGame);	//建立设备DC

	HPEN hPen;					//创建画笔
	HFONT hFont;				//创建字体
	HBRUSH hBrush;				//创建画刷
	LOGFONT lf = { 0 };

	wcscpy(lf.lfFaceName, L"宋体");
	lf.lfWidth = Butten_Size;
	lf.lfHeight = Butten_Size;
	lf.lfWeight = FW_NORMAL;
	lf.lfCharSet = GB2312_CHARSET;

	hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
	hFont = CreateFontIndirect(&lf);
	hBrush = CreateSolidBrush(RGB(192, 192, 192));

	//选择对象到hdc
	SelectObject(hdc, hPen);
	SelectObject(hdc, hFont);
	SelectObject(hdc, hBrush);

	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 0));


	//如果当前位置已经有了标记,意味着现在要取消标记
	if (vis[t.x][t.y]) {
		if (mark[t.x][t.y]) {
			Rectangle(hdc, t.y * Butten_Size, 100 + t.x * Butten_Size, (t.y + 1) * Butten_Size, 100 + (t.x + 1) * Butten_Size);


			wchar_t sstr[10];
			GetWindowText(TEXT_NumBomb, sstr, 10);
			sstr[1]++;
			if (sstr[1] > L'9') {
				sstr[1] = L'0';
				++sstr[0];
			}
			mark[t.x][t.y] = 0;
			vis[t.x][t.y] = 0;
			++num_;
			SetWindowText(TEXT_NumBomb, sstr);
		}
	}
	//如果当前位置没有任何标记才可以标记地雷
	else {
		wchar_t sstr[] = L"#", ssstr[10];
		GetWindowText(TEXT_NumBomb, ssstr, 10);
		if (!(ssstr[0] == L'0' && ssstr[1] == L'0')) {
			hBrush = CreateSolidBrush(RGB(0, 0, 0));
			SelectObject(hdc, hBrush);
			Rectangle(hdc, t.y * Butten_Size, 100 + t.x * Butten_Size, (t.y + 1) * Butten_Size, 100 + (t.x + 1) * Butten_Size);

			--num_;
			ssstr[1]--;
			if (ssstr[1] < L'0') {
				ssstr[1] += 10;
				--ssstr[0];
			}
			vis[t.x][t.y] = 1;
			mark[t.x][t.y] = 1;
			SetWindowText(TEXT_NumBomb, ssstr);
		}
	}

	DeleteObject(hPen);
	DeleteObject(hFont);
	DeleteObject(hBrush);
}
//单击翻开一个方块
void doBox(poi t) {
	if (mark[t.x][t.y]) doMark(t);
	if (used[t.x][t.y] == 0) dfs(t);
	else if (used[t.x][t.y] > 0) {
		if (!vis[t.x][t.y]) draw(t);
		else doDBox(t);
	}
	else num_ = -1;
}
//翻开一圈方块
void doDBox(poi t) {
	if (used[t.x][t.y] <= 0) return;
	if (used[t.x][t.y] > 0 && used[t.x][t.y] < WidthAndHeight[Difficulty]) {
		int f = 0;		//标记的地雷的数量
		//数出玩家已经确定信息的方块
		for (int i = -1; i < 2; ++i) {
			for (int j = -1; j < 2; ++j) {
				if (t.x + i >= 0 && t.x + i < WidthAndHeight[Difficulty] && t.y + j >= 0 && t.y + j < WidthAndHeight[Difficulty]) {
					f += mark[t.x + i][t.y + j];
				}
			}
		}
		//只有 雷 的 标记 符合相应 数量 才可以进行 翻开一圈 动作
		if (f == used[t.x][t.y]) {
			for (int i = -1; i < 2; ++i) {
				for (int j = -1; j < 2; ++j) {
					if (t.x + i < 0 || t.x + i >= WidthAndHeight[Difficulty] || t.y + j < 0 || t.y + j >= WidthAndHeight[Difficulty]) continue;
					poi tt;
					tt.x = t.x + i;
					tt.y = t.y + j;
					if (!vis[tt.x][tt.y]) doBox(tt);
					else if (used[tt.x][tt.y] == 0) dfs(tt);
					else if (used[tt.x][tt.y] > 0) {
						draw(tt);
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------
//---内部核心运算部分结束
//---------------------------------------------------------------

//---------------------------------------------------------------
//---消息处理部分
//---------------------------------------------------------------
void OnLBUTTONDOWN(HWND hWnd, WPARAM wParam, LPARAM lParam) {
	int wmId = LOWORD(wParam);
	poi t; int tem1 = GET_Y_LPARAM(lParam) - (int)100, tem2 = GET_X_LPARAM(lParam);
	if (tem1 > 0 && tem1 < WidthAndHeight[Difficulty] * Butten_Size && tem2 > 0 && tem2 < WidthAndHeight[Difficulty] * Butten_Size) {
		t.x = (GET_Y_LPARAM(lParam) - (int)100) / Butten_Size;
		t.y = GET_X_LPARAM(lParam) / (int)Butten_Size;
		doBox(t);
	}
}
void OnRBUTTONDOWN(HWND hWnd, WPARAM wParam, LPARAM lParam) {
	int wmId = LOWORD(wParam);
	poi t;
	t.x = (GET_Y_LPARAM(lParam) - (int)100) / Butten_Size;
	t.y = GET_X_LPARAM(lParam) / (int)Butten_Size;
	doMark(t);
}
void OnCOMMAND(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	int wmId = LOWORD(wParam);

	// 分析菜单选择:
	switch (wmId) {
	case IDM_ABOUT:
		DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
		break;
	case IDM_EXIT:
		DestroyWindow(hWnd);
		break;
		//按钮响应
	case IDB_ONE:
		Difficulty = 0;
		DestroyWindow(hwnd);
		break;
	case IDB_TWO:
		Difficulty = 1;
		DestroyWindow(hwnd);
		break;
	case IDB_THREE:
		Difficulty = 2;
		DestroyWindow(hwnd);
		break;
		break;
	default:
		DefWindowProc(hWnd, message, wParam, lParam);
	}
}
void OnPAINT(HWND hWnd, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd, &ps);
	// TODO: 在此处添加使用 hdc 的任何绘图代码...
	//HDC hdc = GetDC(hwndGame);	//建立设备DC

	HPEN hPen;					//创建画笔
	HFONT hFont;				//创建字体
	HBRUSH hBrush;				//创建画刷
	LOGFONT lf = { 0 };

	wcscpy(lf.lfFaceName, L"宋体");
	lf.lfWidth = Butten_Size;
	lf.lfHeight = Butten_Size;
	lf.lfWeight = FW_NORMAL;
	lf.lfCharSet = GB2312_CHARSET;

	hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
	hFont = CreateFontIndirect(&lf);
	hBrush = CreateSolidBrush(RGB(0, 0, 0));

	//选择对象到hdc
	SelectObject(hdc, hPen);
	SelectObject(hdc, hFont);
	SelectObject(hdc, hBrush);

	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 255));
	SetTextColor(hdc, RGB(0, 0, 0));

	for (int i = 0; i < WidthAndHeight[Difficulty]; ++i) {
		for (int j = 0; j < WidthAndHeight[Difficulty]; ++j) {
			if (mark[i][j] == 1) {
				Rectangle(hdc, j * Butten_Size, 100 + i * Butten_Size, (j + 1) * Butten_Size, 100 + (i + 1) * Butten_Size);
			}
		}
	}

	hBrush = CreateSolidBrush(RGB(255, 255, 255));
	SelectObject(hdc, hBrush);

	for (int i = 0; i < WidthAndHeight[Difficulty]; ++i) {
		for (int j = 0; j < WidthAndHeight[Difficulty]; ++j) {
			if (mark[i][j] == 0 && vis[i][j] == 1) {
				wchar_t str[] = L"0";
				str[0] += used[i][j];

				SelectObject(hdc, hBrush);
				Rectangle(hdc, j * Butten_Size, 100 + i * Butten_Size, (j + 1) * Butten_Size, 100 + (i + 1) * Butten_Size);
				if (used[i][j]) TextOut(hdc, j * Butten_Size, 100 + i * Butten_Size, str, lstrlen(str));
			}
		}
	}

	hBrush = CreateSolidBrush(RGB(192, 192, 192));
	SelectObject(hdc, hBrush);

	for (int i = 0; i < WidthAndHeight[Difficulty]; ++i) {
		for (int j = 0; j < WidthAndHeight[Difficulty]; ++j) {
			if (OnGame && vis[i][j] == 0) {
				Rectangle(hdc, j * Butten_Size, 100 + i * Butten_Size, (j + 1) * Butten_Size, 100 + (i + 1) * Butten_Size);
			}
		}
	}


	DeleteObject(hPen);
	DeleteObject(hFont);
	DeleteObject(hBrush);

	EndPaint(hWnd, &ps);
}
void OnDESTROY(HWND hWnd, WPARAM wParam, LPARAM lParam) {
	if (hWnd == hwndGame) PostQuitMessage(0);
	Start();
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	switch (message) {
	case WM_LBUTTONDOWN:
		OnLBUTTONDOWN(hWnd, wParam, lParam);
		break;
	case WM_RBUTTONDOWN:
		OnRBUTTONDOWN(hWnd, wParam, lParam);
		break;
	case WM_COMMAND:	//按钮子窗口的WM_COMMAND消息,wParam参数的低位是子窗口ID,高位是通知码, lParam参数是接收消息的子窗口的句柄
		OnCOMMAND(hWnd, message, wParam, lParam);
		break;
	case WM_PAINT:	//WM_PAINT消息,LOWORD(lParam)是客户区的宽,HIWORD(lParam)是客户区的高
		OnPAINT(hWnd, wParam, lParam);
		break;
	case WM_DESTROY:
		OnDESTROY(hWnd, wParam, lParam);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	UNREFERENCED_PARAMETER(lParam);
	switch (message) {
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}
//---------------------------------------------------------------
//---消息处理部分结束
//---------------------------------------------------------------

项目压缩包:https://download.csdn.net/download/weixin_42856843/13131565(仅需5积分)

你可能感兴趣的:(#,其他开发)