ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:44.26KB ,
资源ID:15180004      下载积分:5 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-15180004.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(完整版中国象棋游戏设计与实现毕业设计毕业论文设计.docx)为本站会员(b****1)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

完整版中国象棋游戏设计与实现毕业设计毕业论文设计.docx

1、完整版中国象棋游戏设计与实现毕业设计毕业论文设计优秀论文 审核通过未经允许 切勿外传中国象棋游戏的设计与实现摘 要象棋程序的实现可以被分为人工智能和界面程序辅助两大部分。人工智能部分主要体现计算机的下棋思路,既计算机如何进行思考并以最佳走法完成下一步,先由相应的搜索算法进行搜索,并对各种可能的走法进行估值,从中选择胜利面最大的一步;而界面及程序辅助部分主要便于用户通过以前的下棋步骤,更好地调整下棋思路,着法显示使用户能够清楚地知道下棋过程,更准确地把握整个局面。本文首先研究了中国象棋在计算机中的表示问题,接着讨论如何产生着法一系列相关内容。其次研究了博弈树的极小极大搜索技术及在此基础上发展起来

2、的Alpha-Beta剪枝算法,使用MFC文档视图体系结构和Visual C+开发工具,实现了一个具有一定棋力的中国象棋人机对弈程序。 关键词:中国象棋;人工智能;博弈树;Alpha-Beta搜索The Design and Implementation of Chinese ChessAbstractThe implementation of a chess program can be decomposed into two major parts: the artificial intelligence and the user interface and program assist.

3、 The part of artificial intelligence shows the way of computer thinking, and which step is the best step would be decided by it. Firstly, the computer uses search algorithms to search, and then evaluates every impossible step, finally choses the best one, the other part is used for the player to adj

4、ust computer, then discusses the integrated computer MFC SDI document view architecture by using Visual C+. Key words: Chinese chess; Artificial Intelligence; Game tree; Alpha-Beta searching目 录论文总页数:22页1 引言 11.1 象棋设计背景和研究意义 11.2 象棋设计研究方法 12 人工智能算法设计 22.1 棋局表示 32.2 着法生成 42.3 搜索算法 52.4 历史启发及着法排序 92.5

5、局面评估 92.6 程序组装 113 界面及程序辅助设计 123.1 界面基本框架 123.2 多线程 133.3 着法名称显示 143.4 悔棋和还原 154 系统实现 16结 论 19参考文献 20致 谢 21声 明 221 引言1.1 象棋设计背景和研究意义电脑游戏行业经过二十年的发展,已经成为与影视、音乐等并驾齐驱的全球最重要的娱乐产业之一,其年销售额超过好莱坞的全年收入。游戏,作为一种娱乐活动。早期的人类社会由于生产力及科技的制约,只能进行一些户外的游戏。随着生产力的发展和科技进步,一种新的游戏方式电子游戏也随之诞生。当计算机发明以后,电子游戏又多了一个新的载体。电子游戏在整个计算机

6、产业的带动下不断地创新、发展着。自从计算机发明,向各个领域发展,到成为我们现在每天工作和生活必不可少的一部分的这个过程中,电子游戏也逐步渗入我们每个人的娱乐活动中。而计算机已经普及的今天,对于可以用计算机进行程序编辑的人来说,开发属于自己的游戏,已经不再是梦想。事实上,个人计算机软件市场的大约80%销售份额是来自游戏软件。棋牌游戏属于休闲类游戏,相对于角色扮演类游戏和即时战略类游戏等其它游戏,具有上手快、游戏时间短的特点,更利于用户进行放松休闲,为人们所喜爱,特别是棋类游戏,方便、快捷、操作简单,在休闲娱乐中占主要位置。作为中华民族悠久文化的代表之一,中国象棋不仅源远流长,而且基础广泛,作为一

7、项智力运动,中国象棋开始走向世界。随着计算机处理速度的飞速提高,人们很早就提出了疑问:计算机是否会超越人类?世界国际象棋大师已被计算机打败,计算机已经超过了人类?而人工智能是综合性很强的一门边缘学科,它的中心任务是研究如何使计算机去做那些过去只能靠人的智力才能做的工作。因此,对游戏开发过程中的人工智能技术的研究自然也就成了业界的一个热门研究方向。 1.2 象棋设计研究方法对于象棋来说,核心设计主要包括人工智能算法的以及整个游戏中界面及程序辅助部分的实现,主要用 Visual C+ 进行开发,里面的MFC类库,使游戏开发更加方便,并利用人工智能相关搜索算法实现人工智能的着法生成,从而完善整个游戏

