基于C#实现的支持AI人机博弈的国际象棋游戏程序

Scavengers

发布日期: 2018-11-20 09:05:53 浏览量: 1222
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

1 背景和意义

1.1 项目意义

  • 该项目的成功推进和完成将达到 AI 比赛过程自动化的目的,有助于比赛的顺畅、成功开展以及比赛时间的有效节约

  • 该项目的成果将有助于《人工智能原理》课程的学生对于自己编写的 AI 程序的测试

  • 该项目的成果将有助于国际象棋 AI 的后续研究和教学展示

  • 该项目的成果由于支持人机、机机博弈,也具有一定的游戏性和观赏价值

1.2 项目目标

完成一个图形界面国际象棋棋盘软件。它主要具备以下功能:

  • 图形界面显示(显示与用户交互的窗体控件、显示棋盘和棋子)

  • 游戏参与者加载 AI 程序

  • 游戏组织者选择游戏模式(自动、手动)

  • 游戏组织者开始游戏、进行游戏

    • 软件与 AI 程序通信,完成自动博弈

    • 游戏参与者/游戏测试者手动走子

    • 软件判断走法符合规则

    • 软件判断游戏结束(局面是否出现将军、欠行等,计时是否结束)

    • 软件对走子计时

一些性能约束:

  • 能在时下主流的笔记本电脑(x86和x64 架构的多核 CPU)上运行

  • 在 Windows 7 及以上操作系统运行

1.3 开发用语言和环境

  • 项目的编码用C#语言写成,图形界面用 WinForm(Windows 窗体 API)实现。

  • 开发环境为 Visual Studio 2017,框架为.NET Framework 4.6。

2 详细需求描述

2.1 对外接口需求

2.1.1 用户界面

  • UI1:唯一的一个象棋棋盘和控制窗体主界面。该界面的风格为 WinForm (Windows 风格窗体),方便熟悉 Windows 界面语言的人快速上手和操作。该界面的图示为:

  • UI1.1:在载入 AI 时,应弹出填入载入 AI 信息的对话框,如下图所示:

  • UI1.2:在设置选项时,应弹出含有选项的对话框,如下图所示:

2.1.2 通信接口

本软件与参赛 AI 交互使用的是操作系统的“标准输入输出”;内容协议采用 SAN 格式。

2.2 功能需求

2.2.1 图形化显示棋盘和棋子

2.2.1.1

特性描述 主界面显示一个 8x8 的正方形国际象棋棋盘。棋子以图形的方式显示在国际象棋 棋盘中。参赛者或组织者开始游戏或重置游戏时,应达到初始化棋盘显示的功能;游戏开始后,棋盘显示游戏的棋局,并随着双方走子不断更新显示。如 UI1 所示。

2.2.2 加载 AI

2.2.2.1 特性描述

参与比赛的两方参赛者,均可以在比赛开始前在本软件中载入自己的 AI 作为进程 运行,准备参与博弈。载入内容包括“是白/黑方”、“可执行文件路径”、“执行参数”。

2.2.2.2 刺激/响应序列

  • 刺激:用户点击“载入白方 AI”或载入“黑方 AI”

  • 响应:系统弹出对话框,用户界面如 UI1.1

  • 刺激:用户在可执行文件文本框中输入可执行文件路径

  • 响应:文本框成功接受输入

  • 刺激:用户在对话框中点击“浏览”

  • 响应:系统弹出二级对话框,允许用户浏览文件选择可执行文件

  • 刺激:用户选择完毕,点击“确认”

2.2.3 以不同的模式进行游戏

2.2.3.1 特性描述

进行游戏可以有以下几种模式:

其中,”人类手动”指人类利用鼠标点击棋盘上的棋子、选择其移动位置来完成走 子,”AI 自动”指本软件不经用户确认直接从 AI 读入信息完成走子,”AI 手动”指本软件需要用户手动点击“从 AI 读入”按钮才从 AI 读入信息。

