package game import ( "crypto/rand" "encoding/binary" "math/bits" ) // xoshiro is a random number generator for shotgun. // // Currently xoshiro256**. Subject to change. type xoshiro struct { w, x, y, z uint64 } // newRNG produces a new, uniquely seeded RNG. func newRNG() xoshiro { r := xoshiro{} b := make([]byte, 8*4) // Loop to ensure the state is never all-zero, which is an invalid state // for xoshiro. for r.w == 0 && r.x == 0 && r.y == 0 && r.z == 0 { rand.Read(b) r.w = binary.LittleEndian.Uint64(b) r.x = binary.LittleEndian.Uint64(b[8:]) r.y = binary.LittleEndian.Uint64(b[16:]) r.z = binary.LittleEndian.Uint64(b[24:]) } return r } // Uint64 produces a 64-bit pseudo-random value. func (rng *xoshiro) Uint64() uint64 { w, x, y, z := rng.w, rng.x, rng.y, rng.z r := bits.RotateLeft64(x*5, 7) * 9 t := x << 17 y ^= w z ^= x x ^= y w ^= z y ^= t z = bits.RotateLeft64(z, 45) rng.w, rng.x, rng.y, rng.z = w, x, y, z return r } // Intn produces an int in [0, n). Panics if n <= 0. func (rng *xoshiro) Intn(n int) int { if n <= 0 { panic("shotgun: rng.Intn max below zero") } bad := ^uint(0) - ^uint(0)%uint(n) x := uint(rng.Uint64()) for x > bad { x = uint(rng.Uint64()) } return int(x % uint(n)) } func shuffle[E any, S ~[]E](rng *xoshiro, s S) { for i := len(s) - 1; i > 0; i-- { j := rng.Intn(i + 1) s[i], s[j] = s[j], s[i] } }