/// 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)) // 32-bit wrap-around `*` and `+`. On .NET plain uint32 `*` and `+` already wrap at 2^32, but Fable (the F# to // JavaScript compiler behind the browser page) compiles them to plain JavaScript `*` and `+`, which neither wrap nor // stay exact for big products. Going through uint64 is exact on both: the product of two uint32 values fits in a // uint64, and converting back to uint32 keeps the low 32 bits. let private mul (a: uint32) (b: uint32) : uint32 = uint32 (uint64 a * uint64 b) let private add (a: uint32) (b: uint32) : uint32 = uint32 (uint64 a + uint64 b) // mulberry32. `^^^`, `|||` and `>>>` (an unsigned shift on uint32) behave the same everywhere. let nextUint32: Random = Random(fun seed -> let s = add seed 0x6D2B79F5u let t1 = mul (s ^^^ (s >>> 15)) (s ||| 1u) let t2 = t1 ^^^ add t1 (mul (t1 ^^^ (t1 >>> 7)) (t1 ||| 61u)) (t2 ^^^ (t2 >>> 14), s)) let intBelow (n: int) : Random = map (fun v -> int (v % uint32 n)) nextUint32