Connect4
Connect4

Connect4

Tags
MCTS
UCT
State Compression
Description
Project 3 of THU course Introduction to AI

1. 问题描述

实现一个四子棋落子策略,能够根据当前棋盘状态(动态长宽,含有一个随机不可落子点),自动选择收益较大的落子点。在与测例动态库的博弈中,尽可能多地赢得比赛。

2. 模型与算法

2.1 整体框架

使用“蒙特卡罗搜索”和“信心上限树算法”,构建UCT树,随机模拟对战过程、计算收益,从而确定最佳落子点。

2.2 实现细节

2.2.1 蒙特卡洛搜索
图1:蒙特卡罗树搜索
图1:蒙特卡罗树搜索
  1. Selection:每轮开始前依据己方和对方的上一个决策(共两步)对树进行换根,建立UCT 树,根的子节点即每一列可落子的状态。
  1. Expansion:从根出发,若当前点可以扩展新的子节点,则扩展并选中新扩展的点。若当前点无法扩展新的子节点,则选中以当前点为根的子树中得分最高的叶节点。得分规则(UCB)为
    1. 其中 为当前节点, 为考察的子节点。 被回溯的次数, 回溯总得分。
  1. Simulation:将新的子状态进行模拟,双方轮流随机落子,并在该过程中添加启发性策略(见2.3.1)进行优化。
  1. Backpropagation:将模拟产生的结果——胜(1分),平(0分),负(-1分)作为收益回传至根,选择根收益最大的一个子节点作为最佳落子点。
2.2.2 类与方法
  1. class Agent
模型的主体。其中Search() 作为外部调用的接口,返回每一步的走棋,RootChangeTo(…)对应MCTS的 Selection 作为公有函数对接评测框架的数据接口。
treePolicy(…), defaultPolicy(…), backTrack(…) 作为私有函数分别对应MCTS的 Expansion,Simulation Backpropagation过程。
class Agent { private: ... int treePolicy(int x, Status* board); int defaultPolicy(int x, Status* board); void backTrack(int x, int value); public: ... Agent(int N, int M, int noX, int noY); std::pair<int, int> Search(); void RootChangeTo(std::pair<int, int> decision); ... };
  1. struct Node
