对弈类游戏的人工智能(3)--博弈树优化

前言:
  对弈类游戏的智能算法, 网上资料颇多, 大同小异. 然而书上得来终觉浅, 绝知此事要躬行. 结合了自己的工程实践, 简单汇总整理下. 一方面是对当年的经典<<PC游戏编程(人机博弈)>>表达敬意, 另一方面, 也想对自己当年的游戏编程人生做下回顾.
  承接上两篇博文:
  (1). 评估函数+博弈树算法
  (2). 学习算法
  这篇博文回归到博弈树这边, 具体阐述下博弈树的优化手段, 为了游戏性添加的合理技巧.

启发搜索:
  博弈树本质是极大极小的求解过程, 而alpha+beta剪枝则加速该求解过程.
  让我们来构建一个简单的alpha+beta剪枝用例:

  注: 紫色代表极大值求解, 绿色代表极小值求解.
  通过人工演算和模拟, 整个博弈过程, 成功地减少了3个节点的计算量的, 效果一般.
  这个过程, 我们是否有优化的余地呢? 让我们调整下, 节点S1和S2的搜索顺序.

  与调整顺序之前相比, 其alpha+beta剪枝的效果提升, 砍去了一个大分支, 减少了4个节点的计算量.
  从这个例子中, 我们可以清晰的看到, 对于博弈树而言, 其alpha+beta的剪枝效果, 和搜索顺序是有一定关系的. 简单的总结: alpha+beta效果, 对搜素的顺序敏感.
  于是我们找到了一个优化方向: 调整可行步的顺序, 并优先搜索预期高的分支. 该技巧命名为: 启发搜索. 常有人借助历史值, killer步来构造启发函数.

// 负极大值算法
int negamax(GameState S, int depth, int alpha, int beta) {
    // 游戏是否结束 || 探索的递归深度是否到边界
    if ( gameover(S) || depth == 0 ) {
        return evaluation(S);
    }
    // 依据预估(历史, 经验)对可行步, 进行排序
    sort (candidate list);
    // 遍历每一个候选步
    foreach ( move in candidate list ) {
        S‘ = makemove(S);
        value = -negamax(S‘, depth - 1, -beta, -alpha);
        unmakemove(S‘)
        if ( value > alpha ) {
            // alpha + beta剪枝点
            if ( value >= beta ) {
                return beta;
            }
            alpha = value;
        }
    }
    return alpha;
}

  此时的核心算法结构中: 添加了可行步排序过程(sort (candidate list)).
  当然该过程是有一定代价的, 在alpha+beta剪枝效果提升和排序损耗需要均衡和折中. 一般采用计算简单预估函数即可.
  让我们回到黑白棋AI, 我们可以简单选定, 预估函数等同于位置表, 即P(x, y) = Map(x, y). (Map 为 黑白棋棋面的位置重要度矩阵), 效果斐然.

置换表:
  搞过ACM的人, 都知道DP求解的一种方式: 记忆化搜索. 本质就是把中间状态保存, 减少重复搜索的一种技巧.
  置换表的核心思想基本一致: 状态保存, 减少重复搜索.
  但置换表的难点不在于思想, 而在于状态保存.
  具体可以分析如下:
  1). 游戏局面S本身占用空间大, 而且需要保存的状态S集合多, 因此需要一个转换函数F(S) => key, (key为不长二进制串, 或一个很大的整数)
  2). 转换后的key, 一一对应了某个具体局面S (冲突率很低可忽略, 或不存在)
  让我们以黑白棋来做个例子, 局面转换为矩阵(0: 空白, 1: 黑棋, 2:白棋), 扁平化为字符串, 在借助强有力的Hash函数来转化.

  这边展示了具体的流程, 其效果的好坏, 取决于Hash函数的选择.
  简单采用MD5算法, 其实是可行的, 不过比较消耗CPU. Zobrist hashing算法也是备受推荐.
  和记忆化搜索相比, 置换表对应的局面是, 只是中间的预测节点, 因此该状态除了本身和游戏局面相关, 还和当前的搜索深度有关.
  因此具体代码可修正如下:

