这里只用了少数简单的gdi画图,来展示一下最近几个月学习win32的成果吧。。
下面展示代码,有哪里需要修改的可以指出来。
#include <windows.h>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cmath>
#define BLACK_PLAYER 0
#define RED_PLAYER 1
struct Point {
long x, y;
Point(long i=0,long j=0):x{i}, y{j} {}
bool operator==(const Point& p) const {return x==p.x && y==p.y;}
bool operator!=(const Point& p) const {return !(*this==p);}
bool operator<=(const Point& p) const {return x<=p.x && y<=p.y;}
bool operator>=(const Point& p) const {return x>=p.x && y>=p.y;}
};
struct Color {
static COLORREF black;
static COLORREF red;
};
COLORREF Color::black = RGB(0,0,0);
COLORREF Color::red = RGB(255,0,0);
struct ChessMan {
Point pt; //棋盘数组对应位置,并不是中心位置
int size;
std::string name;
COLORREF color;
bool isKill = false;
bool isFoucs = false;
ChessMan(const Point& p,const std::string& n,COLORREF col,int sz=25): pt{p}, name{n}, color{col}, size{sz} {}
bool operator==(const ChessMan& cm) const {return pt==cm.pt && name==cm.name && color==cm.color;}
Point getCenterPos(int rowLength,int colLength) const;
int getChessPlayer() const {return color==Color::black ? BLACK_PLAYER : RED_PLAYER;}
bool isAllow(std::vector<ChessMan>& vec, std::map<std::string,int>& paraMap, const Point& p, bool isChessMan=false) const;
private:
bool isAllowForCannon(std::vector<ChessMan>& vec, const Point& p,bool isChessMan) const;
bool isAllowForSoldier(const Point& p) const;
bool isAllowForVehicle(std::vector<ChessMan>& vec, const Point& p) const;
bool isAllowForHorse(std::vector<ChessMan>& vec, const Point& p) const;
bool isAllowForElephant(std::vector<ChessMan>& vec, const Point& p) const;
bool isAllowForBodyGuad(const Point& p) const;
bool isAllowForGeneral(const Point& p) const;
int getCountForTwoChessMan(std::vector<ChessMan>& vec,const Point& p) const; //获取两个棋子之间有多少个棋子
};
//辅助函数
bool isInChess(std::vector<ChessMan>& vec,const Point& p)
{
for(auto cm : vec)
{
if (cm.isKill) continue;
if (cm.pt == p)
return true;
}
return false;
}
Point ChessMan::getCenterPos(int rowLength,int colLength) const
{
return {25+pt.x*colLength, 25+pt.y*rowLength};
}
int ChessMan::getCountForTwoChessMan(std::vector<ChessMan>& vec,const Point& p) const
{
int cnt = 0;
if (pt.x == p.x) //同在垂直方向
{
for(auto cm : vec)
{
if (cm.pt.x == pt.x)
if (cm.pt.y>std::min(pt.y,p.y) && cm.pt.y<std::max(pt.y,p.y))
cnt++;
}
} else if (pt.y == p.y) { //同在水平方向
for(auto cm : vec)
{
if (cm.pt.y == pt.y)
if (cm.pt.x>std::min(pt.x,p.x) && cm.pt.x<std::max(pt.x,p.x))
cnt++;
}
}
return cnt;
}
//炮
bool ChessMan::isAllowForCannon(std::vector<ChessMan>& vec,const Point& p,bool isChessMan) const
{
bool bResult = true;
if (pt.x!=p.x && pt.y!=p.y) //必须水平或垂直在一条线上
bResult = false;
if (!isChessMan) { //没有棋子,则中间不能有别的棋子
if (getCountForTwoChessMan(vec, p) > 0) bResult = false;
} else {
if (getCountForTwoChessMan(vec, p) != 1) bResult = false; //中间只能有一个棋子
}
return bResult;
}
//兵
bool ChessMan::isAllowForSoldier(const Point& p) const
{
bool bResult = true;
if ((name == "兵" && p.y > pt.y) || (name == "卒" && p.y<pt.y)) //不能倒走
bResult = false;
if (std::abs(pt.x-p.x) !=1 && std::abs(pt.y-p.y) !=1) bResult = false;
//如果没有过河只能往前走
if (name == "兵" && pt.y>4 && pt.x!=p.x) bResult = false;
if (name == "卒" && pt.y<5 && pt.x!=p.x) bResult = false;
return bResult;
}
//车
bool ChessMan::isAllowForVehicle(std::vector<ChessMan>& vec, const Point& p) const
{
bool bResult = true;
if (pt.x!=p.x && pt.y!=p.y) //必须水平或垂直在一条线上
bResult = false;
if (getCountForTwoChessMan(vec, p) > 0) bResult = false; //中间不能有别的棋子
return bResult;
}
//马
bool ChessMan::isAllowForHorse(std::vector<ChessMan>& vec, const Point& p) const
{
bool bResult = true;
if (pow(std::abs(pt.x-p.x),2) + pow(std::abs(pt.y-p.y),2) !=5) bResult = false; //如果不是日
//是否憋马腿
if (p.x > pt.x)
{
if (p.y < pt.y)
{
if (pt.y-p.y == 2 && isInChess(vec, {pt.x,pt.y-1})) bResult = false;
if (pt.y-p.y == 1 && isInChess(vec, {pt.x+1,pt.y})) bResult = false;
} else {
if (p.y-pt.y == 2 && isInChess(vec, {pt.x,pt.y+1})) bResult = false;
if (p.y-pt.y == 1 && isInChess(vec, {pt.x+1,pt.y})) bResult = false;
}
} else {
if (p.y < pt.y)
{
if (pt.y-p.y == 2 && isInChess(vec, {pt.x,pt.y-1})) bResult = false;
if (pt.y-p.y == 1 && isInChess(vec, {pt.x-1,pt.y})) bResult = false;
} else {
if (p.y-pt.y == 2 && isInChess(vec, {pt.x,pt.y+1})) bResult = false;
if (p.y-pt.y == 1 && isInChess(vec, {pt.x-1,pt.y})) bResult = false;
}
}
return bResult;
}
//象
bool ChessMan::isAllowForElephant(std::vector<ChessMan>& vec, const Point& p) const
{
bool bResult = true;
if (std::abs(pt.x-p.x)!=2 || std::abs(pt.y-p.y)!=2) bResult = false; //如果不为田
if ((name=="象" && p.y>4) || (name=="相" && p.y<5)) bResult = false; //不能过河
//是否憋象腿
if (p.x > pt.x)
{
if (p.y < pt.y && isInChess(vec, {pt.x+1,pt.y-1})) bResult = false;
if (p.y > pt.y && isInChess(vec, {pt.x+1,pt.y+1})) bResult = false;
} else {
if (p.y < pt.y && isInChess(vec, {pt.x-1,pt.y-1})) bResult = false;
if (p.y > pt.y && isInChess(vec, {pt.x-1,pt.y+1})) bResult = false;
}
return bResult;
}
//士
bool ChessMan::isAllowForBodyGuad(const Point& p) const
{
bool bResult = true;
if (std::abs(pt.x-p.x)!=1 || std::abs(pt.y-p.y)!=1) bResult = false;
//是否在田字格内
if (color == Color::black)
{
if (p.x<3 || p.x>5 || p.y<0 || p.y>2) bResult = false;
} else {
if (p.x<3 || p.x>5 || p.y<7 || p.y>9) bResult = false;
}
return bResult;
}
//将
bool ChessMan::isAllowForGeneral(const Point& p) const
{
bool bResult = true;
if (pt.x!=p.x && pt.y!=p.y) bResult = false; //必须水平或垂直在一条线上
if (std::abs(pt.x-p.x) + std::abs(pt.y-p.y) !=1) bResult = false; //相隔为1
//是否在田字格内
if (color == Color::black)
{
if (p.x<3 || p.x>5 || p.y<0 || p.y>2) bResult = false;
} else {
if (p.x<3 || p.x>5 || p.y<7 || p.y>9) bResult = false;
}
return bResult;
}
bool ChessMan::isAllow(std::vector<ChessMan>& vec, std::map<std::string,int>& paraMap, const Point& p, bool isChessMan) const
{
if (pt == p) //目标棋子为源棋子
return false;
if (name == "炮")
return isAllowForCannon(vec, p, isChessMan);
else if (name == "兵" || name == "卒")
return isAllowForSoldier(p);
else if (name == "车")
return isAllowForVehicle(vec, p);
else if (name == "马")
return isAllowForHorse(vec, p);
else if (name == "象" || name == "相")
return isAllowForElephant(vec, p);
else if (name == "士")
return isAllowForBodyGuad(p);
else if (name == "将" || name == "帅")
return isAllowForGeneral(p);
}
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Chess");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(
szAppName, TEXT("Chess"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
850, 950,
NULL, NULL, hInstance, NULL
);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
HFONT GetFont(HDC hdc,LPCTSTR face,int width,int height,int angle)
{
HFONT hFont;
hFont = CreateFont(height, width, angle, 0, FW_REGULAR, FALSE, FALSE, FALSE, GB2312_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FIXED_PITCH|FF_MODERN, face);
return hFont;
}
void DrawChessBoard(HDC hdc,std::map<std::string,int>& paraMap)
{
int i;
RECT blackRect, redRect;
HFONT hPrevFont, hFont;
POINT pt[8];
HPEN hPrevPen, hPen;
std::vector<POINT> vec;
int iChessXStart = paraMap["iChessXStart"];
int iChessXEnd = paraMap["iChessXEnd"];
int iChessYStart = paraMap["iChessYStart"];
int iChessYEnd = paraMap["iChessYEnd"];
int iRow = paraMap["iRow"];
int iCol = paraMap["iCol"];
for(i=0; i<=9; i++)
{
MoveToEx(hdc, iChessXStart, iChessYStart+i*iRow, NULL);
LineTo(hdc, iChessXEnd, iChessYStart+i*iRow);
}
for(i=0; i<=8; i++)
{
MoveToEx(hdc, iChessXStart+i*iCol, iChessYStart, NULL);
LineTo(hdc, iChessXStart+i*iCol, iChessYEnd);
if (i!=0 && i!=8)
{
vec.push_back({iChessXStart+i*iCol, iChessYStart+4*iRow});
vec.push_back({iChessXStart+i*iCol, iChessYStart+5*iRow});
}
}
pt[0] = {iChessXStart+3*iCol, iChessYStart};
pt[1] = {iChessXStart+5*iCol, iChessYStart+2*iRow};
pt[2] = {iChessXStart+5*iCol, iChessYStart};
pt[3] = {iChessXStart+3*iCol, iChessYStart+2*iRow};
pt[4] = {iChessXStart+3*iCol, iChessYStart+7*iRow};
pt[5] = {iChessXStart+5*iCol, iChessYStart+9*iRow};
pt[6] = {iChessXStart+5*iCol, iChessYStart+7*iRow};
pt[7] = {iChessXStart+3*iCol, iChessYStart+9*iRow};
for(i=0; i<8; i+=2)
Polyline(hdc, pt+i, 2);
hPrevPen = (HPEN)SelectObject(hdc, GetStockObject(WHITE_PEN));
for(i=0; i<vec.size(); i+=2)
{
MoveToEx(hdc, vec[i].x, vec[i].y, NULL);
LineTo(hdc, vec[i+1].x, vec[i+1].y);
}
SelectObject(hdc, hPrevPen);
hFont = GetFont(hdc, TEXT("楷体"), 30, 30, 0);
hPrevFont = (HFONT)SelectObject(hdc, hFont);
SetRect(&blackRect, iChessXStart+0*iCol, iChessYStart+4*iRow, iChessXStart+4*iCol, iChessYStart+5*iRow);
SetRect(&redRect, iChessXStart+4*iCol, iChessYStart+4*iRow, iChessXStart+8*iCol, iChessYStart+5*iRow);
SetTextColor(hdc, Color::black);
DrawText(hdc, TEXT("楚河"), -1, &blackRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
SetTextColor(hdc, Color::red);
DrawText(hdc, TEXT("汉界"), -1, &redRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
SelectObject(hdc, hPrevFont);
DeleteObject(hFont); //删除GDI对象
}
void DrawSingleChessMan(HDC hdc,const ChessMan& cm,std::map<std::string,int>& paraMap)
{
RECT rect;
HFONT hPrevFont, hFont;
HBRUSH hPrevBrush, hBrush;
HPEN hPrevPen, hPen;
int focusSize = cm.size-5;
if (cm.isKill) return;
hBrush = CreateSolidBrush(RGB(239,215,189));
hFont = GetFont(hdc, TEXT("微软雅黑"), 10, 20, 0);
SetBkColor(hdc, RGB(239,215,189));
hPrevBrush = (HBRUSH)SelectObject(hdc, hBrush);
hPrevFont = (HFONT)SelectObject(hdc, hFont);
SetTextColor(hdc, cm.color);
Point center = cm.getCenterPos(paraMap["iRow"], paraMap["iCol"]);
Ellipse(hdc, center.x-cm.size, center.y-cm.size, center.x+cm.size, center.y+cm.size);
if (cm.isFoucs)
{
hPen = CreatePen(PS_SOLID, 1, cm.color);
hPrevPen = (HPEN)SelectObject(hdc, hPen);
Ellipse(hdc, center.x-focusSize, center.y-focusSize, center.x+focusSize, center.y+focusSize);
SelectObject(hdc, hPrevPen);
DeleteObject(hPen);
}
SetRect(&rect, center.x-cm.size, center.y-cm.size, center.x+cm.size, center.y+cm.size);
DrawText(hdc, TEXT(cm.name.c_str()), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, hPrevBrush);
SelectObject(hdc, hPrevFont);
DeleteObject(hBrush); //删除GDI对象
DeleteObject(hFont);
}
void DrawChessMan(HDC hdc,std::map<std::string,int>& paraMap,std::vector<ChessMan>& vec)
{
for(int i=0; i<vec.size(); i++)
DrawSingleChessMan(hdc, vec[i], paraMap);
}
void DrawChess(HDC hdc,std::map<std::string,int>& paraMap,std::vector<ChessMan>& vec)
{
DrawChessBoard(hdc, paraMap);
DrawChessMan(hdc, paraMap, vec);
}
int getChessManByPos(std::vector<ChessMan>& vec, const Point& pt, std::map<std::string,int>& paraMap)
{
for(int i=0; i<vec.size(); i++)
{
if (vec[i].isKill)
continue;
Point center = vec[i].getCenterPos(paraMap["iRow"], paraMap["iCol"]);
Point leftTop{center.x-vec[i].size, center.y-vec[i].size};
Point rightBottom{center.x+vec[i].size, center.y+vec[i].size};
if (pt>=leftTop && pt<=rightBottom)
return i;
}
return -1;
}
void fillParam(std::map<std::string,int>& paraMap,int cxClient,int cyClient)
{
int iChessXStart = 25;
int iChessXEnd = cxClient - 25;
int iChessYStart = 25;
int iChessYEnd = cyClient - 25;
int iRow = (iChessYEnd-iChessYStart) / 9;
int iCol = (iChessXEnd-iChessXStart) / 8;
paraMap["iChessXStart"] = iChessXStart;
paraMap["iChessXEnd"] = iChessXEnd;
paraMap["iChessYStart"] = iChessYStart;
paraMap["iChessYEnd"] = iChessYEnd;
paraMap["iRow"] = iRow;
paraMap["iCol"] = iCol;
paraMap["cxClient"] = cxClient;
paraMap["cyClient"] = cyClient;
}
Point getChessManPos(std::map<std::string,int>& paraMap,const Point& pt)
{
int x = (pt.x-paraMap["iChessXStart"])/paraMap["iCol"];
int y = (pt.y-paraMap["iChessYStart"])/paraMap["iRow"];
Point leftTop{paraMap["iChessXStart"]+x*paraMap["iCol"], paraMap["iChessYStart"]+y*paraMap["iRow"]};
Point rightTop{paraMap["iChessXStart"]+(x+1)*paraMap["iCol"], paraMap["iChessYStart"]+y*paraMap["iRow"]};
Point leftBottom{paraMap["iChessXStart"] + x*paraMap["iCol"], paraMap["iChessYStart"] + (y+1)*paraMap["iRow"]};
Point rightBottom{paraMap["iChessXStart"]+(x+1)*paraMap["iCol"], paraMap["iChessYStart"]+(y+1)*paraMap["iRow"]};
if (pt.x>=leftTop.x && pt.x<=leftTop.x+25 && pt.y>=leftTop.y && pt.y<=leftTop.y+25)
return {x, y};
else if (pt.x>=rightTop.x-25 && pt.x<=rightTop.x && pt.y>=rightTop.y && pt.y<=rightTop.y+25)
return {x+1, y};
else if (pt.x>=leftBottom.x && pt.x<=leftBottom.x+25 && pt.y>=leftBottom.y-25 && pt.y<=leftBottom.y)
return {x, y+1};
else if (pt.x>=rightBottom.x-25 && pt.x<=rightBottom.x && pt.y>=rightBottom.y-25 && pt.y<=rightBottom.y)
return {x+1, y+1};
else
return {-1,-1};
}
std::vector<ChessMan> getChessMen()
{
return std::vector<ChessMan> {
{{0,0},"车",Color::black}, {{1,0},"马",Color::black}, {{2,0},"象",Color::black}, {{3,0},"士",Color::black},
{{8,0},"车",Color::black}, {{7,0},"马",Color::black}, {{6,0},"象",Color::black}, {{5,0},"士",Color::black},
{{4,0},"将",Color::black},
{{1,2},"炮",Color::black}, {{7,2},"炮",Color::black},
{{0,3},"卒",Color::black}, {{2,3},"卒",Color::black}, {{4,3},"卒",Color::black},
{{6,3},"卒",Color::black}, {{8,3},"卒",Color::black},
{{0,9},"车",Color::red}, {{1,9},"马",Color::red}, {{2,9},"相",Color::red}, {{3,9},"士",Color::red},
{{8,9},"车",Color::red}, {{7,9},"马",Color::red}, {{6,9},"相",Color::red}, {{5,9},"士",Color::red},
{{4,9},"帅",Color::red},
{{1,7},"炮",Color::red}, {{7,7},"炮",Color::red},
{{0,6},"兵",Color::red}, {{2,6},"兵",Color::red}, {{4,6},"兵",Color::red},
{{6,6},"兵",Color::red}, {{8,6},"兵",Color::red}
};
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int currentChessPlayer = RED_PLAYER;
static int cxClient, cyClient, idFoucs=-1;
static std::map<std::string,int> paraMap;
static std::vector<ChessMan> vec{getChessMen()};
int iChessXStart, iChessXEnd, iChessYStart, iChessYEnd, iRow, iCol;
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
RECT rect;
int i, currentId;
Point pt;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rect);
cxClient = rect.right - rect.left;
cyClient = rect.bottom - rect.top;
ReleaseDC(hwnd, hdc);
fillParam(paraMap, cxClient, cyClient);
return 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
fillParam(paraMap, cxClient, cyClient);
return 0;
case WM_LBUTTONDOWN:
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (idFoucs != -1) //已选中棋子
{
currentId = getChessManByPos(vec, pt, paraMap);
if (currentId == -1) { //空白地方
pt = getChessManPos(paraMap, pt);
if (pt == Point{-1,-1})
MessageBeep(0);
else {
if (vec[idFoucs].isAllow(vec,paraMap,pt))
{
vec[idFoucs].pt = pt;
currentChessPlayer ^= 1;
} else
MessageBeep(0);
vec[idFoucs].isFoucs = false;
idFoucs = -1;
}
} else {
if (vec[idFoucs].color == vec[currentId].color) //己方
{
if (idFoucs == currentId)
{
vec[idFoucs].isFoucs = false;
idFoucs = -1;
}
else
MessageBeep(0);
}
else { //对方
if (vec[idFoucs].isAllow(vec,paraMap,vec[currentId].pt,true))
{
vec[currentId].isKill = true;
vec[idFoucs].pt = vec[currentId].pt;
currentChessPlayer ^= 1;
if (vec[currentId].name == "将" || vec[currentId].name == "帅")
{
InvalidateRect(hwnd, NULL, TRUE);
MessageBeep(0);
if (vec[idFoucs].color == Color::red)
MessageBox(hwnd, TEXT("红方获胜!"), TEXT("结果"), MB_OK);
else
MessageBox(hwnd, TEXT("黑方获胜!"), TEXT("结果"), MB_OK);
//复原棋盘
vec = getChessMen();
}
} else
MessageBeep(0);
vec[idFoucs].isFoucs = false;
idFoucs = -1;
}
}
} else {
currentId = getChessManByPos(vec, pt, paraMap);
if (currentId == -1)
MessageBeep(0);
else {
if (vec[currentId].getChessPlayer() == currentChessPlayer)
{
idFoucs = currentId;
vec[idFoucs].isFoucs = true;
} else
MessageBeep(0);
}
}
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
DrawChess(hdc, paraMap, vec);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}