围棋AI之路(四):来自UCG的改进

UCT的实现

前文只顾抱怨,忘记讲述UCT的实现部分了,这里补上。UCT算法本身在第一篇已经详细阐述过了,具体实现上唯一的一个要点就是使用内存池来为Tree分配节点。树的结构如下所示:
  1. template<uintT>
  2. classNode{
  3. staticPool<Node,uct_max_nodes>m_pool;
  4. public:
  5. Vertex<T>v;
  6. intwin;
  7. uintcount;
  8. Node*first_child;
  9. Node*sibling;
  10. staticvoid*operatornew(size_tt){
  11. assertc(pool_ac,t==sizeof(Node));
  12. returnNode::m_pool.malloc();
  13. }
  14. staticvoidoperatordelete(void*p){
  15. Node::m_pool.free((Node*)p);
  16. }
  17. };
  18. template<uintT>
  19. classUCBTree{
  20. public:
  21. union{
  22. Node<T>*root;
  23. Node<T>*history[uct_max_depth];
  24. };
  25. uinthistory_top;
  26. };
UCBTree中的history成员是用来跟踪树的当前节点的路径的,因为现在这棵树的节点是用孩子兄弟的二叉链表来做的,这种结构不方便得到父节点,如果增加一个父节点指针又嫌浪费内存,毕竟UCT算法的任何时刻只有一个分支需要访问父节点,而让所有节点都承受这个代码太不公平了,因此这里另设了一个数组来存储。

至于Pool的实现可以有多种方式,只要效率高就可以了。我目前用的是一个简单的定长数组,据说用标准库的list来做效率也不错,而且它是变长的。可惜物理内存不是无限大的,因此对于我的试验阶段,用定长数组,限定一个最大节点数更合适一些。

UCG的改进

博弈树并不是一棵数,因为可能由不同的走棋顺序走出来同样的局面,如果每个节点代表一个局面,那么这个局面就应该有不止一个父节点,那么这应该是图结构(Graph)而非树。

图的理解和编程要比树复杂一些,反正我大学时的数据结构课就没好好学图。不过这次我想到了一个技巧来避开图结构,但是同样达到图的目的。

这个想法来自设计模式中的享元,也就是我依然使用树作为存储结构,但是对于节点的值:
  1. Vertex<T>v;
  2. intwin;
  3. uintcount;

仅保留v,而win和count用一个指针代替。这个指针指向另一个结构:
  1. classStat{
  2. public:
  3. intwin;
  4. uintcount;
  5. boolbexist;
  6. Hashhash;
  7. Playerpl;
  8. };
这里hash是一个64位的数,由局面求出来的Zorbist Hash。基本可以保证不冲突(至少对现在的普通计算机内存而言不冲突),pl则是要区分是哪一个玩家所留下的局面,因为同一个局面轮谁走的结果是不一样的,因此把它看成是不同的局面。bexist则用于hash表中判断一个位置是否被占用过。

这样,大家都看出来我是用hash表来存储共享的节点值,并且我解决冲突的方法是用移位。编程上是简单了,但是凭空浪费了一倍以上的额外空间,心痛呀!

pl和bexist可以压缩存储,能省点空间,或者用链表来解决冲突,也能省点空间。或者读者中有谁能告诉我一个更好的方法来实现UCG?

在更进一步优化UCG的实现前,我先试试我这个粗糙的UCG的效果,确实比起单纯的UCT来,感觉棋力立即改善了。

以五子棋为例,AI先行前几步竟然走出了花月必胜局的走法,吓了我一跳,一看log,思考深度达到9层。虽然许多五子棋程序都能走出开局定式,不过那是用了开局库,这个算法可是纯靠自己算出来的。

不过接下来它没能把胜势演变为胜局,这是我意料之中的,因为上一篇文章中提到,在五子棋中我人为设定了选点范围为已有棋子的邻点(严格说这算是给了它知识吧?),因此它是绝对发现不了跳二甚至跳一的妙手的,也会因此忽略对方的强防。这导致它不是一个一致的算法。

即使这样,它无论执白还是执黑的凌厉攻势还是让我叹服,只要你给它机会它就能抓住。不挡对方活三的问题已经解决的很好了,现在如果它不挡活三绝大多数情况下是因为挡了也会输。

不过它比起黑石还是差了很多。

在围棋上的19路盘,这个算法和UCT一样,还是没法下,因为AI老自己走自己的,根本不理你。换到9路棋盘上,测试了一下,比单纯的UCT好一些,可以和业余k级的人一战了。虽然它仍然没有解决好征子的问题,不过我试了一局它逃征子好像逃的还很有道理。但是和mogo的对局仍然是完败。


总结一下:UCG的引入会比UCT好一些,不过没有质的飞跃。在大棋盘或者较多选点的情况下,依然无法做到快速收敛。下次我该尝试一下RAVE/AMAF了,据说这是一个高速评估局面的方法,但愿它能模拟一局顶十局。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值