游戏组织者在开始游戏前,需要可以从本软件中选择其中一种模式。

2.2.3.2 刺激/响应序列

  • 刺激:用户点击界面 UI 上的“模式选择”单选按钮

  • 响应:本软件接受用户的点选输入,并相应地更新 UI

  • 刺激:用户点击“开始游戏”

  • 响应:本软件进入游戏进行状态,根据选好的模式决定是否向 AI 发送其所在方(黑/白)信息,并开始从 AI 读入走子信息走子

2.2.3.3 相关功能需求

2.2.4 与 AI 进程通信、处理用户交互,实现自动、手动博弈;判断走子是否符合规则

2.2.4.1 特性描述

作为棋盘平台,本软件在象棋游戏进行过程中,要根据模式选择的不同,与 AI 进程通信以获得它们的走子信息,以及处理用户交互以获得人工走子信息,实现无组织 者人工干预的自动、手动博弈。同时,本软件还应充当裁判的作用,预防不合法的走子产生。

2.2.4.2 刺激/响应序列

  • 刺激:用户点击棋盘

  • 响应:UI 以颜色的方式提醒用户点击是否有效合法;若有效合法,推进走子的流程

  • 刺激:用户成功走子

  • 响应:更新 UI 为走子后的局面;判断游戏是否结束并给出提醒;若对方 为 AI,向其发送走子信息

  • 刺激:AI 发来走子信息

  • 响应:判断走子信息是否有效合法;若有效合法,更新 UI 为走子后的局面;判断游戏是否结束并给出提醒;且若对方为 AI 则向其发送走子信 息。若不合法,则回送特殊信息说明走子错误

  • 刺激:用户点击“停止”

  • 响应:等待所有附加线程运行完毕,然后停止游戏,更新 UI,给出提示 “游戏已停止”

  • 刺激:用户点击“重置”

  • 响应:等待所有附加线程运行完毕,然后重置游戏,更新 UI

2.2.4.3 相关功能需求

2.2.5 给游戏计时

2.2.5.1 特性描述

由于需要控制 AI 走子的时间,故组织者需要对 AI 的走子进行计时。计时的方法是,轮到该 AI 走子时,AI 在走子前等待的时间累计起来,即是该 AI 所用的时间。

2.2.5.2 刺激/响应序列

  • 刺激:由于上一步的用户手动走子或 AI 的自动走子,将主动权让给了我方 AI

  • 响应:本软件开始对我方 AI 的走子计时

  • 刺激:我方 AI 思考后走子

  • 响应:本软件停止对我方 AI 的走子计时,将计时的这段时间累加到我方 AI 所用时间上

  • 刺激:用户点击“重置”

  • 响应:两方 AI 的计时均归零

2.2.5.3 相关功能需求

2.2.6 保存比赛棋谱

2.2.6.1 特性描述

一场游戏结束后,组织者可能需要保存其棋谱。本软件允许组织者将比赛棋谱复制到剪贴板,以便粘贴到别处保存。

2.2.6.2 刺激/响应序列

  • 刺激:组织者选择历史记录里所有走子

  • 响应:本软件的 UI 做出相应变化,表示组织者成功选定了这些走子

  • 刺激:组织者按下“Ctrl-C”键。(“复制”操作快捷键)

  • 相应:本软件将已经选定的走子送至操作系统的剪贴板上

2.2.6.3 相关功能需求

2.2.7 处理和保存用户的设置

2.2.7.1 特性描述

本软件应处理和保存用户的一些参数偏好,包括默认可执行文件路径、是否在运行时隐藏 AI 窗口、观棋时间、是否自动保存 AI 配置。

2.3 性能需求

  • Performance1:速度:每一步走棋所产生的相应变化需在 1 秒内完成,点击按钮后所产生的变化应在 3 秒内完成

  • Performance2:负载:能够接受两个 AI 同时运行

  • Performance3:适应性:在不同 Windows 版本上能够运行

