中国象棋游戏的设计与实现-毕业论文_第1页
中国象棋游戏的设计与实现-毕业论文_第2页
中国象棋游戏的设计与实现-毕业论文_第3页
中国象棋游戏的设计与实现-毕业论文_第4页
中国象棋游戏的设计与实现-毕业论文_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、中国象棋游戏的设计与实现 - 毕业论文毕 业 设 计中国象棋游戏的设计与实现摘要中国象棋发展至今已有数千年的历史了, 它是中华民族智慧的结晶。 在我国, 中国象棋的普及程度是其它棋类无法比拟的, 大至国际、 国内比赛, 小至社区街 道。如今,仅中国就有 2亿人会下中国象棋, 且中国象棋的发展趋势日益国际化。 本文首先研究了中国象棋在计算机中的表示问题, 讨论如何产生着法等一系列相 关内容,其次研究了博弈树的搜索技术及在此基础上发展起来的相关剪枝算法。系统使用MFC文档视图体系结构和Visual C+开发工具,实现了一个具有一定 棋力的中国象棋人机对弈程序。 此博弈程序实现了人机博弈, 悔棋,电

2、脑难度设 置,着法名称生成等功能。关键词:中国象棋 人工智能 博弈树 Alpha-Beta 搜索1 前言 11.1 中国象棋游戏设计背景和研究意义 11.2 国内外象棋软件发展概况11.3 中国象棋游戏设计研究方法11.4 本文的主要工作 22 棋局表示和着法生成 22.1 棋盘和棋子的表示 22.2 着法生成 43 走棋和博弈程序的实现 53.1 博弈程序的实现 5 搜索算法 5 着法排序 8 局面评估 93.2 悔棋和还原功能的实现 113.3 着法名称显示功能的实现 123.4 胜败判定 144 界面设计和系统实现 154.1界面设计154.2系统实现175总结 21参考文献23ABST

3、RACT24致 谢 2526仲恺农业工程学院毕业论文 设计 成绩评定表1 前言中国象棋游戏设计背景和研究意义 中国象棋游戏流传至今已经有数千年的历史了, 是一种古老的文化, 它集文 化、科学、艺术、竞技于一体,有利于开发人的智慧,锻炼人的思维,培养人的 毅力,增强人的竞争意识。自从计算机发明,向各个领域发展,到成为我们现在 每天工作和生活必不可少的一部分的这个过程中, 电子游戏也逐步渗入我们每个 人的娱乐活动中。 在计算机已经普及的今天, 对于可以用计算机进行程序编辑的 人来说,开发属于自己的游戏, 已经不再是梦想, 中国象棋历史悠久不仅源远流 长,而且基础广泛,作为一项智力运动更成为我们游戏

4、开发的首选对象。中国象棋是一项智力游戏, 以往都是人和人下棋, 现在有了计算机我们可以 和计算机竞技, 人可以与计算机进行对弈。 控制计算机的是人类, 而人工智能是 综合性很强的一门边缘学科, 它的中心任务是研究如何使计算机去做那些过去只 能靠人的智力才能做的工作。 因此,对游戏开发过程中的人工智能技术的研究自 然也就成了业界的一个热门研究方向。国内外象棋软件发展概况 最早的象棋软件是一副可以外出携带的电子棋盘,后来升级到电视游戏机。开始出现的一些容量很小的象棋软件如: DOS界面将族、WIN31程序的中国 象棋等等, 与其说人类下不过电脑, 倒不如说是没有耐性等待电脑程序慢吞吞 的搜索算法,

5、 有时甚至怀疑软件是否在搜索中死掉了。 后来,网络上先后出现了 真正的WINDOWS口界面的象棋专业高级软件棋隐、象棋世家、象棋参谋、 象棋奇兵等。总而言之,各类象棋软件既有自身的优点,也存在共通性的缺 陷,如:中局审势不够智能化,走不出弃子取势的人性化佳构,残局时智力明显 低于人脑,难以走出残局例胜的必然着法等。 放眼未来, 象棋软件已经走完了一 波持续上涨的行情,有可能出现逐步降温的滑坡趋势。中国象棋游戏设计研究方法本系统主要用Visual C+进行开发,里面的MFC类库,使游戏开发更加方 便,并利用人工智能相关搜索算法实现人工智能的着法生成, 从而完善整个游戏 的功能。该象棋人机博弈系统

6、实现的功能主要包括:1、选手选择(人或电脑) ;2、人机对弈(人与电脑竞技) ;3、电脑棋力难度选择(电脑下棋能力难度选择,共有4 级:按电脑配置选择难度);4、悔棋、还原;5、着法名称显示(象棋走棋规范名称) 。本文的主要工作第一部分主要介绍了中国象棋游戏开发的背景及意义、 国内外象棋软件的发 展概况和象棋游戏的设计研究方法;第二部分介绍了棋局表示方法和着法生成; 第三部分介绍了走棋和博弈程序的实现; 第四部分介绍了界面设计和系统的实现。棋局表示和着法生成棋盘和棋子的表示即用一个对于中国象棋棋盘局面的表示可采用传统而简单的“棋盘数组”9*10 的数组来存储棋盘上的信息,数组的每个元素存储棋盘

