首页  编辑  

关于五子棋的讨论

Tags: /超级猛料/Alogrith.算法和数据结构/杂项/   Date Created:

回复人: San_Daniel(丹少爷) (2001-8-28 19:52:14)  得0分

这是我前一段写的文档,参考一下吧

3 总论

 这个项目的开发目标是一个ActiveX控件,因而整个项目的核心是如何构造一个

符合ActiveX标准的类,使之能够完成需求分析书给定的功能。我们把最终提供

的产品类命名为 GobangGame,由它的名称可以看出,该类描述的是"五子棋游

戏"这一类事物。

 现实生活中的经验告诉我们,一场五子棋游戏由以下几个要素构成:

两名游戏者,他们分别执黑、白棋

一名裁判,负责裁定比赛的胜负,以及适时的开始和结束比赛

一套具体规则,供裁判作为判定的依据

一个五子棋盘

两盒棋子

 游戏者在游戏中的作用是在棋盘上落子,我们用一个抽象类Player来描述游戏者

的角色,它的主要接口方法如下:

   playAStep():在棋盘上下一颗子

   setSide():指定游戏者执黑或执白

   对于计算机游戏者而言,落子之前要根据棋盘的形势,决定落子位置,然后完

成自己的任务--在棋盘上落子。而对于"真人"游戏者而言,事实上是用户的代

理,他应该按照用户的指示,在棋盘上指定位置处落子。这两类游戏者都兼容游

戏者的接口,并且在具体动作上有所区别,因而我们设计Player的两个子类

Computer和LocalUser,它们对Player有不同的扩展。

Computer

   setAILevel():指定人工智能等级

LocalUser

   setCommand():命令游戏者在指定位置处落子

   至此,我们完成了对游戏者的描述,这时,应当注意到,裁判事实上也是一类特

殊的游戏者,但是他在游戏中所起的作用是判定胜负和控制比赛,有理由使他也成

为Player的一个子类,这时,我们修改Player抽象类的方法playAStep(),赋予它一个更

贴切的名称:act()每一个游戏者在游戏中轮流有act的机会,当裁判act的时候,他将

判断棋局,决定是否宣布某人获胜,或者因为某种原因中止比赛。而游戏者act的时

候可以选择在棋盘上落子或者干点别的。

   很容易注意到,在上述抽象分析中,我们给予了五子棋组件最大的灵活性,可以

方便的对Player抽象类派生产生新的游戏者类型。

   五子棋盘和棋子我们从简化程序的考虑,将它们合为一体,这套器具我们抽象为

一个抽象类ChessInfo,从这个命名中可以知道,对于棋局状况的记录也由这个类负

责。这样,ChessInfo中就必须包含已经走出的每一步棋,考虑到悔棋的需要,栈结

构是最合适不过的。这个抽象类的接口暂时做以下设计。

   redraw():重绘棋盘,包括已经落下的棋子

 showMessage(): 显示信息

   addAStep():添加一步棋

   removeLastStep():移除最后一步棋

   clear():清除棋盘,清除对局状态

   getPosStatus():查询指定位置的状态,是否落子,落的是哪一方的子

   接下来,我们考虑一个智能化的棋具,能够主动的对棋盘形势做出分析,进而对

游戏者提供一定的帮助,比如:重要形势的报警。这样,我们需要增加一个重要的

接口:

   analyse():分析棋局,提供参考数据

   最后,我们考虑游戏规则,显然一套游戏规则要完成的唯一功能就是判定胜负,

因此我们设计一个抽象类Rule,接口如下:

   judge():判定一个棋局,是否有人获胜,以及谁因为什么原因获胜。

   getRule():查询一个具体的规则细则。

   setRule():设置一个具体的规则细则。

   在上述分析过程中,我们得到这样一组类:

   Player

       Computer

       LocalUser

       Umpire

   ChessInfo

   Rule

   而最终产品GobangGame将组合上述类对象,这时,注意观察可以发现,上面的类

