Minesweeper-2

温馨提示:本篇较长,如果没耐心,请勿观看!!!


嗨嗨嗨~~~我双缀缀的来啦~

今天先写游戏主体部分,不然难度选择连接不上。

首先呢,新建一个Windows Application(和昨天一样~),让后在程序外新建一个文本文档,叫什么都可以(最好是英文,不然有可能程序会崩)。我就叫winnum.data啦~

框架一样,省略了啦。

让后先得开六个全局变量:x, y, winx, winy, should, level

x & y是格子的数量描述,有三种:(9, 9) / (16, 16) / (30, 16)

winx & winy是窗口大小的描述,所以框架的这个:

HWND hWnd = CreateWindowExA(WS_EX_ACCEPTFILES,"WindowClass", "Minesweeper",
  		WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		310,
		150,
		NULL, NULL, hInstance, NULL)

得换成这个:

HWND hWnd = CreateWindowExA(WS_EX_ACCEPTFILES,"WindowClass", "Minesweeper",
  		WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		winx + 10,
		winy + 20,
		NULL, NULL, hInstance, NULL)

顺带提一嘴,+10 和 +20是让窗口美观一点的,不然不对称,要逼死强迫症。 

最后这个should是判断游戏结束的,代表要扫除几个格子。

而level就是用来设置should的,虽然x, y也可以,但我不想写那么多条件。

Coding, 启动~

首先,在开头导入<iostream>

不是输入输出文字,是文件读写。

在库下面加上这串:

int x, y, winx, winy, should, level
namespace std {
    void read()
    {
        freopen("winnum.data", "r", stdin);
        cin >> x >> y >> winx >> winy >> level;
    }
}

很明显,namespace std就是为cin准备的。

winmain里面的开头加上这个:

std::read();

这样就可以拿到数据啦。

在加控件之前,再来两个全局:HWND edit[30][30], ret;

edit是扫雷棋盘的控件HWND,ret是返回按钮。

让后可以美妙地加控件了。

int num = 1;
CreateWindow("Button", "Return", WS_VISIBLE | WS_CHILD, 10, 10, 70, 15, hWnd, (HMENU)0, hInstance, NULL);
for (int i = 0; i < y; i++)
{
	for (int j = 0; j < x; j++)
	{
		button[i][j] = CreateWindow("Button", " ", WS_VISIBLE | WS_CHILD, 25 + j * 20, 55 + i * 20, 20, 20, hWnd, (HMENU)num, NULL, NULL);
		num++;
	}
}

 但运行就会告诉你类型不匹配。

所以得换成这个:

long long num = 1;
CreateWindow("Button", "Return", WS_VISIBLE | WS_CHILD, 10, 10, 70, 15, hWnd, (HMENU)0, hInstance, NULL);
for (int i = 0; i < y; i++)
{
	for (int j = 0; j < x; j++)
	{
		button[i][j] = CreateWindow("Button", " ", WS_VISIBLE | WS_CHILD, 25 + j * 20, 55 + i * 20, 20, 20, hWnd, (HMENU)num, NULL, NULL);
		num++;
	}
}

这就好啦。

让后呢就是winProc。

先来初始化,这不用多说。不过全局还是得有:short number[30][30];

//在std中!!!
//这里的库要导入一下。<stdlib.h>
void init() {
    int l;
	if (level == 1)
	{
		l = 10;
		should = 71;
	}
	else if (level == 2)
	{
		l = 40;
		should = 216;
	}
	else
	{
		l = 99;
		should = 381;
	}
	for (int i = 1; i <= l; i++)
	{
		srand(time(0));
		int lx, ly;
		lx = rand() % x + 1;
		ly = rand() % y + 1;
		while (number[ly][lx] == -1)
		{
			lx = rand() % x + 1;
			ly = rand() % y + 1;
		}
		number[ly][lx] = -1;
	}
	for (int i = 1; i <= y; i++)
	{
		for (int j = 1; j <= x; j++)
		{
			if (number[i][j] == -1) continue;
			int num = 0;
			for (int k = -1; k <= 1; k++)
			{
				for (int l = -1; l <= 1; l++)
				{
					if (number[i + k][j + l] == -1) num++;
				}
			}
			number[i][j] = num;
		}
	}
}

让后是WM_COMMAND。这个比较少。

不过全局……还是有滴~

int score = 0;

真的不多。

case WM_COMMAND: {
    if (int(wParam) == 0) {
        system("start Minesweeper.exe");
    }
    else {
        int hmenu = int(wParam) - 1;
	    DestroyWindow(button[hmenu / x][hmenu % x]);
	    bflag[hmenu / x][hmenu % x] = 1;
	    flag[hmenu / x][hmenu % x] = 1;
        if (number[hmenu / x][hmenu % x] != -1)
        {
            score++;
            break;
        }  
        InvalidateRect(hWnd, NULL, TRUE);
    }
}

这个InvalidateRect(hWnd, NULL, TRUE)是标记绘画无效并触发WM_PAINT的 

WM_PAINT和比昨天的多,首先还是开全局:bool flag[30][30], bflag[30][30];