7、上是否有棋子。这 种表示方法简单易行。按此方法棋盘的初始情形如下所示:BYTE CChessBoard910R, 0, 0, P, 0, 0, p, 0, 0, r,H, 0, C, 0, 0, 0, 0, c, 0, h,E, 0, 0, P, 0, 0, p, 0, 0, e,A, 0, 0, 0, 0, 0, 0, 0, 0, a,K, 0, 0, P, 0, 0, p, 0, 0, k,A, 0, 0, 0, 0, 0, 0, 0, 0, a,E, 0, 0, P, 0, 0, p, 0, 0, e,H, 0, C, 0, 0, 0, 0, c, 0, h,R, 0, 0, P, 0,

8、 0, p, 0, 0, rJ给所有棋子定义一个值:#define R_BEGIN R_KING#define R_END R_PAWN#define B_BEGIN B_KING#define B_END B_PAWN#define NOCHESS 0 / 没有棋子黑方: #define B_KING 1 / 黑帅#define B_CAR 2 / 黑车#define B_HORSE 3 / 黑马#define B_CANON4 /黑炮#define B_BISHOP5 /黑士#define B_ELEPHANT6 /黑象#define B_PAWN7 /黑卒八、1红方: #define R

9、_KING8 / 红将#define R_CAR9 /红车#define R_HORSE10/红马#define R_CANON11/红炮#define R_BISHOP12/红士#define R_ELEPHANT13/红相#define R_PAWN14/红兵判断颜色:#define IsBlack x x B_BEGIN & x B_END / 判断某个棋子是不 是黑色#define IsRed x x R_BEGIN & x R_END / 判断某个棋子是不 是红色对于着法的表示, 直接借用棋盘数组的下标来记录着法的起点和目标点。 至 于是什么棋子在走,以及是否吃子、吃的是什么子,在着

10、法结构中并不记录。这 些信息由外部读取棋盘上起点、 终点的数据获得。 着法结构定义如下, 其中还包 含了对着法的历史得分的记录项,以供后面要讲到的“历史启发”所用typedef structshort nChessID; /表明是什么棋子CHESSMANPOS From;/起始位置CHESSMANPOS To; / 走到什么位置int Score; /走法的分数CHESSMOVE;有了对棋盘局面和着法的表示之后,程序才能够完成以下操作:生成所有合法着法;执行着法、撤销着法;针对某一局面进行评估。因而,棋局表示好比是整个程序(计算机下棋引擎部分)的地基,之后 所有的操作都将建立在其基础上。着法生

11、成在着法生成器中, 采用的基本思想就是遍历整个棋盘 (一个接一个地查看棋 盘上的每个位置点),当发现有当前下棋方的棋子时先判断它是何种类型的棋子, 然后根据其棋子类型而相应地找出其所有合法着法并存入着法队列。这里谈到的“合法着法”包括以下几点:1、各棋子按其行子规则行子。诸如马跳“日”字、象走“田”字、士在九 宫内斜行等等 (这里需要特别注意的是卒 (兵)的行子规则会随其所在位置的不 同而发生变化过河后可以左右平移) 。2、行子不能越出棋盘的界限。当然所有棋子都不能走到棋盘的外面,同时 某些特定的棋子还有自己的行棋界限,如将、士不能出九宫,象不能过河。3、行子的半路上不能有其它子阻拦(除了炮需

12、要隔一个子才能打子之外)以及行子的目的点不能有本方的棋子(当然不能自己吃自己了)4、将帅不能碰面(本程序中只在生成计算机的着法时认为将帅碰面是非法 的,而对用户所走的导致将帅碰面的着法并不认为其非法,而只是产生败局罢 了)。产生了着法后要将其存入着法队列以供搜索之用, 由于搜索会搜索多层 (即 考虑双方你来我往好几步, 这样才有利于对局面进行评估以尽可能避免 “目光短 浅”),所以在把着法存入着法队列的时候还要同时存储该着法所属的搜索层数。 因此可以将着法队列定义为二维数组 m_MoveList870 ,其中第一个数组下标 为层数,第二个数组下标为每一层的全部着法数。关于搜索层数,设定为4,实

13、际使用的是1到3(在界面中将其限定为1 3)。搜索层数的增加会显著提高电脑的下棋水平 (当然计算机的棋力在很大程度上也 依赖于局面评估)。在配置为1.5G, 512M内存的计算机上最多只能搜索 3层,再 多将导致搜索时间达到令人无法容忍的地步 (这里还需要特别说明的是, 搜索的 速度也和着法生成的效率以及局面评估的复杂度有关, 因为每分析一个结点都要 执行这两种操作) 。对于每一层的着法数, 也就是当前下棋方针对当前局面的所有可选的合法着 法,据有关数据统计在象棋实战中一般最多情况下也就五六十种。 定义第二个数 组下标为 70,应当可以保证十分的安全。走棋和博弈程序的实现博弈程序的实现搜索算法

