« July 2026 | Main

August 20, 2026

银河竞逐的乐趣和策略

银河竞逐是我最喜欢的桌游之一。我曾经为它写过两篇 blog :银河竞逐的设计如何教人玩银河竞逐

最近我教会了小孩玩这个游戏,我自己也在电脑上玩了 200 多盘。主要是在 BGA 上和人对战,以及在官方授权的电子版 中挑战最高难度的 AI 。和过去喜欢多人混战不同,这段时间我主要玩双人规则。虽然在线下,我玩这个游戏的水平超过一般桌游玩家,但在线上,我只有略高于 40% 的胜率。说明这个游戏的策略还有很高的天花板我没能摸到。和最高难度的 AI 相比,我的胜率超过了 50% 。

这是为数不多有着极深的策略,却又超快节奏的桌游。在 BGA 上默认只给两个选手各 3 分钟的总时间,通常在 5~6 分钟就可以完成一局游戏。应了它名字里的 Race ,玩起来颇有竞速的感觉。我在游戏过程中总是觉得思考时间不够,如果纯凭借经验(感觉)打牌,很容易输掉。特别在最后两个回合,往往需要精算自己和对手的各种可能性,找到取胜的可能。胜负比分通常差距非常小,运气成分虽然存在,但总有希望通过计算挽回。而判断上的一个小的失误却会迅速落下对手一大截。

这个游戏核心目标就是:在触碰结束条件的那一刻,比对手拿到更多的 VP 。新手则往往理解成单一的如何更高效的获取 VP 。这两者是有区别的。因为比拼获取 VP 的效率往往会减少 PvP 游戏的对抗性,变成埋头干自己的事情。初玩 RFTG 的时候,我也一度觉得这是个对抗性不强的游戏。但在实战中却发现,必须时刻关注对手在做什么。首先是通过判断对手的行动,帮助自己更高效的获取 VP ,其次是根据对手的节奏,扭转游戏结束的时间,做到在结束那一刻超过对手的 VP (而不是追求获得更多 VP)。

所以在高手对决时,尤其是最后,需要仔细计算游戏会在几轮结束,以及对手希望在什么时候结束。自己有什么方法维持几轮,是需要拖延多一轮、还是抢先减少一轮。而轮次最终又是双方一同决定的,这就存在很多博弈空间。需要提前估算每种情况的胜率和最佳对策。游戏规则给玩家提供了大量的选择。VP 可以通过铺在桌面的卡牌积累,也可以用场上的牌组持续生产。游戏终止条件依此分成了两个:任意玩家在桌面铺了 12 张卡牌,或耗尽 12 倍玩家数量的 VP token 。

在引擎构建方面,主流又能分为主导开发设施、军事占领、生产消费流派。细分还可以更多。这让玩家无论抽到什么手牌,总有办法构筑出有效的组合。而不同的流派决定了玩家每轮的行动倾向性,影响游戏的节奏。对手的流派影响着自己的流派的推进节奏。这种影响,可以让人可以预测对手的行动。正确预测对手的行动可以让自己获利,而这个游戏的胜利的本质是找到比对手快一点的路线。这就提供了博弈空间。这就有点像打(德州?)扑克,玩家的思考会有多个层次:第一层是计算如何行动构筑一个最强大的引擎,让自己的利益最大化;第二层则是猜测对手想做什么,怎么利用对手的行动产生优势(对自己构筑引擎,未必是最优的,但只需要比对手好);第三层是,去思考对手在第二层上猜测自己,从而避免让对手取得优势;第四层是,分析对手在前三个层面做决策,制定对自己的最优策略。