2.4 数据需求

2.4.1 数据定义和格式要求

本软件需要在计算机上存取的数据只有用户设置。用户设置包含如下定义和格式的数据:

2.5 安全性需求

  • Safety1:本软件的运行应对操作系统的完整性无害

  • Safety2:本软件的运行应不违反操作系统规则,不导致操作系统陷入崩溃

  • Safety3:本软件应不对用户的其他文件造成危害,包括修改和删除

  • Safety4:本软件应对每个 Windows 用户的用户设置数据设置屏蔽,防止互通

2.6 可靠性需求

  • Reliability1:本软件不应在用户对其正常使用时突然退出、崩溃

  • Reliability2:本软件不应在 AI 程序出现错误、故障、突然退出时发生故障

  • Reliability2.1:本软件应该检测到 AI 出现异常,并弹窗报告用户,并停止 正在进行的游戏

  • Reliability3:本软件应当合理管理分配的内存,防止出现内存泄漏

  • Reliability4:本软件应当线程安全,防止多个线程同时操作一个对象产生的不 协调和错误

2.7 用例图

2.8 用例描述

2.8.1 选择模式

2.8.2 进行国际象棋游戏

2.8.3 AI接受系统标准输入

2.8.4 AI给出标准输出

2.9 概念类图

由于本软件从需求来看功能要求不多,故对于所有用例,统一绘制一个概念类图于此。

解释:ChessGameLogic 是关于游戏运行时逻辑的类,为软件的核心; ChessGameForm 是游戏运行的窗体,充当 UI;ChessGameRule 是判断走子是否合法、游戏是否结束时用到的象棋规则类;AIProcess 和 StopWatch 分别为 AI 进程类和计时类。

2.10 系统顺序图

同概念类图,我们将所有用例集合在一起,以一个用户开启软件到进行完一局游戏的全过程绘制了系统顺序图。

2.11 状态图

3 整体架构描述

本节将描述本软件的整体架构,从逻辑视角和组合视角来描述,采用 UML 包图、 构件图。

3.1 逻辑视角

3.1.1 体系结构设计风格

本软件体系结构设计的风格采用模型-视图-控制器(Model-View-Control, MVC)风格。采用该风格的方案明显较好,因为:

  • 实际开发时使用 C# 结合.NET Framework,非常适合于实现 MVC 风格的体系结构

  • 能够促进并行开发,缩短开发时间

  • 该风格的部件区分明朗,便于进行体系结构设计和详细设计

MVC 风格将整个系统分为三个部件(子系统):

  • 模型(Model):封装系统的数据和状态信息,也包含业务逻辑。在本软件 中,模型包含国际象棋的规则部分、国际象棋游戏进程的逻辑

  • 视图(View):提供业务展现,接受用户行为。在本软件中,视图包含程序 的显示窗体、控件。控件既可向用户展示信息,也可以被用户以点击的形式交互

  • 控制(Controller):封装系统的控制逻辑。在本软件中,控制包含用户点击 控件后触发的事件函数,以及刷新 UI 所需调用的事件函数

构件图如下:

3.1.2 概要功能设计和逻辑包图

由于本软件的界面和控制不复杂,实现较为简单,视图和控制部件包均可只用一 个逻辑包实现;模型涉及到功能较多,用多个逻辑包实现。用包图表达的最终软件体系结构逻辑设计方案如下:

3.2 组合视角

3.2.1 开发包设计

在逻辑视角的基础上,可以用组合视角对于体系结构进行开发包的设计。由于我们的项目较为简单,故采用以下的开发包设计:

  • 每一个组合包最多转化为一个开发包

  • 模型部件中依赖关系较多的包组合为一个开发包

  • 逻辑包中没有循环依赖需要解决,故无需再增加开发包

  • 为简洁,不再另设不同部件之间的接口包