14、搜索算法对于整个下棋引擎来说都是至关重要的。 它如同程序的心脏, 驱动 着整个程序。搜索算法的好坏直接影响着程序执行的效率 (从某种角度上, 它影 响着计算机的下棋水平。 因为,计算机必须在有限的时间内完成思考, 搜索速度 快意味着在相同的时间内程序可以“看”得更远, “想”的更多)。关于棋类对弈 程序中的搜索算法,已有成熟的 Alpha-Beta 搜索算法以及其它一些辅助增强算 法(还有众多基于 Alpha-Beta 算法的派生、 变种算法)。我们在程序中直接借鉴 了 Alpha-Beta 搜索算法并辅以历史启发。 本节先介绍 Alpha-Beta 搜索算法: 在 中国象棋里,双方棋手获得相

15、同的棋盘信息。 他们轮流走棋, 目的就是吃掉对方 的将或帅,或者避免自己的将或帅被吃。图 1 博弈树又此,可以用一棵“博弈树”(图1)来表示下棋的过程树中每一个结 点代表棋盘上的一个局面, 对每一个局面 (结点) 根据不同的走法又产生不同的 局面(生出新的结点) ,如此不断进行下去直到再无可选择的走法,即到达叶子 结点(棋局结束)。中国象棋的博弈树的模型大概如下图所示,可以把其中连接 结点的线段看作是着法,不同的着法自然产生不同的局面。该树包含三种类型的结点:1、奇数层的中间结点(以及根结点) ,表示轮到红方走棋;2、偶数层的中间结点,表示轮到黑方走棋;3、叶子结点,表示棋局结束。现在让计算机

16、来下中国象棋, 它应当选择一步对它最有利的着法 (最终导致 它取胜的着法)。获得最佳着法的方法就是“试走”每一种可能的着法,比较它 们所产生的不同后果,然后从中选出能够产生对自己最有利的局面的着法结合上面所讲的博弈树,若给每个结点都打一个分值来评价其对应的局面 (这一任务由后面所讲的局面评估来完成) ,那么可以通过比较该分值的大小来 判断局面的优劣。 假定甲乙两方下棋, 甲胜的局面是一个极大值 (一个很大的正 数),那么乙胜的局面就是一个极小值 (极大值的负值),和棋的局面则是零值 (或 是接近零的值)。如此,当轮到甲走棋时他会尽可能地让局面上的分值大,相反 轮到乙走棋时他会选尽可能地让局面上

17、的分值小。 反映到博弈树上, 即如果假设 奇数层表示轮到甲方走棋, 偶数层表示轮到乙方走棋。 那么由于甲方希望棋盘上 的分值尽可能大,则在偶数层上会挑选分值最大的结点一一偶数层的结点是甲走 完一步棋之后的棋盘局面, 反映了甲方对棋局形势的要求。 同样道理, 由于乙方 希望棋盘上的分值尽可能小, 那么在奇数层上会选择分值最小的结点。 这是“最 小-最大”(Mini)的基本思想。这样搜索函数在估值函数的协助下可以通过在奇 数层选择分值最大(最小)的结点,在偶数层选择分值最小(最大)的结点的方 式来搜索以当前局面为根结点、 限定搜索层数以内的整棵树来获得一个最佳的着 法。然而不幸的是,博弈树相当庞大

18、(它会成指数增长) ,因而搜索(限定层数 以内的)整棵树是一件相当费时的工作一一其时间复杂度为O bn。其中b是分枝因子,即针对各种局面的合法着法的数目的平均值,n是搜索的深度。对于中国象棋而言,在中盘时平均着法数目大约是 40种左右,那么搜索 4层需要检查 250万条路线,搜索 5 层需要检查 1 亿条路线,搜索 6 层需要检查 40 亿条路线!Alpha-Beta 搜索能在不影响搜索精度的前提下大幅减少工作量。 因为,如果考虑到下棋是一个你来我往的交替进行并且相互 “较劲”的过程。 由于每一方都会尽可能将局面导向对自己有利而对对方不利的方向 (假定下棋双 方对棋局有着同样的认知, 即你认为

19、对你很糟糕的局面, 在你的对手看来则是对 他很有利的局面),那么某些局面由于能够产生出很糟糕的局面因而根本没有再 继续考虑的价值。 所以当你看到某个局面有可能产生很糟糕的局面时 (确切地说 这里的“很糟糕”是与之前分析的情况相比较而言的) ,你应当立刻停止对其剩 余子结点的分析不要对它再抱任何幻想了, 如果你选择了它, 那么你必将得 到那个很糟糕的局面, 甚至可能更糟。 这样一来便可以在很大程度上减少搜索的 工作量,提高搜索效率,这称为“树的裁剪” 。下面用图来进一步说明“树的裁剪” 。为了简便起见,将博弈树进行了简化一一每个结点只有三个分支,实际情况中,刚才讲过在盘中应有大约40个分支。假定

