38 lines
1.2 KiB
Go
38 lines
1.2 KiB
Go
// Package drawstate holds a generator's seeded randomness and its {seq()} counters.
|
|
package drawstate
|
|
|
|
import (
|
|
"math/rand/v2"
|
|
"sort"
|
|
)
|
|
|
|
// State is one generator's draw state, kept across its renders.
|
|
type State struct {
|
|
rand *rand.Rand
|
|
counters map[string]uint64
|
|
}
|
|
|
|
// New builds the state a seed reproduces.
|
|
func New(seed uint64) *State {
|
|
return &State{rand: rand.New(rand.NewPCG(seed, seed^0x9e3779b97f4a7c15)), counters: map[string]uint64{}}
|
|
}
|
|
|
|
func (s *State) IntN(n int) int { return s.rand.IntN(n) }
|
|
|
|
func (s *State) Float64() float64 { return s.rand.Float64() }
|
|
|
|
// Weighted draws an index into cum, a running sum of weights of 0 or more with a positive total,
|
|
// with odds by weight, so an index of weight 0 is never drawn.
|
|
func (s *State) Weighted(cum []float64) int {
|
|
total := cum[len(cum)-1]
|
|
x := s.Float64() * total
|
|
// x stays below total: a product of a float64 under 1 and a positive float64 rounds below it.
|
|
return sort.Search(len(cum), func(i int) bool { return cum[i] > x })
|
|
}
|
|
|
|
// Seq advances the counter {seq(key)} reads, or {seq()} for an empty key, and returns it.
|
|
func (s *State) Seq(key string) uint64 {
|
|
s.counters[key]++
|
|
return s.counters[key]
|
|
}
|