8、的功能。本文的目标是实现一款有着一定下棋水平且交互友好的中国象棋人机对弈程序。该程序功能包括:*人机对弈;*搜索深度设定;(电脑棋力选择)*悔棋、还原;*着法名称显示;整个程序的实现可分为两大部分:一、人工智能算法设计(计算机下棋引擎)该部分实现了如何让计算机下中国象棋,其中涉及人机对弈的基本理论及思想,是该程序的核心部分,同时也是本项目研究的重点所在。二、界面及程序辅助设计光有下棋引擎尚不能满足人机交互的基本要求,因此还需要一个框架(界面)来作为引擎的载体,同时提供一些诸如悔棋,还原之类的附属功能(程序辅助)。下面分别介绍各部分实现。由于界面及程序辅助部分涉及内容宽泛而又繁琐,因而本文只介绍

9、其中重点部分。2 人工智能算法设计程序的基本框架:从程序的结构上讲,大体上可以将引擎部分划分为四大块:棋局表示;着法生成;搜索算法;局面评估。程序的大概的思想是:首先使用一个数据结构来描述棋局信息,对某一特定的棋局信息由着法生成器生成当前下棋方所有合法的着法并依次存入着法队列。然后通过搜索算法来逐一读取着法并调用局面评估函数对该着法所产生的后继局面进行评估打分,从中选出一个最有可能导致走棋方取胜的着法。在搜索的过程中还可以采用一些辅助手段来提高搜索的效率。其过程如下所示(图1):图 1 程序结构图下面将分别介绍程序各个部分:2.1 棋局表示计算机下棋的前提是要让计算机读懂象棋。所谓读懂,即计算

10、机应该能够清楚地了解到棋盘上的局面(棋盘上棋子的分布情况)以及下棋方所走的每一种着法。因而首先需要设计一套数据结构来表示棋盘上的局面以及着法。对于棋盘局面的表示可采用传统而简单的“棋盘数组”。即用一个9*10的数组来存储棋盘上的信息,数组的每个元素存储棋盘上是否有棋子。这种表示方法简单易行(缺点是效率不是很高)。按此方法棋盘的初始情形如下所示:BYTE CChessBoard910 = R, 0, 0, P, 0, 0, p, 0, 0, r,H, 0, C, 0, 0, 0, 0, c, 0, )。其中b是分枝因子,即针对各种局面的合法着法的数目的平均值,n是搜索的深度。对于中国象棋而言,在

11、中盘时平均着法数目大约是40种左右,那么搜索4层需要检查250万条路线,搜索5层需要检查1亿条路线,搜索6层需要检查40亿条路线!Alpha-Beta搜索能在不影响搜索精度的前提下大幅减少工作量。因为,如果考虑到下棋是一个你来我往的交替进行并且相互“较劲”的过程。由于每一方都会尽可能将局面导向对自己有利而对对方不利的方向(假定下棋双方对棋局有着同样的认知,即你认为对你很糟糕的局面,在你的对手看来则是对他很有利的局面),那么某些局面由于能够产生出很糟糕的局面因而根本没有再继续考虑的价值。所以当你看到某个局面有可能产生很糟糕的局面时(确切地说这里的“很糟糕”是与之前分析的情况相比较而言的),你应当

12、立刻停止对其剩余子结点的分析不要对它再抱任何幻想了,如果你选择了它,那么你必将得到那个很糟糕的局面,甚至可能更糟这样一来便可以在很大程度上减少搜索的工作量,提高搜索效率,这称为“树的裁剪”。下面用图来进一步说明“树的裁剪”。为了简便起见,将博弈树进行了简化每个结点只有三个分支,实际情况中,刚才讲过在盘中应有大约40个分支。假定棋盘上的局面发展到了结点A(图3),现在轮到你走棋了,你是“最大的一方”即你希望棋局的分值尽可能的高。用搜索两层来看一看“树的裁剪”对提高搜索效率的帮助。图中表示该结点要取子结点中的最大值;表示该结点要取子结点中的最小值。图3 树的裁剪首先,考察结点A的子结点B。结点B所

13、属的这一层是轮到你的对手“最小者”来走棋了,目的是使得棋局的分值尽可能的小。依次考察结点B的各个子结点,查看它们的分值(因为事先约定好了搜索两层,现在已达到搜索深度的要求了,所以就停下来调用局面评估函数来给它打分)。结点B的第一个子结点(从左到右算起)返回10,第二个子结点返回了-5,第三个子结点返回了2。由于结点B这层是你的对手来做选择,假设他一定会做出明智的选择(你不能寄希望于你的对手会走出一步“昏招”),那么他会选择返回值为-5的那个结点。-5最终也就成了从结点B传递回的值,即倘若你(现在位于结点A)选择了产生结点B的走法,使得局面发展到了结点B。那么下一步,你的对手的选择就会使得棋局发

