基于博弈树的开源五子棋AI教程[7 多线程搜索]

这篇具有很好参考价值的文章主要介绍了基于博弈树的开源五子棋AI教程[7 多线程搜索]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引子

多线程加快搜索速度这一认知是经受住实践考验的。博弈树搜索的并行搜索方式有很多种,例如叶子并行,根并行,树分裂等算法。笔者给出一种实现起来比较简单的根并行算法。
在是实现时需要注意两点,第一,怎么安全的剪枝;第二,如何进行线程间的通信。对于AB剪枝有三点发现可以指导我们设计多线程的并行算法:

  1. 当某一节点搜索完成,其分数才能安全的更新父亲节点的AB值。
  2. 一个节点的AB值可以安全的更新其所有子孙节点的AB值。
  3. 如果一个节点alpha >= beta, 这个节点可以安全的被剪枝

这样一来,就可以知道一个节点搜索完成后,如何更新博弈树所有节点的AB值,如何剪枝。通信方式使用的全局变量+读写锁控制的,全局变量保存所有节点状态的AB值。当搜索开始,从根节点沿着搜索路径开始更新沿路的所有节点AB值,然后从全局变量中读取该节点的AB值。搜索完成后,更新父亲节点AB值。

定义

struct parallelNABSearchNode{
    int alpha, beta;
    parallelNABSearchNode() : alpha(-INT_MAX), beta(INT_MAX){}
    parallelNABSearchNode(int aalpha, int abeta) : alpha(aalpha), beta(abeta){}
    QString str();
    //返回值:true已经更新,false表示没更新
    bool getAlphaBeta(int &aalpha, int &abeta);

    bool updateLeaf2RootAlphaBeta(int score);

    //返回值:true已经更新,false表示没更新
    bool updateRoot2LeafAlphaBeta(int aalpha, int abeta);
};
    //并行化搜索技术
    static QReadWriteLock parallelSearchTableLock;
    static QHash<quint64, parallelNABSearchNode> parallelSearchTable;

函数实现三个方法,一个getAlphaBeta(int &aalpha, int &abeta)是从全局变量中获取AB值,一个updateLeaf2RootAlphaBeta是从更新该节点的父亲的AB值,还有一个updateRoot2LeafAlphaBeta是更新儿子节点的AB值。

bool parallelNABSearchNode::getAlphaBeta(int &aalpha, int &abeta){
    if(!globalParam::utilGameSetting.IsOpenParallelSearch) return false;
    if(aalpha >= alpha && beta >= abeta) return false;
    if(aalpha < alpha){
        aalpha = alpha;
    }
    if(beta < abeta){
        abeta = beta;
    }
    return true;
}
bool parallelNABSearchNode::updateLeaf2RootAlphaBeta(int score){
    if(!globalParam::utilGameSetting.IsOpenParallelSearch) return false;
    if(score > alpha){
        alpha = score;
        return true;
    }
    return false;
}
bool parallelNABSearchNode::updateRoot2LeafAlphaBeta(int aalpha, int abeta){
    if(!globalParam::utilGameSetting.IsOpenParallelSearch) return false;
    if(alpha >= aalpha && abeta >= beta) return false;
    if(alpha < aalpha){
        alpha = aalpha;
    }
    if(abeta < beta){
        beta = abeta;
    }
    return true;
}

实现

现在已经实现了线程间通信的工具,只需要在搜索时调用这些利器就可以了,总体的实现思路和常规负极大搜索如出一撤。为了能后续兼容树分裂的算法,这里给出了并行化搜索指定深度的接口。