20、棋盘上的局面发展到了结点 A (图2),现在轮到你走棋了,你是“最大 的一方”即你希望棋局的分值尽可能的高。 用搜索两层来看一看 “树的裁剪” 对提高搜索效率的帮助。图中 表示该结点要取子结点中的最大值; 表示该结点要取子结点中的最 小值。图 2 树的裁剪首先,考察结点A的子结点B。结点B所属的这一层是轮到你的对手一一“最 小者”来走棋了,目的是使得棋局的分值尽可能的小。 依次考察结点B的各个子 结点,查看它们的分值 (因为事先约定好了搜索两层, 现在已达到搜索深度的要 求了,所以就停下来调用局面评估函数来给它打分)。结点B的第一个子结点(从 左到右算起)返回 -8,第二个子结点返回了 -2,

21、第三个子结点返回了 2。由于结点 B 这层是你的对手来做选择, 假设他一定会做出明智的选择 (你不能寄希望于你的对手会走出一步“昏招” ),那么他会选择返回值为 -2 的那个结点。 -2 最终也就成了从结点B传递回的值,即倘若你(现在位于结点A)选择了产生结点B的走法,使得局面发展到了结点 B。那么下一步,你的对手的选择就会使得棋局 发展成为分值为 -2 的那个结点所表示的局面。再来分析结点A的第二个子结点C,结点C与结点B同属一层,它依然是轮 到你的对手作选择。依次查看结点C的各个子结点的分值,其第一个子结点返回 了 2, 。采用 “裁剪”方法。不必再继续考察结点 C 的剩余子结点了,因为结

22、点 C 已经够糟糕的了,不管结点 C 的剩余子结点有怎样的分值,它最多只能传回 -8(有可能其剩余子结点中还有分值更小的结点,因而结点C还有可能传回更小的值)。而与前面已经分析过的结点 B所传回-2相比较,作为“最大一方”的你显 然更不愿意看到 2 的局面。所以,你当然不会选择相应的着法使得局面发展成为 结点C。因为那样的话,下一步你的对手就会带给你一个分值不高于2的局面。由此,在不影响搜索质量的前提下避免了搜索“无价值的”结点C的剩余子 结点的大量工作, 从而节省了宝贵时间, 为在同样机器配置下搜索更多的层数提 供了可能。“最小 - 最大”的思想再加上“对树的裁剪” ,这就是 Alpha-B

23、eta 搜索算法 的核心。最基本的 Alpha-Beta 算法的代码如下:int AlphaBeta int depth, int alpha, int betaif depth 0/ 如果是叶子节点(到达搜索深度要求)return Evaluate ;/ 则由局面评估函数返回估值GenerateLegalMoves ;/ 产生所有合法着法while MovesLeft/ 遍历所有着法MakeNextMove ; / 执行着法int val -AlphaBeta depth - 1, -beta, -alpha ; /递归调用UnmakeMove ; / 撤销着法if val beta/ 裁剪

24、return beta;if val alpha/ 保留最大值alpha val;return alpha;着法排序Alpha-Beta 搜索算法是在“最小 - 最大”的基础上引入“树的裁剪”的思想以期提高效率,它的效率将在很大程度上取决于树的结构一一如果搜索了没多久就发现可以进行“裁剪”了,那么需要分析的工作量将大大减少,效率自然也就大大提高;而如果直至分析了所有的可能性之后才能做出“裁剪”操作,那此时“裁剪”也已经失去了它原有的价值(因为你已经分析了所有情况,这时的Alpha-Beta 搜索已和“最小 -最大”搜索别无二致了)。因而,要想保证 Alpha-Beta搜索算法的效率就需要调整树

25、的结构, 即调整待搜索的结点的顺序, 使得“裁剪” 可以尽可能早地发生因为,通常可以根据部分已经搜索过的结果来调整将要搜索的结点的顺序 当一个局面经过搜索被认为较好时, 其子结点中往往有一些与它相似的局面 (如 个别无关紧要的棋子位置有所不同) 也是较好的。 由 J.Schaeffer 所提出的 “历 史启发”(History Heuristic )就是建立在这样一种观点之上的。在搜索的过程 中,每当发现一个好的走法,就给该走法累加一个增量以记录其“历史得分” , 一个多次被搜索并认为是好的走法的 “历史得分” 就会较高。 对于即将搜索的结 点,按照“历史得分”的高低对它们进行排序,保证较好的

26、走法( “历史得分” 高的走法)排在前面,这样 Alpha-Beta 搜索就可以尽可能早地进行“裁剪” ,从 而保证了搜索的效率。对于着法的排序可以使用各种排序算法, 在程序中采用了归并排序。 归并排 序的空间复杂度为 O n ,时间复杂度为 O nlog2n ,具有较高的效率。局面评估前文已经讲过了棋局表示、 着法生成、搜索算法(包括搜索辅助“历史启发”), 在象棋程序中如果说搜索算法是心脏, 那么局面评估就是大脑。 搜索算法负责驱 动整个程序, 而局面评估则负责对搜索的内容进行判断和评价。 因而搜索与局面 评估是整个下棋引擎的核心。首先,先介绍一下在局面评估中需要考虑的因素。 就不同的棋类

