C++面向对象课程设计报告
院(系): 计算机工程学院 专业: 软件 学生姓名: xxx 班级:_XXX_学号: XXX
题目: 中国象棋程序设计
起迄日期: 2011.06.30————2011.07.13
设计地点: 现代教育中心机房 指 导 教 师: XXXX
完成日期: 2010 年7 月13 日
一、 课程设计目的
面向对象程序设计作为一门软件设计的课程,具有极强的实践性,要求学生具备灵活应用理论知识的能力及面向对象程序设计技能的基础。所以在《C++面向对象程序设计》课程学习完成后,安排课程设计教学环节。
通过课程设计,学生能了解C++面向对象的设计方法与技巧,有效地、深刻地理解课程内容,体会理论、方法和设计原则;培养分析实际问题和解决问题的能力,具备使用面向对象程序设计开发工具设计实际系统的能力。
通过这次课程设计,能了解并通过自学MFC,掌握一种可视化编程的方法,并通过实践加深对可视化编程与C++面向对象程序设计语言特点的认识与理解。同时,可以提高运用C++编程语言解决实际问题的能力;提高调查研究、查阅技术文献、资料以及编写软件设计文档的能力。
二、 课程设计内容与实现的功能
课程设计的题目是中国象棋,所实现的功能是能画出象棋棋盘,在此基础上实现人机对弈。对弈中符合现实中下棋的规则情况。
三、 系统分析与设计
1、系统分析
中国象棋具有悠久的历史,几千年以来,一直被认为是人们所热爱的休闲活动之一。象棋具有独特的规则,主要为车行直路,马走狭日,相行田,士相不离老王边,炮翻山,兵、卒只进不退,过河后方能左右行。程序设计就是把现实中的人与人之间的对弈,改成电脑与人的对弈。电脑的形为仿照人的形为,正确的走棋,“思考”,下棋。任务要求,画出棋盘,实现简单的人机对弈。 2、系统设计:
概要设计
1、数据表示:主要包括棋盘的表示和棋子的表示。
2、走法的产生:包括车马相士炮等的基本走法和检测是否将军,或被将死,还包括检 查一种棋子是否还有剩余,判断走法是否和法以及枚举可能有的合法的走法。
3、搜索引擎:搜索棋子可能有的走法,并且产生最佳的走法。搜索引擎包括许多算法,如极大极小值、负极大值、深度优先搜索、置换表、历史启发等等搜索算法。
4、估值方法:即对棋子的价值进行评估,如兵为300,车为1000,将帅位无限大等;对棋子的位置进行评估,如过河后越逼近九宫的卒/兵价值越大;对棋子的灵活性进行评估,灵活性越大,价值越大,如一般车的灵活性大于卒等;对棋子间关系进行评估,即棋子受到的威胁越大,价值越低,受到的保护越多,价值越高;还要与搜索算法相结合。
5、界面设计:主要有棋盘、棋子、拖动实现走棋、悔棋、还原、搜素引擎(包括算法和搜索深度)设置等。一些美化可以直接使用网络上的美化资源,消息响应函数与处理函数等的实现。 详细设计
1棋盘 BITMAP BitMap; m_BoardBmp.LoadBitmap(IDB_CHESSBOARD);
m_BoardBmp.GetBitmap(&BitMap); //取BitMap 对象 m_nBoardWidth=BitMap.bmWidth; //棋盘宽度 m_nBoardHeight=BitMap.bmHeight;//棋盘高度 m_BoardBmp.DeleteObject(); memcpy(m_byChessBoard,InitChessBoard,90);//初始化棋盘 memcpy(m_byShowChessBoard,InitChessBoard,90); memcpy(m_byBackupChessBoard,InitChessBoard,90); m_pSE->SetSearchDepth(3); //设定搜索层数为3 m_pSE->SetMoveGenerator(m_pMG);//给搜索引擎设定走法产生器 m_pSE->SetEveluator(m_pEvel); //给搜索引擎设定估值核心 m_pSE->SetUserChessColor(m_nUserChessColor); //设定用户为黑方或红方 m_pSE->SetThinkProgress(&m_progressThink); //设定进度条 m_MoveChess.nChessID=NOCHESS;//将移动的棋子清空 return TRUE; // return TRUE unless you set the focus to a control 2颜色 public: void SetTextColor(COLORREF color){m_clrText=color;} void SetBkColor(COLORREF color){m_clrBkGround=color;} void SetStartColor(COLORREF color){m_clrStart=color;} void SetEndColor(COLORREF color){m_clrEnd=color;} void ShowPercent(BOOL bShowPercent=TRUE){m_bShowPercent=bShowPercent;} void ShowText(CString str,bool bIsShowText){m_strShow=str,m_bIsShowText=bIsShowText;};
COLORREF GetTextColor(void){return m_clrText;} COLORREF GetBkColor(void){return m_clrBkGround;} COLORREF GetStartColor(void){return m_clrStart;} COLORREF GetEndColor(void){return m_clrEnd;} int SetPos(int nPos); int SetStep(int nStep); int StepIt(); void SetRange(int nLower,int nUpper); 3棋子走法
CString CChessDlg::GetMoveStr(int nFromX,int nFromY,int nToX,int nToY,int nSourceID)
{
Y-1)
CString str; bool bIsAgain; int i;
int nCount; int nPos[5]; int j=0;
switch(nSourceID) {
case B_KING://黑将 if(nFromY==nToY) { str.Format(\"黑:将%d平%d\ break; } if(nFromY>nToY) str.Format(\"黑:将%d退%d\ else str.Format(\"黑:将%d进%d\ break;
case B_CAR://黑车 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==B_CAR && i!=nFromY-1 && i!=nTo
{ bIsAgain=true; break; }
if(nFromY>nToY) { if(bIsAgain) { if(i>nFromY-1) str.Format(\"黑:后车进%d\ else str.Format(\"黑:前车进%d\ } else str.Format(\"黑:车%d退%d\
ToY-1)
} else if(nFromY case B_HORSE://黑马 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==B_HORSE && i!=nFromY-1 && i!=n { bIsAgain=true; break; } if(bIsAgain) { if(i>nFromY-1) nToY-1) { if(nFromY>nToY) str.Format(\"黑:后马退%d\ else str.Format(\"黑:后马进%d\ } else { if(nFromY>nToY) str.Format(\"黑:前马退%d\ else str.Format(\"黑:前马进%d\ } } else { if(nFromY>nToY) str.Format(\"黑:马%d退%d\ else str.Format(\"黑:马%d进%d\ } break; case B_CANON://黑炮 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==B_CANON && i!=nFromY-1 && i!= { bIsAgain=true; break; } if(nFromY>nToY) { if(bIsAgain) { if(i>nFromY-1) str.Format(\"黑:后炮进%d\ else str.Format(\"黑:前炮进%d\ } else str.Format(\"黑:炮%d退%d\ } else if(nFromY str.Format(\"黑:士%d进%d\ break; case B_ELEPHANT://黑象 bIsAgain=false; for(i=0;i<5;i++) if(m_byChessBoard[i][nFromX-1]==B_ELEPHANT && i!=nFromY-1 && i!=nToY-1) { bIsAgain=true; break; } if(bIsAgain) { if(i>nFromY-1) { if(nFromY>nToY) str.Format(\"黑:后象退%d\ else str.Format(\"黑:后象进%d\ } else { if(nFromY>nToY) str.Format(\"黑:前象退%d\ else str.Format(\"黑:前象进%d\ } } else { if(nFromY>nToY) str.Format(\"黑:象%d退%d\ else str.Format(\"黑:象%d进%d\ } break; case B_PAWN://黑卒 nCount=0; j=0; ToY-1) for(i=0;i<5;i++) nPos[i]=-1; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==B_PAWN && i!=nFromY-1 && i!=n { nPos[j]=i; nCount++; } if(nCount==0) { if(nFromY==nToY) str.Format(\"黑:卒%d平%d\ else str.Format(\"黑:卒%d进%d\ break; } if(nCount==1) { if(nFromY>nPos[0]) { if(nFromY==nToY) str.Format(\"黑:前卒平%d\ else str.Format(\"黑:前卒进%d\ } else { if(nFromY==nToY) str.Format(\"黑:后卒平%d\ else str.Format(\"黑:后卒进%d\ } break; } j=0; if(nCount>1) { for(i=0;i<5;i++) if(nPos[i]==-1) break; else if(nPos[i]>nFromY) break; else j++; if(nFromY==nToY) str.Format(\"黑:%d卒平%d\ else str.Format(\"黑:%d卒进%d\ break; } case R_KING://红帅 if(nFromX==nToX) { if(nFromY>nToY) str=\"红:帅\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(nFromY-nToY); else str=\"红:帅\"+ConvertDigit2Chinese(10-nFromX)+\"退\"+ConvertDigit2Chinese(nToY-nFromY); } else str=\"红:帅\"+ConvertDigit2Chinese(10-nFromX)+\"平\"+ConvertDigit2Chinese(10-nToX); break; case R_CAR://红车 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==R_CAR && i!=nFromY-1 && i!=nToY-1) { bIsAgain=true; break; } if(nFromY>nToY) { if(bIsAgain) { if(i>nFromY-1) str=\"红:前车进\"+ConvertDigit2Chinese(nFromY-nToY); else str=\"红:后车进\"+ConvertDigit2Chinese(nFromY-nToY); } else str=\"红:车\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(nFromY-nToY); } else if(nFromY } else { if(bIsAgain) { if(i>nFromY-1) str=\"红:后车平\"+ConvertDigit2Chinese(nToX); else str=\"红:前车平\"+ConvertDigit2Chinese(nToX); } else str=\"红:车\"+ConvertDigit2Chinese(10-nFromX)+\"平\"+ConvertDigit2Chinese(10-nToX); break; } break; case R_HORSE://红马 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==R_HORSE && i!=nFromY-1 && i!=nToY-1) { bIsAgain=true; break; } if(bIsAgain) { if(i>nFromY-1) { if(nFromY>nToY) str=\"红:前马进\"+ConvertDigit2Chinese(10-nToX); else str=\"红:前马退\"+ConvertDigit2Chinese(10-nToX); } else { if(nFromY>nToY) str=\"红:前马进\"+ConvertDigit2Chinese(10-nToX); else str=\"红:前马退\"+ConvertDigit2Chinese(10-nToX); } } else { if(nFromY>nToY) str=\"红:马\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(10-nToX); else str=\"红:马\"+ConvertDigit2Chinese(10-nFromX)+\"退\"+ConvertDigit2Chinese(10-nToX); } break; case R_CANON://红炮 bIsAgain=false; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==R_CANON && i!=nFromY-1 && i!=nToY-1) { bIsAgain=true; break; } if(nFromY>nToY) { if(bIsAgain) { if(i>nFromY-1) str=\"红:前炮进\"+ConvertDigit2Chinese(nFromY-nToY); else str=\"红:后炮进\"+ConvertDigit2Chinese(nFromY-nToY); } else str=\"红:炮\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(nFromY-nToY); } else if(nFromY } else { if(bIsAgain) { if(i>nFromY-1) str=\"红:前炮平\"+ConvertDigit2Chinese(10-nToX); else str=\"红:后炮平\"+ConvertDigit2Chinese(10-nToX); } else str=\"红:炮\"+ConvertDigit2Chinese(10-nFromX)+\"平\"+ConvertDigit2Chinese(10-nToX); } break; case R_BISHOP://红士 if(nFromY>nToY) str=\"红:士\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(10-nToX); else str=\"红:士\"+ConvertDigit2Chinese(10-nFromX)+\"退\"+ConvertDigit2Chinese(10-nToX); break; case R_ELEPHANT://红相 bIsAgain=false; for(i=0;i<5;i++) if(m_byChessBoard[i][nFromX-1]==R_ELEPHANT && i!=nFromY-1 && i!=nToY-1) { bIsAgain=true; break; } if(bIsAgain) { if(i>nFromY-1) { if(nFromY>nToY) str=\"红:前相退\"+ConvertDigit2Chinese(10-nToX); else str=\"红:前相进\"+ConvertDigit2Chinese(10-nToX); } else { if(nFromY>nToY) str=\"红:后相退\"+ConvertDigit2Chinese(10-nToX); else str=\"红:后相进\"+ConvertDigit2Chinese(10-nToX); } } else { if(nFromY>nToY) str=\"红:相\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(10-nToX); else str=\"红:相\"+ConvertDigit2Chinese(10-nFromX)+\"退\"+ConvertDigit2Chinese(10-nToX); } break; case R_PAWN://红兵 nCount=0; j=0; for(i=0;i<5;i++) nPos[i]=-1; for(i=0;i<10;i++) if(m_byChessBoard[i][nFromX-1]==R_PAWN && i!=nFromY-1 && i!=nToY-1) { nPos[j]=i; nCount++; } if(nCount==0) { if(nFromY==nToY) str=\"红:兵\"+ConvertDigit2Chinese(10-nFromX)+\"平\"+ConvertDigit2Chinese(10-nToX); else str=\"红:兵\"+ConvertDigit2Chinese(10-nFromX)+\"进\"+ConvertDigit2Chinese(1); break; } if(nCount==1) { if(nFromY-1>nPos[0]) { if(nFromY==nToY) str=\"红:前兵平\"+ConvertDigit2Chinese(10-nToX); else str=\"红:前兵进\"+ConvertDigit2Chinese(1); } else { if(nFromY==nToY) str=\"红:后兵平\"+ConvertDigit2Chinese(10-nToX); else str=\"红:后兵进\"+ConvertDigit2Chinese(1); } break; } j=0; if(nCount>1) { for(i=0;i<5;i++) if(nPos[i]==-1) break; else if(nPos[i]>nFromY-1) break; else j++; if(nFromY==nToY) str=\"红:\"+ConvertDigit2Chinese(j+1)+\"兵平\"+ConvertDigit2Chinese(nToX); else str=\"红:\"+ConvertDigit2Chinese(j+1)+\"兵进\"+ConvertDigit2Chinese(1); } break; default: break; } return str; } 2.1、模块设计: 开发Windows下的应用程序,首先的路是:Windows应用程序运行的消息循环的机制。当一个应用程序建立的时候,操作系统会为这个应用程序分配一个消息队列,凡是跟这个应用程序相关的消息,操作系统都会把这些消息放到消息队列中。然后应用程序得用GetMessage()从消息队列中取出一条具体的消息。再利用TranslateMessage(),将用户产生的信息转换成类似WM_KEYDOWRR 的消息,再放到消息队列中。然后再利用DispatchMessage()将消息投递出去。投递给操作系统。操作系统再根据设计窗口类时的那个窗口处理函数WndProc(),对不同的消息产生不同的响应。这样就完成了整个消息的循环。 主程序模块窗口模块摆棋子绘制棋盘规则定义模块电脑智能模块 2.2、数据结构说明: 人机对弈程序采用了多种搜索算法.以下是本程序主要的类说明: 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搜索引擎. 2.3、算法流程图: 开始搜索算法定义搜索深度depth,本方最优值best对depth,best,worst进行search搜索判断深度depth是否小于0调用局面评估函数分析局面红方走棋判断value 是否大于bestBest=value判断 depth是否等于MaxdepthBestmove = 当前走法 四、系统测试与调试分析 1、系统测试 测试方法:黑盒 测试技术:、功能测试 测试数据: 测试报告: 测试说明 测试测试名称 测试目的 测试技术 测试方法 测试内容 测试步骤 中国象棋 验证用户下棋的整个过程 功能测试 黑盒测试法 象棋运行 打开象棋 运行 实现与电脑对弈 用测试数据 例 预期结果 测试结果 正常 正常进入系统 与预期相符 正常 正常运行 与预期相符 正常 正常 与预期相符 2、调试分析: 由于能力和时间以及所学的知识很有限,在设计的过程中一边对照资料,一边用查询。更重要的是请教同学和老师,对于有些我无法改正的错误,我采取了有点消极的做法,注释掉程序之后再进行改正。 五、用户手册 1、使用的平台是Windows 操作系统。 2、不需要安装,直接打开Source Code文件夹,双击Release文件夹,再双击其中的象棋图标,就可以运行程序。 3、详细使用说明: 第一步:打开运行文件 第二步:双击选择chess.exe 第三步:开始运行 第四步:运行完毕,直接退出。 六、程序清单 1//鼠标左键响应消息 void CChessDlg::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(m_iChessSort==CS_CCCHESS) { m_staticTip.SetWindowText(m_strWelcome); return; } if(m_Status==Previewing) { m_staticTip.SetWindowText(\"现在处于预览状态,如要下棋,请弹出右键菜单点 预览完毕\"); return; } if(m_bIsGameOver) { m_staticTip.SetWindowText(\"老兄,这盘棋下完了\"); return; } if(m_bIsThinking)//电脑正在想 return; int x,y; //将坐标换算成棋盘上的格子 y=(point.y-14)/GRILLEHEIGHT; x=(point.x-15)/GRILLEWIDTH; //判断鼠标是否在棋盘内,并且点中了用户棋子 if(y>=0 && y<10 && x>=0 && x<9 && (m_nUserChessColor==REDCHESS?IsRed(m_byChessBoard[y][x]):IsBlack(m_byChessBoard[y][x]))) { memcpy(m_byBackupChessBoard,m_byChessBoard,90);//备份棋盘 //将当前棋子的信息装入,记录移动棋子的结构中 m_ptMoveChess.x=x; m_ptMoveChess.y=y; m_MoveChess.nChessID=m_byChessBoard[y][x]; //将该棋子原位置棋子去掉 m_byChessBoard[y][x]=NOCHESS; m_byShowChessBoard[y][x]=NOCHESS; //让棋子中点坐标位于鼠标所在点 point.x-=18; point.y-=18; m_MoveChess.ptMovePoint=point; //重绘屏幕 InvalidateRect(NULL,FALSE); UpdateWindow(); SetCapture();//独占鼠标焦点 } else if(m_Status==Chessing) if(y>=0 && y<10 && x>=0 && x<9 && (m_nUserChessColor!=REDCHESS?IsRed(m_byChessBoard[y][x]):IsBlack(m_byChessBoard[y][x]))) m_staticTip.SetWindowText(\"不好意思,这是我的棋子,请你不要乱动\"); else m_staticTip.SetWindowText(\"老兄,那又没有棋子,你瞎点什么啊\"); CDialog::OnLButtonDown(nFlags, point); } 2//响应鼠标左键弹起的消息 void CChessDlg::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(m_bIsGameOver) { m_staticTip.SetWindowText(\"老兄,这盘棋下完了\"); return; } if(m_bIsThinking || m_Status!=Chessing) return; BOOL bTurnSide=FALSE; int x,y; CString str; //将坐标换算成棋盘上的格子 y=(point.y-14)/GRILLEHEIGHT; x=(point.x-15)/GRILLEWIDTH; //判断是否有移动棋子,并且该棋子的移动是一个合法走法 // if(m_MoveChess.nChessID && CMoveGenerator::IsValidMove(m_byBackupChessBoard,m_ptMoveChess.x,m_ptMoveChess.y,x,y,m_nUserChessColor)) if(m_MoveChess.nChessID && m_pMG->IsValidMove(m_byBackupChessBoard,m_ptMoveChess.x,m_ptMoveChess.y,x,y,m_nUserChessColor)) { //---------将用户走法压栈--------- 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); //-------------------------------- m_btnUndo.EnableWindow(1);//激活悔棋按钮 if(m_nUserChessColor==REDCHESS) m_iBout++; this->AddChessRecord(m_ptMoveChess.x+1,m_ptMoveChess.y+1,x+1,y+1,m_nUserChessColor,m_MoveChess.nChessID); m_byChessBoard[y][x]=m_MoveChess.nChessID; m_byShowChessBoard[y][x]=m_MoveChess.nChessID; bTurnSide=TRUE; } else//否则恢复移动前的棋盘状态 { memcpy(m_byShowChessBoard,m_byBackupChessBoard,90); memcpy(m_byChessBoard,m_byBackupChessBoard,90); } m_MoveChess.nChessID=NOCHESS;//将移动的棋子清空 //重绘屏幕 InvalidateRect(NULL,FALSE); UpdateWindow(); ReleaseCapture();//释放鼠标焦点 if(m_iChessSort==CS_PPCHESS) { m_iWhoChess=m_iWhoChess%2+1; return; } if(bTurnSide==TRUE) { m_btnStop.EnableWindow(1); m_hHandle=::CreateThread(0,0,ThinkProc,this,0,&m_dwThreadID); } else m_staticTip.SetWindowText(m_strWelcome); CDialog::OnLButtonUp(nFlags,point); } 3//响应鼠标移动 函数 void CChessDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(m_bIsThinking) return; if(m_MoveChess.nChessID) { if(m_Status==Chessing) { //防止将棋子拖出棋盘 if(point.x<15)//左边 point.x=15; if(point.y<15)//上边 point.y=15; if(point.x>m_nBoardWidth-15)//右边 point.x=m_nBoardWidth-15; if(point.y>m_nBoardHeight-15)//下边 point.y=m_nBoardHeight-15; //让棋子中心位于鼠标所在处 point.x-=18; point.y-=18; m_MoveChess.ptMovePoint=point;//保存移动棋子的坐标 } else { //让棋子中心位于鼠标所在处 point.x-=18; point.y-=18; m_MoveChess.ptMovePoint=point;//保存移动棋子的坐标 //棋子拖出棋盘时将该棋子删掉 if(point.x<15 || point.y<15 || point.x>m_nBoardWidth-15 || point.y>m_nBoardHeight-15) m_byChessBoard[m_ptMoveChess.y][m_ptMoveChess.x]=NOCHESS; } InvalidateRect(NULL,FALSE);//刷新窗口 UpdateWindow();//立即执行刷新 } CDialog::OnMouseMove(nFlags, point); } 4//统计红黑两方总分 int nRedValue=0;int nBlackValue=0; for(i=0;i<10;i++) for(j=0;j<9;j++) { nChessType=position[i][j]; if(nChessType!=NOCHESS) { if(IsRed(nChessType)) nRedValue+=m_chessValue[i][j]; //把红棋的值加总 else nBlackValue+=m_chessValue[i][j];//把红棋的值加总 } } if(nUserChessColor==REDCHESS) { if(bIsRedTurn) return nRedValue-nBlackValue;//如果轮到红棋走返回估值 return nBlackValue-nRedValue;//如果轮到黑棋走返回负估值 } if(bIsRedTurn) return nBlackValue-nRedValue;//如果轮到黑棋走返回负估值 return nRedValue-nBlackValue;//如果轮到红棋走返回估值 } int CEveluation::GetRelatePiece(BYTE position[][9], int j, int i) { nPosCount=0; BYTE nChessID; BYTE flag; int x,y; nChessID=position[i][j]; switch(nChessID) { case R_KING://红帅 case B_KING://黑将 //循环检查九宫之内哪些位置可到达/保护 //扫描两边就宫包含了照像的情况 for(y=0;y<3;y++) for(x=3;x<6;x++) if(CanTouch(position,j,i,x,y))//能否到达 AddPoint(x,y);//可达到/保护的位置加入数组 //循环检查九宫之内哪些位置可到达/保护 //扫描两边就宫包含了照像的情况 for(y=7;y<10;y++) for(x=3;x<6;x++) if(CanTouch(position,j,i,x,y))//能否到达 AddPoint(x,y);//可达到/保护的位置加入数组 break; case R_BISHOP://红士 //循环检查九宫之内哪些位置可到达/保护 for(y=7;y<10;y++) for(x=3;x<6;x++) if(CanTouch(position,j,i,x,y)) AddPoint(x,y);//可达到/保护的位置加入数组 break; case B_BISHOP://黑士 //循环检查九宫之内哪些位置可到达/保护 for(y=0;y<3;y++) for(x=3;x<6;x++) if(CanTouch(position,j,i,x,y)) AddPoint(x,y);//可达到/保护的位置加入数组 break; case R_ELEPHANT://红相 case B_ELEPHANT://黑象 //右下 x=j+2; y=i+2; if(x<9 && y<10 && CanTouch(position,j,i,x,y)) AddPoint(x,y); //右上 x=j+2; y=i-2; if(x<9 && y>=0 && CanTouch(position,j,i,x,y)) AddPoint(x,y); //左下 x=j-2; y=i+2; if(x>=0 && y<10 && CanTouch(position,j,i,x,y)) AddPoint(x,y); //左上 x=j-2; y=i-2; if(x>=0 && y>=0 && CanTouch(position,j,i,x,y)) AddPoint(x,y); break; case R_HORSE://红马 case B_HORSE://黑马 //检查右下方能否到达/保护 x=j+2; y=i+1; if((x<9 && y<10)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查右上方能否到达/保护 x=j+2; y=i-1; if((x<9 && y>=0)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查左下方能否到达/保护 x=j-2; y=i+1; if((x>=0 && y<10)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查左上方能否到达/保护 x=j-2; y=i-1; if((x>=0 && y>=0)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查右下方能否到达/保护 x=j+1; y=i+2; if((x<9 && y<10)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查右上方能否到达/保护 x=j+1; y=i-2; if((x<9 && y>=0)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查左下方能否到达/保护 x=j-1; y=i+2; if((x>=0 && y<10)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); //检查左上方能否到达/保护 x=j-1; y=i-2; if((x>=0 && y>=0)&&CanTouch(position,j,i,x,y)) AddPoint(x,y); break; case R_CAR://红车 case B_CAR://黑车 //检查向右能否到达/保护 x=j+1; y=i; while(x<9) { if(NOCHESS==position[y][x])//空白 AddPoint(x,y); else{ //碰到第一个棋子 AddPoint(x,y); break;//后面的位置不能走了 } x++; } //检查向左能否到达/保护 x=j-1; y=i; while(x>=0) { if(NOCHESS==position[y][x])//空白 AddPoint(x,y); else{ //碰到第一个棋子 AddPoint(x,y); break;//后面的位置不能走了 } x--; } //检查向下能否到达/保护 x=j; y=i+1; while(y<10) { if(NOCHESS==position[y][x])//空白 AddPoint(x,y); else{ //碰到第一个棋子 AddPoint(x,y); break;//后面的位置不能走了 } y++; } //检查向上能否到达/保护 x=j; y=i-1; while(y>=0) { if(NOCHESS==position[y][x])//空白 AddPoint(x,y); else{ //碰到第一个棋子 AddPoint(x,y); break;//后面的位置不能走了 } y--; } break; case R_PAWN://红兵 //观看向前是否到底 y=i-1; x=j; if(y>=0) AddPoint(x,y);//没到底,可走 if(i<5) { //如已过河 y=i; x=j+1;//向右 if(x<9) AddPoint(x,y);//未到右边,可走 x=j-1;//向左 if(x>=0) AddPoint(x,y);//未到左边,可走 } break; case B_PAWN://黑卒 //观看向前是否到底 y=i+1; x=j; if(y<10) AddPoint(x,y);//没到底,可走 if(i>4) { } //如已过河 y=i; x=j+1;//向右 if(x<9) AddPoint(x,y);//未到右边,可走 x=j-1;//向左 if(x>=0) AddPoint(x,y);//未到左边,可走 break; case B_CANON://黑炮 case R_CANON://红炮 //检查向右能否到达/保护的位置 x=j+1; y=i; flag=FALSE; while(x<9) { if(NOCHESS==position[y][x]) { //空白位置 if(!flag) AddPoint(x,y); } else { if(!flag) flag=TRUE;//是第一个棋子 else { //是第二个棋子 AddPoint(x,y); break; } } x++;//继续向右 } //检查向左能否到达/保护的位置 x=j-1; y=i; flag=FALSE; while(x>=0) { if(NOCHESS==position[y][x]) { //空白位置 if(!flag) AddPoint(x,y); } else { if(!flag) flag=TRUE;//是第一个棋子 else { //是第二个棋子 AddPoint(x,y); break; } } x--;//继续向左 } //检查向下能否到达/保护的位置 x=j; y=i+1; flag=FALSE; while(y<10) { if(NOCHESS==position[y][x]) { //空白位置 if(!flag) AddPoint(x,y); } else { if(!flag) flag=TRUE;//是第一个棋子 else { //是第二个棋子 AddPoint(x,y); break; } } y++;//继续向下 } //检查向上能否到达/保护的位置 x=j; y=i-1; flag=FALSE; while(y>=0) { if(NOCHESS==position[y][x]) { //空白位置 if(!flag) AddPoint(x,y); } else { if(!flag) flag=TRUE;//是第一个棋子 else { //是第二个棋子 AddPoint(x,y); break; } } y--;//继续向上 } break; default: break; } return nPosCount; } 5//下面的循环继续统计扫描到的数据 int nHalfvalue; for(i=0;i<10;i++) for(j=0;j<9;j++) { if(position[i][j]!=NOCHESS) { nChessType=position[i][j]; nHalfvalue=m_BaseValue[nChessType]/16; //棋子基本价值的1/16作为威胁/保护增量 m_chessValue[i][j]+=m_BaseValue[nChessType];//每个棋子的基本价值加入其总价值 if(IsRed(nChessType))//红棋 { if(m_AttackPos[i][j])//当前红棋如果被威胁 { if(bIsRedTurn)//轮到红棋走 { if(nChessType==R_KING)//如果是红将 m_chessValue[i][j]-=20;//价值降低20 else { //价值减去2倍nHalfvalue m_chessValue[i][j]-=nHalfvalue*2; if(m_GuardPos[i][j])//是否被己方棋子保护 m_chessValue[i][j]+=nHalfvalue;//被保护再加上nHalfvalue } } else//当前红棋被威胁,轮到黑棋走 { if(nChessType==R_KING)//是否是红帅 return 18888;//返回失败极值 m_chessValue[i][j]-=nHalfvalue*10;//减去10倍的nHalfvalue,表示威胁程度高 if(m_GuardPos[i][j])//如果被保护 m_chessValue[i][j]+=nHalfvalue*9;//被保护再加上9倍的nHalfvalue } //被威胁的棋子加上威胁差,防止一个兵威胁 //一个被保护的车,而估值函数没有反映此类问题 m_chessValue[i][j]-=m_AttackPos[i][j]; } else { //没受威胁 if(m_GuardPos[i][j]) m_chessValue[i][j]+=5;//受保护,加一点分 } } else { //如果是黑棋 增加nHalfvalue 倍nHalfvalue 9倍nHalfvalue 问题 if(m_AttackPos[i][j]) { //受威胁 if(!bIsRedTurn) { //轮到黑棋走 if(nChessType==B_KING)//如果是黑将 m_chessValue[i][j]-=20;//棋子价值降低20 else { //棋子价值降低2倍nHalfvalue m_chessValue[i][j]-=nHalfvalue*2; if(m_GuardPos[i][j])//如果受保护 m_chessValue[i][j] +=nHalfvalue;//棋子价值 } } else { //轮到红棋走 if(nChessType==B_KING)//是黑将 return 18888;//返回失败极值 m_chessValue[i][j]-=nHalfvalue*10;//棋子价值减少10 if(m_GuardPos[i][j])//受保护 m_chessValue[i][j]+=nHalfvalue*9;//被保护再加上 } //被威胁的棋子再加上威胁差 //防止一个兵威胁一个被保护的车,而估值函数没有反映此类的 m_chessValue[i][j]-=m_AttackPos[i][j]; } else { //不受威胁 if(m_GuardPos[i][j]) m_chessValue[i][j]+=5;//受保护,加一点分 } } } } 6//棋子移动 //define.h #ifndef define_h_ #define define_h_ #define BLACKCHESS 1//黑方 #define REDCHESS 2//红方 //深度方式DepthSort #define DS_DEFAULTSET 1 #define DS_USERDEFINE 2 #define CS_PCCHESS 1//人机对弈 #define CS_PPCHESS 2//人人对弈 #define CS_CCCHESS 3//机机对弈 #define CS_HASHCHESS 4//混杂对弈 //--------棋子-------- #define NOCHESS 0 //没有棋子 #define B_KING 1 //黑帅 #define B_CAR 2 //黑车 #define B_HORSE 3 //黑马 #define B_CANON 4 //黑炮 #define B_BISHOP 5 //黑士 #define B_ELEPHANT 6 //黑象 #define B_PAWN 7 //黑卒 #define B_BEGIN B_KING #define B_END B_PAWN #define R_KING 8 //红将 #define R_CAR 9 //红车 #define R_HORSE 10//红马 #define R_CANON 11//红炮 #define R_BISHOP 12//红士 #define R_ELEPHANT 13//红相 #define R_PAWN 14//红兵 #define R_BEGIN R_KING #define R_END R_PAWN //-------------------- #define IsBlack(x) (x>=B_BEGIN && x<=B_END)//判断某个棋子是不是黑色 #define IsRed(x) (x>=R_BEGIN && x<=R_END)//判断某个棋子是不是红色 //判断两个棋子是不是同色 #define IsSameSide(x,y) ((IsBlack(x) && IsBlack(y)) || (IsRed(x) && IsRed(y))) //棋子位置 typedef struct { BYTE x; BYTE y; }CHESSMANPOS; //棋子走法 typedef struct { short nChessID; //表明是什么棋子 CHESSMANPOS From;//起始位置 CHESSMANPOS To; //走到什么位置 int Score; //走法的分数 }CHESSMOVE; //悔棋时需要的数据结构 typedef struct { CHESSMOVE cmChessMove; short nChessID;//被吃掉的棋子 }UNDOMOVE; #endif 七、体会与自我评价 两周的课程设计结束了,在这次的课程设计中不仅检验了我所学习的知识,也培养了我如何去把握一件事情,如何去做一件事情,又如何完成一件事情。在设计过程中,和同学们相互探讨,相互学习,相互监督。学会了合作。 C++是一门广泛用于工业软件研发的大型语言。它自身的复杂性和解决现实问题的能力,使其极具学术研究价值和工业价值。和C语言一样,C++已经在许多重要的领域大获成功。 通过这次课程设计,我知道了如何在Windows下利用MFC开发简单程序的过程,也更好的体会到了Windows应用程序运行的消息循环的机制。与些同时,对VC6.0的使用也更加的了解与熟练了。而且调试能力也有很大提高。并且理解了从事职业工作前一个必不 少的过程.”千里之行,始于足下”,通过这次课程设计,我深深体会到这句千古名言的真正含义 通过这次课程设计,我在多方面都有所提高。更是进行一次熟悉的规范和标准,同时各科相关的课程都有了全面的复习,独立思考的能力也有了提高。 在这次设计过程中,体现出自己单独设计课程的能力以及综合运用知识的能力,体会了学以致用、突出自己劳动成果的喜悦心情,从中发现自己平时学习的不足和薄弱环节,从而加以弥补。 感谢对我帮助过的同学们,谢谢你们对我的帮助和支持,让我感受到同学的友谊,以及在困难中的那份温暖。 由于本人的设计能力有限,在设计过程中难免出现错误,恳请老师们多多指教,我十分乐意接受你们的批评与指正,我将万分感谢。 八、参考文献 [1] C++面向对象程序设计 西安电子科技大学出版社 李兰 任凤华 [2] 蒋鹏, 雷贻祥,陈园园. C\\C++中国象棋程序入门与提高. 电子工业出版社, 2009 [3] 刘路放. Visual C++与面向对象程序设计教程(第二版). 北京:高等教育出版社,2007.8 [4] 钱能.C++程序设计教程(第2版).北京:清华大学出版社,2005.8 [5] http://www.xqbase.com/computer/stepbystep1.htm 象棋百科全书 [6] 网络视频 孙鑫教程 [7]《C++程序设计语言(特别版)》,机械工业出版社 九、课程设计评价(由任课教师填写) 课 程 设 计 评 价 成绩: 教师: 年 月 日 因篇幅问题不能全部显示,请点此查看更多更全内容