Skip to content

Commit

Permalink
Adds unit tests to assure randomness
Browse files Browse the repository at this point in the history
Tests now run 3 lots of 200,000,000 rolls of dice and check the
distribution of the results in order to test the assertion made in #750
  • Loading branch information
hobnob committed Oct 7, 2024
1 parent 96e7db9 commit 6b90f8e
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
4 changes: 1 addition & 3 deletions indigo/indigo/src/main/scala/indigo/shared/dice/Dice.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ object Dice:

val r: Random = new Random(seed)

def roll: Int =
r.nextInt(sanitise(sides)) + 1

def roll: Int = roll(sides)
def roll(sides: Int): Int =
r.nextInt(sanitise(sides)) + 1

Expand Down
81 changes: 81 additions & 0 deletions indigo/indigo/src/test/scala/indigo/shared/dice/DiceTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package indigo.shared.dice

import indigo.shared.collections.NonEmptyList

import scala.collection.immutable.SortedMap

@SuppressWarnings(Array("scalafix:DisableSyntax.throw"))
class DiceTests extends munit.FunSuite {

Expand All @@ -10,6 +12,8 @@ class DiceTests extends munit.FunSuite {
def checkDice(roll: Int, to: Int): Boolean =
roll >= 1 && roll <= to

def almostEquals(d: Double, d2: Double, p: Double) = (d - d2).abs <= p

test("diceSidesN") {
val roll: Int = Dice.diceSidesN(1, 0).roll(10)

Expand Down Expand Up @@ -57,4 +61,81 @@ class DiceTests extends munit.FunSuite {
assertEquals(actual, expected)
}

test("all dice rolls have an approximately uniform distribution") {
val diceSides = 64
val numRuns = 200_000_000
val dice = Dice.diceSidesN(diceSides, 0)
val generatedNums =
Array
.range(0, numRuns)
.foldLeft(SortedMap[Int, Int]()) { (acc, _) =>
val roll = dice.roll
acc.updated(roll, acc.getOrElse(roll, 0) + 1)
}

assertEquals(generatedNums.size, diceSides)
assertEquals(generatedNums.head._1, 1)
assertEquals(generatedNums.last._1, diceSides)

val expectedDistribution = 1.0 / diceSides
generatedNums.foreach { case (num, count) =>
val distribution = count.toDouble / numRuns
assert(
almostEquals(distribution, expectedDistribution, 0.01),
s"""The distribution for $num was $distribution, but expected $expectedDistribution"""
)
}
}

test("all dice rolls in rollRange have an approximately uniform distribution") {
val diceSides = 64
val halfSides = diceSides / 2
val numRuns = 200_000_000
val dice = Dice.diceSidesN(diceSides, 0)
val generatedNums =
Array
.range(0, numRuns)
.foldLeft(SortedMap[Int, Int]()) { (acc, _) =>
val roll = dice.rollRange(halfSides, diceSides)
acc.updated(roll, acc.getOrElse(roll, 0) + 1)
}

assertEquals(generatedNums.size, halfSides + 1)
assertEquals(generatedNums.head._1, halfSides)
assertEquals(generatedNums.last._1, diceSides)

val expectedDistribution = 1.0 / halfSides
generatedNums.foreach { case (num, count) =>
val distribution = count.toDouble / numRuns
assert(
almostEquals(distribution, expectedDistribution, 0.01),
s"""The distribution for $num was $distribution, but expected $expectedDistribution"""
)
}
}

test("all dice rolls in rollRange(1, 4) have an approximately uniform distribution") {
val numRuns = 200_000_000
val dice = Dice.diceSidesN(4, 0)
val generatedNums =
Array
.range(0, numRuns)
.foldLeft(SortedMap[Int, Int]()) { (acc, _) =>
val roll = dice.rollRange(1, 4)
acc.updated(roll, acc.getOrElse(roll, 0) + 1)
}

assertEquals(generatedNums.size, 4)
assertEquals(generatedNums.head._1, 1)
assertEquals(generatedNums.last._1, 4)

val expectedDistribution = 0.25
generatedNums.foreach { case (num, count) =>
val distribution = count.toDouble / numRuns
assert(
almostEquals(distribution, expectedDistribution, 0.01),
s"""The distribution for $num was $distribution, but expected $expectedDistribution"""
)
}
}
}

0 comments on commit 6b90f8e

Please sign in to comment.