// 负极大值算法
int negamax(GameState S, int depth, int alpha, int beta) {
    // 判断状态已存在于置换表中, 且搜索深度小于等于已知的, 则直接返回
    if ( exists(TranspositionTable[S]) && TranspositionTable[S].depth >= depth ) {
        return TranspositionTable[S].value
    }
    // 游戏是否结束 || 探索的递归深度是否到边界
    if ( gameover(S) || depth == 0 ) {
        return evaluation(S);
    }
    // 遍历每一个候选步
    foreach ( move in candidate list ) {
        S‘ = makemove(S);
        value = -negamax(S‘, depth - 1, -beta, -alpha);
        // 保存S‘到置换表中, 当depth更深时.
        TranspositionTable[S‘] <= (depth, value) If TranspositionTable[S‘].depth < depth
        unmakemove(S‘)
        if ( value > alpha ) {
            // alpha + beta剪枝点
            if ( value >= beta ) {
                return beta;
            }
            alpha = value;
        }
    }
    return alpha;
}

总结:
  启发搜索和置换表, 两者都是很好的思路, 前者通过调整搜索顺序来加速剪枝效果. 后者通过空间换时间. 总而言之, 这些都是博弈树上很常见的优化手段. 当然在具体游戏中, 需要权衡和评估. 下一篇讲讲出于游戏性的考虑, 如何进行优化和策略选择.

写在最后:
  
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

  

时间: 2024-08-19 06:40:58

对弈类游戏的人工智能(3)--博弈树优化的相关文章

对弈类游戏的人工智能(2)--学习算法

前言: 对弈类游戏的智能算法, 网上资料颇多, 大同小异. 我写这篇文章, 一方面是对当年的经典<<PC游戏编程(人机博弈)>>表达敬意, 另一方面, 也想对自己当年的游戏编程人生做下回顾. 上一篇博文:对弈类游戏的人工智能(1)--评估函数+博弈树算法, 着重讲述了评估函数+博弈树, 本文着重讲述学习算法, 以及性能优化和游戏性问题. 分析: 评估函数的引入, 为游戏AI提供了理论基础. G(s) = a1 * f1(s) + a2 * f2(s) + ... + an * fn

对弈类游戏的人工智能(5)--2048游戏AI的解读

前言: 闲得没事, 网上搜"游戏AI", 看到一篇<<2048游戏的最佳算法是?来看看AI版作者的回答>>的文章. 而这篇文章刚好和之前讲的对弈类游戏AI对应上. 于是有了想法, 想把它作为一个实例来进行解读, 从而对之前偏理论的文章做个总结. 承接上四篇博文: (1). 评估函数+博弈树算法 (2). 学习算法 (3). 博弈树优化 (4). 游戏AI的落地 可能有些人会疑惑? 2048并非对弈类类型? 传统的博弈树模型是否能应用于此? 客官莫急, 让我们来一

对弈类游戏的人工智能(一)

前言: 对弈类游戏的智能算法, 网上资料颇多, 大同小异. 我写这篇文章, 并非想做互联网的搬运工. 而是想对当年的经典<<PC游戏编程(人机博弈)>>表达敬意, 另一方面, 也想对自己当年的游戏编程人生做下回顾. 这边我们以黑白棋游戏为例, 从博弈和学习两方面来阐述游戏AI的编写要点. 本文侧重于讲述博弈(评估函数+博弈算法). 博弈: 以前看围棋比赛, 常有人评价棋手水平高: 大局观强(评估局面好), 算路精准(计算步数深, 实战效果好). 他山之石可以攻玉, 对弈类游戏的AI

如何解决FPS/RTS/赛车类游戏的同步问题