游戏(尤其是双人模式)节奏非常快,以我的经验,通常在 6-8 轮就会结束。而牌库空间相比较而言非常大,抽到什么卡几乎无法预测。假设所有人都对牌库及其熟悉,那么在前期就必须做好多手准备,中期随时转型。而游戏规则也提供了大量中途转型的可能,玩家几乎不会一门心思的贯彻刚开始的计划,都是随机应变。除了根据自己抽的新卡做调整,更会根据对手的情况而变化。毕竟,要想胜利,不在于你是否完美的执行了计划,取得了高分,而是在最后一轮超过了对手(提前冲线)。在这种思路下,最后一轮的行动选择往往是决胜一手。怎样让自己的利益和对手潜在的利益差最大。因为随机性的存在,这反映为估算哪个选择让自己击败对手的概率更大。在胜券在握时,减少黑天鹅事件事件(对手低概率摸到扭转胜负的牌并有机会打出来);在形势不利时,去博一个翻盘的可能性;在机会对等时,精算哪怕是一张手牌数量的差异……


银河竞逐很早就有人为它制作 AI 。这是个开源项目,以人工智能网络驱动的 AI 。代码其实挺好读的,因为它几乎不包含任何人为输入的策略,就存粹是以训练出来的网络指导 AI 做决策。所有 AI 的策略都是涌现出来的。但在我随意的浏览代码后,我感觉其实它还有许多的改进空间。比如,这段开源代码看起来只考虑了当前盘面的自己和对手的桌面,自己的手牌,以此来训练和推理做为行动判断。但以我的游戏经验来看,其实是不够的。因为我在玩游戏时,还要根据过往局面,推算抽牌堆可能有哪些牌,不可能有哪些牌,来做决策参考。我相信有丰富经验的玩家都会考虑这些。不过,这个 AI 的原作者 Keldon Jones 后来参加了官方授权的电子版制作。我相信他其后做了进一步的改进(包括我提到的这一点)。这里有一篇文章 介绍了后面这个版本的 AI 的思路。其中在神经网络的 input 里,专门列出了 Cards in deck, hand, discard 。

但另一方面,我感觉即使是新的版本也没有做到的,就是根据对手调整策略。因为这和围棋不一样,是一个有大量不公开信息的游戏。猜测对手的行动非常重要。如果非常了解对手,更容易找到对手的模式,猜中对手的行动更准确。而正确的预测在实战中的确可以大大提高胜率。对手会做什么,并不总能从历史中学习。如果要以此为网络的输入,我认为需要一定程度的加入当前局的历史,而不只有当前的局面。在文章中也提到,他们分叉了许多个模型,分别针对双人、多人、不同的扩展包规则。因为这样比把规则集作为参数输入同一个模型效率更高。在决策时,也不完全是一股脑把游戏的状态输入,而是把判断局面和猜测对手行动分为了两个独立部分,再用传统的搜索法整合起来:根据对手行动的不同预测结果和概率,参考局面胜率的判定,搜索最优策略。

我感觉还能细化更多:根据初始星球分开不同的开局策略、中盘和终局也可以分成不同的决策网络、甚至给对手的模式进行分类(保守还是激进)。尤其是对终局的处理,以我有限的游戏经验判断,最后是需要精算的,这有点类似围棋的收官,但又不完全相同。固然一个超大的统一模型或许可以涌现出人类需要在不同阶段分而治之的策略模式。但这同时也是个成本问题。从开源代码看,它也对最后一轮前的桌面状态做了一点区别对待(比如不再关心手牌保留了什么)。

无论如何,这是个高质量 AI 。我和 AI 对战也能获得不少乐趣。无法碾压 AI ,当然 AI 更无法碾压我。它能极大的帮助新手学习游戏,充当一个优秀的陪练。

不过,我感觉和人玩(哪怕是和线上完全不认识的人玩)还是更有趣一些。

August 09, 2026

用地标改进 A star 寻路的启发函数

今天读到 redblobgames 写的关于如何改进寻路算法中启发式函数的文章 ,感觉很棒。我们可以用很简单的方法改进 A star 算法中的传统计算欧氏距离的方式实现启发函数的方法。

这意味着,可以直接利用之前写好的寻路模块 改变一下启发函数,就可以大幅提升性能。

A star 寻路算法,本质上是对 Dijkstra 算法的一种改进:即从一个端点扩展节点时不盲目的向外扩展,而是利用一个启发函数优先尝试最可能在最短路径上的邻接节点。启发函数设计的越好,每一步的估计都和正确路径一致,那么就越快。依靠启发函数的结果就能避开所有不必要的节点。