27、可能要考虑 的因素略有差异。在中国象棋中所要考虑的最基本的几个因素包括如下四点:1、子力总和 子力是指某一棋子本身所具有的价值。通俗地讲就是一个棋子它值个什么 价。例如,车值 10 的话,那可能马值 6,卒值 2 等等。所以在评估局面时,首 先要考虑双方的子力总和的对比。 比如红方拥有士象全加车马炮, 而黑方只有残士象加双马,则红方明显占优。2、棋子位置棋子位置,或称控制区域,是指某一方的棋子在棋盘上所占据(控制)的位 置。例如,沉底炮、过河卒、以及车占士角等都是较好的棋子位置状态,而窝心 马、将离开底线等则属较差的棋子位置状态。3、棋子的机动性棋子的机动性指棋子的灵活度(可移动性) 。例如,

28、起始位置的车机动性较 差,所以下棋讲究早出车。 同样四面被憋马腿的死马机动性也较差 (对于一步也 不能走的棋子,可以认为其机动性为零) 。4、棋子的相互关系这一点的分析较为复杂, 因为一个棋子与其它子之间往往存在多重关系。 如: 一个马可能在对方的炮的攻击之下同时它又攻击着对方的车。在程序中,估值函数最后返回的是每一方的总分的差值, 而各方的总分就是 上面所提到的四个因素的打分的总和。对于子力打分和控制区域打分, 只要遍历棋盘, 当遇到棋子时简单地去查事 先定义好的“子力价值表”和“控制区域价值表” ,取出相对应的值进行累加即 可。对于机动性打分, 需要求出各个子总共有多少种走法, 然后根据各

29、个子所不同的机动性价值每多一种走法就加一次相应的分数。对棋子间相互关系的打分,要用到以下几个数据:int m_BaseValue15; / 存放棋子基本价值int m_FlexValue15;/ 存放棋子灵活性分值short m_AttackPos109;/ 存放每一位置被威胁的信息BYTE m_GuardPos109; / 存放每一位置被保护的信息BYTE m_FlexibilityPos109;/存放每一位置上棋子的灵活性分值int m_chessValue109; / 存放每一位置上棋子的总价值其中计算机会进行所有棋子值的判断, AttackPos 和 GuardPos 分别记录该 棋子

30、受到的威胁和被保护的值。当遍历一遍棋盘之后,子力打分、控制区域打分和机动性打分都可以完成, 而关系表也可以填完。 之后, 再根据关系表来具体考察棋子的相互关系, 进行关 系打分。分析关系时, 首先,对王的攻击保护应分离出来单独考虑, 因为对王的保护 没有任何意义,一旦王被吃掉整个游戏就结束了。其次,对一个普通子, 当它既受到攻击又受到保护的时候要注意如下几个问 题:1、攻击者子力小于被攻击者子力,攻击方将愿意换子。比如,一个车正遭 受一个炮的攻击,那么任何对车的保护都将失去意义一一对方肯定乐意用一个炮 来换一个车。2、多攻击 / 单保护的情况, 并且攻击者最小子力小于被攻击者子力与保护者 子力

31、之和,则攻击方可能以一子换两子。3、三攻击/两保护的情况, 并且攻击者子力较小的二者之和小于被攻击者子 力与保护者子力之和,则攻击方可能以两子换三子。4、攻击方与保护方数量相同,并且攻击者子力小于被攻击者子力与保护者子力之和再减去保护者中最大子力,则攻击方可能以 n子换n子。当然,上述四条只是覆盖了最常见的几种情况,覆盖并不全面。而且,在程 序中并没有直接地重新考虑双方兑子之后的控制区域及机动性变化情况 (之所以 说没有“直接地重新考虑” ,是因为搜索继续展开结点后仍会考虑这些因素,只 是目前不知这样效果是否受影响考察这两种方法在效果上的差异需要一定 数量的试验数据的支持) 。所以,如果今后要

32、对引擎进行改进,提高程序的下棋 水平的话,还应当在此进行研究。悔棋和还原功能的实现悔棋和还原是棋类软件中较为基本的功能。 要实现悔棋和还原功能, 首先要 明确哪些信息应当被保存以供悔棋和还原所使用。在程序中保存了如下信息:棋局表示中所定义的棋盘数组;各棋子的贴图位置; 这里需要特别说明的是通常象棋程序处于程序效率的考虑并不保存所有棋 子的信息,而只是保存之前一步的走棋信息。 此后当悔棋的时候, 需要撤销着法; 还原的时候,需要执行着法。 然而,在编写自己的程序时一来考虑到程序的可读 性和不易出错性,二来考虑到对当今的计算机的配置来说这点开销基本上不会对 程序的效率产生什么影响。因此保存了全部棋

33、子的信息。根据所要保存的数据定义了如下基本结构类型:typedef structCHESSMOVE cmChessMove;short nChessID;/ 被吃掉的棋子UNDOMOVE;在对弈过程中,每一回合都将棋局信息 (这里指前面所说的需要保存的信息) 保存至走法队列, 以供悔棋所用。 还原功能是与悔棋功能相对应的, 只有当产生 了悔棋功能之后, 还原功能才会被激活。 一个回合的结束意味着前一次操作没有 悔棋功能的产生,因此还原队列也应被清空。在悔棋中主要完成了以下任务:1、下棋回合数减一;2、将当前局面信息保存至走法队列,以供还原所用;3、从走法队列中取出上一回合的棋局信息,恢复到当前