算法简述 动作类游戏如何在高延迟下实现同步?不同的客户端网络情况,如何实现延迟补偿?十年前开始关注该问题,转眼十年已过,看到大家还在问这类问题,旧文一篇,略作补充(关于游戏同步相关问题还可以见我写于2005年的另外两篇文章,帧锁定算法 和 网游同步法则): 影子跟随算法由普通DR(dead reckoning)算法发展而来,我将其称为"影子跟随"意再表示算法同步策略的主要思想: 1. 屏幕上现实的实体(entity)只是不停的追逐它的"影子"(shadow). 2.

现象级吃鸡类游戏软件调研

序 吃鸡类游戏是一种战术竞技型射击类沙盒游戏,每一局游戏将有最多100名玩家参与,他们将被投放在绝地岛上,在游戏的开始时所有人都一无所有.玩家需要在岛上收集各种资源,在不断缩小的安全区域内对抗其他玩家,让自己生存到最后."吃鸡"一词最早来源于电影<决胜21点>中来自拉斯维加斯赌场的一段台词:"Winner winner, chicken dinner!","大吉大利,今晚吃鸡"于是有了全新的生命力.时值当下,吃鸡类游戏已然成为一种现象

24分钟让AI跑起飞车类游戏

WeTest 导读 本文主要介绍如何让AI在24分钟内学会玩飞车类游戏.我们使用Distributed PPO训练AI,在短时间内可以取得不错的训练效果. 本方法的特点: 1. 纯游戏图像作为输入 2. 不使用游戏内部接口 3. 可靠的强化学习方法 4. 简单易行的并行训练 1. PPO简介 PPO(Proximal Policy Optimization)是OpenAI在2016年NIPS上提出的一个基于Actor-Critic框架的强化学习方法.该方法主要的创新点是在更新Actor时借鉴了T

谈一款MOBA类游戏《码神联盟》的服务端架构设计与实现

一.前言 <码神联盟>是一款为技术人做的开源情怀游戏,每一种编程语言都是一位英雄.客户端和服务端均使用C#开发,客户端使用Unity3D引擎,数据库使用MySQL.这个MOBA类游戏是笔者在学习时期和客户端美术策划的小伙伴一起做的游戏,笔者主要负责游戏服务端开发,客户端也参与了一部分,同时也是这个项目的发起和负责人.这次主要分享这款游戏的服务端相关的设计与实现,从整体的架构设计,到服务器网络通信底层的搭建,通信协议.模型定制,再到游戏逻辑的分层架构实现.同时这篇博客也沉淀了笔者在游戏公司实践五

html5文字冒险类游戏《归途》从零开始(0)

转眼从太原来到上海一个多月了,实习也稳定下来了,开始记录自己第一个游戏的制作历程. 首先是技术方面的选择,因为同时肩负着前端技术的练习和定题时定为<基于安卓的文字冒险类游戏归途的设计与实现>的毕业设计,而且我现在在做的实习是JavaWeb开发,所以选择html5是在我知识范围内最不造成冲突的方案. 然后谈谈游戏本身,顾名思义,准备展现一个人在回家路上遇到的种种给人带来一些思考.最初的构思是一个互动短剧的形式,在火车车厢上进行点击探索推动情节发展,根据对话选项和探索程度的不同触发不同的路线和结局

unity3d游戏开发经验之对于关卡类游戏的技巧

过关类游戏在单机类游戏中出现会比较多,但多以休闲为主,比如<Candy Crush>.<Angry Birds>.<P V Z>.<小鳄鱼顽皮爱洗澡>.<Tiny Thief>等经典休闲游戏,鉴于很多圈内人士预测2014年是手游爆发年,且重点在ARPG类型,似乎会冒出很多横版过关或者全3D的过关动作类游戏,我们就针对此类型的游戏进行分析. 首先,此类型的游戏需要关注的是每关卡的独立玩家数量,即玩家ID数量,目的是为了监测玩家主要集中在哪个阶段.比