14、展成为分值为-5的那个结点所表示的局面。再来分析结点A的第二个子结点C,结点C与结点B同属一层,它依然是轮到你的对手作选择。依次查看结点C的各个子结点的分值,其第一个子结点返回了-8采用 “裁剪”方法。不必再继续考察结点C的剩余子结点了,因为结点C已经够糟糕的了,不管结点C的剩余子结点有怎样的分值,它最多只能传回-8(有可能其剩余子结点中还有分值更小的结点,因而结点C还有可能传回更小的值)。而与前面已经分析过的结点B所传回-5相比较,作为“最大一方”的你显然更不愿意看到-8的局面。所以,你当然不会选择相应的着法使得局面发展成为结点C。因为那样的话,下一步你的对手就会带给你一个分值不高于-8的局

15、面。由此,在不影响搜索质量的前提下避免了搜索“无价值的”结点C的剩余子结点的大量工作,从而节省了宝贵时间,为在同样机器配置下搜索更多的层数提供了可能。“最小-最大”的思想再加上“对树的裁剪”,这就是Alpha-Beta搜索算法的核心。最基本的Alpha-Beta算法的代码如下:int AlphaBeta(int depth, int alpha, int beta) if (depth = 0) 如果是叶子节点(到达搜索深度要求) return Evaluate(); 则由局面评估函数返回估值 GenerateLegalMoves(); 产生所有合法着法 while (MovesLeft()

16、遍历所有着法 MakeNextMove(); 执行着法 int val = -AlphaBeta(depth - 1, -beta, -alpha); 递归调用 UnmakeMove(); 撤销着法 if (val = beta) 裁剪 return beta; if (val alpha) 保留最大值 alpha = val; return alpha;2.2 历史启发及着法排序既然Alpha-Beta搜索算法是在“最小-最大”的基础上引入“树的裁剪”的思想以期提高效率,那么它的效率将在很大程度上取决于树的结构如果搜索了没多久就发现可以进行“裁剪”了,那么需要分析的工作量将大大减少,效率自然

17、也就大大提高;而如果直至分析了所有的可能性之后才能做出“裁剪”操作,那此时“裁剪”也已经失去了它原有的价值(因为你已经分析了所有情况,这时的Alpha-Beta搜索已和“最小-最大”搜索别无二致了)。因而,要想保证Alpha-Beta搜索算法的效率就需要调整树的结构,即调整待搜索的结点的顺序,使得“裁剪”可以尽可能早地发生。可以根据部分已经搜索过的结果来调整将要搜索的结点的顺序。因为,通常当一个局面经过搜索被认为较好时,其子结点中往往有一些与它相似的局面(如个别无关紧要的棋子位置有所不同)也是较好的。由J.Schaeffer所提出的“历史启发”(History Heuristic)就是建立在这

18、样一种观点之上的。在搜索的过程中,每当发现一个好的走法,就给该走法累加一个增量以记录其“历史得分”,一个多次被搜索并认为是好的走法的“历史得分”就会较高。对于即将搜索的结点,按照“历史得分”的高低对它们进行排序,保证较好的走法(“历史得分”高的走法)排在前面,这样Alpha-Beta搜索就可以尽可能早地进行“裁剪”,从而保证了搜索的效率。对于着法的排序可以使用各种排序算法,在程序中采用了归并排序。归并排序的空间复杂度为O(n),时间复杂度为O(nlog2n),具有较高的效率。2.3 局面评估前文已经讲过了棋局表示、着法生成、搜索算法(包括搜索辅助), 在象棋程序中如果说搜索算法是心脏,那么局面

19、评估就是大脑。搜索算法负责驱动整个程序,而局面评估则负责对搜索的内容进行判断和评价。因而搜索与局面评估是整个下棋引擎的核心。首先,先介绍一下在局面评估中需要考虑的因素。就不同的棋类可能要考虑的因素略有差异。在中国象棋中所要考虑的最基本的几个因素包括如下四点:1、子力总和子力是指某一棋子本身所具有的价值。通俗地讲就是一个棋子它值个什么价。例如,车值500的话,那可能马值300,卒值80等等。所以在评估局面时,首先要考虑双方的子力总和的对比。比如红方拥有士象全加车马炮,而黑方只有残士象加双马,则红方明显占优。2、棋子位置棋子位置,或称控制区域,是指某一方的棋子在棋盘上所占据(控制)的位置。例如,沉

20、底炮、过河卒、以及车占士角等都是较好的棋子位置状态,而窝心马、将离开底线等则属较差的棋子位置状态。3、棋子的机动性棋子的机动性指棋子的灵活度(可移动性)。例如,起始位置的车机动性较差,所以下棋讲究早出车。同样四面被憋马腿的死马机动性也较差(对于一步也不能走的棋子,可以认为其机动性为零)。4、棋子的相互关系这一点的分析较为复杂,因为一个棋子与其它子之间往往存在多重关系。如:一个马可能在对方的炮的攻击之下同时它又攻击着对方的车。在程序中,估值函数最后返回的是每一方的总分的差值,而各方的总分就是上面所提到的四个因素的打分的总和。对于子力打分和控制区域打分,只要遍历棋盘,当遇到棋子时简单地去查事先定义

21、好的“子力价值表”和“控制区域价值表”,取出相对应的值进行累加即可(这些值的具体设定参考了前人的程序并作了适当的调整,今后仍应根据电脑下棋所反映出的实际问题对这些值作适当修改)。对于机动性打分,需要求出各个子总共有多少种走法,然后根据各个子所不同的机动性价值每多一种走法就加一次相应的分数。 对棋子间相互关系的打分,要用到以下几个数据:int m_BaseValue15; 存放棋子基本价值int m_FlexValue15; 存放棋子灵活性分值short m_AttackPos109; 存放每一位置被威胁的信息BYTE m_GuardPos109; 存放每一位置被保护的信息BYTE m_Flex

22、ibilityPos109;存放每一位置上棋子的灵活性分值int m_chessValue109; 存放每一位置上棋子的总价值其中计算机会进行所有棋子值的判断,AttackPos和GuardPos分别记录该棋子受到的威胁和被保护的值。当遍历一遍棋盘之后,子力打分、控制区域打分和机动性打分都可以完成,而关系表也可以填完。之后,再根据关系表来具体考察棋子的相互关系,进行关系打分。分析关系时,首先,对王的攻击保护应分离出来单独考虑,因为对王的保护没有任何意义,一旦王被吃掉整个游戏就结束了。其次,对一个普通子,当它既受到攻击又受到保护的时候要注意如下几个问题:1、攻击者子力小于被攻击者子力,攻击方将愿

23、意换子。比如,一个车正遭受一个炮的攻击,那么任何对车的保护都将失去意义对方肯定乐意用一个炮来换一个车。2、多攻击单保护的情况,并且攻击者最小子力小于被攻击者子力与保护者子力之和,则攻击方可能以一子换两子。3、三攻击两保护的情况,并且攻击者子力较小的二者之和小于被攻击者子力与保护者子力之和,则攻击方可能以两子换三子。4、攻击方与保护方数量相同,并且攻击者子力小于被攻击者子力与保护者子力之和再减去保护者中最大子力,则攻击方可能以n子换n子。当然,上述四条只是覆盖了最常见的几种情况,覆盖并不全面。而且,在程序中并没有直接地重新考虑双方兑子之后的控制区域及机动性变化情况(之所以说没有“直接地重新考虑”

24、,是因为搜索继续展开结点后仍会考虑这些因素,只是目前不知这样效果是否受影响考察这两种方法在效果上的差异需要一定数量的试验数据的支持)。所以,如果今后要对引擎进行改进,提高程序的下棋水平的话,还应当在此进行研究。2.4 程序组装至此,已具备了实现一款中国象棋对弈程序引擎部分的所有要素,将上述模块分别写作.(UINT nFlags, CPoint point)当用户在窗口客户区按下鼠标左键时,程序就会调用OnLButtonDown(UINT nFlags, CPoint point)函数来进行响应。其中第二个参数CPoint point是在本程序中所要用到的,它给出了当鼠标左键被按下时,鼠标指针的

25、位置坐标。可以通过这一信息来得知用户的走法。在OnLButtonDown函数里处理如下两种操作:1、如果用户点击鼠标的位置落在己方的棋子上,表示用户选中了该棋子,下一步将移动该子进行走棋(也可能用户下一步将会选择己方另外的棋子,总之这一操作会记录下用户所选的将要走的棋子)。2、如果之前用户已经选过了棋子,那么这一次的点击(如果不是另选本方的其它棋子的话)表达了用户的一次走棋过程。在收到用户传达的走棋信息后,可先判断该着法是否合法(是否符合中国象棋的游戏规则),如果合法,则执行之。紧接着调用引擎的搜索函数计算出计算机对用户着法的应着,然后执行该应着。如此,在OnLButtonDown函数里,实现

26、了人与机器的对弈(当然每走一步棋,也还需要绘图函数来显示棋盘局面的更新)。以上三部分并非界面程序的全部,而仅仅是与程序密切相关的部分。此外还有其它部分对程序同样必不可少,但这些部分主要由MFC自动生成,无需人为改动,故在此不多做介绍。2.5 多线程如采用单线程方式,按照如下顺序编写代码:用户走棋计算机思考并走棋按这种方式编写的程序似乎毫无问题,程序运行一切正常。然而,在给程序加入这些功能(后面将会在讲到其实现)后,程序出现了异常:有时对用户方的功能完全正确,而对电脑方的有些功能却不起作用!是由于程序在进行搜索时会占用大量的CPU时间,因而阻塞了位于同一线程内的其他指令,使之无法正常工作。解决方

27、案就是另外开一个线程,让各程序分开于多个线程。启动一个新的线程的方法非常简单,只需调用函数AfxBeginThread即可,函数原型:CWinThread* AfxBeginThread( AFX_THREADPROC ThinkProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );该函数启动一个新的线程并返回一个指向该新线程对象的指针,然后新

28、的线程与启动该新线程的线程同时运行。该函数的第一个参数AFX_THREADPROC ThinkProc指定了线程函数。线程函数的内容即为新线程所要执行的内容,线程函数执行完毕,新线程结束(自动销毁)。线程函数必须被定义为全局函数,其返回值类型必须是UINT,必须有一个LPVOID类型的参数。可以把调用引擎部分的搜索函数的代码以及完成走棋动作的代码放入所定义的思考线程内,如下:DWORD WINAPI ThinkProc(LPVOID pParam) CChessDlg* pDlg=(CChessDlg*)pParam; pDlg-Think(); 计算机思考并走棋 return 0;然后,只要

29、将原先调搜索函数并完成走棋的代码代之以调用AfxBeginThread来启动新线程即可,实现了程序的多线程,不能正常工作的问题也就随之解决了。2.6 着法名称显示每当下棋方(用户或是计算机)走一步棋,在棋盘旁边的一个列表框控件(List Box)中按照中国象棋关于着法描述的规范要求显示出该着法的名称。如:红炮二平五、黑马8进7此类。为了获得该着法名称,写了一个六百余行的函数。其功能就是将被移动的棋子类型以及走法的起点坐标、终点坐标这些信息转换成中国象棋所规范的着法名称。由于该函数主要涉及的是中国象棋关于着法表示的规范要求,故在此不对其具体实现做额外的解释。主要讨论的是如何对列表框控件(List

30、 Box)进行操作,以显示或删除着法名称。首先,在ChessDlg下定义以下函数:this-GetMoveStr(nFromX,nFromY,nToX,nToY,nSourceID);用来获得刚下的一步棋的走法;void CChessDlg:AddChessRecord(int nFromX,int nFromY,int nToX,int nToY,int nUserChessColor,int nSourceID) 将走法添加进下棋记录;然后,显示在Listbox中。当列表框中的项的数目超过列表框的显示范围时,列表框会自动添加垂直滚动条(前提是其VerticalScrollbar属性要为Tr

31、ue该属性默认即为True)。但是显示的内容依然是最早加进来的项。在控件属性里选择Vertical Scroll,使得列表框可垂直滚动以显示最新的着法名称。想要从列表框中删除项时,可以使用m_lstChessRecord.DeleteString(m_lstChessRecord.GetCount()-1);减一之后正好是最后一项的行号。2.7 悔棋和还原悔棋和还原是棋类软件中较为基本的功能。要实现悔棋和还原功能,首先要明确哪些信息应当被保存以供悔棋和还原所使用。在程序中保存了如下信息:棋局表示中所定义的棋盘数组;各棋子的贴图位置。这里需要特别说明的是通常象棋程序处于程序效率的考虑并不保存所有棋子的信息,而只是保存之前一步的走棋信息。此后当悔棋的时候,需要撤销着法;还原的时候,需要执行着法。然而,在编写自己的程序时一来考虑到程序的可读性和不易出错性,二来考虑到对当今的计算机的配置来说这点开销基本上不会对程序的效率产生什么影响。因此保存了全部棋子的信息。根据所要保存的数据定义了如下基本结构类型:typedef struct CHESSMOVE cmC

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2