zoukankan      html  css  js  c++  java
  • 再写围棋的MC模拟

      有了棋串的数据结构后,落子就变得很高效了,接下来要生成随机棋步。

      以9x9棋盘为例,直接生成0~80的随机数进行模拟会很低效,因为它们并不都是合法落子点——有3类落子点是非法的:1. 非空点 2. 劫争点 3. 自杀点。

      自然想在模拟过程中增量维护这3类点集,前两类很容易维护,难点在于自杀点的增量维护。

      着手写自杀点的维护后,才发现问题出乎意料的复杂。我先写了个IsSuiside函数,通过试下作为最终的裁决,而这又涉及到对象的拷贝,太过昂贵。于是想尽量少用,通过位运算来判定多数情况。然后随着测试的进行,陆续发现多处的位运算判定不可靠,不得不用IsSuiside函数替代……

      最后的效率可想而知,9 x 9棋盘模拟1w局,用时约40秒——这是不可授受的。

      回过头来思考,越发觉得这件事并不必要做。多数情况下,自杀点本身并无价值(除了打劫时也许可作劫材),即使允许自杀,在UCT搜索过程中,自杀点自然会冷下来——用过多的资源判定自杀点划不来。

      于是修改程序,让规则允许自杀,不过为了尽早结束棋局,不允许自填眼位,也不允许填对方眼位而不提子。增量维护眼位集合要简单得多。

      测试下来要快很多,9 x 9棋盘,2.3秒模拟1万局,CPU是2.4G core 2,编译器是clang,开O3优化级。

      Simulate函数:

    template <BoardLen BOARD_LEN>
    PointIndex
    MCSimulator<BOARD_LEN>::Simulate(const BoardInGm<BOARD_LEN> &input_board) const
    {
        BoardInGm<BOARD_LEN> bingm;
        bingm.Copy(input_board);
    
        do {
            PlayerColor last_player = bingm.LastPlayer();
            PlayerColor cur_player = OppstColor(last_player);
            const auto &playable = bingm.PlayableIndexes(cur_player);
    
            std::bitset<BLSq<BOARD_LEN>()> noko_plbl(playable);
            PointIndex ko = bingm.KoIndex();
            if (ko != BoardInGm<BOARD_LEN>::NONE) {
                std::bitset<BLSq<BOARD_LEN>()> kobits;
                kobits.set();
                kobits.reset(ko);
                noko_plbl &= kobits;
            }
    
            PointIndex play_c = noko_plbl.count();
            if (play_c > 0) {
                PointIndex rand = this->Rand(play_c - 1);
                PointIndex cur_indx =
                    GetXst1<BLSq<BOARD_LEN>()>(noko_plbl, rand);
                bingm.PlayMove(Move(cur_player, cur_indx));
            } else {
                bingm.Pass(cur_player);
            }
        } while(bingm.PlayableIndexes(BLACK_PLAYER).count() > 0 ||
                bingm.PlayableIndexes(WHITE_PLAYER).count() > 0);
    
        return bingm.BlackRegion();
    }

      忽然想试试19路标准棋盘下,双方随机落子黑棋能赢多少,测试函数:

    template <BoardLen BOARD_LEN>
    void MCSimulator<BOARD_LEN>::TEST()
    {
        int begin = clock();
        int sum = 0;
        const int a = 10000;
        for (int i=0; i<a; ++i) {
            BoardInGm<TEST_LEN> b;
            b.Init();
            auto &mcs = MCSimulator<TEST_LEN>::Ins();
            int r = mcs.Simulate(b);
            sum += r;
        }
        int end = clock();
        printf("time = %f\n", (float)(end - begin) / 1000000);
        printf("simulate complte.\n");
        printf("average black = %f\n", (float)sum / a);
    }

      19路的模拟速度有点慢,28秒1w局。看来随机落子的先行优势并不明显……

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

  • 相关阅读:
    CSS 文档流布局以及盒子模型
    CSS样式继承和样式权重
    CSS选择器餐厅练习
    node.js读写json文件
    C#单例模式的2种实现方式,Lazy模式和双检锁模式
    The Entity Framework provider type 'MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework, Version=8.0.18.0,
    mysql主从配置
    nginx 限制ip并发数,nginx限制IP连接数的范例参考
    certbot 调用cloudflare api申请证书
    proxy_pass url 反向代理的坑
  • 原文地址:https://www.cnblogs.com/qswang/p/2815632.html
Copyright © 2011-2022 走看看