diff --git a/game/game.go b/game/game.go index 1db6a28..07bab89 100644 --- a/game/game.go +++ b/game/game.go @@ -11,6 +11,7 @@ package game import ( "errors" + "math/rand/v2" "time" "git.sunturtle.xyz/studio/shotgun/player" @@ -18,8 +19,6 @@ import ( ) 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 // and the second is the challenger. players [2]playerState @@ -50,7 +49,6 @@ type Match struct { // New creates a new match started at round 1. func New(dealer, challenger player.ID) *Match { g := &Match{ - rng: newRNG(), players: [2]playerState{ {id: dealer}, {id: challenger}, @@ -62,7 +60,7 @@ func New(dealer, challenger player.ID) *Match { // NextRound starts the next round of a match. 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[1].startRound(g.hp) g.round++ @@ -71,10 +69,10 @@ func (g *Match) NextRound() { // NextGame starts the next game of a round. func (g *Match) NextGame() { - items := g.rng.Intn(4) + 1 - g.players[0].startGame(&g.rng, items) - g.players[1].startGame(&g.rng, items) - shells := g.rng.Intn(6) + 2 + items := rand.IntN(4) + 1 + g.players[0].startGame(items) + g.players[1].startGame(items) + shells := rand.IntN(6) + 2 for i := 0; i < shells/2; i++ { g.shellArray[i] = true } @@ -82,7 +80,7 @@ func (g *Match) NextGame() { g.shellArray[i] = false } g.shells = g.shellArray[:shells] - shuffle(&g.rng, g.shells) + shuffle(g.shells) g.turn = 0 g.prev = nil g.nextTurn() diff --git a/game/item.go b/game/item.go index 577ce87..c5e0fb6 100644 --- a/game/item.go +++ b/game/item.go @@ -1,5 +1,7 @@ package game +import "math/rand/v2" + type item int8 const ( @@ -11,8 +13,8 @@ const ( itemKnife ) -func newItem(rng *xoshiro) item { - return item(rng.Intn(5)) + 1 +func newItem() item { + return item(rand.IntN(5)) + 1 } func (i item) apply(g *Match) bool { diff --git a/game/player.go b/game/player.go index b4444e7..532fdd8 100644 --- a/game/player.go +++ b/game/player.go @@ -19,13 +19,13 @@ func (p *playerState) startRound(hp int8) { clear(p.items[:]) } -func (p *playerState) startGame(rng *xoshiro, items int) { +func (p *playerState) startGame(items int) { for i := 0; i < items; i++ { k := slices.Index(p.items[:], itemNone) if k < 0 { break } - p.items[k] = newItem(rng) + p.items[k] = newItem() } p.cuffs = uncuffed } diff --git a/game/rng.go b/game/rng.go index a4533be..48784c1 100644 --- a/game/rng.go +++ b/game/rng.go @@ -1,65 +1,10 @@ package game -import ( - "crypto/rand" - "encoding/binary" - "math/bits" -) +import "math/rand/v2" -// 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) { +func shuffle[E any, S ~[]E](s S) { 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] } }