DarkForest 简化对局的经典随机游走模型
本文抽去装备、信息和策略差异,把角色行动近似为固定权重的网格随机游走。模型用于分离地图边界、遭遇和淘汰对时间分布的影响;下面的概率与回合数属于这一简化规则,不能直接视为完整 DarkForest 对局的预测。
设若干角色在一张可通行范围有限的二维方格地图上游走。每回合按固定顺序让各角色行动一次;轮到某角色时,它选择向上、下、左、右移动一格,或留在原地。五种行动的概率可以不同,但对同一角色保持不变;不同角色独立选择行动。若某方向通向不可通行方格,就删去该动作,并在其余动作上重新归一化概率。
一名角色移动到另一名存活角色所在的方格时,立即发生遭遇。回合暂停,双方以相同概率淘汰其中一人,然后继续本回合尚未完成的行动。场上只剩一名角色时,游戏结束。需要计算:
- 指定角色在指定回合到达指定方格的概率,以及首次到达该方格的期望回合数和标准差;
- 指定角色在指定回合遭遇的概率,以及首次遭遇的期望回合数和标准差;
- 游戏在指定回合结束的概率,以及结束时间的期望和标准差。
本文以一张 方格地图为算例:中央 的方格不可通行,七名角色从不同位置出发,五种行动的权重均为 。
计算方法取决于所问事件。若暂时忽略淘汰,单名角色的位置只依赖自身的上一步位置,可以用有限状态 Markov 链精确计算到达概率和首达时间。实际游戏内的占用概率则还受淘汰影响。遭遇及游戏结束更依赖多名角色在回合内部的行动路径;七人局的联合位置状态约有 个,无法直接递推。本文对这部分采用蒙特卡洛模拟,并用同一地图上可精确求解的两人局校核。
一、地图、行动权重与边界转移

