Files
2026-10-29 00:07:27 +01:00

72 lines
2.5 KiB
Scala

package snake
import Fixtures.*
class M04RandomSuite extends munit.FunSuite:
// Same values as the JS and F# tracks: if mulberry32 drifts (signed math, missing unsigned conversion), these fail.
cases("m04-random").foreach { c =>
test(s"nextUint32: ${c("name").str}") {
val e = c("expected").arr
assertEquals(Random.nextUint32.run(c("args")(0).num.toLong), (e(0).num.toLong, e(1).num.toLong))
}
}
cases("m04-int-below").foreach { c =>
test(s"intBelow: ${c("name").str}") {
val a = c("args")
val e = c("expected").arr
assertEquals(Random.intBelow(a(0).num.toInt).run(a(1).num.toLong), (e(0).num.toInt, e(1).num.toLong))
}
}
test("the recorded steps chain: each step starts from the previous step's seed") {
val cs = cases("m04-random")
cs.zipWithIndex.foreach { (c, i) =>
if !c("name").str.endsWith("-step-1") then
assertEquals(c("args")(0).num.toLong, cs(i - 1)("expected")(1).num.toLong)
}
}
test("pure returns its value and leaves the seed alone") {
assertEquals(Random.pure("x").run(99L), ("x", 99L))
}
test("map applies f to the value and keeps the seed from the underlying step") {
val (v, s) = Random.nextUint32.run(5L)
assertEquals(Random.nextUint32.map(_ + 1).run(5L), (v + 1, s))
}
test("map with the identity function changes nothing") {
assertEquals(Random.nextUint32.map(identity).run(5L), Random.nextUint32.run(5L))
}
test("flatMap threads the seed through both steps") {
val (a, s1) = Random.nextUint32.run(5L)
val (b, s2) = Random.nextUint32.run(s1)
val both = Random.nextUint32.flatMap(x => Random.nextUint32.map(y => (x, y)))
assertEquals(both.run(5L), ((a, b), s2))
}
test("flatMap with pure on the left or right changes nothing") {
assertEquals(
Random.pure(3L).flatMap(x => Random.nextUint32.map(_ + x)).run(5L),
Random.nextUint32.map(_ + 3L).run(5L)
)
assertEquals(Random.nextUint32.flatMap(Random.pure).run(5L), Random.nextUint32.run(5L))
}
test("intBelow always lands in [0, n)") {
def draws(n: Int, seed: Long, count: Int): List[Int] =
if count == 0 then Nil
else
val (v, s) = Random.intBelow(n).run(seed)
v :: draws(n, s, count - 1)
List(1, 2, 7, 400).foreach { n =>
draws(n, 123L, 50).foreach(v => assert(v >= 0 && v < n, s"$v not in [0, $n)"))
}
}
test("running a Random does not mutate anything: same seed, same answer") {
assertEquals(Random.nextUint32.run(8L), Random.nextUint32.run(8L))
}