《深度学习与围棋》学习笔记——棋链GoString实现解析

科技一点鑫得 2024-06-02 18:42:43

阅读本文需要具备一点点围棋的基础,如果你对围棋及如何实现自己的围棋游戏感兴趣,可以在online-go.com这个网站学习围棋的入门知识。

《深度学习与围棋》这本书中3.1.2章节给出了棋链的实现,弄明白这部分代码还是花了一些时间的,这里做下记录希望给有类似困惑的朋友做个参考,先列出完整的实现代码及注释,之后再针对其中的方法逐个解释。

class GoString(): """ 棋链:棋盘上所有相连的同色棋子组成一条链 表示一条棋链,需要包含黑或白棋的颜色属性、棋链上的棋子(包含坐标)、棋链周围的气数 之所以抽象出棋链就是为了方便地计算它的气 注意:stone和liberties都是集合类型,它们之间操作的都是集合之间的操作 """ def __init__(self, color, stones, liberties): self.color = color self.stones = set(stones) self.liberties = set(liberties) def remove_liberty(self, point): self.liberties.remove(point) def add_liberty(self, point): self.liberties.add(point) def merge_with(self, go_string): # 能够合并的两个棋链必须是同色的 assert go_string.color == self.color # 合并后的棋子是两条棋链上棋子的并集,这里|表示集合的并操作 combine_stones = self.stones | go_string.stones # 合并后的棋链的气(类似与棋子的交叉点,只不过它是棋子相邻的空交叉点) # 合并后棋链的气为两条棋链气的并集,去除合并的棋子(因为原来棋链上的气合并后可能会成为棋子的位置) combine_liberties = (self.liberties | go_string.liberties) - combine_stones return GoString(self.color, combine_stones, combine_liberties) @property def num_liberties(self): return len(self.liberties) def __eq__(self, other) -> bool: # 判断两条棋链是否相同 return isinstance(other, GoString) and \ self.color == other.color and \ self.stones == other.stones and \ self.liberties == other.liberties

围棋中气是非常重要的概念,针对单个棋子来说它的气由相邻交叉点的空点数来决定,下图方框标记的是黑子在棋盘内部、边线、角落时的气数,一旦气被对手填满则处于无气状态,该子可以被对方提走。

但是针对下图这种棋子相连的情况,黑子A落子后,由1、2、3、4、5组成的白棋已无气,可以被提走,那么程序该如何检测是否可以提走它们呢?首先需要检测A相邻白棋1是否已无气,接着继续检测白棋1的相邻棋子2是否已无气,直到检查到白棋5也无气的情况下才能确定是否可以提走它们。

上面示例中相连的白棋还是比较少的,想象一下,在一个已经经过200步的棋局中,一条蜿蜒的大龙穿过对方领地的情形。为了加快检测的速度,把所有相邻的棋子作为一个整体来对待是比较好的选择,这就是使用棋链的原因。

棋链的初始化

一条棋链需要记录棋子的颜色、棋链上的所有棋子、以及整个棋链的气,因此棋链GoString的初始化方法中需要传入color、棋子坐标集合stones、棋链气的坐标集合liberties提醒:气其实也是由棋盘坐标表示,只是它的位置是空的交叉点

减少棋链的气remove_liberty

棋链上的某个气(气就是空的交叉点坐标)被落子后气需减少一口气,通过将self.liberties移除对应气的坐标来实现减少一口气。

增加棋链的气add_liberty

某此落子提走了对方的棋子可能导致棋链的气增加,通过将self.liberties添加对应气的坐标来实现增加一口气。

获取棋链的气num_liberties

整个棋链的气就是self.liberties的集合长度

检测两条棋链是否相同eq

两条棋链相同的条件是棋链的棋子颜色、棋链上的棋子、棋链上的气都完全相同。

合并两条棋链merge_with

这是GoString类最核心的方法,在落子将两条棋链连接起来的情形就需要对两条棋链进行合并,如下图1-7这条棋链在7处落子后就会和A-H这条棋链相连,这时就需要将这两条棋链进行合并处理。

很明显,只有同色的棋链才允许合并,assert go_string.color == self.color针对这个条件进行检测,如果不同色棋链进行合并则会抛出错误。合并后的棋链的棋子就是两条棋链棋子的并集,combine_stones = self.stones | go_string.stones中符号|是求并集的操作。最后就是计算新棋链的气,图中使用方框和三角框标记了新棋链的气,其中三角框是两条链共有的气。先说最终结果,新棋链的气是两条棋链气的并集与新棋链棋子的差集,为什么这样可以得到新棋链的气哪?

接下来逐个来分析。1. 首先两条棋链的气的并集就是周围方框+三角框+棋子7+棋子A,因为棋子7本来是A-H棋链的气,棋子A本来是1-7棋链的气。2. 其次新棋链的棋子就是1-7、A-H3. 求集合(方框+三角框+棋子7+棋子A)与(1-7、A-H)的差集,因为棋子7+棋子A是两个集合都存在的,因此求差集之后就只剩下方框+三角框了,这就是新棋链的气。

参考文献

[1]. 《深度学习与围棋》[美]马克斯·帕佩拉、[美]凯文·费格森著,第29页;

0 阅读:1

科技一点鑫得

简介:感谢大家的关注