34、局面,然后将其从 队列中剔除掉;4、将显示着法名称的列表框中的本回合的着法名称保存到一个着法名称队 列,以供还原所用。然后从列表框中删除它。而在还原中所做的刚好和悔棋相反:1、下棋回合数加一;2、将当前局面信息保存至队列,以供悔棋所用;3、从队列中取出最近一次悔棋前的棋局信息,恢复到当前局面,然后将其 从队列中剔除;4、从走法队列中取出最近一次存入的着法名称(两项,因为每回合会产生 两步着法),将其重新显示到列表框中。然后将其从中剔除。以上便是悔棋和还原功能所完成的具体操作, 其代码分别写入悔棋和还原按 钮( Button )的事件处理函数中。着法名称显示功能的实现每当下棋者(用户或是计算机)

35、走一步棋,在棋盘旁边的一个列表框控件(List Box)中按照中国象棋关于着法描述的规范要求显示出该着法的名称。如:炮八进四、马二进三此类。为了获得该着法名称,我们编写了一个函数,其功能 就是将被移动的棋子类型以及走法的起点坐标、 终点坐标这些信息转换成中国象 棋所规范的着法名称。实现此功能代码如下:void CGradientProgressCtrl:OnPaintCPaintDC dc this ; / device context for painting/ TODO: Add your message handler code hereif m_nCurrentPosition m_n

36、Lower | m_nCurrentPosition m_nUpperCRect rect;GetClientRect rect ;CBrush brush;brush.CreateSolidBrush :GetSysColor COLOR_3DFACE dc.FillRect &rect,&brush ;VERIFY brush.DeleteObject ;return;CRect rectClient;GetClientRect rectClient ;float Width float m_nCurrentPosition/ float m_nUpper*floatrectClient.

37、right ;/ 绘制DrawGradient &dc,rectClient, int Width ;/ 显示进程条进度文字if m_bShowPercentCString percent;floatpercent.Format %d%, int 100* m_nCurrentPosition/m_nUpper ;dc.SetTextColor m_clrText ;dc.SetBkMode TRANSPARENT ;dc.DrawText percent,&rectClient,DT_VCENTER|DT_CENTER|DT_SINGLELINE ;/ 显示其他文字if m_bIsShowT

38、extdc.SetTextColor m_clrText ;dc.SetBkMode TRANSPARENT ;dc.DrawTextm_strShow,&rectClient,DT_VCENTER|DT_CENTER|DT_SINGLELINE ;int CGradientProgressCtrl:SetPos int nPos/Set the Position of the Progressm_nCurrentPosition nPos;return CProgressCtrl:SetPos nPos ;int CGradientProgressCtrl:StepItm_nCurrentP

39、osition+ m_nStep;return CProgressCtrl:StepIt ;以下介绍如何对列表框控件(List Box)进行操作,以显示或删除着法名称。首先,在 ChessDlg 下定义以下函数:this- GetMoveStr nFromX,nFromY,nToX,nToY,nSourceID ;/ 用来获得刚下的一步棋的走法;void CChessDlg:AddChessRecord int nFromX,int nFromY,int nToX,int nToY,int nUserChessColor,int nSourceID/ 将走法添加进下棋记录;然后,显示在 Lis

40、tbox 中。当列表框中的项的数目超过列表框的显示范围时, 列表框会自动添加垂直滚动条(前提是其 VerticalScrollbar属性要为 True 该属性默认即为 True)但是显示的内容依然是最早加进来的项。 在控件属性里选择 Vertical Scroll 使得列表框可垂直滚动以显示最新的着法名称。想要从列表框中删除项时,可以使用m_lstChessRecord.DeleteString m_lstChessRecord.GetCount -1 ; 减一之后正好是最后一项的行号。胜败判定胜负判定只要一方将另一方的将或帅吃掉就是胜者。主要代码如下:int CSearchEngine:Is

41、GameOver BYTE position9, int nDepthint i,j;BOOL RedLive FALSE,BlackLive FALSE;/ 检查红方九宫是否有帅for i 7;i 10;i+for j 3;j 6;j+if positionij B_KINGBlackLive TRUE;if positionij R_KINGRedLive TRUE;/ 检查黑方九宫是否有将for i 0;i 3;i+for j 3;j 6;j+if positionij B_KINGBlackLive TRUE;if positionij R_KINGRedLive TRUE;i m_n

42、Depth-nDepth+1 %2;/ 取当前奇偶标志,奇数层为电脑方,偶数层为 用户方。/ 红方不在if !RedLiveif ireturn 19990+nDepth; /奇数层返回极大值elsereturn -19990-nDepth;/偶数层返回极小值/ 黑方不在if !BlackLiveif ireturn -19990-nDepth;/奇数层返回极小值elsereturn 19990+nDepth; /偶数层返回极大值return 0;/ 将帅都在,返回 0 界面设计和系统实现界面设计关于棋盘和棋子,建了一个基于对话框的 MFC应用程序。主要工作都在对话框类的两个文件 CChess

