Files
mayhem-party/src/device/input/raw/random.go

93 lines
1.6 KiB
Go

package raw
import (
"bytes"
"fmt"
"io"
"math/rand"
"os"
"sort"
"time"
"github.com/go-yaml/yaml"
)
type Random struct {
generator func() byte
down []byte
}
func NewRandom(generator func() byte) *Random {
rand.Seed(time.Now().UnixNano())
return &Random{generator: generator}
}
func (r *Random) Close() {
}
func (r *Random) Read() []byte {
return []byte{r.generator()}
}
func randomCharFromRange(start, stop byte) func() byte {
return func() byte {
return start + byte(rand.Int()%int(1+stop-start))
}
}
func randomCharFromWeightFile(p string) func() byte {
b, err := os.ReadFile(p)
if err != nil {
panic(err)
}
return randomCharFromWeightReader(bytes.NewReader(b))
}
func randomCharFromWeightReader(r io.Reader) func() byte {
var m map[string]int
if err := yaml.NewDecoder(r).Decode(&m); err != nil {
panic(err)
}
byted := map[byte]int{}
for k, v := range m {
if len(k) != 1 {
panic(fmt.Sprintf("keys must be 1 character but got %q", k))
}
byted[k[0]] = v
}
return randomCharFromWeights(byted)
}
func randomCharFromWeights(m map[byte]int) func() byte {
type pair struct {
b byte
i int
}
result := make([]pair, 0, len(m))
sum := 0
for k, v := range m {
result = append(result, pair{b: k, i: v})
if v < 0 {
panic("each weight must each be natural")
}
sum += v
}
sort.Slice(result, func(i, j int) bool {
return result[i].i < result[j].i
})
if sum <= 0 {
panic("weights must total nonzero")
}
return func() byte {
r := rand.Int()
n := r % (sum + 1)
for _, v := range result {
n -= v.i
if n <= 0 {
return v.b
}
}
panic("how")
}
}