跳到正文

预算决定一波能花多少点数。两种不同的选择规则决定它买什么:先按索引选择种类,再在仍然买得起的种类中反复进行加权抽取。

01 / 种类

先选择种类

从世界的基础僵尸 bb 和配置池 R0R_0 开始。生成器每次使用一次有界整数抽取,从池中不放回地选择 k=4k=4 个条目。Weight 不参与这个选择。11按索引选择池条目构建器先加入场景的基础种类,再从配置池中选择 ZombieTypeCount 个条目。每个选中的向量条目在下一次有界抽取前移除。基线数量为 4。这个选择流程不读取 Weight。0x101ab6840

jt=ut mod ∣Rt∣,zt+1=Rt[jt],Rt+1=Rt∖{entry jt},t=0,…,k−1.\begin{aligned} j_t &= u_t\bmod |R_t|,\\ z_{t+1} &= R_t[j_t],\\ R_{t+1} &= R_t\setminus\{\text{entry }j_t\},\qquad t=0,\ldots,k-1. \end{aligned}

移除向量条目,可以防止同一个池条目被再次选中。Weight 较大的类型在这次初始抽取中并不占有更长区间:每个剩余池条目只贡献一个索引。

选中的种类与 bb 一起,从第一波起就可供普通填充流程使用。因此,是否进入候选种类与最终出现多少次,是两个不同的问题。

当 L>50L>50 时,额外规则会检查已选种类中是否有成本至少为 1500 点的类型。如果没有,就从剩余池中随机选择一个满足该成本阈值的种类,替换最后一次池选择。这检查的是 WavePointCost,而不是 Gargantuar 类。22高成本替换超过 BigZombieSureExistLevel(基线为 50)后,辅助函数检查已选种类中是否有 WavePointCost 至少为 1500 的类型。若没有,就从剩余池中符合条件的条目里抽取,替换最后一次池选择。这个谓词检查成本,而非某个特定的运行时僵尸类。0x101ab6d78

02 / 抽取

成本决定准入,权重决定选择

令 SS 为当前可用种类。对种类 ii,以 cic_i 表示 WavePointCost,以 aia_i 表示 Weight。剩余 rr 点时,正权重候选集合为

A(r)={i∈S:ci≤r, ai>0},T(r)=∑i∈A(r)ai.A(r)=\{i\in S:c_i\le r,\ a_i>0\},\qquad T(r)=\sum_{i\in A(r)}a_i.

每个候选在大小为 T(r)T(r) 的抽签袋中,占据长度为 aia_i 的整数区间。对于给定顺序,令 CiC_i 为 ii 之前的累计权重。一次抽取选择包含 dd 的唯一区间:33整数权重抽签袋普通填充流程去掉 WavePointCost 超过剩余预算的候选,通过整数权重袋抽取,追加选中种类,并扣除其成本。FirstWave 和单独的 Cost 属性不参与这些辅助函数。有界整数抽取使用模约减;袋内份额描述区间长度,并不声称随机数余数严格均匀。0x101ce7f080x100366650

d=u mod T(r),Ci≤d<Ci+ai,r←r−ci.d=u\bmod T(r),\qquad C_i\le d<C_i+a_i,\qquad r\leftarrow r-c_i.

候选在袋中的名义份额为 ai/T(r)a_i/T(r)。随着可负担范围改变,抽签袋也会重建;即使权重很高,种类也可能不再进入候选范围。当没有正权重候选符合预算时,填充结束,因此预算可以有余数。

这个填充流程既不使用 FirstWave,也不使用单独名为 Cost 的属性。因此,条目声明的首波编号不是这条路径上的额外门槛。

阵容:基础类型与四个池选择,附成本与权重

  • 木乃伊僵尸100 · 1000
  • 骆驼僵尸300 · 3000
  • 法老僵尸450 · 3000
  • 埃及巨人僵尸(无尽)1500 · 3000
  • 路障木乃伊僵尸200 · 3000
  1. 1,825d = 11848 / 13000路障木乃伊僵尸d = 11848 / 13000
  2. 1,625d = 10487 / 13000路障木乃伊僵尸d = 10487 / 13000
  3. 1,425d = 2301 / 10000骆驼僵尸d = 2301 / 10000
  4. 1,125d = 3896 / 10000骆驼僵尸d = 3896 / 10000
  5. 825d = 8710 / 10000路障木乃伊僵尸d = 8710 / 10000
  6. 625d = 8331 / 10000路障木乃伊僵尸d = 8331 / 10000
  7. 425d = 2442 / 7000骆驼僵尸d = 2442 / 7000
  8. 125d = 515 / 1000木乃伊僵尸d = 515 / 1000