43、DIg.h和CChessDIg.cpp下展开。代码主要分布于以下三大部分:1、初始化部分BOOL CCChessUIDIg:OnInitDiaIogOnInitDiaIog 负责的是对话框的初始化。 可以把有关中国象棋的棋局初始 化情况也放在了这里面。初始化的内容包括:对引擎部分所用到的变量的初始化。包括对棋盘上的棋子位置进行初始化 (棋盘数组的初始化) ,对搜索深度、当前走棋方标志、棋局是否结束标志等的 初始化;对棋盘、棋子的贴图位置 (即棋盘、棋子在程序中实际显示位置) 的初始化; 对程序辅助部分所用到的一些变量的初始化。 包括对悔棋、还原队列的清空, 棋盘、棋子样式的默认形式, 下棋模式

44、的默认选择, 以及着法名称列表的初始化 等。2、绘图部分void CCChessUIDlg:OnPaintOnPaint 函数负责的是程序界面的绘图。因此,在这里将要完成棋盘、棋 子的显示走棋起始位置和目标位置的提示框的显示。由于棋盘、棋子等都是以位图的形式给出的。所以在 OnPaint 函数里做的 工作主要都是在贴位图。需要注意的是由于位图文件不能像 GIF 文件那样有透明的背景并且棋子是 圆形的而位图文件只能是矩形的, 所以如果直接贴图的话会在棋盘上留下一块白 色的边框一一棋子的背景。因此,要想让棋子文件的背景“隐藏”需要通过一些 “与”和“异或”操作来屏蔽掉棋子的背景。3、走棋部分(用户

45、动作响应部分)为WM_LBUTTONDOW添加消息响应事件,可得到如下函数:void CCChessUIDlg:OnLButtonDown UINT nFlags, CPoint point当用户在窗口客户区按下鼠标左键时,程序就会调用 OnLButtonDown UINT nFlags, CPoint point 函数来进行响应。其中第二个参数 CPoint point 是在本 程序中所要用到的, 它给出了当鼠标左键被按下时, 鼠标指针的位置坐标。 可以通过这一信息来得知用户的走法在OnLButtonDown函数里处理如下两种操作:1、如果用户点击鼠标的位置落在己方的棋子上,表示用户选中了该

46、棋子, 下一步将移动该子进行走棋 (也可能用户下一步将会选择己方另外的棋子, 总之 这一操作会记录下用户所选的将要走的棋子) 。2、如果之前用户已经选过了棋子,那么这一次的点击(如果不是另选本方 的其它棋子的话)表达了用户的一次走棋过程。在收到用户传达的走棋信息后, 可先判断该着法是否合法(是否符合中国象棋的游戏规则) ,如果合法,则执行 之。紧接着调用引擎的搜索函数计算出计算机对用户着法的应着, 然后执行该应 着。如此,在On LButt on Down函数里,实现了人与机器的对弈(当然每走一步棋, 也还需要绘图函数来显示棋盘局面的更新) 。以上三部分并非界面程序的全部, 而仅仅是与程序密切

47、相关的部分。 此外还 有其它部分对程序同样必不可少,但这些部分主要由MFC自动生成,无需人为改 动,故在此不多做介绍。系统实现现在已具备了实现一款中国象棋对弈程序引擎部分的所有要素, 将上述模块 分别写作 .h 头文件。如下:ChessDlg.h象棋相关定义。包括棋盘局面和着法的表示。BaseClasses.h着法生成器。就当前局面生成某一方所有合法着法。MoveList.h搜索部分。使用搜索求出最佳着法。Thinkdef.h历史启发。 Alpha-Beta 搜索之补充,以提高搜索效率。Thinker.h着法排序。对着法按其历史得分进行降序排序,以提高搜索效率。ThinkOptionDlg.h

48、局面评估。为某一特定局面进行评分。当实现了引擎部分的各要素时, 可先建立一个 Win32 控制台项目, 之后只要 再添加一个 .cpp 文件负责接受用户的输入、调用搜索函数、显示搜索结果,便 可简单的测试引擎了 (采用输入着法的起点坐标和终点坐标的方式来传送用户走 棋的信息。同样,程序显示计算机走棋的起点坐标和终点坐标来做出回应) 。此后,等到界面部分初步完成,引擎的上述各模块无需作任何改动, 仍以.h 头文件的形式加入界面工程, 只要由界面中的某个 .cpp 文件调用搜索函数即可。 这种连接方式实现起来非常简单。首先,执行该软件,系统并不需要很高的配置,CPU在 1.5G以上,内存在512M

