以下是关于单人模式《杀戮尖塔2》的三条客观事实: 1.若你在暗港选择涅奥骨骰,随机获得的诅咒有约54%概率是债务诅咒*。 2.垃圾堆事件永远不可能刷出反弹这张卡牌。 3.你的第一场战斗,暗港有76%概率掉落药水,密林仅4%概率掉落药水**。 *前提:涅奥骨骰给的两件遗物不包含新叶、万花筒 **前提:涅奥赠送遗物不会产出卡牌或其他遗物 ***全部基于当前测试版v0.107.0补丁 什么情况? 问题根源在于各类随机数生成器(RNG)之间存在意料之外的数值关联——只要获取其中一个随机生成器的第一条输出结果,就能推算出其余所有生成器的首个随机值。 杀戮尖塔2的随机数生成器原理 本节会用极度简化的方式解释关联机制;文末会展开完整细节,不在意原理的读者可直接跳过,查看下方各类实战案例。 “关联随机数(CRNG)”现象在杀戮尖塔玩家社群早有耳闻,初代杀戮尖塔就存在同类漏洞,遗忘仲裁者曾在博客中完整记录过该问题[1]。 简单回顾初代机制:游戏启用多套独立伪随机生成器,本意是避免一场战斗内的随机结果干扰后续卡牌奖励。但所有生成器共用同一个初始种子,导致每套生成器输出完全相同的数字序列。老玩家可以通过过往随机事件的结果,预判后续所有随机内容。 制作组为规避同款问题,在二代中给每套伪随机生成器分配了不同初始种子。简化示意代码如下(仅用于教学,非完整源码): csharp // 全局基础种子 + 字符串哈希值 作为每套RNG的独立种子 Rng UpFront = new Rng(seed + hash("up_front")); Rng Shuffle = new Rng(seed + hash("shuffle")); Rng UnknownMapPoint = new Rng(seed + hash("unknown_map_point")); Rng CombatCardGeneration = new Rng(seed + hash("combat_card_generation")); Rng CombatPotionGeneration = new Rng(seed + hash("combat_potion_generation")); Rng CombatCardSelection = new Rng(seed + hash("combat_card_selection")); Rng CombatEnergyCosts = new Rng(seed + hash("combat_energy_costs")); Rng CombatTargets = new Rng(seed + hash("combat_targets")); Rng MonsterAi = new Rng(seed + hash("monster_ai")); Rng Niche = new Rng(seed + hash("niche")); Rng CombatOrbGeneration = new Rng(seed + hash("combat_orbs")); Rng TreasureRoomRelics = new Rng(seed + hash("treasure_room_relics")); // 省略大量其他生成器 游戏内还有更多未列出的随机生成器,每一个独立事件都配有专属RNG。 哈希函数会根据输入字符串生成一串看似随机的数字,相同字符串永远产出相同哈希值。设计初衷是打乱各套RNG初始状态,保证同一全局种子游玩流程完全固定。 漏洞出在:这些种子最终传入C 内置伪随机算法对初始种子几乎呈完全线性关系。 具体原理较为复杂,后文附录详细拆解;直观后果是:两套种子差值固定的RNG,输出结果的差值存在可计算、可利用的稳定规律。 你可能会问:这个机制能被利用到什么程度?下文列举大量关联随机数造成的游戏现象,从无伤大雅的趣味规律,到切实改变游玩平衡的严重影响(普通玩家即便不懂原理,也会被动受其干扰)。




换一换 
