在引入.NET Framework 框架提供的类库之后,整个软件的开发包图如下:

各包的名称、功能和依赖关系均已在图中呈现,故不另外列开发包表。

3.2.2 运行时进程

由于软件简单,故运行时排除 AI 进程外,只有一个主进程。

3.2.3 物理部署

由于软件简单,只需要一个可执行文件部署在本地计算机。

4 模块分解

对于诸模块的分解设计采用结构视角和接口视角来说明。

4.1 模块的职责

按照 MVC 的部件划分,可以直接将每个部件转换为一个大的模块:Model 模块、 View 模块、Control 模块。其职责如下:

不同模块之间通过简单的函数调用完成连接。

4.2 MODEL的分解

Model 模块包含与象棋的状态和信息有关的对象类,如 ChessGame(以象棋规则为中心的象棋棋局类)、ChessGameLogic(以象棋游戏进程逻辑为中心的象棋棋局 类)、ChessPieces(象棋的棋子类)等。

4.2.1 Model分解后的职责

Model 模块包含三个开发包,其职责如下表所示:

4.2.2 Model 分解后的接口规范

注:只列出对于本软件有关键作用的接口,重要性较小的接口如计时、用户设置有关的在此不列出。

4.2.2.1 ChessGameWithRule 的接口规范

4.2.2.2 AlProcess 的接口规范

4.2.2.3 ChessGameLogic 的接口规范

5 详细设计

本软件详细设计的基本方法为面向对象设计方法(Object-oriented Design Method),意在将各个构件实现时,用抽象为一系列对象的方式看待。

5.1 MODEL的分解

5.1.1 模块概述和整体结构

Model 模块的职责为记录软件运行的状态、象棋的局面信息,处理象棋的走子、 规则。在软件的体系结构设计中,其下分为 ChessGameWithRule、ChessGameLogic、 AIProcess 三个包,分别包含象棋规则、象棋游戏进程逻辑、AI 进程逻辑三个方面的逻 辑。后两个包可各用一个类实现,而前一个包由于构成与所实现的功能更复杂一些, 故可以用多个类来实现。

这三个开发包的内部构造和职责、相互协作描述如下:

  • ChessGameWithRule 包中的核心实现类是同名类 ChessGameWithRule(在实 际代码编写中,更名为 ChessGame)。它除了对.NET Framework 框架,以及 同一包内的一些数据结构类有依赖之外,是一个自成一体的象棋规则实现 类。一个 ChessGameWithRule 对象可以完备地从规则角度上实现一局象棋游 戏的过程。其内有包含棋盘(棋子对象构成的数组 Piece [][])、历史行棋 (Move 对象构成的列表 List<Move>)等,也有 ApplyMove(实现走子)、 GetValidMoves(获得当前所有合法走子)等具体功能方法。除此之外, ChessGameWithRule 包还包含与象棋规则有关的数据结构,如 Piece(棋 子)、Move->MoreDetailedMove(走子,MoreDetailedMove 继承自 Move,包含更多信息)、Position(位置)等等,被 ChessGameWithRule 核心类所聚合。ChessGameWithRule 还包含了一个用于处理 SAN 字符串为 Move 对象的分析函数 PgnMoveParser,便于游戏进程逻辑层面的使用。

  • AIProcess 由一个同名类实现,实现一个 AI 进程的逻辑,如启动、停止、标准 输入输出的读写。它调用.NET Framework 系统类,可以控制启动、停止系统 进程,并操作标准输入输出。可以说它是系统进程与本软件的接洽。

  • ChessGameLogic 由一个同名类实现,注重于象棋游戏的进程(开始、循环走 棋、何时结束游戏)来实现一局象棋游戏。由于象棋游戏的进程取决于规 则,故它依赖并使用 ChessGameWithRule 作为规则的实现。同时,它也使用 AIProcess 类,向其发送有关于黑白双方 AI 的命令,以实现机器博弈。至于人 工博弈,人类的行棋是通过用户界面,从 View 模块传导到 Control 模块,再调用 ChessGameLogic 里的 ApplyMove 实现的,不全部由ChessGameLogic 实现。

