use math/rand/v2 for randomness

This commit is contained in:
Branden J Brown 2024-04-07 15:14:57 -05:00
parent e25c952bcd
commit 29a14960f4
4 changed files with 16 additions and 71 deletions

View File

@ -11,6 +11,7 @@ package game
import ( import (
"errors" "errors"
"math/rand/v2"
"time" "time"
"git.sunturtle.xyz/studio/shotgun/player" "git.sunturtle.xyz/studio/shotgun/player"
@ -18,8 +19,6 @@ import (
) )
type Match struct { type Match struct {
// rng is the PRNG state used for this match.
rng xoshiro
// players are the players in the match. The first element is the dealer // players are the players in the match. The first element is the dealer
// and the second is the challenger. // and the second is the challenger.
players [2]playerState players [2]playerState
@ -50,7 +49,6 @@ type Match struct {
// New creates a new match started at round 1. // New creates a new match started at round 1.
func New(dealer, challenger player.ID) *Match { func New(dealer, challenger player.ID) *Match {
g := &Match{ g := &Match{
rng: newRNG(),
players: [2]playerState{ players: [2]playerState{
{id: dealer}, {id: dealer},
{id: challenger}, {id: challenger},
@ -62,7 +60,7 @@ func New(dealer, challenger player.ID) *Match {
// NextRound starts the next round of a match. // NextRound starts the next round of a match.
func (g *Match) NextRound() { func (g *Match) NextRound() {
g.hp = int8(g.rng.Intn(3) + 2) g.hp = int8(rand.IntN(3) + 2)
g.players[0].startRound(g.hp) g.players[0].startRound(g.hp)
g.players[1].startRound(g.hp) g.players[1].startRound(g.hp)
g.round++ g.round++
@ -71,10 +69,10 @@ func (g *Match) NextRound() {
// NextGame starts the next game of a round. // NextGame starts the next game of a round.
func (g *Match) NextGame() { func (g *Match) NextGame() {
items := g.rng.Intn(4) + 1 items := rand.IntN(4) + 1
g.players[0].startGame(&g.rng, items) g.players[0].startGame(items)
g.players[1].startGame(&g.rng, items) g.players[1].startGame(items)
shells := g.rng.Intn(6) + 2 shells := rand.IntN(6) + 2
for i := 0; i < shells/2; i++ { for i := 0; i < shells/2; i++ {
g.shellArray[i] = true g.shellArray[i] = true
} }
@ -82,7 +80,7 @@ func (g *Match) NextGame() {
g.shellArray[i] = false g.shellArray[i] = false
} }
g.shells = g.shellArray[:shells] g.shells = g.shellArray[:shells]
shuffle(&g.rng, g.shells) shuffle(g.shells)
g.turn = 0 g.turn = 0
g.prev = nil g.prev = nil
g.nextTurn() g.nextTurn()

View File

@ -1,5 +1,7 @@
package game package game
import "math/rand/v2"
type item int8 type item int8
const ( const (
@ -11,8 +13,8 @@ const (
itemKnife itemKnife
) )
func newItem(rng *xoshiro) item { func newItem() item {
return item(rng.Intn(5)) + 1 return item(rand.IntN(5)) + 1
} }
func (i item) apply(g *Match) bool { func (i item) apply(g *Match) bool {

View File

@ -19,13 +19,13 @@ func (p *playerState) startRound(hp int8) {
clear(p.items[:]) clear(p.items[:])
} }
func (p *playerState) startGame(rng *xoshiro, items int) { func (p *playerState) startGame(items int) {
for i := 0; i < items; i++ { for i := 0; i < items; i++ {
k := slices.Index(p.items[:], itemNone) k := slices.Index(p.items[:], itemNone)
if k < 0 { if k < 0 {
break break
} }
p.items[k] = newItem(rng) p.items[k] = newItem()
} }
p.cuffs = uncuffed p.cuffs = uncuffed
} }

View File

@ -1,65 +1,10 @@
package game package game
import ( import "math/rand/v2"
"crypto/rand"
"encoding/binary"
"math/bits"
)
// xoshiro is a random number generator for shotgun. func shuffle[E any, S ~[]E](s S) {
//
// 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-- { for i := len(s) - 1; i > 0; i-- {
j := rng.Intn(i + 1) j := rand.IntN(i + 1)
s[i], s[j] = s[j], s[i] s[i], s[j] = s[j], s[i]
} }
} }