首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >实现随机滚动表的仿生方法是什么?

实现随机滚动表的仿生方法是什么?
EN

Stack Overflow用户
提问于 2019-07-26 10:15:26
回答 2查看 273关注 0票数 2

随机滚动表是用于桌面游戏等中随机生成的表。虽然它们可以非常简单,但也可以向复杂的方向发展。

随机滚动表的一个不寻常的例子是“财富”式游戏的轮子,在这种游戏中,某些类别可能有更高的发生几率。

我正在为D&D &D 5e魔术项目构建一个随机生成器(参见此处:发电机/),但我决定对表进行加权,以便在特定的时间选择特定的结果。

一个非常简单的表可能如下所示:

代码语言:javascript
运行
复制
(1d6)
1: First result
2: Second result
3: Third result
4: Fourth result
5: Fifth result
6: Sixth result

通过使用列表和1到6之间的随机化,可以很容易地解决这个问题。

一个稍微复杂一些(但仍然基于1d6的)表可能如下所示:

代码语言:javascript
运行
复制
(1d6)
1-3: First result
4-5: Second result
6: Third result

我的桌子看起来更像:

代码语言:javascript
运行
复制
(1d20)
1-5: First result
6-15: Second result
16-17: Third result
18: Fourth result
19: Fifth result
20: Sixth result

这些表对某些类别具有较高的weighting,以便更频繁地选择这些类别。在我的魔术项目示例中,剑应该有6-15作为metal刃,而1-5则是bone刃,而20将是非常不寻常的东西,如darkness刀片或light刀片。

一种可能的选择是在每个类别中添加一个weight,并对每个滚动执行一个计算,通过将先前的权重相加来查看它的重量,但是这感觉非常笨重。

我尝试在PHP中实现这个项目生成器,并且我使用了一个用比较运算符来解决这个问题的开关情况,但是很明显,这在Python中是行不通的。

对于这个加权的表实现,建议的Python设计是什么?或者有人对实现有任何想法吗?我愿意完全重写我的数据结构,以充分利用任何想法。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-07-26 10:51:10

选项1:在你的模具上使用一个每个数字都有一个元素的list --它的优点是算法映射非常接近于滚动骰子和从表中查找结果的行为。不利之处在于,如果用一万面的骰子,它的规模会很差。但这可能不关你的事。

代码语言:javascript
运行
复制
roll_results = ([first_result] * 5 
                + [second_result] * 10 
                + [third_result] * 2
                + [fourth_result,
                   fifth_result,
                   sixth_result])

roll = random.randint(0, 20)
result = roll_results[roll]

选项2:使用民防 --这是一种有效的数学方法。它的规模会很大。但是,如果您不知道统计数据,并且没有明显地映射到用例,那么理解起来就更复杂了。

代码语言:javascript
运行
复制
from bisect import bisect_left

import numpy as np

options = ["a", "b", "c", "d", "e", "f"]
weights = np.array([5, 10, 2, 1, 1, 1])
pdf = weight/np.sum(weights)
cdf = np.cumsum(pdf)

def get_item(cdf, options):
    roll = np.random.random()
    idx = bisect_left(cdf, roll)  # This uses a binary search to find the point on the CDF (F) such that F(roll) == P(X <= roll)
    return options[idx]

选项3:完全按照您在PhP中所做的做:

代码语言:javascript
运行
复制
options = ["a", "b", "c", "d", "e", "f"]
roll = random.randint(1, 21)
if 1 <= roll <= 5:
    return option[0]
elif 6 <= roll <= 15
    return option[1]
elif 16 <= roll <= 17
    return option[2]
elif roll == 18
    return option[3]
elif roll == 19
    return option[4]
elif roll == 20
    return option[5]

如果您有许多这样的表,我建议使用选项二,但将其封装在类或闭包中:

代码语言:javascript
运行
复制
class OptionsWeightsLengthMismatchException(BaseException):
    """Raises when the number of elements in the options list doesn't match the number of elements in the weight list."""
    pass


# Using an class (OOP approach):
class RollTable:

    def __init__(self, options: List[Any], weights: np.array) -> "RollTable":
        self.options = options
        self.weights = weights
        self._validate_inputs()
        pdf = weights/np.sum(weights)
        self.cdf = np.cumsum(pdf)

    def _validate_inputs(self) -> None:
        if len(self.options) != self.weights.size:
            raise OptionsWeightsLengthMismatchException(f"options and weights must have the same number of elements.")
        if max(self.weights.shape) != np.prod(self.weights.shape): # i.e. make sure it's 1D
            raise ValueError("Weights must be 1D, i.e. only one non-singular dimension.")

    def get_item(self):
        roll = np.random.random()
        idx = bisect_left(self.cdf, roll)
        return self.options[idx]


#Use it like:
table_1 = RollTable(["a", "b"], np.array([3, 2]))

for _ in range(10):
    print(table_1.get_item())

# =============================================================================
# using a closure (functional programming approach)
def make_roll_table(options: List[Any], weights: np.array) -> Callable[[None], Any]:
    # Do the input validation here
    pdf = weights/np.sum(weights)
    cdf = np.cumsum(pdf)

    def get_item():
        roll = np.random.random()
        idx = bisect_left(cdf, roll)
        return options[idx]

    return get_item


# Use it like
get_item_from_table_2 = make_roll_table(["a", "b", "c"], np.array([3, 1, 1]))

for _ in range(10):
    print(get_item_from_table_2())
票数 0
EN

Stack Overflow用户

发布于 2019-07-26 10:47:15

一个简单的解决方案就是简单地使用random.choice。第二个例子就是:

代码语言:javascript
运行
复制
>>> random.choice([1, 1, 1, 2, 2, 3])

你的第三个问题很简单:

代码语言:javascript
运行
复制
>>> choices = [1]*5 + [2]*10 + [3]*2 + [4, 5, 6]
>>> random.choice(choices)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57217923

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档