//fail-soft negMax Alpha-Beta pruning search
int GameAI::NABParallelSearch(int depth, int alpha, int beta, bool maximizingPlayer, quint8 searchSpaceType)
{
    int score = -INT_MAX;
    QWriteLocker writeLock(&globalParam::parallelSearchTableLock);
    // 更新根节点->当前节点搜索路径上AB值
    for(int pid = 0;pid < parallelSsearchHistoryPlayersHash.size() - 1; ++pid){
        //表项不存在会自动调用默认构造函数
        parallelNABSearchNode *curNode = &globalParam::parallelSearchTable[parallelSsearchHistoryPlayersHash[pid]];
        parallelNABSearchNode *sontNode = &globalParam::parallelSearchTable[parallelSsearchHistoryPlayersHash[pid + 1]];
        //更新下一层的AB值
        sontNode->updateRoot2LeafAlphaBeta(- curNode->beta, - curNode->alpha);
    }
    // 获取当前AB值
    globalParam::parallelSearchTable[zobristSearchHash.hash()].getAlphaBeta(alpha, beta);
//    // 更新AB值后可能引发剪枝
//    if(alpha >= beta){   // AB剪枝
//        aiCalInfo.cutTreeTimesCurrentTurn ++;
//        return beta;
//    }
    writeLock.unlock();

    //探查置换表中值
    if(zobristSearchHash.getNABTranspositionTable(score, depth, alpha, beta)) {
        return score;
    }

    // ??或 分数过大过小
//    if (qAbs(score) > globalParam::utilGameSetting.MaxScore){
//        //保存置换表
//        return score;
//    }

    int evalPlayer = globalParam::AIPlayer;
    MPlayerType searchPlayer = maximizingPlayer ? evalPlayer : UtilReservePlayer(evalPlayer);

    // 达到搜索深度
    if (depth == 0 || checkSearchBoardWiner() != PLAYER_NONE){
        //保存置换表
        score = evaluateBoard(evalPlayer);//负极大搜索中评估必须searchPlayer
        if(!maximizingPlayer) score *= -1;

//        //VCF
//        QList<MPoint> vcf, vcfpath;
//        if(VCXSearch(globalParam::utilGameSetting.MaxVctSearchDepth, maximizingPlayer, VCT_SEARCH, vcf, vcfpath)){
//            qDebug() << "NABsearch : find vct";
//            if(maximizingPlayer) return globalParam::utilGameSetting.MaxScore;
//            else return -globalParam::utilGameSetting.MaxScore;
//        }
        return score;
    }

    // 着法生成
    QVector<MPoint> searchPoints;
    getSortedSearchSpace(searchPoints, evalPlayer, searchPlayer, searchSpaceType);

    int scoreBest = -INT_MAX;
    int hashf = hashfUperBound;
    MPoint moveBest(InvalidMPoint);
    quint16 savedSearchBoardPatternDirection[boardSize][boardSize];

    for (const auto &curPoint : searchPoints) {
        if (!searchBoardHasPiece(curPoint)) {
            setSearchBoard(curPoint, searchPlayer, savedSearchBoardPatternDirection);// searchPlayer落子
            score = -NABParallelSearch(depth - 1, -beta, -alpha, !maximizingPlayer,searchSpaceType);
            setSearchBoard(curPoint, PLAYER_NONE, savedSearchBoardPatternDirection);// 撤销落子

            if (score > scoreBest) {
                scoreBest = score;
                moveBest = curPoint;
                if (score >= beta) {
                    hashf = hashfLowerBound;
                    appendSearchKillerTable(curPoint, depth, hashf);
                    aiCalInfo.cutTreeTimesCurrentTurn ++;
                    break; // Alpha-Beta 剪枝
                }
                if (score > alpha) {
                    alpha = score;
                    hashf = hashfExact;

                    //更新当前层的AB值
                    writeLock.relock();
                    parallelNABSearchNode *curNode = &globalParam::parallelSearchTable[zobristSearchHash.hash()];
                    curNode->alpha = scoreBest;
                    writeLock.unlock();
                }
            }
        }
    }

//    writeLock.relock();
//    //更新当前层的AB值
//    parallelNABSearchNode *curNode = &globalParam::parallelSearchTable[zobristSearchHash.hash()];
//    curNode->alpha = scoreBest;
//    writeLock.unlock();
    writeLock.relock();
    //更新上一层的AB值:只有当前所有节点搜索完成后,得到的值才是可靠的,才能用来更新父亲节点的AB值
    if(parallelSsearchHistoryPlayersHash.size() >= 2){
        const quint64& fatherHash = parallelSsearchHistoryPlayersHash[parallelSsearchHistoryPlayersHash.size()-2];
        parallelNABSearchNode *fatherNode = &globalParam::parallelSearchTable[fatherHash];
        fatherNode->updateLeaf2RootAlphaBeta(-scoreBest);
    }
    writeLock.unlock();

    //更新历史表
    appendSearchHistoryTable(moveBest, depth, hashf);
    // 更新置换表
    zobristSearchHash.appendNABTranspositionTable(depth, scoreBest, hashf, moveBest, UtilReservePlayer(searchPlayer));

    return scoreBest;
}

结果

这里实现的并行化搜索效果并不出众,只能说是有一定效果。在深度为6搜索情况下,线程数为4的并行化搜索能加速2~3倍。这一点也是可以理解的,因为负极大搜索的节点如果排序较好,搜索量主要集中在PV路径的搜索上。简单的分裂根节点能提升的速度是可预见,只有动态的分裂树,把算力平摊到PV路径搜索,加速PV路径产生能提高博弈树搜索的瓶颈。

尾记

