#effect #mass #decision #analysis #path #outcome #mass-effect-2

me2finale

质量效应2最终任务分析

2个不稳定版本

0.2.0 2023年2月7日
0.1.0 2023年2月6日

#129 in 模拟

MIT/Apache

73KB
989

质量效应2 最终任务分析

本文档假定读者熟悉《质量效应2》的整个内容,包括可能被视为剧透的引用和术语。你已经收到警告! :)

这个Rust crate定义了与《质量效应2》最终任务相关的 决策路径结果 类型。虽然游戏为玩家提供了许多选择,但只有其中一些会影响如果你的存档文件被转移到《质量效应3》中,你的盟友的生存。这个crate只关注游戏的这些方面。

结果数据

由函数 generate::outcome_map() 生成的数据是结果到描述导致这些结果的决策路径的元数据的映射。元数据包含一个决策路径的示例和导致相同结果的决策路径总数的计数,而不是存储 所有 决策路径——有超过 64亿 条!

Outcome 编码以下信息

  • 哪些盟友存活了?
  • 哪些幸存的盟友是 忠诚的
    • 如果玩家成功完成他们的 忠诚任务,盟友就会变得忠诚。
  • 《诺曼底SR2》的幸存船员是否被营救?
    • "幸存船员"指的是谢泼德到达营救他们时仍然活着的那一组人。

DecisionPath 编码影响结果的选择。每个结果元数据中包含的示例回答以下问题,其中一些问题根据之前的选择是可选的

  • 应该招募哪些可选盟友?
  • 应该完成哪些忠诚任务?
  • 应该购买哪些《诺曼底》的升级?
  • 应该选择谁为小队防御《诺曼底》的货舱?
  • 应该选择谁担任各种专家角色?
  • 应该选择谁加入生物盾牌小队?
  • 谁应该在最终战役中被选入队伍?

在《质量效应2》及以后版本中,一些玩家行为对盟友产生的后果并未在本项目中考虑。请参阅局限性,了解排除这些行为背后的原因。

要自行生成数据,请运行源代码中提供的generate示例

$ cargo run --release --features generate --example generate -- PATH

或者,您可以使用outcome_map.rmp中包含的数据。它是一个以MessagePack格式序列化的OutcomeMap(通过rmp-serde包)。

有趣的事实

为了展示可以利用数据回答的问题,以下统计数据是通过提供的analyze示例生成的(并重新格式化为Markdown格式)。

$ cargo run --example analyze -- outcome_map.rmp
  • 总共有64,396,302,636条决策路径。
  • 总共有714,852个结果。
  • 有111个结果只能独特地实现。
    • 以下结果有以下共同点
      • Miranda幸存且忠诚。
      • Garrus和Jacob是不忠的。
  • 最常见的结果可以通过68,263,592种方式实现
    • 只有Jacob和Miranda幸存,且他们都忠诚。
    • 船员获救。
  • 最常见的10个结果覆盖了419,652,725条决策路径。
  • 最常见的100个结果覆盖了2,068,928,184条决策路径。
  • 最常见的1000个结果覆盖了8,572,525,662条决策路径。
  • 在341,570,226条决策路径中,只有43个结果中Sh Shepard会死亡。
    • 换句话说,Sh Shepard在99.47%的所有决策路径中幸存。
  • (这是一个主观的度量,公平起见),在所有人(除了Morinth)幸存且忠诚的情况下,可以以7,968种方式实现“最佳”结果。
    • 如果您第一次运行就做到了,给自己鼓掌吧!

生存率

以下表格,显示不同条件下每个盟友的绝对生存率,也是由analyze示例生成的。盟友按照其总绝对生存率降序排列。

盟友 忠诚 不忠 总计
Miranda 0.51996 0.19516 0.71513
Jacob 0.43366 0.15589 0.58954
Garrus 0.37451 0.15837 0.53288
Zaeed 0.32652 0.19610 0.52263
Grunt 0.30619 0.18244 0.48864
Legion 0.26426 0.10292 0.36718
Thane 0.22933 0.13319 0.36252
Samara 0.21059 0.14190 0.35250
Mordin 0.30812 0.03100 0.33912
Jack 0.24870 0.06639 0.31509
Kasumi 0.26672 0.02895 0.29567
Tali 0.26154 0.02397 0.28551
Morinth 0.22909 0.00000 0.22909

