纸牌牌局在开始时需要不可预测,在你之后想检查它时又需要可重复。如果洗牌从一个种子开始,这两项要求就能兼顾:种子是一个决定整副牌如何排列的数字。
Cards4.net 的纸牌游戏采用的正是这种方法。借助种子,我们可以为重玩、每日挑战或错误报告重建一局牌。各模式之间的区别在于我们如何选择这个数字,而在描述一局牌是随机的还是可解的时候,这一选择很重要。
从一个数字到一副牌
我们的共享卡牌引擎把种子传给 Erlang 的 exsplus 伪随机数生成器,然后调用 Elixir 的 Enum.shuffle 重新排列牌。该生成器会产生一串可重复的数值,这就是相同的输入能给出同一副洗好的牌的原因。
要重建开局局面,我们还需要知道游戏及其变体。经典纸牌(Klondike)、空当接龙(FreeCell)和蜘蛛纸牌(Spider)的摆牌方式各不相同,而蜘蛛纸牌的花色数量还会改变牌组本身。因此,种子只有与这些规则以及解释它的实现结合在一起才有意义。
这种可重复性适用于当前的实现。初始牌序或洗牌过程的改动都可能改变最终得到的牌局。种子是一局游戏的有用参考,但它并不保证未来的任何实现都会以完全相同的方式解释这个数字。
种子从何而来
对于普通的随机单人游戏请求,服务器使用 Erlang 的 rand 模块在 0 到 2^63 - 1 之间选出一个数字,然后把这个数字交给游戏引擎。这一选择不会参考玩家最近的胜负或账号历史。
引擎中还有一个辅助函数,可以通过 :crypto.strong_rand_bytes 获取种子。它的存在并不意味着每个游戏请求都会用到它:上面描述的普通请求路径是通过 rand 来选择种子的。
这一区别很重要,因为无论是请求所用的生成器,还是用种子初始化的 exsplus 洗牌,都不应被描述为密码学安全的。Erlang 的文档明确区分了它的伪随机数生成器与适用于密码学的生成器。我们的实现让纸牌牌局可以复现;它并不提供密码学意义上的公平性协议。
可能有多少种排列?
一副由 52 张互不相同的牌组成的牌有 52! 种可能的顺序,约为 8.07 × 10^67。这个数字等于第一张牌的所有选择,乘以第二张牌剩余的所有选择,依此类推。
63 位种子只能取 2^63 个值,约为 9.22 × 10^18。由于在固定实现下每个种子只产生一个结果,它最多只能得到这么多种牌序;如果不同的种子产生相同的牌序,数量还会更少。我们不能声称能够抽样到一副实体牌的所有可能排列。
这个较小的数字仍然很大,但仅凭它的大小并不能证明分布的质量。同样,几局别扭的牌也不能证明洗牌存在偏差。有意义的调查需要记录下来的种子、相关的游戏设置,以及足够多的结果来检验某个具体的说法。
可解牌局来自一个更小的池
可解模式在洗牌之前增加了一个筛选步骤。离线求解器会尝试各种牌局,并记录下它们找到获胜路径的种子。当相应的种子库可用时,服务器会从这个池中随机选取一个条目。
由此得到的牌局与普通牌局一样可以复现,但它来自一个经过筛选的集合。求解器未能完成的牌局不在其中,即使它们可能存在解法。这个池是有限的,所以有可能再次遇到同一局牌。
每日挑战也从这些种子库中选牌,使用游戏、变体和 UTC 日期来选定一个条目。当天在同一变体中参加挑战的每个人都会得到相同的局面。这个选择对当天是固定的,而不是为每位玩家单独做出。
微软编号的空当接龙牌局使用其自己的兼容算法。这些编号属于那个体系,不能与我们普通的洗牌种子互换。牌局编号指南介绍了如何打开某一局特定的牌。
你可以自己核对什么
公平性页面可以根据公开的种子,重建经典纸牌 Turn 1 和 Turn 3、空当接龙,以及蜘蛛纸牌单花色、双花色和四花色变体的开局局面。把结果与原始牌局进行对比,就能确认种子与牌面是否一致。
该工具使用的是我们的后端。它不能独立确认种子是如何选出的,不能证明选择过程没有偏差,也不能表明一局牌可以获胜。这些是不同的问题。
如果遇到可疑的牌局或要报告错误,请把种子和变体与事情经过的描述一起保留下来。一个可以复现的局面能让我们有据可查。除非我们同时说明哪些内容确实可以核对,否则声称洗牌是公平的并没有多大用处。
常见问题
我能复现 Cards4.net 的牌局吗?
对于支持的纸牌游戏,相同的种子、变体和洗牌实现会复现相同的开局局面。公平性页面可以重建该局面。
洗牌使用什么随机数生成器?
共享卡牌引擎用种子初始化 Erlang 的 exsplus 伪随机数生成器,并调用 Enum.shuffle。普通的单人游戏请求同样使用伪随机数生成器来选出一个 63 位种子。
洗牌能产生所有可能的牌序吗?
不能。采用 63 位种子的固定实现最多只能产生 2^63 种不同的结果,远少于一副标准扑克牌的 52! 种排列。
公平性页面能证明种子的选择没有偏差吗?
不能。它只检查某个种子能否复现一个开局局面。由于它使用的是我们的后端,它既不是独立审计,也不能证明种子是如何选出的。