程序结构

工程结构 已经把代码放进目录和文件。本章看这些文件里的类怎样连成一份程序。棋盘、对局、电脑落子和终端循环四块,靠函数调用与组成关系连接。当前构建只收进动态库、app 与棋盘测试,总览图保留终端链的设计。入口 main 只创建 Game、落一子并检查返回值。

结构总览

Board、Game、Ai、PlayToEnd 和入口 RunMatch 之间的调用与组成关系画在一张图里。

Board 与坐标类型 CellPosition 在棋盘一章。Game 拥有棋盘,并记录当前玩家和结果。Ai 读取对局,选出一手。

当前构建的入口 main 直接创建 Game 并调用 TryPlace;完整设计里的 RunMatch 读取设置,再把控制交给 PlayToEnd。PlayToEnd 创建对局和电脑选手,终端循环在终端程序。

分块明细

棋盘

Board 保存九个棋盘格子,把查询、规则判定与写入分成三组公开操作。图里只画本章用到的代表性入口,完整列表见 board.h。

规则判断做成只读成员函数,所以 GetWinner、PreviewWin 这些查询不修改棋盘。写入入口是 PlaceCell 与 Clear;PlaceCell 用 PlaceCellResult 区分成功、越界、空类型与目标格已占用,Clear 把整盘恢复为空。这样分的原因见棋盘一章。

对局

Game 拥有棋盘,记录当前玩家、结果与落子历史;TryPlace 接收 Move,返回 PlaceResult。

Move 是 TryPlace 的输入,包含 CellPosition position 与 Player player。TryPlace 返回的 PlaceResult 定义在对局,有已落子和被拒绝两种取值。history_ 保存已执行的落子,快照里的 sequence 就取自它的长度。

电脑落子

Ai 是电脑落子的接口,Choose 只读对局;三种强度各有一个派生类。

Ai 声明 Choose,三个派生类各自实现一种强度。

终端程序

完整设计里,RunMatch 读取对局类型和电脑强度,再调用 PlayToEnd 进入一局的循环。

完整设计里,PlayToEnd 每一轮打印当前棋盘,然后读入人类落子,或让 Ai 选子;人类落子和电脑的一手都通过 Game::TryPlace 写入对局。PrintGame 在棋盘文本之后调用 WriteSnapshot 追加 JSON 行。

常见错误

接口当作实现类

Ai 是接口,三种强度各自派生。Ai::Choose 在 ai.h 里声明,具体算法在三个派生类里。

自测问题

  1. Game 与 Board 是什么关系?

    查看答案

    Game 持有一个 Board 作为组成部分。棋盘随对局对象一起创建和销毁,对局通过这个成员读写棋盘。

  2. 人机模式下,PlayToEnd 每一轮把落子交给哪两个对象?

    查看答案

    人机模式下,O 的回合调用 Ai::Choose 取得一步落子;X 的回合由 ReadHumanMove 读入。两条路径都调用 Game::TryPlace 写进对局。

本章回顾

你已经能按总图定位四块并说明边的关系,也能分清当前构建与保留的终端程序的设计。下一章是棋盘,从 Board 的保存与读写开始。