package sim import ( "math/bits" "github.com/drjones/quantum-arcade/pkg/fixed" ) // HouseEdgeBP is the house edge in basis points (200 = 2.00%). const HouseEdgeBP int64 = 200 // TickHz is the simulation rate. Rounds advance in whole ticks only. const TickHz = 60 // growthPerTickBP is multiplier growth per tick, in basis points of the current // value. At 6bp and 60Hz the multiplier reaches 2x in roughly 19 seconds, which // is long enough to feel the climb and short enough to keep rounds moving. const growthPerTickBP int64 = 6 // CrashPoint derives the multiplier at which a round ends, as a pure function of // the seed. // // The distribution is the inverse-uniform curve scaled by the house edge: // // crash = (1 - edge) / u, u uniform over (0, 1] // // which yields the same expected return of (1 - edge) at every cash-out target. // No target is smarter than any other, so there is nothing to grind out. func CrashPoint(seed [32]byte) fixed.F { r := NewRNG(seed) // u is uniform over [1, 2^32], giving a resolution of one part in 4 billion. u := (r.Uint64() >> 32) + 1 // payoutRatio is (1 - edge) in Q32.32, e.g. 0.98. payoutRatio := uint64((10000 - HouseEdgeBP) << 32 / 10000) // crash = payoutRatio / (u / 2^32), computed as (payoutRatio * 2^32) / u // through a 128-bit intermediate so no precision is lost. // hi is zero because payoutRatio < 2^32, so the division cannot overflow. hi, lo := bits.Mul64(payoutRatio, 1<<32) q, _ := bits.Div64(hi, lo, u) cp := fixed.F(q) if cp < fixed.One { cp = fixed.One } return cp } // step is the per-tick growth factor, 1 + growthPerTickBP/10000, in Q32.32. func step() fixed.F { return fixed.One + fixed.F(growthPerTickBP<<32/10000) } // MultiplierAt returns the multiplier displayed at a given tick of the round, // compounding from 1.0. func MultiplierAt(tick int) fixed.F { m := fixed.One s := step() for i := 0; i < tick; i++ { m = m.Mul(s) } return m } // TicksToMultiplier returns the first tick at which MultiplierAt reaches m. func TicksToMultiplier(m fixed.F) int { cur := fixed.One s := step() for tick := 0; tick < 1_000_000; tick++ { if cur >= m { return tick } cur = cur.Mul(s) } return 1_000_000 }