flag是给绘画标记的,bflag是给按钮标记的。

case WM_PAINT:{
    hdc = BeginPaint(hWnd, &ps);
	TextOut(hdc, (winx - 88) / 2, 25, "Minesweeper", lstrlen("Minesweeper"));
	if (score == should)
	{
		return DefWindowProc(hWnd,Msg,wParam,lParam);
	}
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			if (flag[i][j])
			{
				if (number[i + 1][j + 1] == -1)
				{
					return DefWindowProc(hWnd,Msg,wParam,lParam);
				}
				else if (number[i + 1][j + 1] == 0)
				{
					TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, " ", lstrlen(" "));
				}
				else
				{
					char out[2] = {};
					out[0] = char(number[i + 1][j + 1] + '0');
					TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, out, lstrlen(out));
				}
			}
		}
	}
	EndPaint(hWnd, &ps);
}

一开始我也是这样写的,但是会闪现黑字,所以得加上这句flag[i][j] = 0;

变成这样:

case WM_PAINT:{
    hdc = BeginPaint(hWnd, &ps);
	TextOut(hdc, (winx - 88) / 2, 25, "Minesweeper", lstrlen("Minesweeper"));
	if (score == should)
	{
		return DefWindowProc(hWnd,Msg,wParam,lParam);
	}
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			if (flag[i][j])
			{
				if (number[i + 1][j + 1] == -1)
				{
					return DefWindowProc(hWnd,Msg,wParam,lParam);
				}
				else if (number[i + 1][j + 1] == 0)
				{
					TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, " ", lstrlen(" "));
				}
				else
				{
					char out[2] = {};
					out[0] = char(number[i + 1][j + 1] + '0');
					TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, out, lstrlen(out));
				}
                flag[i][j] = 0;
			}
		}
	}
	EndPaint(hWnd, &ps);
}

 但这样运行会发现只有最近的一个格子会显示!

为什么呢?

是InvalidateRect(hWnd, NULL, TRUE)把画的全擦了!

只要把WM_COMMAND提前,把判断加上else,如果是雷就不break,不是雷再break。、

winProc变成这样:

LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam){
	HDC hdc;
	PAINTSTRUCT ps;
	switch(Msg)
	{
		case WM_COMMAND: {
			if (wParam == 0) {
				system("start Minesweeper.exe");
				wParam = WM_DESTROY;
			}
			else {
				delbutton(int(wParam));
				if (number[(wParam - 1) / x + 1][(wParam - 1) % x + 1] == -1)
				{
					wParam = WM_PAINT;
				}
				else
				{
					score++;
					break;
				}
			}
		}
		case WM_PAINT: {
			if (win) break;
			hdc = BeginPaint(hWnd, &ps);
			TextOut(hdc, (winx - 88) / 2, 25, "Minesweeper", lstrlen("Minesweeper"));
			if (score == should)
			{
				win = 1;
				paintall(hdc);
				MessageBox(0, "You Win", "Game Over", MB_OK);
				EndPaint(hWnd, &ps);
				return 0;
			}
			for (int i = 0; i < y; i++)
			{
				for (int j = 0; j < x; j++)
				{
					if (flag[i][j])
					{
						if (number[i + 1][j + 1] == -1)
						{
							paintall(hdc);
							MessageBox(0, "You Lose", "Game Over", MB_OK);
							win = 1;
							EndPaint(hWnd, &ps);
							return 0;
						}
						else if (number[i + 1][j + 1] == 0)
						{
							TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, " ", lstrlen(" "));
						}
						else
						{
							char out[2] = {};
							out[0] = char(number[i + 1][j + 1] + '0');
							TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, out, lstrlen(out));
						}
						flag[i][j] = 0;
					}
				}
			}
	        EndPaint(hWnd, &ps);
			break;
		}
        case WM_DESTROY: {
      		PostQuitMessage(0);
      		return 0;
      	}
        default:return DefWindowProc(hWnd,Msg,wParam,lParam);
   }
}

 我提前把胜利判断给放了进来,虽然……有bug,输了有两次MessageBox!

蒟蒻双缀缀的向DALAO求助~

让后可以把棋盘打到别的文件。

完整代码:

#include <windows.h>
#include <iostream>
#include <stdlib.h>
#include <ctime>

int x, y, winx, winy, level;
HWND button[30][30];
bool bflag[30][30];
bool flag[30][30];
bool win = 0;
int number[32][32] = {0};
int score = 0;
int should;

