中国象棋游戏开发设计报告.docx
- 文档编号:28088390
- 上传时间:2023-07-08
- 格式:DOCX
- 页数:24
- 大小:482.42KB
中国象棋游戏开发设计报告.docx
《中国象棋游戏开发设计报告.docx》由会员分享,可在线阅读,更多相关《中国象棋游戏开发设计报告.docx(24页珍藏版)》请在冰豆网上搜索。
中国象棋游戏开发设计报告
中国象棋游戏开发设计报告
班级:
小组编号:
小组成员:
指导教师:
一、开发的目的和意义
面向对象程序设计作为一门软件设计的课程,具有极强的实践性,要求学生具有灵活应用理论知识的能力及面向对象程序设计技术的基础。
通过游戏开发,学生能了解C++面向对象的设计方式与技术,有效地、深刻地明白得课程内容,体会理论、方式和设计原那么,培育分析实际问题和解决问题的能力,具有利用面向对象程序设计开发工具设计实际系统的能力。
还能够了解并通过利用MFC,把握一种可视化编程的方式,并通过游戏的开发加深对可视化编程的明白得。
同时,能够提高运用C++编程语言解决实际问题的能力。
棋牌游戏属于休闲类游戏,具有上手快、游戏时刻短的特点,更利于用户进行放松休闲,为人们所喜爱,专门是棋类游戏,方便、快捷、操作简单,在休闲娱乐中占要紧位置。
中国象棋作为中国自古以来的经典棋牌游戏之一,一直都是人之间的较量,将中国象棋制作成游戏,能够实现人与运算机之间的对弈。
而且人工智能是综合性很强的一门边缘学科,它的中心任务是研究如何使运算机去做那些过去只能靠人的智力才能做的工作。
开发出了运算机象棋游戏,以后不单单能够进行休闲游戏,还能锻炼自己的智力和象棋技术,加倍方便了人们的日常生活。
二、功能描述和分析(用户需求分析)
开发背景
咱们周围有许多同窗喜爱下象棋,尤其是男同窗,希望能有人能够和自己下象棋,但这种意愿常因为受到条件的限制而难以如愿,比如说需要身旁恰好有现成的棋盘棋子,比如说需若是一样知道中国象棋的对手,可是大伙儿都明白咱们这所大学男性同窗占少数,即即是条件都知足了,还要考虑这位对手是不是有何自己下棋的心情。
这时,若是有一台运算机,一个能够支持人机对弈的程序,上面的问题迎刃而解。
而咱们小组的那个想起游戏设计,正是希望能够做出一款拥有良好性能,良好的智能,能够知足大多数爱好象棋的同窗的需求中国象棋人机对弈程序。
用户需求分析
一款能够与用户对弈,知足用户需求的中国象棋程序,需要有棋盘棋子的局面、鼠标响应操纵棋子移动、棋子的走法规那么、人机对弈的搜索算法、幸免异样引入的多线程、输赢判定,具体分析如下:
2.2.1棋盘棋子的局面
作为中国象棋的这项游戏,其必不可少的是确实是棋子和棋盘,没有这两个部份,想起功能无法实现,不单单如此,若是,仅仅有棋子和棋盘,而没有将二者结合起来,那么,也将无法实现中国象棋的游戏功能,因此,棋子和棋盘的设计在那个游戏设计中相当重要。
2.2.2鼠标响应
在对弈中,棋子是必需能够移动的,不然游戏无法进行。
因此,鼠标左键点击是必不可少的一部份。
2.2.3棋子的功能分析:
中国象棋中各色的象棋棋子的功能使象棋具有了真正的趣味性,中国象棋的棋子的类型大致分为:
帅(将)、士、象、马、车、炮、兵(卒)等几个类型。
帅(将):
红方中的帅和黑方中的将的功能相同,都是只能在九宫格中进行横向和竖向的移动,每次移动一格,而且不能移动超出九宫格,帅和将不能见面。
士:
士在整片棋盘中,和帅的移动范围类似,也是只能在九宫格中移动,只是士的移动方向是对角线,而且每次只能在一个格子中移动。
象:
象的走法遵循“象走田”的原那么,不能绊象腿。
马:
马的走法遵循“马走日”的原那么,不能绊马腿。
车:
在整块棋盘中,车能够横向或纵向3移动任意格。
炮:
每次移动和车的类似,可是在吃对方棋子的时候必需中间有且只能有一个棋子的距离。
兵(卒):
红方的兵和黑方的卒的功能相同,特点是只能向对方前进,而不能后退,过河之前不能横向移动,过河以后能够横向移动,不管是前进仍是横向移动,每次都只能移动一格。
2.2.4良好的人机对弈
要实现人机的对弈,搜索算法是很重要的一部份。
关于棋类对弈程序中的搜索算法,已有成熟的Alpha-Beta搜索算法。
咱们在程序中直接借鉴了Alpha-Beta搜索算法并辅以历史启发。
Alpha-Beta搜索算法:
在中国象棋里,两边棋手取得相同的棋盘信息。
他们连番走棋,目的确实是吃掉对方的将或帅,或幸免自己的将或帅被吃。
搜索算法的搜索进程很漫长,因此对搜索算法进行简化是有必要的。
2.2.5多线程的必要性
由于程序在进行搜索时会占用大量的CPU时刻,因此阻塞了位于同一线程内的其他指令,使之无法正常工作,因此引入了多线程的思想另外开一个线程,让各程序分开于多个线程。
就能够够解决程序异样的问题了,因此,多线程思想的引入是有必要的。
2.2.6判定输赢
游戏需要判定最后由谁胜出
三、采纳的开发工具和技术,开发环境,适用环境
开发工具:
VisualC++MFC工程;
开发环境:
win7;
适用环境:
windows系统;
四、小组成员分工
初始化、局面设计部份(贺景);
判定输赢、棋子走法部份(邹京甫);
鼠标响应、画图部份(吴鑫);
搜索引擎部份等由组员一起完成。
五、具体开发方式和进程
初始化部份
OnInitDialog()负责的是对话框的初始化。
能够把有关中国象棋的棋局初始化情形也放在了那个地址面。
初始化的内容包括:
对引擎部份所用到的变量的初始化。
包括对棋盘上的棋子位置进行初始化(棋盘数组的初始化),对搜索深度、当前走棋方标志、棋局是不是终止标志等的初始化;
对棋盘、棋子的贴图位置(即棋盘、棋子在程序中实际显示位置)的初始化;
对程序辅助部份所用到的一些变量的初始化。
棋盘、棋子样式的默许形式,和着法名称列表的初始化等。
1.对棋盘的初始化memcpy(m_byChessBoard,InitChessBoard,90);
2.对棋盘、棋子的贴图位置(即棋盘、棋子在程序中实际显示位置)的初始化;(&pOldBmp);程序辅助部份所用到的一些变量的初始化
棋盘、棋子样式的默许形式,下棋模式的默许选择,和着法名称列表的初始化等。
初始化部份的代码如下:
BOOLCChessDlg:
:
OnInitDialog()
{
CDialog:
:
OnInitDialog();
."menuitemtosystemmenu.
ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX<0xF000);
CMenu*pSysMenu=GetSystemMenu(FALSE);
if(pSysMenu!
=NULL)
{
CStringstrAboutMenu;
(IDS_ABOUTBOX);
if(!
())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu);
}
}
SetIcon(m_hIcon,TRUE);//Setbigicon
SetIcon(m_hIcon,FALSE);//Setsmallicon
//彩色进度条设置
(RGB(0xFF,0xFF,0x00));//黄色
(RGB(0x00,0x93,0x00));//绿色
(RGB(0xE6,0xE6,0xFA));//淡紫色
(RGB(0,0,255));
(1);
(this);
(1);
(IDB_CHESSMAN,36,14,RGB(0,255,0));//创建含有棋子图形的ImgList,用于绘制棋子
//下面这段代码取棋盘图形的宽,高
BITMAPBitMap;
(IDB_CHESSBOARD);
(&BitMap);//取BitMap对象
m_nBoardWidth=;//棋盘宽度
m_nBoardHeight=;//棋盘高度
();
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);
//设定进度条
=NOCHESS;//将移动的棋子清空
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
局面设计
游戏设计中,咱们的象棋棋盘采纳的是直接加载位图生成棋盘,图片的大小是宽度377*高度417,棋盘上每一个格子的大小:
39*39,图片格式为:
BMP。
棋子部份是通过加载位图实现的,图片的大小是:
宽度32*高度32,图片的格式也是BMP。
咱们用一个10*9的数组来存储棋盘上的信息,数组的每一个元素存储棋盘上是不是有棋子。
棋盘的初始情形如下所示(图1是整个棋盘与棋子的局面图):
constBYTEInitChessBoard[10][9]=
{
{B_CAR,B_HORSE,B_ELEPHANT,B_BISHOP,B_KING,B_BISHOP,B_ELEPHANT,B_HORSE,B_CAR},
{NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS},
{NOCHESS,B_CANON,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,B_CANON,NOCHESS},
{B_PAWN,NOCHESS,B_PAWN,NOCHESS,B_PAWN,NOCHESS,B_PAWN,NOCHESS,B_PAWN},
{NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS},
//楚河//汉界
{NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS},
{R_PAWN,NOCHESS,R_PAWN,NOCHESS,R_PAWN,NOCHESS,R_PAWN,NOCHESS,R_PAWN},
{NOCHESS,R_CANON,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,R_CANON,NOCHESS},
{NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS,NOCHESS},
{R_CAR,R_HORSE,R_ELEPHANT,R_BISHOP,R_KING,R_BISHOP,R_ELEPHANT,R_HORSE,R_CAR}
};
图1局面设计图
棋子的概念:
#defineNOCHESS0//没有棋子
#defineB_KING1//黑帅
#defineB_CAR2//黑车
#defineB_HORSE3//黑马
#defineB_CANON4//黑炮
#defineB_BISHOP5//黑士
#defineB_ELEPHANT6//黑象
#defineB_PAWN7//黑卒
#defineB_BEGINB_KING
#defineB_ENDB_PAWN
#defineR_KING8//红将
#defineR_CAR9//红车
#defineR_HORSE10//红马
#defineR_CANON11//红炮
#defineR_BISHOP12//红士
#defineR_ELEPHANT13//红相
#defineR_PAWN14//红兵
#defineR_BEGINR_KING
#defineR_ENDR_PAWN
#defineIsBlack(x)(x>=B_BEGIN&&x<=B_END)//判定某个棋子是不是黑色。
#defineIsRed(x)(x>=R_BEGIN&&x<=R_END)//判定某个棋子是不是红色。
//判定两个棋子是不是同色
#defineIsSameSide(x,y)((IsBlack(x)&&IsBlack(y))||(IsRed(x)&&IsRed(y)))
//棋子位置
typedefstruct
{
BYTEx;
BYTEy;
}CHESSMANPOS;
画图部份
关于画图部份,要紧实现的是程序界面的画图因此咱们在那个地址将要完成棋盘、棋子的显示走棋起始位置和目标位置的提示框的显示。
而要实现这些咱们必需通过voidCChessDlg:
:
OnPaint()那个函数实现
voidCChessDlg:
:
OnPaint()
{
CPaintDCdc(this);
CDCMemDC;
inti,j;
POINTpt;
CBitmap*pOldBmp;
(&dc);
(IDB_CHESSBOARD);
pOldBmp=(&m_BoardBmp);
//绘制棋盘上的棋子
for(i=0;i<10;i++)
for(j=0;j<9;j++)
{
if(m_byShowChessBoard[i][j]==NOCHESS)
continue;
=j*GRILLEHEIGHT+14;
=i*GRILLEWIDTH+15;
(&MemDC,m_byShowChessBoard[i][j]-1,pt,ILD_TRANSPARENT);
}
//绘制用户正在拖动的棋子
if!
=NOCHESS)
(&MemDC,,,ILD_TRANSPARENT);
(0,0,m_nBoardWidth,m_nBoardHeight,&MemDC,0,0,SRCCOPY);
//将绘制的内容刷新到屏幕
(&pOldBmp);//恢复内存Dc的原位图
();//释放内存
();//删除棋盘位图对象
}
鼠标响应部份
鼠标响应部份包括LButtonDown和LButtonUp两个功能,
LButtonDown实现的要紧功能是拖动棋子在棋盘上的移动,他的重要性是,若是没有那个功能,将无法走棋,其函数实现通过:
voidCChessDlg:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
……
}
LButtonUp那个函数要紧实现的功能是:
拖动棋子完毕后放置到拖动后的位置,在进行放置的进程中,需要利用一个Drop的释放函数。
函数实现通过:
voidCChessDlg:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
……
}
棋子走法
typedefstruct
{
shortnChessID;//说明是什么棋子
CHESSMANPOSFrom;//起始位置
CHESSMANPOSTo;//走到什么位置
intScore;//走法的分数
}CHESSMOVE;
在着法生成器中,采纳的大体思想确实是遍历整个棋盘(一个接一个地查看棋盘上的每一个位置点),当发觉有当前下棋方的棋子时先判定它是何种类型的棋子,然后依照其棋子类型而相应地找出其所有合法着法并存入着法队列。
那个地址谈到的“合法着法”包括以下几点:
一、各棋子按其行子规那么行子。
诸如马跳“日”字、象走“田”字、士在九宫内斜行等等(那个地址需要专门注意的是卒(兵)的行子规那么会随其所在位置的不同而发生转变——过河后能够左右平移)。
二、行子不能越出棋盘的界限。
固然所有棋子都不能走到棋盘的外面,同时某些特定的棋子还有自己的行棋界限,如将、士不能出九宫,象不能过河。
3、行子的半路上不能有其它子阻拦(除炮需要隔一个子才能打子之外)和行子的目的点不能有本方的棋子。
4、将帅不能碰面(本程序中只在生成运算机的着法时以为将帅碰面是非法的,而对用户所走的致使将帅碰面的着法并非以为其非法,而只是产生败局算了)。
产生了着法后要将其存入着法队列以供搜索之用,由于搜索会搜索多层,因此在把着法存入着法队列的时候还要同时存储该着法所属的搜索层数。
因此能够将着法队列概念为二维数组,其中第一个数组下标为层数,第二个数组下标为每一层的全数着法数。
着法生成中的各个棋子走法和其他规那么代码见。
棋子的移动由以下的函数别离执行:
帅(将):
VoidCMoveGenerator:
:
Gen_KingMove()
{
}
士:
红士voidCMoveGenerator:
:
Gen_RBishopMove()
{
}
黑士voidCMoveGenerator:
:
Gen_BBishopMove()
{
}
象:
voidCMoveGenerator:
:
Gen_ElephantMove()
{
}
马:
voidCMoveGenerator:
:
Gen_HorseMove()
{
}
车:
voidCMoveGenerator:
:
Gen_CarMove()
{
}
炮:
voidCMoveGenerator:
:
Gen_CanonMove()
{
}
兵(卒):
红兵voidCMoveGenerator:
:
Gen_RPawnMove()
{
}
黑卒voidCMoveGenerator:
:
Gen_BPawnMove()
{
}
搜索算法
咱们用一棵象棋树来表示下棋的进程:
树中每一个结点代表棋盘上的一个局面,对每一个局面依照不同的走法又产生不同的局面。
该象棋树包括三种类型的结点:
奇数层的中间结点和根结点,表示轮到红方走棋;偶数层的中间结点,表示轮到黑方走棋;叶子结点,表示棋局终止。
结合上面所讲的树,假设给每一个结点都打一个分值来评判其对应的局面,咱们通过估值引擎SetEveluator()来实现,过比较该分值的大小来判定局面的好坏。
voidSetEveluator(CEveluation*pEval)
{
m_pEval=pEval;
};
假定甲乙两方下棋,甲胜的局面是一个极大值(一个专门大的正数),那么乙胜的局面确实是一个极小值(极大值的负值),和棋的局面那么是零值(或是接近零的值)。
如此,当轮到甲走棋时他会尽可能地让局面上的分值大,相反轮到乙走棋时他会选尽可能地让局面上的分值小。
反映到博弈树上,即若是假设奇数层表示轮到甲方走棋,偶数层表示轮到乙方走棋。
那么由于甲方希望棋盘上的分值尽可能大,那么在偶数层上会挑选分值最大的结点——偶数层的结点是甲走完一步棋以后的棋盘局面,反映了甲方对棋局形势的要求。
一样道理,由于乙方希望棋盘上的分值尽可能小,那么在奇数层上会选择分值最小的结点。
这是“最小-最大”(Minimax)的大体思想。
如此搜索函数在估值函数的协助下能够通过在奇数层选择分值最大(最小)的结点,在偶数层选择分值最小(最大)的结点的方式来搜索以当前局面为根结点、限定搜索层数之内的整棵树来取得一个最正确的着法。
下面是“最大-最小”的要紧代码
intCNegaMaxEngine:
:
NegaMax(intnDepth)
{
intcurrent=-20000;
intscore;
intCount,i;
BYTEtype;
i=IsGameOver(CurPosition,nDepth);//检查棋局是不是终止
if(i!
=0)
returni;//棋局终止,返回极大/极小值
if(nDepth<=0)//叶子节点取估值
returnm_pEval->Eveluate(CurPosition,(m_nMaxDepth-nDepth)%2,m_nUserChessColor);
//列举当前棋局下一步所有可能的走法
Count=m_pMG->CreatePossibleMove(CurPosition,nDepth,(m_nMaxDepth-nDepth)%2,m_nUserChessColor);
if(nDepth==m_nMaxDepth)
{
//在根节点设定进度条
m_pThinkProgress->SetRange(0,Count);
m_pThinkProgress->SetStep
(1);
}
for(i=0;i { if(nDepth==m_nMaxDepth) m_pThinkProgress->StepIt();//走进度条 type=MakeMove(&m_pMG->m_MoveList[nDepth][i]);//依照走法产生新局面 score=-NegaMax(nDepth-1);//递归调用负极大值搜索下一层节点 UnMakeMove(&m_pMG->m_MoveList[nDepth][i],type);//恢复当前局面 if(score>current)//如果score大于已知的最大值 { current=score;//修改当前最大值为score if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][i];//靠近根部时保留最正确走法 } } returncurrent;//返回极大值 } “最小-最大”思想再加上“树的裁剪”确实是Alpha-Beta搜索算法的核心。 最大体的Alpha-Beta算法的代码如下: intCAlphaBetaEngine: : AlphaBeta(intnDepth,intalpha,intbeta) { intscore; intCount,i; BYTEtype; i=IsGameOver(CurPosition,nDepth);//检查是不是游戏终止 if(i! =0) returni;//终止,返回估值 //叶子节点取估值 if(nDepth<=0) returnm_pEval->Eveluate(CurPosition,(m_nMaxDepth-nDepth)%2,m_nUserChessColor); //此函数找出当前局面所有可能的走法,然后放进m_pMG->m_MoveList当中 Count=m_pMG->CreatePossibleMove(CurPosition,nDepth,(m_nMaxDepth-nDepth)%2,m_nUserChessColor);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 中国象棋 游戏 开发 设计 报告