注意:生存意味着招募。也就是说,如果一个盟友没有被招募,那么他们不被视为生存。

局限性

以下小节讨论了本库的一些已知和感知到的局限性。

部分数据

如前所述,尽管决策路径和结果之间存在一对一的多对多关系,但数据中只记录了与结果相同数量的决策路径。

作为一个简单的思想实验,假设每个决策路径可以通过某种方式完美编码和压缩到4.5个字节(36位)。仅决策路径本身就需要近290 GB的内存/存储空间——这是最佳情况!

实际上,编码需要超过36位,因此不能期望潜在的用户为存储数据而牺牲这么多的存储空间。通过接受这一局限性,可以在库的源代码仓库中提供完全生成的数据,这样用户就不需要花费10-15分钟来生成它了。

范围

本项目的范围仅限于影响《质量效应2》盟友命运的相关参数,当这些参数延续到《质量效应3》时。如果一个盟友在《ME2》中幸存,他们将在《ME3》中遇到。此外,如果他们忠诚,他们可能在《ME3》中成为战争资产。然而,如果他们不忠诚,他们将在《ME3》中死去。

然而,有两个《诺曼底SR2》船员在本实现中未特别说明:卡伦·查卡斯博士和YN凯利·查默斯。结果只编码了是否救出了船员,但这只是故事的一部分。一些船员可能在安装收割者IFF后完成的任务数量基础上在救援之前死去。

任务 结果
0 所有船员都幸存。
1–3 一半的船员死亡,包括YN凯利·查默斯。
>3 除卡伦·查卡斯博士外,所有船员都死亡。

如果他们在《ME2》的最终任务中幸存,查默斯和查卡斯将在《ME3》中回归,查默斯在《ME2》中甚至有“隐含的忠诚”因素,这会影响她在《ME3》的命运。那么,为什么他们没有被明确考虑呢?

在查卡斯博士的情况下,她的生存完全取决于是否选择了护航,因此“救援船员”至少意味着她幸存。

然而,导致YN查默斯忠诚和生存的决策对玩家在《ME2》终局可以做出的任何选择都没有影响。任何决策路径都可以任意标注凯利忠诚和生存的所有可能组合,而不改变该决策路径中做出的任何其他选择,并且你总是会得到一个有效的决策路径。

最终,尽管之前提到的选择在《ME3》中有后果,但作者认为它们与本项目处理的决策是正交的。

参考文献

本项目得益于《质量效应》社区中的一些了不起的人,尤其是那些花费时间回答subreddit上一些深奥问题的人。其中特别重要的是这个流程图,我遗憾的是找不到任何归属,尽管我相信这个特定的版本是从一个我也不知道的原始来源提炼出来的。

历史

本项目基于我最初用Python编写的几乎相同的项目。该项目的README中包含的统计数据与上述内容明显不同。我并不完全清楚原因,但我怀疑逻辑中存在一个微小的错误,允许暂停/继续生成结果数据。这种机制是必要的,以防止无关问题(例如停电)导致数天的计算时间丢失。

性能

你可能想知道:“真的吗?真的需要这么多天?”是的,真的。

回顾过去,我在Python实现中做出了一些糟糕的决定,这次我避免了这些决定

  • 我坚持将一切都手动编码到Python int中,这些整数具有可变位宽
    • 虽然可变大小对于执行与极大量数相关的计算很方便,但它使得位运算相当缓慢。
    • 我实际上并不需要如此关注序列化数据结构的大小,因此编码/解码东西只是浪费了时间——并使得查询数据变得非常讨厌。
  • 生成逻辑每五秒钟将所取得的进度序列化到磁盘
    • 如果你像我一样经常丢失数据,也许你会将算法绑定到I/O。
  • 所有决策路径遍历都发生在同一个线程上。

最后这个点在Rust中并没有成为一个大问题。一个原始的Python实现移植——去除了位打包和过于积极的磁盘I/O——在我的系统上生成所有结果数据只需不到90分钟。不错!

当我最终开始使用CPU的更多核心时,速度变得非常快。结合了rayondashmap库,算法的并行化变得非常容易。然而,将rayon应用到所有事物上最终会达到收益递减的点,所以我只做了足够的修改以最大化速度提升。结果是,并行化前三个级别的递归就足够了。因此,现在生成所有数据只需要超过十分钟。足够好了!

依赖项

~0.4–7MB
~34K SLoC