但是,启发函数的复杂度不能太高,最好是 O(1) 的。因为每一步都要调用一次,时间复杂度乘在整个运算过程中。这使得启发函数不能去探测远方的状态。通常我们用当前位置到目标点的直线距离作为启发值,这个值一定小于实际路径距离,这样才能保证按启发值得到的路径一定不会比真实路径更糟糕。但在有墙和复杂迷宫的地图中,它几乎难以起到正确的引导作用。

例如,当起点在一个房间中,目标点在东边,而房间的门在西边。采用这种启发函数,算法一定会向东尝试完几乎所有路径后,才会从西边的门出去。毕竟启发函数感知不到门的位置,房间内部的东侧距离目标点的直线距离一定更近。如果房间是密闭的那就更糟了,算法会尝试完房间内的每个位置才会停下来。


有没有什么简单的方法可以改进启发函数,让它可以识别出墙,却不增加时间复杂度呢?最简单的方法是用一个地标。

在现实里,如果一个人要从北京出发去广州某地,他肯定不会从出发点地开始用 A 星算法寻路。而是先去到广州再说,北京到广州的路径是事前就已知的。到了广州再找怎么去特定地点。广州作为一个区域比较抽象,但我们可以预算计算出到广州某距离地点(例如广州塔)的路线图。对于固定目的地,可以用 Dijkstra 算法计算出到地图每个点的最短路径图( 流图 ),全部缓存下来。这样从任意点都可以用 O(1) 查询到如何去广州塔的当前移动方向。

当目的地离地标很近且当前位置离目的地很远时(从北京出发,还没进广州),按着去地标点的预设路径走一段肯定没错。直到离目的地比较近了(已经到了广州),就没必要按预设路径(去广州塔)行进了。

问题在于怎么确定离目标点比较近了?还是计算欧氏距离。

假设当前位置是 S(北京某地) ,目标点为 E (广州某地),地标点为 L (广州塔) 。这三个点可以构成一个三角形。因为三角形的任意两条边之和大于等于第三边,所以有 SE + EL >= SL 。也就是说在估价函数中,我们判断 SE 是不是大于等于 SL - EL ,即北京某地到广州某地的直线距离是不是小于等于出发地到广州塔的已知距离减去广州塔到目的地的已知距离。如果是,说明直线距离太短了,不值得按直线方向行进,应该遵循直接计算好的去广州塔的路径行进。反之,直线路径指向的方向可能是更有效的。

再看前面的房间例子:如果起点和东边的目的地之隔着一堵墙,直线距离为 1 ,但实际路径必须先绕向西侧的墙。而如果我们在目的地附近有一个预设的地标,距离目的地路程为 2,而当前位置到地标点的路程为 10 ;那么显然,从当前位置去目的地的最短路程不可能小于 10 - 2 = 8 。因为假设目的地就在去地标点的路径上,那么我们向地标行进,在剩下路程还有 2 的时候,就能抵达终点。这已经是最短路径,如果目的地偏离了这条路径,路程只能更远。

用地标的预设路程,可以更容易的排除目的地不可达的情况:如果我们可以去到地标,而地标到目的地不可达,那么目的地就不可达;同样,如果地标到目的地可达,而我们无法去到地标,目的地也不可达。如果目的地和当前位置都可以抵达地标,路径一定是存在的。只有当前位置和目的地都无法通向地标试,可达性才是未知的。


所以,我们在估价函数中,只要利用预算计算好的地标流图,就可以 O(1) 计算出是否可以复用到地标的流图给出预设方向,或是退化成传统以直线方向给出的估价值。

我们还可以多计算几个地标,比较它们哪个更有效:SL - EL 中较小的那个,决定采用到哪个地标的预设路径。地标可以是提前计算的,也可以是在多次寻路中生成的。可以想象成,如果我们没有去过广州,那么就先以一个离广州比较近的去过的地标,比如深圳为地标导引路径,一旦抵达目的地,就把新的这个目的地广州为地标记录下来,方便后续的寻路。