写五子棋 AI,minimax 搜到第六层就要十几秒,再加深度直接卡死。换成蒙特卡洛树搜索,同样的时间能搜到更深的有效分支。

四个步骤循环

选择、扩展、模拟、回传。从根节点往下走,每步挑最值得走的子节点;走到还没展开的节点就生一个新子节点;从这个新局面随机下完一整盘;再把胜负沿路径回传给每个节点。循环几千次,访问次数多的分支就是好棋。

怎么定义值得走

UCT 把利用和探索放进同一个式子:胜率高的节点优先,访问次数少的节点也留机会。大意是胜率加上一个常数,乘以父节点访问次数取对数再除以本节点访问次数后的平方根。常数 C 一般取 1.414,也就是根号二;C 调大更爱探索,调小更爱利用已经找到的好路。

score = wins / visits + C * Math.sqrt(Math.log(parentVisits) / visits)

分母是零要特判。新节点访问次数还是 0,直接返回一个极大值,逼它先被走一次。

模拟策略决定强弱

纯随机下完,围棋上基本是废棋。AlphaGo 之后的做法是用策略网络给候选走法,用价值网络估局面,模拟步数可以缩短到几次。没有神经网络也能做,五子棋用启发式就够:能连五就下,对手要连五就堵,其余随机。

什么时候不该用它

状态少、能完整枚举的棋,minimax 加剪枝更准也更快。蒙特卡洛树搜索的优势在分支因子大、评估函数写不好的场景,比如围棋、复杂调度、游戏里的战术决策。单步代价高的任务用它反而绕远。

几个实际参数

  • 每步预算按时间给,比如 800 毫秒,而不是固定迭代次数
  • 节点数过万要按需释放,内存涨得比想象快
  • 最终选访问次数最高的子节点,别用单次胜率最高的

并行要加虚拟损失

单线程跑一万次迭代,时间预算就那么多。多线程共享一棵树得加虚拟损失:选中某节点时先给它记一笔虚拟败局,别的线程就不会同时钻同一条路。线程数按核数给,开太多锁竞争比搜索还慢。叶子节点存胜负和访问次数两个整数就够,别把整个棋盘复制一份放进去。

代码两百行以内就能跑通,难的是调参和评估。先让它能下完整盘棋,再谈棋力。