结构已经超出了"五子棋"的范畴,它事实上可以描述大多数棋类游戏!这些棋类游

戏的的差别只在于规则,于是我们从Rule中派生出GobangRule,并从ChessInfo中派生

出GobangChessInfo,专门用于我们的五子棋。

   GobangGame的接口在需求分析书中有详细的描述,在这场游戏中,GobangGame

扮演着联系用户和各类对象之间的桥梁作用,接收用户的输入,对内部对象发出请

求,再将结果反馈给用户,如此周而复始。

   接下来,我们讨论这些类横向的关系。

   首先,作为一个Player,无论他是电脑或是用户甚至于裁判,他必须知道规则,因

而在Player类中,应该存在一个Rule的引用。同时,他必须了解棋局的状态,所以在

Player类中,还应该有一个ChessInfo的引用。

   其次,棋局状态与外部情况无关,不须要知道任何人的存在,所以这是一个相对

单纯的类。

   最后,规则要判断一个棋局,必须与该棋局相识,所以对judge的调用应该传递一

个棋局作为参数。

   在具体设计中,又注意到一个问题,ChessInfo中包含了绘制的方法,而考虑到最终

产品的移植性,不应该将平台相关的绘制操作与平台无关的分析操作混在一起,因

而引入Bridge(设计模式4.2)模式将两部分分离,设计另一个ChessPainter类族用于实现

具体绘图操作,并被ChessInfo组合,合作完成ChessInfo的设计目标。

 以下,描述如何将上述类的对象组合成为一个完整的程序。

 我们的最终产品是一个ActiveX控件,因而很自然的想到使用ATL来实现GobangGame类。

可是,由于COM/ActiveX标准是Windows平台上的标准,同时使用ATL会带来很多平台相

关的代码,不利于未来的移植,所以,我们考虑实现一个平台无关的GobangGame类

和一个ATL的GobangGameShell类,两个类的接口大体上一致,用GobangGameShell实现

一些平台相关的代码。并且用户的输入首先被GobangGameShell接收,然后委托

GobangGame完成处理。这样,对于一切支持PUSH式程序的平台,都可以写这样一个

接收消息的Shell,从而保证了GobangGame的纯洁性。

   一个GobangGame对象如何与其他对象协同工作呢?首先,GobangGame创建一个棋局

对象(ChessInfo),被所有参与游戏的游戏者共享。接下来创建两名游戏者(Player),用

户对接口属性的控制决定了这两名Player的实际类型(Computer或是LocalUser)。下一步

创建一名特殊的"游戏者"--裁判(Umpire)。最后按照用户对接口属性的控制创建一

套规则(Rule)。游戏者、裁判都应该与棋局对象以及规则相识,这样,整套游戏创

建完毕。

   注意,上面没有提到ChessPainter对象的创建过程,因为ChessPainter的子类将是平台

相关的,我们不应该在纯洁的GobangGame中提到它们的名字,因而,在不纯洁的

GobangGameShell中创建ChessPainter对象(与具体的子类相联系)是正确的选择。我们

应该在GobangGame中设置一个setChessPainter()接口,用于接收创建好的ChessPainter对

象,并使用它来初始化ChessInfo。

   当所有对象都被创建并正确初始化后,GobangGame要做的事就是接收用户的控制

(包括输入事件和属性、方法调用)。这是一类典型的PUSH程序样式,一组消息处

理函数是必不可少的。大致罗列如下:

   onStandardClick() 主键点击,我们把鼠标左键点击事件广义化为这个事件,这样它还

可以描述其他输入条件下的同类事件。

   onExtraClick() 附加键点击,我们把鼠标右键点击事件广义化为这个事件。

   onRun() 得到运行机会。这个事件应该尽可能频繁的发生,因为每个游戏者只有在

得到运行机会的时候才能完成自己的职责。GobangGameShell可以将它挂在诸如时钟

中断之类的位置上。