93 lines
1.6 KiB
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")
|
|
}
|
|
}
|