地图给出了角色能到达哪些位置。接下来为每名角色指定出发点,以及它选择五种行动的相对权重。
角色 由出发点 与一个五元非负权重
给出,其中原地不动的权重 。权重只表示各动作的相对倾向:在不考虑地图边界时,选择动作 的概率为
地图边界会使某些动作失效,因此不能直接把上述五个概率用于每个方格。需要按当前位置重新计算可执行动作的概率。
记五种动作的位移分别为 、、、、。角色 位于方格 时,只有落点仍在 中且权重大于 的动作才可执行。将这些动作组成集合
记
是角色 从 出发时所有可执行动作的权重之和,用于对这些动作重新归一化。由于原地不动始终可执行且权重为正,。
以 中的方格为行、列索引,定义角色 的一步位置转移矩阵 。矩阵元 表示角色当前位于 时,下一次行动后位于 的条件概率:
分子汇总所有以 为落点的合法动作的权重,分母则是该位置全部合法动作的权重之和。因此边界处先排除不可通行的方向,再对其余动作重新归一化;动作的原始权重本身不随位置改变。
这个定义需要满足两个基本要求:从任一方格出发,各落点的概率之和应为 ;而“原地不动”应使留在原格的概率为正。
有了转移矩阵,还可以问角色持续游走后是否偏向某些方格。对称的方向权重使相邻方格之间的概率流量相互平衡,从而给出平稳分布。
若权重满足对称性 且 ,定义
证明. 验证任意两个方格之间的细致平衡,即 。按方格之间的关系分三种情形:
- 若 ,两边都是 ,自动成立。
- 若 与 相邻,则从 到 只有唯一一个方向 ,从 到 只有它的反向 ;把 代入,两边分别是 与 ,由对称性假设相等。
- 其它情形两边都是 。
因此 是平稳分布。若四个移动方向的权重均为正,地图连通性保证链不可约;再由对角元为正知其非周期,故平稳分布唯一,并且分布几何收敛。
对算例的单位权重, 有更直观的含义:它等于原地不动这一种动作,加上从 可以走到的相邻方格数。按位置分类,可得:
| 含义 | 格数 | 平稳概率 | |
|---|---|---|---|
| 地图四角(只有 个可通行邻居) | |||
| 四条边或紧贴障碍块 | |||
| 内部(四面可通) |
这三类方格的权重总和为 。因此,四角的平稳概率为 ,内部方格为 ,前者是后者的 。用幂法计算得到的平稳分布与解析式的最大绝对偏差为 。
以上只定义了单次移动。为了确定遭遇发生在回合内的哪个时刻,还需明确行动、决斗和终止的顺序:
- A1(地图) 是有限连通方格集。
- A2(顺序行动)每回合按固定顺序 让每名存活角色各行动一次(已淘汰者跳过)。“行动顺序固定”因此是有内容的:它决定了谁先移动、谁被撞到时还未行动。
- A3(边界与可达性)非法方向从合法动作集中删除,其余按权重重新归一化(定义 3)。所有首达时间的有限性与几乎必然遭遇的结论还要求四个移动方向的权重严格为正;算例满足此条件。
- A4(遭遇)角色 移动后若落点上有另一名当前存活角色 ,立即发生遭遇;同格超过两人不会出现(见 命题 4)。
- A5(决斗)决斗在此刻完成、不消耗额外回合;两人各以 被淘汰,幸存者留在相遇格。
- A6(回合继续)决斗结束后,本回合从 之后的角色继续;若被撞者 尚未行动且幸存,它仍保有本回合自己的行动。
- A7(终止)只剩一名角色时游戏结束。
Code/gridGameOptimized.py 的实现一致;若采用同时行动模型,遭遇事件及其分布需要重新定义(见 “四、讨论”)。1. 单角色:到达概率与首次到达
先研究不受淘汰影响的移动过程。为此,给每名角色定义一条延续轨迹 :即使它已在游戏中出局,也设想它继续按同一个转移矩阵独立移动。这样得到的是单角色位置分布。若要问实际游戏中角色在第 回合末占据格 的概率,还必须同时要求它没有被淘汰;两种概率的含义不同。
设 是在出发点取 的行向量,则延续轨迹在第 步的位置分布为
位置分布回答“第 步在哪里”;若要回答“最早何时到达目标格”,需改看整条轨迹,并定义首达时间。
以下固定目标格 ,把角色 的出发格记为 。从 中删除 对应的行和列,得到 矩阵 。它只记录游走尚未进入 时,在其余方格之间的转移;以下矩阵下标均表示方格。
对 ,
上式给出每一步仍未到达目标格的概率。要由这些尾概率计算期望和标准差,还需要下列适用于非负整数随机变量的恒等式。
对取值于 的 ,
尾和公式可能包含无限多项。为保证它们对本文的首达时间收敛,需要利用地图连通性和正方向权重建立一个统一的尾概率上界。
尾和适合逐步计算首达时间分布;若只需要期望和标准差,可以把尚未到达目标格的转移写成线性方程组,直接求出所有起点的结果。
记 。对任意起点格 ,令 、,其中下标 表示从 出发,则
证明. 由 定理 3,矩阵级数 收敛。 是从 出发、首次到达 之前访问 的期望次数。每个 的时刻链恰好位于一个格子,所以对 求和得到 。
再按第一步条件化。对 ,有 ;整理得 。由于 且 ,故 。
得到尾概率后,首达时间恰好等于 的概率就是相邻两项尾概率之差。
2. 多角色:顺序行动下的遭遇与结束
单角色转移矩阵只记录一名角色在行动后的落点。遭遇可能发生在回合中途,并随即改变存活人数;因此完整计算需记录存活集合及其位置,递推一回合时还需跟踪已经处理到哪名角色。
在每个回合结束时,用 表示局面。其中 是存活角色的集合, 是角色 的位置;不同的存活角色占据不同方格,即 时 。
一回合转移核 给出从当前局面到下一回合结束时各局面的概率。它按固定顺序处理角色;轮到存活角色 时,先按 抽取落点 ,再根据落点是否被占据执行下列规则:
- 若 上没有别的存活角色,局面更新为 ,继续下一个;
- 若 上停着存活角色 ( 可能在 之前或之后行动),则 、 立即决斗:以 淘汰 、以 淘汰 ;幸存者停在 ;然后从 的下一个角色继续本回合。
这套规则只在每次行动后、决斗完成后保存局面。下一命题说明这样做始终不会留下两个存活角色同格的状态。
还需确认上述流程没有遗漏或重复计入任何概率质量。只要每名角色的移动分支、决斗分支都保持总概率,整回合流程就定义了合法的转移核。
由于每一步的分支和概率均已确定,可以用张量保存各存活集合下的位置分布,并逐回合推进。下面给出这一递推及其可提取的时间分布。
对每个存活集合 ,用形状为 的次概率张量 保存其各位置组合的概率;所有张量的总质量为 。初始时,只有包含全部角色的张量在初始位置组合上取值 。每回合依 定义 5 的顺序,对轮到的角色沿其坐标轴应用 ,再按是否遭遇拆分,并对决斗结果分别更新存活集合。这样得到每回合结束时局面的精确分布。记录每次决斗发生的回合与参与者,还可同时得到
如果无视遭遇机制,各角色的位置在任意固定时刻是相互独立的(每人的移动只依赖自己),联合分布就是乘积测度 。可是遭遇并不由“回合结束时的位置”决定:A2 的顺序模型里,两人交换位置也会触发遭遇——先动的那个人正好走进了对方当时所在的格子。所以
这个上界虽然不能代替真实的“尚未遭遇”概率,却能直接由各角色的单人位置分布计算。其计算形式是矩形概率矩阵的积和式。
记 为 矩阵 ,则
上界可能长期接近 ,并不能说明游戏会持续多久。另一方面,只要每个方向都具有正权重,就能证明角色最终必然相遇;这个结论只保证终止,不给出实用的等待时间估计。
理论上可递推,不等于在给定规模下能够存储全部状态。下一步估计状态数,以决定哪些情形采用精确计算、哪些情形采用模拟。
七人局采用独立重复的模拟。对每个回合的遭遇、结束等事件,记录出现次数便得到概率估计;误差的量级由模拟局数决定。
设独立重复 局。对任意事件 , 无偏,标准误为 ,95% 的 Wilson 区间为
二、面向对象的实现
完整代码把上述数学对象分为地图、单角色、精确游戏递推、蒙特卡洛模拟和可视化五部分。地图文本、行动顺序与默认权重分别由 MAP_TEXT、TURN_ORDER 和 DEFAULT_MOVE_WEIGHTS 给出,与 Code/gridGameOptimized.py 中的算例配置一致。下表列出各类与模型的对应关系。
| 类 | 表示的数学对象 | 关键成员 |
|---|---|---|
GridMap | 地图 与角色标签 | cells / index / step_table( 查表,非法记 ) / 连通性自检 |
Walker | 角色权重与链 | legal / total_weight() / Q / occupancy / first_passage_maps / stationary_analytic |
Game | 定义 5 的一回合核与张量递推 | _advance(逐角色推进) / _split_at / first_duel / self_check |
MonteCarlo | 同一核的采样版本 | 向量化整局推进 + 已结束试验即时剔除 |
Visualizer | 可视化 | 逐帧概率 GIF / 分布曲线 / 首达热图 |
定义 3 的“先筛合法方向、再归一化”直接落成一张逐格的合法动作表;均匀权重时可以走一条更快的采样捷径,但这里有一个真实的坑:
# 均匀权重时动作在合法集合上均匀分布,可以把合法目标压缩到前几列
order = np.argsort(~self.legal, axis=1, kind="stable") # 合法排在前
self.legal_targets = np.take_along_axis(grid.step_safe, order, axis=1)
# 反例:某格"向上"是障碍而其余方向合法时,合法掩码是 [F,T,T,T,T],
# 若直接按方向顺序取前 d=4 列,就会把"向上"(原地)算进来、"原地不动"挤出去,
# 采样分布立刻偏离 Q_i。必须先把合法项压到前 d 列。定理 4 的核心是“逐角色推进 + 即时决斗”,实现上就是一个带分支的递归:
def _advance(self, turn, alive, arr, k, dueled, turn_dueled, nxt):
if k == len(self.order): # 本回合队列走完
nxt[alive] = self._add(nxt.get(alive), arr); return
who = self.order[k]
if who not in alive: # 已淘汰 -> 跳过
self._advance(turn, alive, arr, k + 1, dueled, turn_dueled, nxt); return
axis = alive.index(who)
arr = _contract(self.walkers[who].Q, arr, axis) # who 移动一格
free, pieces = self._split_at(arr, alive, axis) # 拆分:互异 / 第一对重合
if free.any():
self._advance(turn, alive, free, k + 1, dueled, turn_dueled, nxt)
for partner, sel in pieces: # who 撞上了 partner
...
for dead in (who, partner): # 各以 1/2 被淘汰
alive2 = tuple(x for x in alive if x != dead)
new = 0.5 * sel.sum(axis=alive.index(dead)) # 求边缘,消去死者坐标
if len(alive2) == 1: # 只剩一人 -> 结束
self.end_pmf[turn] += float(new.sum())
else:
self._advance(turn, alive2, new, k + 1, new_dueled, True, nxt) # 本回合继续单角色那一侧, 与 走 命题 3( 次 求逆),定理 2 只用来抽查校核,避免把四万步迭代套在 个格对上。
复杂度:精确递推单回合 、存储 ;本文只对 与一个 的测试地图用精确递推, 人局用蒙特卡洛, 局、上限 回合、已结束的试验即时从数组里剔除,几分钟跑完。
三、数值结果
以下结果使用同一地图与初始配置。七人局模拟 次,每局最多运行 回合,所有试验都在上限内结束;两人局另模拟 次,与精确递推对照。单角色首达量由转移矩阵计算。阅读图表时应区分:占用动画对应实际游戏,首达热图对应忽略淘汰的延续轨迹。
1. 单角色的到达概率与首次到达
先看实际游戏内的短时占用概率,再看不考虑淘汰时的首达时间。两类图使用同一移动规则,但对应的随机事件不同,数值不能直接互换。

