WINAPI实现简易扫雷游戏
//扫雷
#include <windows.h>
#include <windowsx.h>
#include <strsafe.h>
#include <time.h>
//格子区域大小(DIVISIONS * DIVISIONS)
#define DIVISIONS 20
//地雷数
#define MINECOUNT 40 //消息处理
LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//写入地雷
void SetMine(int(*pChess)[DIVISIONS]);
//获取随机数
unsigned int GetRand();
//判断胜利
bool Win(int(*pChess)[DIVISIONS], int *pNotClickCount, int *pClickCount);
//重置
void Reset(HWND hWnd,int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS]); int WINAPI WinMain(
HINSTANCE hInstance, // 当前实例句柄
HINSTANCE hPrevInstance,// 前一实例句柄
LPSTR lpCmdLine, // 指向命令行参数的指针
int nCmdShow // 窗口的显示方式
)
{
HWND hWnd;
MSG msg;
WNDCLASS wndClass; wndClass.cbClsExtra = ;
wndClass.cbWndExtra = ;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//画刷背景BLACK_PEN
wndClass.hCursor = LoadCursor(NULL, IDI_APPLICATION);
wndClass.hIcon = LoadIcon(NULL, IDC_ARROW);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = DealMessage;
wndClass.lpszClassName = TEXT("MineSweeping");
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClass(&wndClass))
{
MessageBox(NULL, TEXT("注册类失败,请检查参数是否成功设置"), TEXT("提示"), MB_OK);
} hWnd = CreateWindow(TEXT("MineSweeping"), // 窗口类名称
TEXT("扫雷"), // 窗口标题栏名称
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, // 窗口水平位置
CW_USEDEFAULT, // 窗口垂直位置
CW_USEDEFAULT, // 窗口宽度
CW_USEDEFAULT, // 窗口高度
NULL, // 父窗口句柄
NULL, // 窗口菜单句柄
hInstance, // 窗口实例句柄
NULL); // 窗口创建参数
if (!hWnd) // 新窗口创建失败
{
return FALSE;
} ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//初始化要用到的变量
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
HBRUSH hBrush, hOldBrush;
TEXTMETRIC tm;
static int cxChar,cxCaps,cyChar; //输出整形用的缓冲区
TCHAR szBuffer[];
size_t iTarget; //地雷以及点击后雷个数存储区
static int iChess[DIVISIONS][DIVISIONS];
int(*pChess)[DIVISIONS] = iChess;
//点击后记录点击事件存储区
static int iClick[DIVISIONS][DIVISIONS];
int(*pClick)[DIVISIONS] = iClick; //pSize:当前一个格子宽高
//p:通用
static POINT pSize = { , }, p; //未点击的格子
int iNotClickCount;
//已经点击的格子
int iClickCount; switch (uMsg)
{
case WM_CREATE:
hdc = GetDC(hWnd);
//初始化
Reset(hWnd, pChess, pClick);
//获取字体高度
GetTextMetrics(hdc, &tm);
cyChar = tm.tmHeight + tm.tmExternalLeading;
//平均宽度
cxChar = tm.tmAveCharWidth;
//判断是否等宽字体
cxCaps = (tm.tmPitchAndFamily & ? : ) * cxChar / ; ReleaseDC(hWnd, hdc);
return ;
case WM_KEYDOWN:
//在没有鼠标的情况下,键盘模拟鼠标操作
ShowCursor(false);
GetCursorPos(&p);
ScreenToClient(hWnd, &p);
p.x = max(, min(DIVISIONS - , p.x / pSize.x));
p.y = max(, min(DIVISIONS - , p.y / pSize.y)); switch (wParam)
{
case VK_UP:
p.y = max(, p.y - );
break;
case VK_DOWN:
p.y = min(DIVISIONS - , p.y + );
break;
case VK_LEFT:
p.x = max(, p.x - );
break;
case VK_RIGHT:
p.x = min(DIVISIONS - , p.x + );
break;
case VK_HOME:
p.x = p.y = ;
break;
case VK_END:
p.x = p.y = DIVISIONS - ;
break;
case VK_RETURN:
case VK_SPACE:
SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(p.x * pSize.x, p.y * pSize.y));
break;
}
p.x = (p.x * pSize.x) + (pSize.x / );
p.y = (p.y * pSize.y) + (pSize.y / ); ClientToScreen(hWnd, &p);
SetCursorPos(p.x, p.y);
ShowCursor(true);
return ;
case WM_SIZE:
//pSize每个格子大小,X宽,Y高
pSize.x = LOWORD(lParam) / DIVISIONS;
pSize.y = HIWORD(lParam) / DIVISIONS;
return ;
case WM_LBUTTONDOWN:
p.x = GET_X_LPARAM(lParam) / pSize.x;
p.y = GET_Y_LPARAM(lParam) / pSize.y;
if (p.x >= DIVISIONS || p.y >= DIVISIONS)
{
MessageBeep();
return ;
}
switch (iClick[p.x][p.y])
{
case :
MessageBeep();
return ;
case :
MessageBeep();
return ;
}
//判断是否点击到了雷
if (iChess[p.x][p.y] == -)
{
if (MessageBox(hWnd, TEXT("你踩到地雷了!"), TEXT("boom"), MB_OK) == IDOK)
{
Reset(hWnd,pChess, pClick);
InvalidateRect(hWnd, NULL, true);
}
}
else
{
//当前位置修改为点击过
iClick[p.x][p.y] = ;
//判断是否胜利
if (Win(pClick, &iNotClickCount, &iClickCount))
{
if (MessageBox(hWnd, TEXT("you win!"), TEXT("wow"), MB_OK) == IDOK)
{
Reset(hWnd,pChess, pClick);
}
}
//标题显示信息
StringCchPrintf(szBuffer, sizeof(szBuffer), TEXT("当前已经翻出的区域数%d,剩余区域数%d"), iClickCount, iNotClickCount);
StringCchLength(szBuffer, sizeof(szBuffer), &iTarget);
SetWindowText(hWnd, szBuffer);
//计算当前点击位置附近雷数
for (int x = -; x <= ; x++)
{
for (int y = -; y <= ; y++)
{
//超出客户区的格子不计算
if (p.x + x >= && p.x + x < DIVISIONS && p.y + y >= && p.y + y < DIVISIONS)
{
//当前格子不计算
if (x != || y != ) {
if (iChess[p.x + x][p.y + y] == -)
{
iChess[p.x][p.y]++;
}
}
}
}
}
}
//重绘格子
rect.left = p.x * pSize.x;
rect.top = p.y * pSize.y;
rect.right = (p.x + ) * pSize.x;
rect.bottom = (p.y + ) * pSize.y;
InvalidateRect(hWnd, &rect, true);
return ;
case WM_RBUTTONDOWN:
p.x = GET_X_LPARAM(lParam) / pSize.x;
p.y = GET_Y_LPARAM(lParam) / pSize.y;
if (p.x >= DIVISIONS || p.y >= DIVISIONS)
{
MessageBeep();
return ;
}
//当前格子标记地雷状态切换
switch (iClick[p.x][p.y])
{
case :
iClick[p.x][p.y] = ;
break;
case :
iClick[p.x][p.y] = ;
break;
default:
MessageBeep();
return ;
}
//重绘格子
rect.left = p.x * pSize.x;
rect.top = p.y * pSize.y;
rect.right = (p.x + ) * pSize.x;
rect.bottom = (p.y + ) * pSize.y;
InvalidateRect(hWnd, NULL, true);
return ;
case WM_MOUSEMOVE:
//鼠标超出扫雷区域鼠标禁止点击
p.x = GET_X_LPARAM(lParam) / pSize.x;
p.y = GET_Y_LPARAM(lParam) / pSize.y;
if (p.x >= DIVISIONS || p.y >= DIVISIONS)
{
SetCursor(LoadCursor(NULL, IDC_NO));
}
else
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
return ;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
//扫雷棋盘绘画
for (int i = ; i < DIVISIONS; i++)
{
MoveToEx(hdc, pSize.x * i, , NULL);
LineTo(hdc, pSize.x * i, pSize.y * DIVISIONS); MoveToEx(hdc, , pSize.y * i, NULL);
LineTo(hdc, pSize.x * DIVISIONS, pSize.y * i);
}
//扫雷点击绘画
for (int x = ; x < DIVISIONS; x++)
{
for (int y = ; y < DIVISIONS; y++)
{
//0:未开过的格子,1:开过的格子,2:标记过的格子
switch (iClick[x][y])
{
case :
//每个格子背景变成灰色
hBrush = CreateSolidBrush(RGB(, , ));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
Rectangle(hdc, x * pSize.x, y * pSize.y, (x + ) * pSize.x, (y + ) * pSize.y);
SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush); //当前格子写入地雷数
//地雷数颜色随地雷数改变
switch (iChess[x][y])
{
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
case :
SetTextColor(hdc, RGB(, , )); break;
}
SetBkMode(hdc, TRANSPARENT);
StringCchPrintf(szBuffer, sizeof(szBuffer), TEXT("%d"), iChess[x][y]);
StringCchLength(szBuffer, sizeof(szBuffer), &iTarget);
TextOut(hdc, (x * pSize.x) + (pSize.x / ) - (cxChar / ), (y * pSize.y) + (pSize.y / ) - (cyChar / ), szBuffer, iTarget);
break;
case :
hBrush = CreateSolidBrush(RGB(, , ));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
//画一个标记的旗子
MoveToEx(hdc, (x * pSize.x) + (pSize.x / ), (y * pSize.y) + (pSize.y / ) - , NULL);
LineTo(hdc, (x * pSize.x) + (pSize.x / ), (y * pSize.y) + (pSize.y / ) + ); Rectangle(hdc, (x * pSize.x) + (pSize.x / ), (y * pSize.y) + (pSize.y / ) - , (x * pSize.x) + (pSize.x / ) + , (y * pSize.y) + (pSize.y / )); MoveToEx(hdc, (x * pSize.x) + (pSize.x / ) - , (y * pSize.y) + (pSize.y / ) + , NULL);
LineTo(hdc, (x * pSize.x) + (pSize.x / ) + , (y * pSize.y) + (pSize.y / ) + ); SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush);
break;
} }
} EndPaint(hWnd, &ps);
return ;
case WM_DESTROY:
PostQuitMessage();
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return ;
}
//产生随机数
unsigned int GetRand()
{
int r = rand();
r = r % DIVISIONS;
return r;
} //写入地雷
void SetMine(int(*pChess)[DIVISIONS])
{
POINT pPoint;
for (int i = ; i < MINECOUNT; i++)
{
while (true)
{
pPoint.x = GetRand();
pPoint.y = GetRand();
if (pChess[pPoint.x][pPoint.y] != -)
{
pChess[pPoint.x][pPoint.y] = -;
break;
}
}
}
return;
} //判断获胜
bool Win(int(*pChess)[DIVISIONS],int *pNotClickCount,int *pClickCount)
{
int i = ;
for (int x = ; x < DIVISIONS; x++)
{
for (int y = ; y < DIVISIONS; y++)
{
if (pChess[x][y] == || pChess[x][y] == )
{
i++;
}
}
}
*pNotClickCount = i;
*pClickCount = (DIVISIONS * DIVISIONS) - i;
if (i - MINECOUNT == )
{
return true;
}
return false;
} //重置
void Reset(HWND hWnd,int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS])
{
//防止随机数重复
srand((unsigned(time(NULL))));
SetCursor(LoadCursor(NULL, IDC_WAIT));
for (int x = ; x < DIVISIONS; x++)
{
for (int y = ; y < DIVISIONS; y++)
{
pChess[x][y] = ;
pClick[x][y] = ;
}
}
//memset(pChess, 0, sizeof(pChess));
//memset(pClick, 0, sizeof(pClick));
SetMine(pChess);
SetCursor(LoadCursor(NULL, IDC_ARROW));
InvalidateRect(hWnd, NULL, true);
return;
}
WINAPI实现简易扫雷游戏的更多相关文章
- 洛谷 P2670 扫雷游戏==Codevs 5129 扫雷游戏
题目描述 扫雷游戏是一款十分经典的单机小游戏.在n行m列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格).玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有 ...
- 原生javascript扫雷游戏
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Java练习(模拟扫雷游戏)
要为扫雷游戏布置地雷,扫雷游戏的扫雷面板可以用二维int数组表示.如某位置为地雷,则该位置用数字-1表示, 如该位置不是地雷,则暂时用数字0表示. 编写程序完成在该二维数组中随机布雷的操作,程序读入3 ...
- JAVA_扫雷游戏(布置地雷)
1.要为扫雷游戏布置地雷,扫雷游戏的扫雷面板可以用二维int数组表示.如某位置为地雷,则该位置用数字-1表示, 如该位置不是地雷,则暂时用数字0表示. 编写程序完成在该二维数组中随机布雷的操作,程序读 ...
- [LeetCode] Minesweeper 扫雷游戏
Let's play the minesweeper game (Wikipedia, online game)! You are given a 2D char matrix representin ...
- C语言二维数组实现扫雷游戏
#include<stdio.h> //使用二维数组实现 扫雷 int main() { char ui[8][8]={ '+','+','+','+','+','+','+','+', ...
- 【Android】自己动手做个扫雷游戏
1. 游戏规则 扫雷是玩法极其简单的小游戏,点击玩家认为不存在雷的区域,标记出全部地雷所在的区域,即可获得胜利.当点击不包含雷的块的时候,可能它底下存在一个数,也可能是一个空白块.当点击中有数字的块时 ...
- C#编写扫雷游戏
翻看了下以前大学学习的一些小项目,突然发现有个项目比较有意思,觉得有必要把它分享出来.当然现在看来,里面有很多的不足之处,但因博主现在已经工作,没有时间再去优化.这个项目就是利用C#编写一个Windo ...
- [Swift]LeetCode529. 扫雷游戏 | Minesweeper
Let's play the minesweeper game (Wikipedia, online game)! You are given a 2D char matrix representin ...
随机推荐
- IOS xib和代码自定义UIView
https://www.jianshu.com/p/1bcc29653085 总结的比较好 iOS开发中,我们常常将一块View封装起来,以便于统一管理内部的子控件. 下面就来说说自定义View的封装 ...
- 【慕课网实战】Spark Streaming实时流处理项目实战笔记十九之铭文升级版
铭文一级:(没有内容) 铭文二级: 创建Spring boot项目: 看官网,Quick Start下面有两个依赖,必须得使用 但是如果用IDEA构建Spring boot,则会自动添加 New Pr ...
- jsp和html的区别
html是超文本标记语言,只要有浏览器,就可以显示出来了. jsp是java server page就是在java服务器端的页面,需要通过jdk的编译才可以显示在客户端的浏览器上. 不仅如此,jsp还 ...
- a标签使用href=”javascript:void(0); 在火狐浏览器跟chrome 不兼容
使用如下方式的链接.在Chrome中点击后行为符合预期,但在IE下会新开标签卡(根据参考资料,Firefox中有相同问题).<a href=”javascript:void(0);” targe ...
- 二叉搜索树的平衡--AVL树和树的旋转
二叉搜索树只有保持平衡时其查找效率才会高. 要保持二叉搜索树的平衡不是一件易事.不过还是有一些非常经典的办法可以做到,其中最好的方法就是将二叉搜索树实现为AVL树. AVL树得名于它的发明者 G.M. ...
- cad.net 更改高版本填充交互方式为低版本样子
/// <summary> /// 修改cui,双击填充 /// </summary> /// https://blog.csdn.net/hfmwu/article/deta ...
- xtrabackup备份mysql-2 增量备份
---恢复内容开始--- 增量备份 ---恢复内容结束---] 增量备份恢复 1,重演日志 2,恢复数据
- vue 自学笔记(4): 样式绑定与条件渲染
一:对象绑定 Vue 对于页面的样式加载也有独特的方式,按照 Vue 提供的方式,我们可以轻松的控制它们的呈现. 假使我们要实现点击 div 变色 Vue 提供的样式方案的本质是对元素节点进行属性的绑 ...
- 排名前 16 的 Java 工具类
在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码. 一. ...
- mongodb4.0.2 复制集主从部署
介绍 复制集(Replica Sets),是一个基于主/从复制机制的复制功能,进行同一数据的异步同步,从而使多台机器拥有同一数据的都多个副本,由于有自动故障转移和恢复特性,当主库宕机时不需要用户干预的 ...