// Package rng ger seedade slumptalsgeneratorer. // // Allt som ska vara reproducerbart (minispel, oändligt läge, event-timing) // måste ta sin slump härifrån med en känd seed, aldrig från den globala // generatorn. Seeden sparas i sparfilen så att en laddad omgång ger samma // utfall som den avbrutna. package rng import ( crand "crypto/rand" "encoding/binary" "math/rand/v2" ) // Seed är en 64-bitars seed. Två värden för att kunna avleda del-seeds // (t.ex. per bana eller per minispel) utan att de påverkar varandra. type Seed struct { A, B uint64 } // NewSeed drar en färsk slumpmässig seed från operativsystemet. func NewSeed() Seed { var b [16]byte if _, err := crand.Read(b[:]); err != nil { panic("rng: kunde inte läsa slump från OS: " + err.Error()) } return Seed{binary.LittleEndian.Uint64(b[:8]), binary.LittleEndian.Uint64(b[8:])} } // FromInt skapar en seed från ett enkelt heltal (praktiskt i tester och för // seeds som spelaren skriver in manuellt). func FromInt(n uint64) Seed { return Seed{A: n, B: n ^ 0x9E3779B97F4A7C15} } // Derive avleder en deterministisk del-seed för en namngiven kontext, så att // t.ex. biljettautomatens minispel på bana 3 alltid får samma slump oavsett // vad spelaren gjort innan. func (s Seed) Derive(context string) Seed { h := rand.New(rand.NewPCG(s.A, s.B)) a, b := h.Uint64(), h.Uint64() for _, c := range context { a ^= uint64(c) * 0x100000001B3 b = b*31 + uint64(c) } return Seed{A: a, B: b} } // New returnerar en generator för seeden. Samma seed ger alltid samma sekvens. func (s Seed) New() *rand.Rand { return rand.New(rand.NewPCG(s.A, s.B)) }