轨道
/
Lean
Lean
/
练习
/
D&D 角色
D&D 角色

D&D 角色

中等

简介

经过几周的期待,你和朋友们终于聚在一起,开始你们的第一局 Dungeons & Dragons(D&D)。 因为这是游戏的第一场,每位玩家都要生成一个角色来玩。 角色的能力由掷 6 面骰子决定,可骰子_到底_在哪儿呢? 你震惊地发现,朋友们正等着_你_来准备骰子,毕竟玩 D&D 可是你出的主意! 慌乱之中,你意识到自己忘了带骰子,那就意味着没法玩 D&D 了。 好在你有一些基本的编程技能,很快就想出了一个解决办法:写一个程序来模拟掷骰子。

说明

在 Dungeons & Dragons 游戏中,每位玩家首先要生成一个可以扮演的角色。 这个角色除了其他特征之外,还有 6 项属性:力量、敏捷、体质、智力、感知和魅力。 这 6 项属性的数值是随机确定的。 具体做法是掷 4 颗六面骰子,记下最大的 3 颗骰子的点数之和。 每项属性掷 1 次,一共重复 6 次。

你角色的初始生命值是 10 加上你角色的体质调整值。 用你角色的体质减去 10,再除以 2 并向下取整,就得到你角色的体质调整值。

编写一个遵循上述规则的随机角色生成器。

例如,这 6 次掷 4 颗骰子的结果可能如下:

  • 5, 3, 1, 6:舍弃 1,求和 5 + 3 + 6 = 14,把这个值赋给力量。
  • 3, 2, 5, 3:舍弃 2,求和 3 + 5 + 3 = 11,把这个值赋给敏捷。
  • 1, 1, 1, 1:舍弃一个 1,求和 1 + 1 + 1 = 3,把这个值赋给体质。
  • 2, 1, 6, 6:舍弃 1,求和 2 + 6 + 6 = 14,把这个值赋给智力。
  • 3, 5, 3, 4:舍弃一个 3,求和 5 + 3 + 4 = 12,把这个值赋给感知。
  • 6, 6, 6, 6:舍弃一个 6,求和 6 + 6 + 6 = 18,把这个值赋给魅力。

因为体质是 3,所以体质调整值是 -4,生命值是 6。

Note

大多数编程语言都有(伪)随机数生成器,但很少有编程语言是专门为掷骰子设计的。 其中一种就是 Troll。

在 Lean 中生成伪随机值

在函数式语言中,大多数函数都是_纯函数_。 这意味着,对于同一组实参,它们总是返回相同的输出,除此之外什么都不做。 换句话说,纯函数是确定性的。

然而,顾名思义,随机值是不可预测的,也是非确定性的。 即使是伪随机性,也就是返回一串_看起来_随机的数字,在纯函数的语境里也没那么容易实现。 因为伪随机数生成器(PRNG)需要跟踪内部状态,才能返回序列中的下一个数字。

Lean 提供了两种解决这个问题的思路:

  1. 使用一种 monad,让你可以改变 PRNG 的内部状态。 有一个现成的函数 IO.rand 正好能做这件事。

  2. 使用纯函数,把生成器作为实参传入,返回值不仅有生成的伪随机值,还有一个更新后的生成器,它已经“准备好”产出序列中的下一个数字。

本练习采用第二种方式。 每个需要生成伪随机值的函数都要接收一个生成器,并且要同时返回这个值和更新后的生成器。

注意,给定的生成器是确定性的,也就是说,它_总是_产出相同的值。 想要生成序列中的下一个伪随机值,就必须使用更新后的生成器。

检查随机性

在本练习中,随机性通过卡方检验来检查,显著性水平为 p < 0.0001。

这意味着,如果按照说明生成属性值,一个正确的实现因随机因素而无法通过测试的概率不到 0.01%。

注意,按照说明,属性值_是四次投掷无偏 d6(六面骰)中最高的三个结果之和_。

通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Lean Exercism

准备好开始 D&D 角色 了吗?

注册 Exercism,借助 100 个练习 和真人导师指导,学习并掌握 Lean,全部免费。

深入探索 D&D 角色!

我们探讨如何生成(看似)随机的掷骰结果。你将了解种子、伪随机数以及一些常见的坑。最后,我们来看看基于属性的测试为什么非常适合测试那些返回随机值的函数。