UCT树中的节点,包含的属性和方法有
struct Node { int child[MAX_WIDTH]; // 子节点 int cnt, value; // N(v), Q(v) int parent; // 父节点 void refresh(int parent_) // 重用前重置 (见2.3.2) }
  1. struct NodePool
内存池,Queue型数据结构,实现空间的重复高效利用(见2.3.2)
struct NodePool { int q[MAX_TREE_NODE + 10]; int h, w; NodePool() : h(0), w(0) {} void enqueue(int x) {...} int dequeue() {...} bool empty() {...} }
  1. class Status
存储棋局信息,辅助Agent中函数。其私有属性中包含当前棋局的落子信息,双方的Urgent Threat,游戏进行状态等。Public中的方法对这些属性进行维护。
class Status { private: ... int n, m, noX, noY; enum State {win, draw, running, lose}; unsigned horizon[12], vertical[12], diag1[30], diag2[30]; // 状态压缩(见2.3.3) int top[12]; // 12列(竖向)的顶端位置 UrgentList urgentList[2]; bool urgent[2][13][13]; // Urgent Threat信息 public: ... }

2.3 性能优化

2.3.1 启发性策略
首先引入Vector Allis关于四子棋必胜策略论文中的 threat 概念。
  • threat:
    • 如果一个点被A的对手B占领后,B会连起4个子,则这个位置是A的 threat。
  • urgent threat:
    • 可以在这一手棋下到的 threat。
基于此,可以将落子策略由单纯依靠UCT算法改进为:
  1. 如果对手有urgent threat, 则下在那里即可获得胜利。
  1. 如果自己有urgent threat, 则必须下在那里,否则下一步对手可以下在那里并获胜。
  1. 按UCT算法进行落子,但不能落在对手threat的下面一格。
此外,还可以对模拟过程进行优化。引入threaturgent threat概念后,我们可以据此判断是否存在必胜必败态,从而实现剪枝。
  1. 若玩家落子前,他的对手有urgent threat,则玩家可以将子下在那个位置直接取得胜利。
  1. 若对手没有urgent threat
    1. 若某玩家存在两个urgent threat,则无论他将棋子落在何处,对手均可以将棋下在urgent threat处取得胜利。因此,如果下完一手棋后,对手出现两个urgent threat, 且己方没有urgent threat, 则己方必胜。如果己方落子前已有两个urgent threat且对手没有urgent threat,则己方必败。
    2. 若玩家有一个urgent threat,并且这个位置上面也是他的threat,则玩家必败。反之,若对手出现这种情况,则玩家必胜。
    3. 玩家A落子后,玩家B所有可行的落子点上方都是自己的threat,则玩家A必胜.
利用必胜/必败态进行随机模拟过程中的剪枝不仅可以节约运算时间,更能比MCTS中随机落子的方式更真实地评价当前局面。
2.3.2 内存池
算法运行过程中,会出现大量的新建节点和删除节点的操作,如果每次都调用new和delete会造成巨大的时间开销。因此选择在开始时建一个大数组作为内存池。此外,通过NodePool 对内存池进行维护。
在新建和删除节点时,为了实现空间的重复利用,进行相应的设计:
  • 删除节点时,设原树根为 root1,新树根 root2 root1的子节点。则在RootchangeTo 时需删除 root1 root2 的兄弟节点
    • root1 的子节点信息清空并放入NodePool
    • root2 的兄弟节点放入NodePool
  • 新建节点时,直接从NodePool中取一个节点,并将其子节点放入内存池中(见Agent::newNode),再将其重置(refresh)后即可使用.
这样可以实现内存池的重复利用,并降低新建和删除节点时的均摊复杂度。
2.3.3 状态压缩
由2.3.1可知,使用启发性策略辅助剪枝时最大的瓶颈在于找到 threat 的位置。为了提升每轮的迭代次数从而提升模型性能,使用状态压缩的方法对寻找 threat 的过程进行优化 — 即对棋盘每个落子点进行状态编码,进而可通过位运算加速状态判断过程。具体实现如下:(代码中由class Status实现)
  1. 将落子点的四种状态:空位,不可落子点,已有A子,已有B子分别用00011011表示。
  1. 为实现方便,将棋盘的四周全部列为不可落子点位置,这样每一行和仅需用位的二进制数表示,并可用int实现。列和斜向的操作类似,两种斜向可分别视为把 相同的以及把 相同的归为一行。
    1. 示意图如下(以4*5棋盘为例)
notion image
  • 则最低行可用 表示,最右边一列可用橙色对角线可用 表示。
  • 落子后,可通过位操作取出落子点及其周围的连续四个位置的编码,若为中的一个,则出现了一个A的threat (即B还差一个位置就可连成四个);而若出现了中的一个则出现了一个B的threat.
  • 由于落子点可能位于取出的位置(串)中的任一位置,故对于行和斜向操作共需取四次数。
  • 而对于竖向,由于必定是按照从低到高的顺序落下的,因此如果落子后产生threat,则threat一定是落子点之上的一个点,因此仅需进行一次取数判定。
💡
实验结果显示,通过状态压缩可将局面判断的时间开销缩短一半以上,每轮迭代次数增加到原来的近两倍

3. 实验结果

与2.dll ~ 100.dll 分别进行5轮对战,每轮对战包括一次先手,一次后手,总胜率为92.8%。下表为部分胜率统计,未列出的均获得全胜。
编号
100
98
96
94
92
90
88
86
84
82
80
68
60
胜率
0.5
0.4
0.6
0.8
0.8
0.8
0.7
0.8
0.8
0.7
0.8
0.8
0.9

4. 总结

通过本次大作业,我进一步加深了对MCTS,UCT算法的理解。同时通过实验中关于启发策略,内存池,状态压缩等优方式的尝试也让我有了很大的收获。
但模型仍有很大改进的空间,比如在扩展和模拟的过程中,仍存在很大的随机性,而对于子节点价值的评估也显得有些简单。之后可以考虑使用AlphaZero的模型,训练策略网络和估值网络,进一步提升模型的性能。

Loading Comments...