温馨提示:本篇较长,如果没耐心,请勿观看!!!
嗨嗨嗨~~~我双缀缀的来啦~
今天先写游戏主体部分,不然难度选择连接不上。
首先呢,新建一个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难度:
蹦篇文章结束啦~下期再见~