5.1.1.1 ChessGameWithRule 内的类图

5.1.1.2 AIProcess 内的类图

5.1.1.3 ChessGameLogic 内的类图

以下将不再分每一个小包进行接口规范的描述,而是直接对 Model 内的类进行接 口规范的描述。

5.1.2 内部类的接口规范

与“体系结构设计”中的 Model 接口有所重合的类接口,这里有些就省略不列出。 现将“体系结构设计”中细化的 Model 内部类接口规范描述如下。

ChessGameWithRule 类的接口规范

MoreDetailedMove 类的接口规范

Piece 类(抽象类,具体棋子类的基类)的接口规范

AIProcess 类的接口规范

ChessGameLogic 类的接口规范

5.1.3 Model 的动态模型

由于本软件详细设计中的动态模型中,状态图的设计与软件结构是否分解为类关系不大,故动态模型中的状态图省略不画。 现将进行一盘游戏的系统顺序图扩展为详细顺序图,描绘如下:

6 核心算法

6.1 GETVALIDMOVES算法描述

ChessGameWithRule.GetValidMoves 是 Model 里象棋规则实现库 ChessGameWithRule 中的一重要函数,可以获得某一方的所有允许的走子。它由两个同名重载函数实现。

  1. public virtual ReadOnlyCollection<MoreDetailedMove> GetValidMoves(Position from, bool returnIfAny, bool careAboutWhoseTurnItIs)

这个函数可以将该行动方从 Position from 出发的所有可行走子返回。returnIfAny 和 careAboutWhoseTurnItIs 是内部使用和调试用参数,可以忽略。

其具体实现如下:

  1. public virtual ReadOnlyCollection<MoreDetailedMove> GetValidMoves(Position from, bool returnIfAny, bool careAboutWhoseTurnItIs)
  2. {
  3. ChessUtilities.ThrowIfNull(from, "from");
  4. Piece piece = GetPieceAt(from);
  5. if (piece == null || (careAboutWhoseTurnItIs && piece.Owner != WhoseTurn))
  6. return new ReadOnlyCollection<MoreDetailedMove>(new List<MoreDetailedMove>());
  7. return piece.GetValidMoves(from, returnIfAny, this, IsValidMove);
  8. }

描述为自然语言:

  • 获取当前棋盘 from 位置的棋子为 piece

  • 如果那个位置没有棋子,直接返回空集合

  • 否则 ,调用这个具体棋子的多态函数 GetValidMoves,给出从 from 出发的所有 可行走子。同时,还传入当前 ChessGameWithRule 对象下的,对于本局 游戏特化的走子验证函数,方便 Piece.GetValidMoves 调用用于二次验证 走子的合法性

  1. public virtual ReadOnlyCollection<MoreDetailedMove> GetValidMoves(Player player, bool returnIfAny, bool careAboutWhoseTurnItIs)

这个函数可以将该行动方(不受 Position 制约)的所有合法走子返回。

  1. public virtual ReadOnlyCollection<MoreDetailedMove> GetValidMoves(Player player, bool returnIfAny, bool careAboutWhoseTurnItIs)
  2. {
  3. if (careAboutWhoseTurnItIs && player != WhoseTurn)
  4. return new ReadOnlyCollection<MoreDetailedMove>(new List<MoreDetailedMove>());
  5. List<MoreDetailedMove> validMoves = new List<MoreDetailedMove>();
  6. for (int r = 1; r <= Board.Length; r++)
  7. {
  8. for (int f = 0; f < Board[8 - r].Length; f++)
  9. {
  10. Piece p = GetPieceAt((File)f, r);
  11. if (p != null && p.Owner == player)
  12. {
  13. validMoves.AddRange(GetValidMoves(new Position((File)f, r),
  14. returnIfAny));
  15. if (returnIfAny && validMoves.Count > 0)
  16. {
  17. return new ReadOnlyCollection<MoreDetailedMove>(validMoves);
  18. }
  19. }
  20. }
  21. }
  22. return new ReadOnlyCollection<MoreDetailedMove>(validMoves);
  23. }