剩余 25 点:没有种类付得起,填充结束。

图版 2.1古埃及第 36 关第 4 波:普通填充在上方阵容中花费 1,825 点,共抽取 8 次,示例种子 2。每一行的抽签袋包含仍付得起的每个种类,按各自权重占据区间,抽取值落在选中种类的区间内;随着点数减少,袋中失去不再付得起的种类。

03 / 最终波

最后一次补入尝试

波次转换流程追踪哪些已选种类已经出现在更早的指令中。在正常填充最终波之前,它按活动集合的顺序访问剩余尚未出现的种类。它尝试按顺序插入,并从剩余预算中扣除每个获准种类的成本。这个流程使用严格不等式:44严格的最终波补入条件在普通最终波填充前,波次转换流程访问尚未出现的已选种类。只有成本严格小于当前剩余预算时才追加。每次插入都会减少预算,因此顺序和更早的插入会影响后续资格。0x101ce8598

Final-wave reservation:ci<r,Ordinary filling:ci≤r.\begin{array}{ll} \text{Final-wave reservation:} & c_i<r,\\ \text{Ordinary filling:} & c_i\le r. \end{array}

成本恰好相等时,普通填充可以接受,补入流程却会拒绝。跳过补入并不会把该类型从之后的加权袋中移除。较早被接受的补入,也会减少后续条目的可用余额。

对于按顺序排列、成本为 200 和 400 的两个尚未出现类型,若剩余 600 点,第一次插入会使第二个类型恰好匹配剩余预算,因此补入会跳过它。反转顺序会改变哪个类型获得补入。之后,填充器仍可以抽到可负担的被跳过类型。

补入流程:成本 < 剩余点数

  1. 路障木乃伊僵尸200 < 600补入→ 400
  2. 铁桶木乃伊僵尸400 < 400跳过

加权填充:成本 ≤ 剩余点数,种子 3

  1. 铁桶木乃伊僵尸400 ≤ 400抽中→ 0

按插入顺序花费的预算

指令:路障木乃伊僵尸 · 铁桶木乃伊僵尸

图版 2.2最终波有 600 点,路障木乃伊僵尸与铁桶木乃伊僵尸依次尚未出现。补入流程只在成本严格低于剩余点数时接受类型,因此跳过其中 1 个。随后填充器接受成本 ≤ 剩余点数,此例中仍抽到了被跳过的类型;示例种子 3,剩余 0 点。

04 / 结果

阵容仍然只是起点

加权抽签袋的顺序会影响抽取值与种类之间的对应关系。原生集合按类型指针排序;因此,即使成本和权重相同,任意替换顺序也不能复现相同的种子序列。55顺序与组件示例原生有序集合比较器比较类型指针值。网站使用所显示的已选顺序和重建的整数抽取。独立填充器展示预算消耗;构建与音乐视图生成此前各波、最终波预留、各指令等级,以及每关一次的首领尝试。0x100c03c8c

从第 4 关起,生成流程为整个关卡进行额外财神条目和额外首领条目的尝试,再把成功的条目分配到选定波次。这不是每一波都重新进行一次 0.4 的首领概率判断。基线首领概率为 0.4;其类型从成本严格大于 100 的种类中按权重选择。

基线财神概率为零,但仍消耗这次概率抽取。如果两个特性均启用且成功,另一次抽取会抑制其中一个。保留下来的额外条目在普通填充之后追加,不从普通预算中扣除成本。随后世界处理仍可替换它;例如 Jam 构造替换指令时,不会保留被移除首领的标记。66额外首领条目波次转换流程从显示关卡 4 起尝试额外条目。共享基线声明 LeaderProbability = 0.4、LeaderMinWaveCost = 100。首领资格使用成本严格大于阈值的检查,随后加权选择。成功条目在普通填充预算之外追加。该基线中的财神概率为零,但仍消耗其概率抽取。0x101ce8a540x101ab6840