walk-1.gif … walk-7.gif。
占用动画展示的是概率质量随回合扩散的过程。若要比较“从某个出发点到不同目标格需要多久”,则应转向延续轨迹的首达时间;下面两幅热图分别给出其期望和标准差。


热图汇总了全部目标格。为看清首达时间分布的形状,再从角色 1 的出发点选取四个距离和位置不同的目标格,绘制各自的概率质量函数与累积分布。

| 角色 1 的目标格 | ||||
|---|---|---|---|---|
| 首达期望 | 458.50 | 709.13 | 1623.23 | 1801.48 |
| 首达标准差 | 1085.07 | 852.28 | 1563.64 | 1571.01 |
全部 个(起点, 目标)组合的期望落在 内,标准差最大 。在这张有限地图上,部分较远目标格的首达标准差与期望同量级,说明单次等待时间分散。定理 3 保证尾概率最终几何衰减,因此不宜把这一有限状态模型的分布直接称为重尾分布。
2. 遭遇
遭遇记录的是回合内部是否出现角色同格,不能仅凭回合结束时的位置判断。以下先看整局第一次遭遇发生在哪里,再比较首次遭遇、任意遭遇及“尚未遭遇”的时间分布。


图中的全局首次遭遇与各角色首次遭遇不是同一随机变量。前者在一局中只出现一次;后者分别记录每名角色第一次参与决斗的回合。相应的期望与离散程度列于下表。
| 量 | 期望 | 标准差 | 中位数 | 众数 | 90% 分位 |
|---|---|---|---|---|---|
| 整局首次遭遇 | 20.594 | 13.606 | 17 | 11 | 40 |
| 角色 1 首次遭遇 | 99.867 | 170.225 | 50 | — | 212 |
| 角色 2 首次遭遇 | 94.711 | 161.971 | 48 | — | 199 |
| 角色 3 首次遭遇 | 95.438 | 172.833 | 43 | — | 212 |
| 角色 4 首次遭遇 | 77.828 | 150.410 | 34 | — | 170 |
| 角色 5 首次遭遇 | 102.465 | 177.879 | 48 | — | 226 |
| 角色 6 首次遭遇 | 82.169 | 151.855 | 39 | — | 175 |
| 角色 7 首次遭遇 | 166.614 | 236.532 | 91 | — | 370 |
积和式上界与模拟得到的“尚未遭遇”概率差别明显: 在第 、、、、 回合分别是 、、、、,而真正的“尚未遭遇”概率同一批时刻只有 、、、、。到第 回合,真实生存概率已降至约 ,仅由终点位置计算的上界仍为约 。这个差距表明,终点位置没有记录回合中途发生的遭遇;要估计遭遇时间和结束时间,必须保留路径信息(定理 4)。
整局第一次遭遇的期望为 回合,各角色首次参与决斗的期望则介于 与 回合之间。原因是整局第一次决斗只涉及两名角色,其余角色仍须等待各自的首次遭遇。角色 7(,地图最右侧)最晚被卷入(),角色 4(,紧贴障碍块下方、离角色 3 只有 步)最早()。
3. 游戏结束
每场决斗淘汰一人,因此七人局在第六场决斗后结束。结束时间不仅取决于第一场遭遇,还取决于其后各次相遇的等待时间。下图和表给出完整分布及其主要统计量。

