下载自
http://www.newxing.com/Code/VC/game/1750.html
运行界面如下;
看下类图;
资源;
主对话框;
源码说明:
本人机对弈程序采用了多种搜索算法.以下是本程序主要的类说明:
1.CEveluation类:估值类,对给定的棋盘进行估值.
2.CMoveGenerator类:走法产生器,对给定的棋盘局面搜索出所有可能的走法.
3.CSearchEngine类:搜索引擎基类.
4.CNegaMaxEngine类:负极大值法搜索引擎.
5.CAlphaBetaEngine类:采用了Alpha-Beta剪枝技术的搜索引擎.
6.CFAlphaBetaEngine类:fail-softalpha-beta搜索引擎.
7.CHistoryHeuristic类:历史启发类.
8.CAlphabeta_HHEngine类:带历史启发的Alpha-Beta搜索引擎.
9.CAspirationSearch类:渴望搜索引擎.
10.CIDAlphabetaEngine类:迭代深化搜索引擎.
11.CMTD_fEngine类:MTD(f)搜索引擎.
12.CTranspositionTable类:置换表.
13.CAlphaBeta_TTEngine类:加置换表的Alpha-Beta搜索引擎.
14.CPVS_Engine类:极小窗口搜索引擎.
15.CNegaScout_TT_HH类:使用了置换表和历史启发的NegaScout搜索引擎.
本程序还具有悔棋,还原功能,还可以记录走法.
那么该源码可以参照写各类搜索引擎;
下面看下其部分代码;主对话框类;
可以看到最多的函数类别是各种菜单的消息映射函数;如下;
BEGIN_MESSAGE_MAP(CChessDlg, CDialog) //{{AFX_MSG_MAP(CChessDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_COMMAND(IDM_SETCHESSBOARD, OnSetchessboard) ON_COMMAND(IDM_SET, OnSet) ON_COMMAND(IDM_ABOUT, OnAbout) ON_COMMAND(IDM_OPENFILE, OnOpenfile) ON_COMMAND(IDM_SAVEFILE, OnSavefile) ON_COMMAND(IDM_SCBOVER, OnScbover) ON_COMMAND(IDM_RPAWN, OnRpawn) ON_COMMAND(IDM_RCANON, OnRcanon) ON_COMMAND(IDM_RCAR, OnRcar) ON_COMMAND(IDM_RHORSE, OnRhorse) ON_COMMAND(IDM_RELEPHANT, OnRelephant) ON_COMMAND(IDM_RBISHOP, OnRbishop) ON_COMMAND(IDM_RKING, OnRking) ON_COMMAND(IDM_BPAWN, OnBpawn) ON_COMMAND(IDM_BCANON, OnBcanon) ON_COMMAND(IDM_BCAR, OnBcar) ON_COMMAND(IDM_BHORSE, OnBhorse) ON_COMMAND(IDM_BELEPHANT, OnBelephant) ON_COMMAND(IDM_BBISHOP, OnBbishop) ON_COMMAND(IDM_BKING, OnBking) ON_COMMAND(IDM_DELETE, OnDelete) ON_WM_RBUTTONDOWN() ON_WM_LBUTTONDBLCLK() ON_WM_CLOSE() ON_COMMAND(IDM_CLEARCB, OnClearcb) ON_COMMAND(IDM_NEWGAME, OnNewgame) ON_BN_CLICKED(IDC_BTNCOMPUTER, OnBtncomputer) ON_BN_CLICKED(IDC_BTNUNDO, OnBtnundo) ON_BN_CLICKED(IDC_BTNREDO, OnBtnredo) ON_LBN_DBLCLK(IDC_LISTCHESSRECORD, OnDblclkListchessrecord) ON_BN_CLICKED(IDC_BTN_STOP, OnBtnStop) ON_LBN_SELCHANGE(IDC_LISTCHESSRECORD, OnSelchangeListchessrecord) ON_COMMAND(IDM_PREVIEW, OnPreview) ON_COMMAND(IDM_PREVIEWOVER, OnPreviewover) ON_COMMAND(IDM_HELP, OnHelp) ON_COMMAND(IDM_INVERSECB, OnInversecb) //}}AFX_MSG_MAP END_MESSAGE_MAP()
在对话框初始化函数中载入棋盘;
BITMAP BitMap; m_BoardBmp.LoadBitmap(IDB_CHESSBOARD); m_BoardBmp.GetBitmap(&BitMap); //取BitMap 对象 m_nBoardWidth=BitMap.bmWidth; //棋盘宽度 m_nBoardHeight=BitMap.bmHeight;//棋盘高度
如上;棋盘是一个位图;
在对话框构造函数中创建三个重要的用于思考的对象:
m_pSE=new CNegaMaxEngine;//创建负极大值搜索引擎 m_pMG=new CMoveGenerator;//创建走法产生器 m_pEvel=new CEveluation; //创建估值核心
鼠标左键弹起时将用户走法压栈;也就是说用户走法用栈存储;
//---------将用户走法压栈--------- m_cmBestMove.From.x=m_ptMoveChess.x; m_cmBestMove.From.y=m_ptMoveChess.y; m_cmBestMove.To.x=x; m_cmBestMove.To.y=y; m_cmBestMove.nChessID=m_MoveChess.nChessID; m_umUndoMove.cmChessMove=m_cmBestMove; m_umUndoMove.nChessID=m_byChessBoard[y][x]; m_stackUndoMove.push(m_umUndoMove); //--------------------------------
......
typedef struct
{
short nChessID; //表明是什么棋子
CHESSMANPOS From;//起始位置
CHESSMANPOS To; //走到什么位置
int Score; //走法的分数
}CHESSMOVE;
......
CHESSMOVE m_cmBestMove;
CHESSMOVE是一个结构体;
......
stack<UNDOMOVE> m_stackUndoMove;//记录走法的栈,便于悔棋
......
m_stackUndoMove是一个栈;
走法产生器;
根据不同的子判断落点是否符合中国象棋规则;例如象,往四个方向走田字:
void CMoveGenerator::Gen_ElephantMove(BYTE position[10][9],int i,int j,int nPly) { int x,y; //插入右下方的有效走法 x=j+2; y=i+2; if(x<9 && y<10 && IsValidMove(position,j,i,x,y,m_nUserChessColor)) AddMove(j,i,x,y,nPly,position[i][j]); //插入右上方的有效走法 x=j+2; y=i-2; if(x<9 && y>=0 && IsValidMove(position,j,i,x,y,m_nUserChessColor)) AddMove(j,i,x,y,nPly,position[i][j]); //插入左下方的有效走法 x=j-2; y=i+2; if(x>=0 && y<10 && IsValidMove(position,j,i,x,y,m_nUserChessColor)) AddMove(j,i,x,y,nPly,position[i][j]); //插入左上方的有效走法 x=j-2; y=i-2; if(x>=0 && y>=0 && IsValidMove(position,j,i,x,y,m_nUserChessColor)) AddMove(j,i,x,y,nPly,position[i][j]); }
搜索引擎基类;其他搜索引擎继承此类;
//Download by http://www.NewXing.com // SearchEngine.h: interface for the CSearchEngine class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_SEARCHENGINE_H__7A7237B9_0908_45D8_B102_94E342B174A5__INCLUDED_) #define AFX_SEARCHENGINE_H__7A7237B9_0908_45D8_B102_94E342B174A5__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "Eveluation.h" #include "MoveGenerator.h" #include "GradientProgressCtrl.h" //搜索引擎的基类 class CSearchEngine { public: CSearchEngine(); virtual ~CSearchEngine(); public: virtual SearchAGoodMove(BYTE position[10][9])=0; //走下一步 CHESSMOVE GetBestMove(){return m_cmBestMove;}; //得到最佳走法 UNDOMOVE GetUndoMove(){return m_umUndoMove;}; //得到悔棋走法 void SetSearchDepth(int nDepth){m_nSearchDepth=nDepth;};//设定最大搜索深度 void SetEveluator(CEveluation* pEval){m_pEval=pEval;}; //设定估值引擎 void SetMoveGenerator(CMoveGenerator* pMG){m_pMG =pMG;};//设定走法产生器 void SetThinkProgress(CGradientProgressCtrl* pThinkProgress){m_pThinkProgress=pThinkProgress;}; //设定显示思考进度的进度条 void SetUserChessColor(int nUserChessColor){m_nUserChessColor=nUserChessColor;}; //设定用户为黑方或红方 void UndoChessMove(BYTE position[10][9],CHESSMOVE* move,BYTE nChessID);//悔棋 void RedoChessMove(BYTE position[10][9],CHESSMOVE* move); //还原 protected: int IsGameOver(BYTE position[10][9],int nDepth);//判断是否已分胜负 BYTE MakeMove(CHESSMOVE* move); //根据某一走法产生走了之后的棋盘 void UnMakeMove(CHESSMOVE* move,BYTE nChessID); //恢复为走过之前的棋盘 public: int m_nUserChessColor; protected: CGradientProgressCtrl* m_pThinkProgress; //用以显示思考进度的进度条指针 BYTE CurPosition[10][9]; //搜索时用于记录当前节点棋盘状态的数组 CHESSMOVE m_cmBestMove; //记录最佳走法 UNDOMOVE m_umUndoMove; CMoveGenerator* m_pMG; //走法产生器 CEveluation* m_pEval; //估值核心 int m_nSearchDepth; //最大搜索深度 int m_nMaxDepth; //当前搜索的最大搜索深度 }; #endif // !defined(AFX_SEARCHENGINE_H__7A7237B9_0908_45D8_B102_94E342B174A5__INCLUDED_)
CoolButton类;用于绘制主界面左下角的四个按钮;可以参照写自己的cool 按钮类;
//Download by http://www.NewXing.com // CoolButton.cpp : implementation file // #include "stdafx.h" #include "CoolButton.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CCoolButton CCoolButton::CCoolButton() { #ifdef XS_FLAT_BUTTON m_MouseOnButton=FALSE; #endif m_hIcon=NULL; m_cyIcon=0; m_cxIcon=0; } CCoolButton::~CCoolButton() { } BEGIN_MESSAGE_MAP(CCoolButton, CButton) //{{AFX_MSG_MAP(CCoolButton) ON_WM_MOUSEMOVE() ON_WM_KILLFOCUS() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CCoolButton message handlers void CCoolButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) { // TODO: Add your code to draw the specified item CDC * pDC=CDC::FromHandle(lpDIS->hDC); unsigned int IsPressed =(lpDIS->itemState&ODS_SELECTED); unsigned int IsFocused =(lpDIS->itemState&ODS_FOCUS); unsigned int IsDisabled =(lpDIS->itemState&ODS_DISABLED); CRect itemRect = lpDIS->rcItem; #ifndef XS_FLAT_BUTTON if(IsFocused) { CBrush br(RGB(0,0,0)); pDC->FrameRect(&itemRect,&br); itemRect.DeflateRect(1,1); } #endif //Fill with bkcolor CBrush br(GetSysColor(COLOR_BTNFACE)); pDC->FillRect(&itemRect,&br); //Is pressed? if(IsPressed) { #ifdef XS_FLAT_BUTTON //浅边界笔 CPen penBtnHiLight(PS_SOLID,0,GetSysColor(COLOR_BTNHILIGHT)); //阴影笔 CPen penBtnShadow(PS_SOLID,0,GetSysColor(COLOR_BTNSHADOW)); //绘边界阴影 pDC->SelectObject(penBtnShadow); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.left,itemRect.top); pDC->LineTo(itemRect.right,itemRect.top); //绘浅边界 pDC->SelectObject(penBtnHiLight); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.top-1); #else CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW)); pDC->FrameRect(&itemRect,&brBtnShadow); #endif } else//没按下 { CPen penBtnHiLight(PS_SOLID,0,GetSysColor(COLOR_BTNHILIGHT)); CPen pen3DLight(PS_SOLID,0,GetSysColor(COLOR_3DLIGHT)); CPen penBtnShadow(PS_SOLID,0,GetSysColor(COLOR_BTNSHADOW)); CPen pen3DDKShadow(PS_SOLID,0,GetSysColor(COLOR_3DDKSHADOW)); #ifdef XS_FLAT_BUTTON if(m_MouseOnButton==TRUE) { pDC->SelectObject(penBtnHiLight); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.left,itemRect.top); pDC->LineTo(itemRect.right,itemRect.top); // pDC->SelectObject(penBtnShadow); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.top-1); } #else pDC->SelectObject(penBtnHiLight); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.left,itemRect.top); pDC->LineTo(itemRect.right,itemRect.top); pDC->SelectObject(pen3DLight); pDC->MoveTo(itemRect.left+1,itemRect.bottom-1); pDC->LineTo(itemRect.left+1,itemRect.top+1); pDC->LineTo(itemRect.right,itemRect.top+1); pDC->SelectObject(pen3DDKShadow); pDC->MoveTo(itemRect.left,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.bottom-1); pDC->LineTo(itemRect.right-1,itemRect.top-1); pDC->SelectObject(penBtnShadow); pDC->MoveTo(itemRect.left+1,itemRect.bottom-2); pDC->LineTo(itemRect.right-2,itemRect.bottom-2); pDC->LineTo(itemRect.right-2,itemRect.top); #endif } #ifndef XS_FLAT_BUTTON // if(IsFocused) { CRect focusRect = itemRect; focusRect.DeflateRect(3,3); pDC->DrawFocusRect(&focusRect); } #endif //获取文本 CString title; GetWindowText(title); //绘制图标 if(m_hIcon!=NULL) { CRect iconRect = lpDIS->rcItem; //根据标题是否存在来设置不同的图标位置 if(title.IsEmpty()==TRUE) { iconRect.left+=((iconRect.Width()-m_cxIcon)/2); } else { iconRect.left+=6; } iconRect.top+=((iconRect.Height()-m_cyIcon)/2); if(IsPressed)iconRect.OffsetRect(1,1); pDC->DrawIcon(iconRect.TopLeft(),m_hIcon); } //绘标题 CRect captionRect = lpDIS->rcItem; captionRect.left+=m_cxIcon; if(title.IsEmpty()==FALSE) { pDC->SetBkMode(TRANSPARENT); captionRect.OffsetRect(0,-1); if(IsPressed) captionRect.OffsetRect(1,1); if(IsDisabled) { captionRect.OffsetRect(1,1); pDC->SetTextColor(GetSysColor(COLOR_BTNHILIGHT)); pDC->DrawText(title,-1,captionRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); captionRect.OffsetRect(-1,-1); pDC->SetTextColor(GetSysColor(COLOR_BTNSHADOW)); pDC->DrawText(title,-1,captionRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); } else { pDC->DrawText(title,-1,captionRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); } } } #ifdef XS_FLAT_BUTTON void CCoolButton::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CWnd* pWnd; CWnd* pParent; CButton::OnMouseMove(nFlags, point); pWnd=GetActiveWindow(); pParent=GetOwner(); if((m_MouseOnButton==FALSE)&&(GetCapture()!=this)&& ((pWnd!=NULL)&&(pWnd->m_hWnd==pParent->m_hWnd))) { SetCapture(); SetFocus(); m_MouseOnButton=TRUE; Invalidate(); UpdateWindow(); } else { CRect rc; GetClientRect(&rc); if(!rc.PtInRect(point)) { m_MouseOnButton=FALSE; Invalidate(); UpdateWindow(); ReleaseCapture(); } } } #endif #ifdef XS_FLAT_BUTTON void CCoolButton::OnKillFocus(CWnd* pNewWnd) { CButton::OnKillFocus(pNewWnd); // TODO: Add your message handler code here if(m_MouseOnButton==TRUE) { m_MouseOnButton=FALSE; Invalidate(); UpdateWindow(); } } #endif void CCoolButton::SetIcon(HICON hIcon, BYTE cx, BYTE cy) { m_hIcon = hIcon; m_cxIcon = cx; m_cyIcon = cy; } BOOL CCoolButton::SubclassDlgItem(UINT nID, CWnd *pParent) { BOOL retValue=CButton::SubclassDlgItem(nID,pParent); LONG bs=::GetWindowLong(m_hWnd,GWL_STYLE); bs|=BS_OWNERDRAW; ::SetWindowLong(m_hWnd,GWL_STYLE,bs); return retValue; }
余下的有空再分析吧;
搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。
搜索算法实际上是根据初始条件和扩展规则构造一棵“解答树”并寻找符合目标状态的节点的过程。
Minimax算法又名极小化极大算法,是一种找出失败的最大可能性中的最小值的算法。Minimax算法常用于棋类等由两方较量的游戏和程序,这类程序由两个游戏者轮流,每次执行一个步骤。我们众所周知的五子棋、象棋等都属于这类程序,所以说Minimax算法是基于搜索的博弈算法的基础。该算法是一种零总和算法,即一方要在可选的选项中选择将其优势最大化的选择,而另一方则选择令对手优势最小化的方法。