← 返回总览随机逻辑解释

FishDice 随机逻辑:从权重候选到分层池漏斗

这页解释当前仓库里两套随机决策:一套服务 Lucky Dice 候选选择,另一套是更通用的 Pool Funnel 分层随机池。它们共同的设计目标是:随机可注入、权重可解释、结果可回放、失败可观测。

一句话模型

随机并不是“从列表里随手挑一个”。框架会先筛候选、算权重、只在最高优先级集合里抽取,再把随机值、选中项、状态变化和后续决策写入结果对象或 Trace。
IRandomSource
随机源由外部注入,测试可以用固定序列复现结果。
Weight
候选通过权重参与抽取,权重为 0 的项不会进入抽奖池。
Trace
Pool Funnel 会记录每层权重、随机值、命中结果、状态前后差异。

流程图:Pool Funnel 随机决策

1. 输入请求 RandomPoolDefinition + PoolFunnelRequest 进入服务。
2. 解析入口层 检查 EntryLayerIds,过滤不可用层,按 priority 选入口。
3. 读取状态 IRandomPoolStateStore 取当前层 luck value 和 roll count。
4. 计算权重 默认公式:BaseWeight + Factor * LuckValue,小于 0 归零。
5. 筛候选池 只保留权重大于 0 的结果,再取最高 priority 的结果组。
6. 权重抽取 Next(0, totalWeight) 得到随机游标,累加权重命中结果。
7. 执行动作 把选中结果声明的 actions 交给 dispatcher,默认只排队成功结果。
8. 沉淀状态 默认将 LuckValue += LuckValueDelta,并让 roll count +1。
9. 判断去向 结果配置决定 Stop 或 NextLayer;NextLayer 会进入下一层循环。
10. 输出结果 返回 outcome、每层详情和 PoolFunnelTrace,便于复盘。

这个流程的关键不是“随机算法多复杂”,而是把随机前后的上下文都保留下来:入口层为什么可用、候选为什么被过滤、某个结果为什么权重更高、抽中的随机游标落在哪个区间。

框架图:随机池组件关系

配置与输入
RandomPoolDefinition

定义 pool id、入口层列表、所有随机层。

RandomLayerDefinition

定义 layer id、优先级、可用性 key、结果集合。

RandomResultDefinition

定义 result key、priority、基础权重、幸运值因子、状态增量和下一步决策。

核心编排
PoolFunnelRandomService

负责入口层解析、循环 RollLayer、处理空池、处理 NextLayer、生成最终结果。

RollLayer

读取状态 → 算全部权重 → 取最高优先级候选 → 权重抽取 → action → settlement → decision。

PoolFunnelRunResult

输出 session、pool、每层结果、最终 outcome 和可回放 Trace。

可替换策略
IRandomSource

控制随机值来源,线上用真实随机,测试用脚本随机。

ILayerAvailabilityPolicy

判断层是否开放,默认 AlwaysAvailable。

IResultWeightCalculator

计算结果权重,默认线性幸运值公式。

IPoolSettlementStrategy

处理抽中后的状态变化,默认按 delta 修改幸运值。

IFunnelDecisionStrategy

决定停在当前结果,还是进入下一层。

核心公式

默认权重计算来自 LinearLuckValueWeightCalculator

weight = max(0, BaseWeight + LuckValueWeightFactor * LuckValue)

抽取时先求最高优先级候选的总权重,然后生成 [0, totalWeight) 的随机数。遍历候选并累加权重,随机数落入哪个区间,就选中哪个结果。

priority 比 weight 更先发生作用:低优先级结果即使权重很高,也不会和最高优先级结果同池竞争。

Lucky Dice 候选随机

先过滤。只保留 enabled、满足 player level、满足 trigger source 的候选。
再按权重抽取。权重大于 0 的候选进入池子,使用同样的累加区间方式命中结果。
最后兜底。如果没有可抽候选,优先找 default candidate;仍没有时返回 NormalRewardFallback
补展示槽。选中候选后决定 result slot count,候选未配置时使用 Lucky Dice 骰子集默认槽数。

因此 Lucky Dice 的随机逻辑更轻:它没有多层状态沉淀,也不循环跳层;它主要服务“从当前可用特殊结果里选一个入口”。

为什么这样设计

问题设计回答收益
测试里怎么复现随机?随机源走 IRandomSource 注入。固定随机序列即可断言选中项。
概率为什么变化?权重计算器显式依赖基础权重和幸运值。策划参数、保底逻辑、状态变化都能解释。
结果池为空怎么办?Pool Funnel 返回 NoWeightedResult;Lucky Dice 走 default 或普通奖励兜底。失败路径不会静默吞掉。
以后要换概率算法怎么办?可替换 weight calculator、settlement、decision strategy。核心编排稳定,策略可以独立演进。

代码证据