| 量 | 期望 | 标准差 | 中位数 | 众数 | 95% 分位 |
|---|---|---|---|---|---|
| 游戏结束 | 731.082 | 459.417 | 614 | 404 | 1631 |
结束时间的 25%、75%、90%、99% 分位分别为 、、、 回合。中位数为 回合,说明约一半的试验需要至少这么久;约百分之一的试验超过 回合。定理 5 给出的是几乎必然终止,并不要求终止时间集中在较短的范围内。
4. 谁会赢:这一配置中的位置与行动顺序
决斗双方的淘汰概率相同,并不意味着七名角色的最终获胜概率相同。出发位置和行动顺序会改变它们参与决斗的时机。先比较原配置下的首次遭遇时间与获胜概率:
| 角色 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| 首次遭遇期望 | 99.9 | 94.7 | 95.4 | 77.8 | 102.5 | 82.2 | 166.6 |
| 获胜概率 | 0.13813 | 0.13478 | 0.14075 | 0.12791 | 0.14315 | 0.12894 | 0.18634 |
这些获胜概率并不相等。若七名角色在地图和规则下完全对称,它们才都应为 ;当前配置并不满足这种对称性。角色 7 是 ,角色 4 只有 ,相差近 倍。在这七名角色中,首次遭遇期望较大的角色通常也有较高的获胜概率。两者的 Spearman 秩相关系数是 ( 个点里只有第 1、3 名的次序互换了)。
这一相关性可以这样理解:每参加一次决斗,角色都有 的概率被淘汰;较晚遭遇的角色可能少经历若干场决斗。不过,首次遭遇时间本身并不能单独决定胜率。角色 7 从地图右侧出发,在本次模拟中首次遭遇最晚,获胜概率也最高。
角色 7 同时具有靠右的出发位置和最后行动的顺序,单凭原配置不能区分两者的作用。保持地图、出发点和行动权重不变,仅将行动顺序整体倒过来()重跑了一遍 局:
| 角色 | 正序 ( 局) | 逆序 ( 局) |
|---|---|---|
| 1 | 0.13813 | 0.14113 |
| 2 | 0.13478 | 0.13486 |
| 3 | 0.14075 | 0.13941 |
| 4 | 0.12791 | 0.12698 |
| 5 | 0.14315 | 0.14277 |
| 6 | 0.12894 | 0.12871 |
| 7 | 0.18634 | 0.18615 |
正序与逆序估计的标准误分别约为 和 。角色 7 的两项估计为 与 ,差值小于合并标准误;其余角色的变化绝对值均不超过 。这组对照支持如下有限结论:在本文配置与这两种行动顺序下,角色间约 的获胜概率差异主要与出发位置相关,倒转顺序的影响较小。角色 1 的两项估计相差约 个合并标准误,提示顺序也可能产生可测影响;仅凭这两组模拟,不能断言任意顺序下都如此。
四、讨论
- 单角色移动。 延续轨迹的到达概率和首达时间只依赖 ,可以精确计算。游戏内占用还取决于角色是否存活,本文用蒙特卡洛估计。若要计算“淘汰前首次到达”,还需在完整游戏模型中另行统计到达概率及条件首达时间;本文的首达热图不表示这一事件。对称权重下,平稳概率与方格的合法动作权重之和成正比。
- 遭遇时间。 命题 6 只根据同一时刻的位置分布给出“尚未遭遇”概率的上界。它不记录回合中途的碰撞,因此不能代替遭遇时间分布。完整局面链在七人局中无法直接存储,故采用蒙特卡洛。
- 游戏结束时间。 七人局需经历六次决斗才会只剩一人。因此结束时间的期望约为 回合,显著大于整局首次遭遇时间的期望 回合。两者之间的差额来自首次遭遇之后的继续游走与后续决斗;不能仅由第一次遭遇的分布推得结束时间。
- 行动顺序决定谁先移动以及遭遇发生时谁尚未行动。“三、数值结果” 的逆序对照中,获胜概率的变化绝对值不超过 ;这一结果只适用于所比较的两种顺序和当前配置。
以上数值结果依赖 “一、地图、行动权重与边界转移” 中的规则。尤其是顺序行动和决斗耗时的约定,改变它们会改变相应的时间分布:
- A2(顺序行动)。若改读为“所有角色同时提交动作、回合结束时检查是否有同格”,那么“回合锁定—继续”就失去了对象,交换位置也不再触发遭遇;三问都会变。本文采用顺序读法的理由已经写在 “一、地图、行动权重与边界转移” 的注记里,也与
Code/gridGameOptimized.py的实现一致。 - A5(决斗同回合完成)。若决斗占用额外回合,首次遭遇之前的分布仍由相同移动规则决定;之后的遭遇时间、游戏内占用与结束时间都要计入锁定时长。只有在额外时长的分布及其与局面的依赖关系明确后,才能进一步修正结果。