zoukankan      html  css  js  c++  java
  • 棋串的数据结构

      Monte Carlo局面评估+UCT博弈树搜索是现代计算机围棋的主流。这种方法对算法的效率有很高要求,因此须要在Mone Carlo模拟过程中保存棋串(直线相连的同色棋子集合)信息,提高落子效率。

      棋串须要满足以下需求:

    1. 频繁查询某棋子所在的“棋串”。
    2. 频繁创建棋串。
    3. 频繁查询棋串的“气”。
    4. 频繁合并两棋串。
    5. 移除棋串,并枚举其中的棋子。

      因此用链表实现的并查集是很合适的数据结构:

    struct Node {
        PointIndex next_, list_head_;
    };
    struct List {
        PointIndex tail_, len_;
        AirSet air_set_;
    };
    
    Node nodes_[FOO_SQUARE(BOARD_LEN)];
    List lists_[FOO_SQUARE(BOARD_LEN)];

      创建只有一颗棋子的棋串:

    template <BoardLen BOARD_LEN>
    void
    ChainSet<BOARD_LEN>::CreateList(PointIndex node_i,
                                    const ChainSet<BOARD_LEN>::AirSet &air_set)
    {
        nodes_[node_i].list_head_ = node_i;
        lists_[node_i].tail_ = node_i;
        lists_[node_i].len_ = 1;
        lists_[node_i].air_set_ = air_set;
    }

      查询棋子所在棋串:

    PointIndex GetListHead(PointIndex node_i) const {
        return nodes_[node_i].list_head_;
    }

      合并两棋串:

    template <BoardLen BOARD_LEN>
    PointIndex ChainSet<BOARD_LEN>::MergeLists(PointIndex list_a, PointIndex list_b)
    {
        FOO_ASSERT(list_a != list_b);
        if (lists_[list_a].len_ > lists_[list_b].len_) swap(list_a, list_b);
    
        for (int i=list_a; ; i=nodes_[i].next_) {
            nodes_[i].list_head_ = list_b;
            if (i == lists_[list_a].tail_) break;
        }
    
        nodes_[lists_[list_b].tail_].next_ = list_a;
        lists_[list_b].tail_ = lists_[list_a].tail_;
        lists_[list_b].len_ += lists_[list_a].len_;
        lists_[list_b].air_set_ |= lists_[list_a].air_set_;
        return list_b;
    }

      移除棋串:

    template <BoardLen BOARD_LEN>
    void ChainSet<BOARD_LEN>::RemoveList(PointIndex head)
    {
        for (int i=head; ; i=nodes_[i].next_) {
            nodes_[i].list_head_ = ChainSet<BOARD_LEN>::NONE_LIST;
            if (i == lists_[head].tail_) break;
        }
    }

      枚举棋子:

    template <BoardLen BOARD_LEN>
    PntIndxVector ChainSet<BOARD_LEN>::GetPiecesOfAChain(PointIndex list_i) const
    {
        const List *pl = lists_ + list_i;
        PntIndxVector v(pl->len_);
        int vi = 0;
    
        for (int i=list_i; ; i=nodes_[i].next_) {
            v[vi++] = i;
            if (i == pl->tail_) break;
        }
    
        return v;
    }

      棋串的“气”可以按位存储,这样既省内存,又增效率。比如,“气”的数量,就是2进制中1的数量;合并两棋串时也须要合并它们的“气”,则作位或运算。这个数据结构不必自己实现:

    typedef std::bitset<FOO_SQUARE(BOARD_LEN)> AirSet;

      当然还须要写一些高层接口供落子函数调用。

      贴张落子函数的test case:

      代码:https://github.com/chncwang/FooGo

  • 相关阅读:
    # 漏洞挖掘技巧清单 #
    CVE-2020-0796——SMBV3客户端/服务端远程代码执行漏洞(远程代码执行+权限提升)
    SSH公钥详细介绍及拓展
    滑动窗口(数据结构)
    反素数解析
    树状数组的基本操作
    Rochambeau POJ
    一般图最大匹配
    2020牛客暑期多校训练营(第一场)H Minimum-cost Flow
    A Bug's Life POJ
  • 原文地址:https://www.cnblogs.com/qswang/p/2801790.html
Copyright © 2011-2022 走看看