32 lines
1.2 KiB
FSharp
32 lines
1.2 KiB
FSharp
/// m04 random: a tiny "state monad" for randomness. A Random<'a> wraps a function seed -> (value, nextSeed),
|
|
/// so every draw is a pure function and the seed is passed along explicitly instead of hidden.
|
|
/// Seeds and values are unsigned 32-bit numbers (uint32).
|
|
module Snake.Random
|
|
|
|
type Random<'a> = Random of (uint32 -> 'a * uint32)
|
|
|
|
let run (seed: uint32) (Random f: Random<'a>) : 'a * uint32 = f seed
|
|
|
|
let pure (a: 'a) : Random<'a> = Random(fun seed -> (a, seed))
|
|
|
|
let map (f: 'a -> 'b) (r: Random<'a>) : Random<'b> =
|
|
Random(fun seed ->
|
|
let a, next = run seed r
|
|
(f a, next))
|
|
|
|
let bind (f: 'a -> Random<'b>) (r: Random<'a>) : Random<'b> =
|
|
Random(fun seed ->
|
|
let a, next = run seed r
|
|
run next (f a))
|
|
|
|
// mulberry32. uint32 arithmetic wraps around at 2^32 (F# operators are unchecked) and `>>>` on uint32 is an
|
|
// unsigned shift, so the formula can be written down as it is.
|
|
let nextUint32: Random<uint32> =
|
|
Random(fun seed ->
|
|
let s = seed + 0x6D2B79F5u
|
|
let t1 = (s ^^^ (s >>> 15)) * (s ||| 1u)
|
|
let t2 = t1 ^^^ (t1 + (t1 ^^^ (t1 >>> 7)) * (t1 ||| 61u))
|
|
(t2 ^^^ (t2 >>> 14), s))
|
|
|
|
let intBelow (n: int) : Random<int> = map (fun v -> int (v % uint32 n)) nextUint32
|