这些规则与波次方案一起,解释了一份点数额度如何变成有序的普通出怪指令列表。位置、强度与时机,是之后在这个结果之上继续处理的内容。

05 / 随机状态

为什么一个种子不能代表整个关卡

抽签袋公式使用所检查的整数随机流:MT 式状态、31 位结果,以及有界抽取的模约减。零种子会变为 4357。生成过程的不同部分拥有不同的随机流状态:77独立的随机状态消耗者全新 DangerRoom 初始化构建设计器状态、转换波次、运行世界后处理、安装运行时动作与掉落标记,随后重新播种 WaveManager。Jam 使用全局 RNG;摩登世界队列打乱调用导入的 C++ 机制。网站使用重建的整数抽取、明确输入的种子与各情境独立的状态。0x101d476240x101c79bec0x101ce66dc0x100121044

决策使用的随机状态
波次描述与初始池选择描述构建器 RNG
将普通波次转换为指令波次转换器 RNG
运行时波次动作与行采样传入动作的 WaveManager RNG
Jam、恐龙与一般掉落抽取全局 RNG
摩登世界家族/行选择传入的设计器 RNG
摩登世界端点插值全局 RNG
传送门队列打乱导入的 C++ RNG 机制

GetSeed(1) 读取 time(NULL),但仅有时间戳,并不能确定每个随机流的状态或此前消耗。被拒绝的重复选择、零概率尝试,以及最终被丢弃的计算结果,仍可能推进随机流。组件控件公开各自的输入种子,使这些单独规则可以重复检验。

注释

每条论断的依据:代码路径,以及追踪到它的原生地址。

  1. 1

    按索引选择池条目

    构建器先加入场景的基础种类,再从配置池中选择 ZombieTypeCount 个条目。每个选中的向量条目在下一次有界抽取前移除。基线数量为 4。这个选择流程不读取 Weight。

    0x101ab6840
  2. 2

    高成本替换

    超过 BigZombieSureExistLevel(基线为 50)后,辅助函数检查已选种类中是否有 WavePointCost 至少为 1500 的类型。若没有,就从剩余池中符合条件的条目里抽取,替换最后一次池选择。这个谓词检查成本,而非某个特定的运行时僵尸类。

    0x101ab6d78
  3. 3

    整数权重抽签袋

    普通填充流程去掉 WavePointCost 超过剩余预算的候选,通过整数权重袋抽取,追加选中种类,并扣除其成本。FirstWave 和单独的 Cost 属性不参与这些辅助函数。有界整数抽取使用模约减;袋内份额描述区间长度,并不声称随机数余数严格均匀。

    0x101ce7f080x100366650
  4. 4

    严格的最终波补入条件

    在普通最终波填充前,波次转换流程访问尚未出现的已选种类。只有成本严格小于当前剩余预算时才追加。每次插入都会减少预算,因此顺序和更早的插入会影响后续资格。

    0x101ce8598
  5. 5

    顺序与组件示例

    原生有序集合比较器比较类型指针值。网站使用所显示的已选顺序和重建的整数抽取。独立填充器展示预算消耗;构建与音乐视图生成此前各波、最终波预留、各指令等级,以及每关一次的首领尝试。

    0x100c03c8c
  6. 6

    额外首领条目

    波次转换流程从显示关卡 4 起尝试额外条目。共享基线声明 LeaderProbability = 0.4、LeaderMinWaveCost = 100。首领资格使用成本严格大于阈值的检查,随后加权选择。成功条目在普通填充预算之外追加。该基线中的财神概率为零,但仍消耗其概率抽取。

    0x101ce8a540x101ab6840
  7. 7

    独立的随机状态消耗者

    全新 DangerRoom 初始化构建设计器状态、转换波次、运行世界后处理、安装运行时动作与掉落标记,随后重新播种 WaveManager。Jam 使用全局 RNG;摩登世界队列打乱调用导入的 C++ 机制。网站使用重建的整数抽取、明确输入的种子与各情境独立的状态。

    0x101d476240x101c79bec0x101ce66dc0x100121044