namespace std{
	void read()
	{
		freopen("winnum.data", "r", stdin);
		cin >> x >> y >> winx >> winy >> level;
	}
	void write()
	{
		freopen("number.data", "w", stdout);
		for (int i = 1; i <= y; i++)
		{
			for (int j = 1; j <= x; j++)
			{
				if (number[i][j] == -1) cout << "● ";
				else cout << number[i][j] << " ";
			}
			cout << endl;
		}
	}
	void init()
	{
		int l;
		if (level == 1)
		{
			l = 10;
			should = 71;
		}
		else if (level == 2)
		{
			l = 40;
			should = 216;
		}
		else
		{
			l = 99;
			should = 381;
		}
		for (int i = 1; i <= l; i++)
		{
			srand(time(0));
			int lx, ly;
			lx = rand() % x + 1;
			ly = rand() % y + 1;
			while (number[ly][lx] == -1)
			{
				lx = rand() % x + 1;
				ly = rand() % y + 1;
			}
			number[ly][lx] = -1;
		}
		for (int i = 1; i <= y; i++)
		{
			for (int j = 1; j <= x; j++)
			{
				if (number[i][j] == -1) continue;
				int num = 0;
				for (int k = -1; k <= 1; k++)
				{
					for (int l = -1; l <= 1; l++)
					{
						if (number[i + k][j + l] == -1) num++;
					}
				}
				number[i][j] = num;
			}
		}
		write(); //For check
	}
}

void delbutton(int hmenu)
{
	hmenu--;
	DestroyWindow(button[hmenu / x][hmenu % x]);
	bflag[hmenu / x][hmenu % x] = 1;
	flag[hmenu / x][hmenu % x] = 1;
}

void paintall(HDC hdc)
{
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			if (bflag[i][j] == 0) DestroyWindow(button[i][j]);
			if (number[i + 1][j + 1] == -1)
			{
				TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, "B", lstrlen("B"));
			}
			else if (number[i + 1][j + 1] == 0)
			{
				TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, " ", lstrlen(" "));
			}
			else
			{
				char out[2] = {};
				out[0] = char(number[i + 1][j + 1] + '0');
				TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, out, lstrlen(out));
			}
		}
	}
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam){
	HDC hdc;
	PAINTSTRUCT ps;
	switch(Msg)
	{
		case WM_COMMAND: {
			if (wParam == 0) {
				system("start Minesweeper.exe");
				PostQuitMessage(0);
			}
			else {
				delbutton(int(wParam));
				if (number[(wParam - 1) / x + 1][(wParam - 1) % x + 1] == -1)
				{
					wParam = WM_PAINT;
				}
				else
				{
					score++;
					break;
				}
			}
		}
		case WM_PAINT: {
			if (win) break;
			hdc = BeginPaint(hWnd, &ps);
			TextOut(hdc, (winx - 88) / 2, 25, "Minesweeper", lstrlen("Minesweeper"));
			if (score == should)
			{
				win = 1;
				paintall(hdc);
				MessageBox(0, "You Win", "Game Over", MB_OK);
				EndPaint(hWnd, &ps);
				return 0;
			}
			for (int i = 0; i < y; i++)
			{
				for (int j = 0; j < x; j++)
				{
					if (flag[i][j])
					{
						if (number[i + 1][j + 1] == -1)
						{
							paintall(hdc);
							MessageBox(0, "You Lose", "Game Over", MB_OK);
							win = 1;
							EndPaint(hWnd, &ps);
							return 0;
						}
						else if (number[i + 1][j + 1] == 0)
						{
							TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, " ", lstrlen(" "));
						}
						else
						{
							char out[2] = {};
							out[0] = char(number[i + 1][j + 1] + '0');
							TextOut(hdc, 25 + j * 20 + 6, 55 + i * 20 + 3, out, lstrlen(out));
						}
						flag[i][j] = 0;
					}
				}
			}
	        EndPaint(hWnd, &ps);
			break;
		}
        case WM_DESTROY: {
      		PostQuitMessage(0);
      		return 0;
      	}
        default:return DefWindowProc(hWnd,Msg,wParam,lParam);
   }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){
	std::read();
	std::init();
	WNDCLASSEX wcex;
	memset(&wcex,0,sizeof(wcex));
	wcex.cbSize=sizeof(wcex);
	wcex.lpfnWndProc=WndProc;
	wcex.hInstance=hInstance;
	wcex.hCursor=LoadCursor(NULL,IDC_ARROW);
	wcex.style=CS_DBLCLKS|CS_SAVEBITS|CS_GLOBALCLASS;
	wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszClassName="WindowClass";
	wcex.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	wcex.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
	if (!RegisterClassEx(&wcex)) {
		MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
	HWND hWnd = CreateWindowExA(WS_EX_ACCEPTFILES,"WindowClass", "Minesweeper",
  		WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		winx + 10,
		winy + 20,
		NULL, NULL, hInstance, NULL);
	if(!hWnd){
		MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
	long long num = 1;
	CreateWindow("Button", "Return", WS_VISIBLE | WS_CHILD, 10, 10, 70, 15, hWnd, (HMENU)0, hInstance, NULL);
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			button[i][j] = CreateWindow("Button", " ", WS_VISIBLE | WS_CHILD, 25 + j * 20, 55 + i * 20, 20, 20, hWnd, (HMENU)num, NULL, NULL);
			num++;
		}
	}
	ShowWindow(hWnd,nShowCmd);
	UpdateWindow(hWnd);
	MSG msg={};
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}

我把删除按钮的东西写成了函数~

我提前向winnum.data写入了9 9 230 260 1,这是Easy难度:

 

Middle难度:

Hard难度:

蹦篇文章结束啦~下期再见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值