这里实现并行化搜索还存在一些值得思考的问题,如何能提高搜索的稳定性,在发生截断返回时,仍能正确的搜索到PV路径,而不是会因为提前的不安全的剪枝与PV路径失之交臂。后面也希望有时间继续研究下如何高效的分裂树,而不是盲目的根分裂。文章来源地址https://www.toymoban.com/news/detail-789385.html

到了这里,关于基于博弈树的开源五子棋AI教程[7 多线程搜索]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 五子棋游戏AI智能算法设计

    五子棋游戏C语言AI智能算法设计  近来发现编制五子棋游戏很有趣,尤其是AI智能算法很烧脑。网上介绍有什么贪心算法,剪枝算法,博弈树算法等等,不一而足。 对于人机对战的电脑智能应子算法,参阅很多五子棋书籍棋谱和五子棋竞赛的对抗棋谱。我感到白棋的后手防御

    2024年02月06日
    浏览(45)
  • C++ 实现对战AI五子棋

     个人主页: 日刷百题 系列专栏 : 〖C/C++小游戏〗 〖Linux〗 〖数据结构〗   〖 C语言 〗 🌎 欢迎各位 → 点赞 👍+ 收藏 ⭐️+ 留言 📝  ​ ​      为了能够快速上手一门语言,我们往往在学习了基本语法后,采用写一个小项目的方式来加深理解语言的语法及运用,本文采

    2024年02月03日
    浏览(53)
  • 五子棋AI算法和开局定式(直指13式)破解

    五子棋AI算法和开局定式( 直指13式 )破解 先前发了几篇五子棋游戏程序设计的博文,设计了游戏程序,也设计了AI智能奕棋的算法,运行程序检测算法的可行性,完成人机模式游戏功能的设置。这还不够,还要提高算法的实战水平。 对于人机对战的电脑智能应子算法,参阅

    2024年02月20日
    浏览(38)
  • 基于C#的五子棋游戏设计

    目 录 一、 毕业设计内容 3 二、 毕业设计目的 3 三、 工具/准备工作 3 四、 设计步骤和方法 3 (一) 总体设计 3 1. 总体设计思路及设计图 3 2. 界面设计 4 3. 全局变量设计 4 (二) 详细设计 5 1. 刷新棋盘 5 2. 绘制棋盘 5 3. 分步计时 5 4. 显示光标 6 5. 判断胜负 8 6.

    2024年02月04日
    浏览(54)
  • 基于FPGA的五子棋游戏设计

    基于FPGA的五子棋游戏设计 本文基于FPGA设计五子棋游戏,使用按键输入,使用VGA接口输出。五子棋的棋具与围棋相同,棋子分为黑白两色,棋盘为10×10,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的

    2024年02月05日
    浏览(45)
  • 基于Python的五子棋人机对战

    在之前的博文基于tkinter的五子棋游戏中使用tkinter做了一个简单的五子棋游戏,只能实现人人对战,后来想着加上人机对战的功能。 不过,最初想想还是挺麻烦的,计算机怎么评估当前的棋局,找到最佳或者较佳的落子点呢,脑子真是越来越不灵光了。站在巨人的肩膀上,科

    2024年02月04日
    浏览(41)
  • 基于Python的pygame库的五子棋游戏

    2024年04月09日
    浏览(41)
  • 基于Java的五子棋游戏的设计与实现

    基于 Java 的五子棋游戏的设计 摘  要 五子棋作为一个棋类竞技运动,在民间十分流行,为了熟悉五子棋规则及技巧,以及研究简单的人工智能,决定用Java开发五子棋游戏。主要完成了人机对战和玩家之间联网对战2个功能。网络连接部分为Socket编程应用,客户端和服务器端的

    2023年04月23日
    浏览(60)
  • 基于Android Studio的五子棋游戏的简单设计

    【摘要】: 随着时代的发展,现代科技的飞跃,我们的日常娱乐生活变得丰富多彩。而手机游戏被业内人士称为继通信之后的有一座“金矿”,手机休闲娱乐应用将成为PC休闲娱乐之后又一重要业务增长点。本文针对该趋势,从用户需求出发,基于Android对五子棋游戏进行设计

    2024年02月11日
    浏览(36)
  • 基于C#开发五子棋游戏 大作业 课程设计源码

    基于C#开发五子棋游戏(大作业/课程设计) 开发环境:  Windows操作系统 开发工具: Microsoft Visual Studio 运行效果图:    基于C#开发五子棋游戏(大作业/课程设计) 开发环境:  Windows操作系统 开发工具:Microsoft Visual Studio 基于C#开发五子棋游戏(大作业/课程设计) 开发环境

    2024年02月04日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包