描述为自然语言:

  • 初始化 validMoves 为空集

  • 对于棋盘上的所有格子(位置 Position)作一遍历:

    • 获得该格子上的棋子为 p

    • 若格子上有棋子(p 非空)且 p 的拥有者是 player:

      • 调用 同名函数 GetValidMoves,获取从该位置出发的所有合法走子

      • 将 validMoves 并上刚刚返回的走子集合

  • 返回 validMoves

6.2 ISMOVEVALID算法描述

这个函数同样属于象棋规则库。它判断某一个走子在当前游戏的进行情况中是不是合法的,这对于合法走子的生成和判定非常重要。

  1. public virtual bool IsValidMove(Move move, bool validateCheck, bool careAboutWhoseTurnItIs)
  2. {
  3. ChessUtilities.ThrowIfNull(move, "move");
  4. if (move.OriginalPosition.Equals(move.NewPosition))
  5. return false;
  6. Piece piece = GetPieceAt(move.OriginalPosition.File, move.OriginalPosition.Rank);
  7. if (careAboutWhoseTurnItIs && move.Player != WhoseTurn)
  8. return false;
  9. if (piece == null || piece.Owner != move.Player)
  10. return false;
  11. Piece pieceAtDestination = GetPieceAt(move.NewPosition);
  12. bool isCastle = pieceAtDestination is Rook && piece is King && pieceAtDestination.Owner == piece.Owner;
  13. if (pieceAtDestination != null && pieceAtDestination.Owner == move.Player && !isCastle)
  14. {
  15. return false;
  16. }
  17. else if(move is MoreDetailedMove m)
  18. if (pieceAtDestination != null && pieceAtDestination.Owner == ChessUtilities.GetOpponentOf(move.Player))
  19. {
  20. m.IsCapture = true;
  21. m.CapturedPiece = pieceAtDestination;
  22. }
  23. if (!piece.IsValidMove(move, this))
  24. {
  25. return false;
  26. }
  27. if (validateCheck)
  28. {
  29. if (!isCastle && WouldBeInCheckAfter(move, move.Player))
  30. {
  31. return false;
  32. }
  33. }
  34. return true;
  35. }

自然语言描述如下:

  • 若 move 为空

    • 返回 false
  • 获取 move 的源位置的行、列,并以此获取该位置的棋子到 piece

  • 若 piece 为空或不是本方棋子

    • 返回 false
  • 获取 move 的目标位置的,并以此获取目标位置的棋子到 pieceAtDestination

  • 对于 move 是王车易位的情况作特殊判断,若王车易位不合法:

    • 返回 false
  • 调用该 piece 的 IsValidMove 函数,获知该棋子能否如此移动。若不能:

    • 返回 false
  • 若该走子非王车易位,且走子后会陷入将军:

    • 返回 false
  • 返回 true

上传的附件 cloud_download 基于C#实现的支持人机博弈的国际象棋游戏程序.7z ( 2.86mb, 5次下载 )
error_outline 下载需要12点积分

keyboard_arrow_left上一篇 : 基于开源Alice的聊天机器人 基于C++实现的坦克大战游戏 : 下一篇keyboard_arrow_right



Scavengers
2018-11-20 09:06:38
项目的编码用C#语言写成,图形界面用 WinForm(Windows 窗体 API)实现。 开发环境为 Visual Studio 2017,框架为.NET Framework 4.6

发送私信

坚持了才叫梦想,放弃了就只是妄想

8
文章数
24
评论数
最近文章
eject