37 lines
1.2 KiB
Scala
37 lines
1.2 KiB
Scala
package snake
|
|
|
|
/**
|
|
* m04 random: a tiny "state monad" for randomness. A Random[A] is a function seed => (A, 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 kept in a Long.
|
|
*/
|
|
final case class Random[A](run: Long => (A, Long)):
|
|
|
|
def map[B](f: A => B): Random[B] =
|
|
Random { seed =>
|
|
val (a, next) = run(seed)
|
|
(f(a), next)
|
|
}
|
|
|
|
def flatMap[B](f: A => Random[B]): Random[B] =
|
|
Random { seed =>
|
|
val (a, next) = run(seed)
|
|
f(a).run(next)
|
|
}
|
|
|
|
object Random:
|
|
|
|
def pure[A](a: A): Random[A] = Random(seed => (a, seed))
|
|
|
|
// The mulberry32 arithmetic is done in Int: `*` wraps like 32-bit multiplication and `>>>` is unsigned.
|
|
// `& 0xFFFFFFFFL` turns the result back into an unsigned value held in a Long.
|
|
lazy val nextUint32: Random[Long] = Random { seed =>
|
|
val s = seed.toInt + 0x6d2b79f5
|
|
val t1 = (s ^ (s >>> 15)) * (s | 1)
|
|
val t2 = t1 ^ (t1 + (t1 ^ (t1 >>> 7)) * (t1 | 61))
|
|
val value = t2 ^ (t2 >>> 14)
|
|
(value.toLong & 0xffffffffL, s.toLong & 0xffffffffL)
|
|
}
|
|
|
|
def intBelow(n: Int): Random[Int] = nextUint32.map(v => (v % n).toInt)
|