49、以上就可以很流畅地执行。下面简单介绍一下象棋相关规则:对局时,由执红棋的一方先走,双方轮流各走一着,直至分出胜、负、和, 对局即终了。 轮到走棋的一方, 将某个棋子从一个交叉点走到另一个交叉点, 或 者吃掉对方的棋子而占领其交叉点, 都算走一着。 双方各走一着, 称为一个回合。 如果有一方的主帅被对方吃了,就算那一方输。各种棋子的走法:帅(将):帅和将是棋中的首脑,是双方竭力争夺的目标。它只能在“九宫” 之内活动,可上可下,可左可右,每次走动只能按竖线或横线走动一格。帅与将 不能在同一直线上直接对面,否则走方判负。仕(士):仕(士)是帅(将)的贴身保镖,它也只能在九宫内走动。它的 行棋路径只能

50、是九宫内的斜线。相(象):相(象)的主要作用是防守,保护自己的帅(将) 。它的走法是每 次循对角线走两格,俗称“象走田” 。相(象)的活动范围限于“河界”以内的 本方阵地,不能过河,且如果它走的“田”字中央有一个棋子,就不能走,俗称 “塞象眼”。车:车在象棋中威力最大,无论横线、竖线均可行走,只要无子阻拦,步 数不受限制。因此,一车可以控制十七个点,故有“一车十子寒”之称。炮:炮在不吃子的时候,走动与车完全相同。 马:马走动的方法是一直一斜, 即先横着或直着走一格, 然后再斜着走一个 对角线,俗称“马走日”。马一次可走的选择点可以达到四周的八个点, 故有“八 面威风”之说。如果在要去的方向有别

51、的棋子挡住,马就无法走过去,俗称“蹩 马腿”。兵(卒):兵(卒)在未过河前,只能向前一步步走,过河以后,除不能后 退外,允许左右移动,但也只能一次一步。在懂的以上规则之后并可进行游戏, 执行该软件后, 并可进入游戏界面。 棋 盘界面(图 3)所示:图 3 棋盘界面从界面上方的菜单栏中可以进行相关设置参数设置界面(图 4)如下:图 4 参数设置界面 等你将参数设置完毕之后,既可进入游戏。走法记录界面(图 5)如下:图 5 走法记录界面其他辅助功能界面(图 6)如下:图 6 其他辅助功能界面 你可以通过上面四个辅助功能对棋局进行研究,从而提高你的下棋水平。例如,您是红方,第一步走的是兵七进一或兵三

52、进一,电脑则会炮 2 进 4 或炮 8 进 4(图 7):图 7 程序运行界面以上是系统实现的所有界面及功能测试。总结2009年 2 月,我开始了我的毕业论文工作,时至今日,论文基本完成。从 最初的茫然, 到慢慢的进入状态, 再到对思路逐渐的清晰, 整个写作过程难以用 语言来表达。历经了几个月的奋战,紧张而又充实的毕业设计终于落下了帷幕。 回想这段日子的经历和感受, 我感慨万千, 在这次毕业设计的过程中, 我拥有了 无数难忘的回忆和收获。脚踏实地,认真严谨,实事求是的学习态度,不怕困难、坚持不懈、吃苦耐 劳的精神是我在这次设计中最大的收益。 我想这是一次意志的磨练, 是对我实际 能力的一次提升

53、,也会对我未来的学习和工作有很大的帮助。在这次毕业设计中也使我们的同学关系更进一步了, 同学之间互相帮助, 有 什么不懂的大家在一起商量, 听听不同的看法对我们更好的理解知识, 所以在这 里非常感谢帮助我的同学。在此更要感谢我的导师和专业老师, 是你们的细心指导和关怀, 使我能够顺 利的完成毕业论文。 在我的学业和论文的研究工作中无不倾注着老师们辛勤的汗 水和心血。老师的严谨治学态度、渊博的知识、无私的奉献精神使我深受启迪。 从尊敬的导师身上, 我不仅学到了扎实、 宽广的专业知识, 也学到了做人的道理。 在此我要向我的导师致以最衷心的感谢和深深的敬意。本论文对计算机博弈技术进行了研究, 在深入

54、研究了机器下中国象棋方法理 论基础上, 实现了一个具有一定棋力的人机对弈中国象棋程序。然而,由于时间关系,程序也存在着几点不足:第一:没对计算机下棋引擎部分作更深一步的挖掘和研究。 对于诸如位棋盘(BitBoard )、迭代加深(Iterative Deepening)、机器学习(Machine Learning ) 等当今棋类对弈程序中所采用的先进技术和思想, 在程序中并未涉及。 这在一定 程度上影响了程序中下棋引擎的工作效率。第二:由于对人工智能算法的不熟悉,在 Alpha-Beta 搜索算法上花了大量 的时间和精力来了解,导致程序进度的缓慢。尽管,这些问题最终都得以解决,但却影响了程序开发的进程。第三、程序仍在局面检测和贴图刷新上存在着随机性的出错可能 (出错几率 很小)参考文献1 王小春.PC游戏编程(人机博弈)M.重庆:重庆大学出版社,2002.2 网冠科技.Visual C+.NET 小游戏开发时尚编程百例M.西安:机械 工业出版社, 2004.3 陈建春 .Visual C+ 高级编程技术开发实例剖析 M. 西安: 电子工 业出版社, 1999.4 涂光平等.V

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论