loot

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package loot 提供加权随机抽取原语:抽卡 / 开宝箱 / 怪物掉落 / 直播抽奖。

核心是 Table[T]:每个物品带权重,按权重比例随机抽取。抽取用 Vose's Alias Method, 预处理 O(n) 建两张表后,每次抽取 O(1)(而非朴素前缀和 + 二分的 O(log n)), 海量抽卡场景下这是关键。

三种进阶能力(可选):

  • 保底(pity):Puller 记录"连续多少次没出某稀有度",达阈值强制出一个该稀有度 物品——几乎所有商业抽卡都有的机制,避免非酋无限空手;
  • 不放回抽取(DrawN):一次抽 N 个不重复(十连抽的去重语义);
  • 有状态抽取器(Puller):把 pity 计数等状态封装,每个玩家一个。

Table 构建后只读、并发安全(Alias 表不可变);随机源默认 math/rand/v2(并发安全, 免播种);Puller 有内部状态,非并发安全(每玩家一个,天然隔离,或自行加锁)。

零值不可用:Table 用 NewTable 构造。

Index

Constants

This section is empty.

Variables

View Source
var ErrEmptyTable = errors.New("loot: table has no positive-weight items")

ErrEmptyTable 表内无有效物品(全部权重 <=0 或列表为空)。

Functions

This section is empty.

Types

type Item

type Item[T any] struct {
	Value  T
	Weight float64
	Rarity int // 稀有度等级(越大越稀有),仅用于 pity;默认 0
}

Item 是一个可被抽取的物品:携带业务值 Value、非负权重 Weight、可选稀有度 Rarity。 Rarity 用于保底(pity)判定;不使用保底时可忽略。

type Option

type Option[T any] func(*Table[T])

Option 配置 Table。

func WithRand

func WithRand[T any](r *rand.Rand) Option[T]

WithRand 指定随机源(用于可复现测试)。默认用 math/rand/v2 全局源(并发安全)。 注意:自定义 *rand.Rand 非并发安全,若 Table 并发抽取请勿使用,或自行加锁。

type Puller

type Puller[T any] struct {
	// contains filtered or unexported fields
}

Puller 是带保底(pity)状态的抽取器:在 Table 之上记录"连续多少次没抽到 >= 目标稀有度的物品",达到 pity 阈值时,强制从"高稀有度子表"抽一个。

典型:抽卡保底——连续 89 抽没出 5 星,第 90 抽必出 5 星(pityLimit=89)。

Puller 有内部计数状态,非并发安全:每个玩家一个 Puller(天然隔离), 或调用方自行加锁。零值不可用,用 NewPuller 构造。

func NewPuller

func NewPuller[T any](table *Table[T], pityLimit, pityRarity int) *Puller[T]

NewPuller 创建带保底的抽取器。 pityLimit:连续未出货达此次数时,下一抽强制出货(<=0 关闭保底,等价裸 Table)。 pityRarity:稀有度阈值,抽到 Rarity>=它视为出货。

若表内没有任何 Rarity>=pityRarity 的物品,保底无法触发(退化为普通抽取)。

func (*Puller[T]) Draw

func (p *Puller[T]) Draw() (Item[T], bool)

Draw 抽一次并维护保底计数:

  • 若已连续未出货达 pityLimit 且存在高稀有度子表,强制出货(从高稀有度子表抽),计数清零;
  • 否则正常抽;抽到 Rarity>=pityRarity 视为出货,计数清零;未出货则计数 +1。

返回抽到的 Item 与本次是否由保底触发(pity)。

func (*Puller[T]) DrawN

func (p *Puller[T]) DrawN(n int) (items []Item[T], pity []bool)

DrawN 连抽 n 次(每次都维护保底计数)。返回抽到的物品及各次是否保底触发。

func (*Puller[T]) Misses

func (p *Puller[T]) Misses() int

Misses 返回当前连续未出货次数(距离保底还差 pityLimit-Misses 抽)。

func (*Puller[T]) Reset

func (p *Puller[T]) Reset()

Reset 清零保底计数。

type Table

type Table[T any] struct {
	// contains filtered or unexported fields
}

Table 是一张加权抽取表。构建后只读、并发安全。零值不可用,用 NewTable 构造。

func NewTable

func NewTable[T any](items []Item[T], opts ...Option[T]) (*Table[T], error)

NewTable 用一组物品构建抽取表。权重 <=0 的物品被忽略。 无有效物品时返回 ErrEmptyTable。

func (*Table[T]) Draw

func (t *Table[T]) Draw() T

Draw 按权重抽取一个物品(O(1))。表非空,必返回有效值。

func (*Table[T]) DrawDistinct

func (t *Table[T]) DrawDistinct(n int) []T

DrawDistinct 不放回抽取 n 个互不相同的物品(按权重加权,已抽中的不再重复)。 n 超过表内物品数时,返回全部物品(数量 < n)。用于"十连里不重复"的场景。 复杂度 O(k·m)(k=抽取数,m=剩余物品数),不走 Alias(动态权重),适合 n 较小的连抽。

func (*Table[T]) DrawItem

func (t *Table[T]) DrawItem() Item[T]

DrawItem 同 Draw,但返回完整 Item(含权重/稀有度)。

func (*Table[T]) DrawN

func (t *Table[T]) DrawN(n int) []T

DrawN 抽取 n 个(放回:每次独立按权重抽,可能重复)。用于普通连抽。

func (*Table[T]) Len

func (t *Table[T]) Len() int

Len 返回表内有效物品数。

func (*Table[T]) TotalWeight

func (t *Table[T]) TotalWeight() float64

TotalWeight 返回所有有效物品的权重之和。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL