renamed some things and snuck in some testing code

This commit is contained in:
dtookey 2023-09-09 19:56:53 -04:00
parent a985417f83
commit 483e67bb16
12 changed files with 263 additions and 172 deletions

View File

@ -1,7 +1,7 @@
package entries package entries
import simulation.* import simulation.*
import simulation.dice.DiceBag import simulation.dice.Dice
import simulation.dice.RollType import simulation.dice.RollType
import simulation.fifthEd.AttackResult import simulation.fifthEd.AttackResult
import simulation.fifthEd.AttackSimulatorModel import simulation.fifthEd.AttackSimulatorModel
@ -16,7 +16,8 @@ fun main() {
val rounds = 1_000_000 val rounds = 1_000_000
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
// val computedRounds = doSimulation(rounds) // val computedRounds = doSimulation(rounds)
crunchBarbarian(21) // crunchPally(21)
crunchRogue(18)
// crunchMonk() // crunchMonk()
val finish = System.currentTimeMillis() val finish = System.currentTimeMillis()
// println("Simulation finished $computedRounds rounds in: ${finish - start}ms") // println("Simulation finished $computedRounds rounds in: ${finish - start}ms")
@ -34,7 +35,7 @@ fun crunchMonk(ac: Int= 18) {
val attackBless = MeleeAttackBuilder("1d20", "1d8", ac) val attackBless = MeleeAttackBuilder("1d20", "1d8", ac)
.withAtkBonus(11) .withAtkBonus(11)
.withAtkBonus(DiceBag.plainDice("1d4")) .withAtkBonus(Dice.regular("1d4"))
.withDmgBonus(7) .withDmgBonus(7)
.build() .build()
@ -77,6 +78,96 @@ fun crunchBarbarian(defense: Int = 16){
val attacks = listOf(Pair("Normal Attack", normalAttack), Pair("GWM Attack", gwmAttack)) val attacks = listOf(Pair("Normal Attack", normalAttack), Pair("GWM Attack", gwmAttack))
generateCombatSuggestions(attacks, rounds, defaultReport())
}
fun crunchPally(defense: Int = 16){
val rounds = 1_000_000
val attackDice = "1d20"
val weaponDice = "1d10"
val normalAttack = MeleeAttackBuilder(attackDice, weaponDice, defense)
//attack
.withAtkBonus(5) // str mod
.withAtkBonus(4) // proficiency bonus
.withAtkBonus(4) // proficiency bonus
.withAtkBonus(2) // weapon bonus
.withAtkBonus(2) // arming
//damage
.withDmgBonus(2) // weapon bonus
.withDmgBonus(5) // str mod
.withDmgBonus(Dice.regular("1d4")) // polearm force
.withDmgBonus(Dice.regular("1d8")) // passive smite
.build()
val gwmAttack = MeleeAttackBuilder(attackDice, weaponDice, defense)
//attack
.withAtkBonus(5) // str mod
.withAtkBonus(-5) // GWM
.withAtkBonus(4) // proficiency bonus
.withAtkBonus(4) // proficiency bonus
.withAtkBonus(2) // weapon bonus
.withAtkBonus(2) // arming
//damage
.withDmgBonus(2) // weapon bonus
.withDmgBonus(5) // str mod
.withDmgBonus(10) // GWM
.withDmgBonus(Dice.regular("1d4")) // polearm force
.withDmgBonus(Dice.regular("1d8")) // passive smite
.build()
val attacks = listOf(Pair("Normal Attack", normalAttack), Pair("GWM Attack", gwmAttack))
generateCombatSuggestions(attacks, rounds, defaultReport())
}
fun crunchRogue(defense: Int = 18){
val attackDice = "1d20"
val bloodthirstDice = "1d20C19"
val weaponDice = "1d10"
val rounds = 1_000_000
val crimsonMischief = MeleeAttackBuilder(attackDice, weaponDice, defense)
//attack
.withAtkBonus(16) // bonus total
//damage
.withDmgBonus(Dice.regular("10d6")) //sneak attack
.withDmgBonus(7) //bonus total
.withDmgBonus(7) //bonus total
val bloodthirst = MeleeAttackBuilder(bloodthirstDice, weaponDice, defense)
//attack
.withAtkBonus(16) // bonus total
//damage
.withDmgBonus(Dice.regular("10d6")) //sneak attack
.withDmgBonus(7) //bonus total
val attacks = arrayListOf(
Pair("Crimson Mischief", crimsonMischief.build()),
Pair("Bloodthirst", bloodthirst.build())
)
crimsonMischief.withAtkBonus(-5).withDmgBonus(10)
bloodthirst.withAtkBonus(-5).withDmgBonus(10)
attacks.add(Pair("crimson Mischief sharpshooter", crimsonMischief.build()))
attacks.add(Pair("Bloodthirst sharpshooter", bloodthirst.build()))
generateCombatSuggestions(attacks, rounds, defaultReport()) generateCombatSuggestions(attacks, rounds, defaultReport())
} }
@ -98,10 +189,12 @@ fun generateCombatSuggestions(
val report = reportFactory.build(label) val report = reportFactory.build(label)
print("${attackInfo.first} $it\t")
val res = report.computeResults(results) val res = report.computeResults(results)
print("${attackInfo.first} $it\t")
println(Report.formatReport(res.name, res.results)) println(Report.formatReport(res.name, res.results))
println()
res res
}.toMap() }.toMap()
} }
@ -128,6 +221,7 @@ fun generateCombatSuggestions(
} }
} }
fun defaultReport(): ReportBuilder { fun defaultReport(): ReportBuilder {
return ReportBuilder.getInstance() return ReportBuilder.getInstance()
.addRateMetric("Accuracy") { it.rollSucceeded } .addRateMetric("Accuracy") { it.rollSucceeded }

View File

@ -3,7 +3,7 @@ package simulation.dice
/** /**
* Dice with a modifiable crit threshold * Dice with a modifiable crit threshold
*/ */
interface CritDice : Dice { interface CritDiceRoller : DiceRoller {
val critThreshold: Int val critThreshold: Int
@ -12,7 +12,7 @@ interface CritDice : Dice {
* for these attack dice. * for these attack dice.
* *
* The critical hit threshold is determined based on the roll string used to construct * The critical hit threshold is determined based on the roll string used to construct
* this [CritDice] instance. * this [CritDiceRoller] instance.
* *
* @param result The [RollResult] to check for crit. * @param result The [RollResult] to check for crit.
* @return True if result meets or exceeds the crit threshold. * @return True if result meets or exceeds the crit threshold.
@ -42,7 +42,7 @@ interface CritDice : Dice {
internal class CritDiceImpl( internal class CritDiceImpl(
override val rollString: String, override val rollString: String,
override val modifiers: List<DiceModifier<Int>> = ArrayList() override val modifiers: List<DiceModifier<Int>> = ArrayList()
) : CritDice { ) : CritDiceRoller {
override val nDice: Int override val nDice: Int
override val dieSize: Int override val dieSize: Int

View File

@ -1,111 +1,19 @@
package simulation.dice package simulation.dice
import simulation.dice.RollType.* object Dice {
import java.util.* fun regular(rollString: String, modifiers: List<DiceModifier<Int>> = ArrayList()): DiceRoller {
import kotlin.math.max return DiceImpl(rollString, modifiers)
import kotlin.math.min
/**
* Enumeration of different dice roll types.
*
* @property Advantage Rolls the dice twice and takes the higher result.
* @property Normal Rolls the dice normally once.
* @property Disadvantage Rolls the dice twice and takes the lower result.
*/
enum class RollType {
/**
* Rolls the dice once normally.
*/
Normal,
/**
* Rolls the dice twice and returns the higher of the two results.
*/
Advantage,
/**
* Rolls the dice twice and returns the lower of the two results.
*/
Disadvantage
} }
data class RollResult(val min: Int, val max: Int, val result: Int) fun critDice(rollString: String, modifiers: List<DiceModifier<Int>> = ArrayList()): CritDiceRoller {
return CritDiceImpl(rollString, modifiers)
interface Dice {
val rollString: String
val modifiers: List<DiceModifier<Int>>
val nDice: Int
val dieSize: Int
/**
* Rolls the dice and returns the result.
*
* The roll result is determined based on the [rollType].
*
* The result is also modified by any [modifiers] that have been added to this [Dice].
*
* @param r The [Random] instance to use for the dice rolls
* @return The result of the dice roll with modifiers applied
* @see RollType
*/
fun roll(r: Random, rollType: RollType = Normal): RollResult {
val range = (dieSize * nDice) - nDice
val result = when (rollType) {
Advantage -> advantageRoll(r, nDice, range)
Disadvantage -> disadvantageRoll(r, nDice, range)
else -> normalRoll(r, nDice, range)
}
return RollResult(nDice, dieSize * nDice, result)
} }
fun rerollDice(
private fun advantageRoll(r: Random, nDice: Int, range: Int): Int { rollString: String,
val roll1 = r.nextInt(range + 1) rerollThreshold: Int,
val roll2 = r.nextInt(range + 1) modifiers: List<DiceModifier<Int>> = ArrayList()
): RerollDiceRoller {
return max(roll1, roll2) + nDice return RerollDiceImpl(rollString, rerollThreshold, modifiers)
}
private fun disadvantageRoll(r: Random, nDice: Int, range: Int): Int {
val roll1 = r.nextInt(range + 1)
val roll2 = r.nextInt(range + 1)
return min(roll1, roll2) + nDice
}
private fun normalRoll(r: Random, nDice: Int, range: Int): Int {
return r.nextInt(range + 1) + nDice
}
/**
* Evaluates all the modifiers passed to this Dice instance and returns their sum.
*
* @param r The Random instance to pass to each modifier's getBonus() method
* @return The summed bonus values from all modifiers
*/
fun evaluateModifiers(r: Random, crit: Boolean = false): Int {
return modifiers.sumOf { it.getBonus(r, crit) }
}
fun defaultParseFn(rollString: String): Pair<Int, Int> {
val cleanRollString = rollString.lowercase()
val parts = cleanRollString.split('d')
return Pair(parts[0].toInt(), parts[1].toInt())
}
}
internal class DiceImpl(
override val rollString: String,
override val modifiers: List<DiceModifier<Int>> = ArrayList()
) : Dice {
override val nDice: Int
override val dieSize: Int
init {
val rollPair = defaultParseFn(rollString)
nDice = rollPair.first
dieSize = rollPair.second
} }
} }

View File

@ -1,19 +0,0 @@
package simulation.dice
object DiceBag {
fun plainDice(rollString: String, modifiers: List<DiceModifier<Int>> = ArrayList()): Dice {
return DiceImpl(rollString, modifiers)
}
fun critDice(rollString: String, modifiers: List<DiceModifier<Int>> = ArrayList()): CritDice {
return CritDiceImpl(rollString, modifiers)
}
fun rerollDice(
rollString: String,
rerollThreshold: Int,
modifiers: List<DiceModifier<Int>> = ArrayList()
): RerollDice {
return RerollDiceImpl(rollString, rerollThreshold, modifiers)
}
}

View File

@ -21,16 +21,16 @@ interface DiceModifier<T> {
} }
/** /**
* A [DiceModifier] that generates a random bonus integer based on a provided [Dice]. * A [DiceModifier] that generates a random bonus integer based on a provided [DiceRoller].
* *
* On each call to [getBonus], it will roll the given [Dice] using the passed [Random] * On each call to [getBonus], it will roll the given [DiceRoller] using the passed [Random]
* instance and return the result as a positive bonus amount. * instance and return the result as a positive bonus amount.
* *
* @param dice The [Dice] instance to use for generating bonus values. * @param dice The [DiceRoller] instance to use for generating bonus values.
*/ */
class DiceBonusModifier(private val dice: Dice) : DiceModifier<Int> { class DiceBonusModifier(private val dice: DiceRoller) : DiceModifier<Int> {
constructor(diceString: String):this(DiceBag.plainDice(diceString)) constructor(diceString: String):this(Dice.regular(diceString))
override fun getBonus(r: Random, crit: Boolean): Int { override fun getBonus(r: Random, crit: Boolean): Int {
return if (crit){ return if (crit){
@ -43,14 +43,14 @@ class DiceBonusModifier(private val dice: Dice) : DiceModifier<Int> {
} }
/** /**
* A [DiceModifier] that applies a random penalty based on a [Dice]. * A [DiceModifier] that applies a random penalty based on a [DiceRoller].
* *
* On each call to [getBonus], it will roll the provided [Dice] object and return the * On each call to [getBonus], it will roll the provided [DiceRoller] object and return the
* result as a negative number. * result as a negative number.
* *
* @param dice The [Dice] to use for generating penalty values. * @param dice The [DiceRoller] to use for generating penalty values.
*/ */
class DicePenaltyModifier(private val dice: Dice): DiceModifier<Int> { class DicePenaltyModifier(private val dice: DiceRoller): DiceModifier<Int> {
override fun getBonus(r: Random, crit: Boolean): Int {//can penalties ever crit? override fun getBonus(r: Random, crit: Boolean): Int {//can penalties ever crit?
return -dice.roll(r).result return -dice.roll(r).result
} }

View File

@ -0,0 +1,111 @@
package simulation.dice
import simulation.dice.RollType.*
import java.util.*
import kotlin.math.max
import kotlin.math.min
/**
* Enumeration of different dice roll types.
*
* @property Advantage Rolls the dice twice and takes the higher result.
* @property Normal Rolls the dice normally once.
* @property Disadvantage Rolls the dice twice and takes the lower result.
*/
enum class RollType {
/**
* Rolls the dice once normally.
*/
Normal,
/**
* Rolls the dice twice and returns the higher of the two results.
*/
Advantage,
/**
* Rolls the dice twice and returns the lower of the two results.
*/
Disadvantage
}
data class RollResult(val min: Int, val max: Int, val result: Int)
interface DiceRoller {
val rollString: String
val modifiers: List<DiceModifier<Int>>
val nDice: Int
val dieSize: Int
/**
* Rolls the dice and returns the result.
*
* The roll result is determined based on the [rollType].
*
* The result is also modified by any [modifiers] that have been added to this [DiceRoller].
*
* @param r The [Random] instance to use for the dice rolls
* @return The result of the dice roll with modifiers applied
* @see RollType
*/
fun roll(r: Random, rollType: RollType = Normal): RollResult {
val range = (dieSize * nDice) - nDice
val result = when (rollType) {
Advantage -> advantageRoll(r, nDice, range)
Disadvantage -> disadvantageRoll(r, nDice, range)
else -> normalRoll(r, nDice, range)
}
return RollResult(nDice, dieSize * nDice, result)
}
private fun advantageRoll(r: Random, nDice: Int, range: Int): Int {
val roll1 = r.nextInt(range + 1)
val roll2 = r.nextInt(range + 1)
return max(roll1, roll2) + nDice
}
private fun disadvantageRoll(r: Random, nDice: Int, range: Int): Int {
val roll1 = r.nextInt(range + 1)
val roll2 = r.nextInt(range + 1)
return min(roll1, roll2) + nDice
}
private fun normalRoll(r: Random, nDice: Int, range: Int): Int {
return r.nextInt(range + 1) + nDice
}
/**
* Evaluates all the modifiers passed to this Dice instance and returns their sum.
*
* @param r The Random instance to pass to each modifier's getBonus() method
* @return The summed bonus values from all modifiers
*/
fun evaluateModifiers(r: Random, crit: Boolean = false): Int {
return modifiers.sumOf { it.getBonus(r, crit) }
}
fun defaultParseFn(rollString: String): Pair<Int, Int> {
val cleanRollString = rollString.lowercase()
val parts = cleanRollString.split('d')
return Pair(parts[0].toInt(), parts[1].toInt())
}
}
internal class DiceImpl(
override val rollString: String,
override val modifiers: List<DiceModifier<Int>> = ArrayList()
) : DiceRoller {
override val nDice: Int
override val dieSize: Int
init {
val rollPair = defaultParseFn(rollString)
nDice = rollPair.first
dieSize = rollPair.second
}
}

View File

@ -3,7 +3,7 @@ package simulation.dice
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
interface RerollDice : Dice { interface RerollDiceRoller : DiceRoller {
val rollThreshold: Int val rollThreshold: Int
override fun roll(r: Random, rollType: RollType): RollResult { override fun roll(r: Random, rollType: RollType): RollResult {
val result = super.roll(r, rollType) val result = super.roll(r, rollType)
@ -20,7 +20,7 @@ class RerollDiceImpl(
override val rollString: String, override val rollString: String,
override val rollThreshold: Int, override val rollThreshold: Int,
override val modifiers: List<DiceModifier<Int>> = ArrayList() override val modifiers: List<DiceModifier<Int>> = ArrayList()
) : RerollDice{ ) : RerollDiceRoller{
override val nDice: Int override val nDice: Int
override val dieSize: Int override val dieSize: Int

View File

@ -1,7 +1,6 @@
package simulation.fifthEd package simulation.fifthEd
import simulation.dice.CritDice import simulation.dice.CritDiceRoller
import simulation.dice.Dice import simulation.dice.DiceRoller
import java.util.*
data class ActionRollInfo(val actionRoll: CritDice, val damageRoll: Dice) data class ActionRollInfo(val actionRoll: CritDiceRoller, val damageRoll: DiceRoller)

View File

@ -91,12 +91,12 @@ class MeleeAttackBuilder(
return this return this
} }
fun withAtkBonus(dice: Dice): MeleeAttackBuilder { fun withAtkBonus(dice: DiceRoller): MeleeAttackBuilder {
attackModifiers.add(DiceBonusModifier(dice)) attackModifiers.add(DiceBonusModifier(dice))
return this return this
} }
fun withAtkPenalty(dice: Dice): MeleeAttackBuilder { fun withAtkPenalty(dice: DiceRoller): MeleeAttackBuilder {
attackModifiers.add(DicePenaltyModifier(dice)) attackModifiers.add(DicePenaltyModifier(dice))
return this return this
} }
@ -106,12 +106,12 @@ class MeleeAttackBuilder(
return this return this
} }
fun withDmgBonus(dice: Dice): MeleeAttackBuilder { fun withDmgBonus(dice: DiceRoller): MeleeAttackBuilder {
damageModifiers.add(DiceBonusModifier(dice)) damageModifiers.add(DiceBonusModifier(dice))
return this return this
} }
fun withDmgPenalty(dice: Dice): MeleeAttackBuilder { fun withDmgPenalty(dice: DiceRoller): MeleeAttackBuilder {
damageModifiers.add(DicePenaltyModifier(dice)) damageModifiers.add(DicePenaltyModifier(dice))
return this return this
} }
@ -119,8 +119,8 @@ class MeleeAttackBuilder(
fun build(): SimpleMeleeAttackAction { fun build(): SimpleMeleeAttackAction {
return SimpleMeleeAttackAction( return SimpleMeleeAttackAction(
ActionRollInfo( ActionRollInfo(
DiceBag.critDice(attackRollString, attackModifiers), Dice.critDice(attackRollString, attackModifiers),
DiceBag.plainDice(dmgRollString, damageModifiers) Dice.regular(dmgRollString, damageModifiers)
), ),
defense defense
) )

View File

@ -2,7 +2,6 @@ package simulation
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import simulation.dice.Dice import simulation.dice.Dice
import simulation.dice.DiceBag
import simulation.dice.RollResult import simulation.dice.RollResult
import java.util.* import java.util.*
import kotlin.test.Test import kotlin.test.Test
@ -13,7 +12,7 @@ class AttackDiceTest {
fun testAttackDiceImplementation() { fun testAttackDiceImplementation() {
val r = Random() val r = Random()
// Test no crit below threshold // Test no crit below threshold
val dice = DiceBag.critDice("1d20") val dice = Dice.critDice("1d20")
val result = dice.roll(r) val result = dice.roll(r)
if (result.result < 20) { if (result.result < 20) {
Assertions.assertFalse(dice.isCrit(result)) Assertions.assertFalse(dice.isCrit(result))
@ -21,7 +20,7 @@ class AttackDiceTest {
Assertions.assertTrue(dice.isCrit(result)) Assertions.assertTrue(dice.isCrit(result))
} }
// Test no crit below threshold // Test no crit below threshold
val dice2 = DiceBag.critDice("1d20c10") val dice2 = Dice.critDice("1d20c10")
val result2 = dice2.roll(r) val result2 = dice2.roll(r)
if (result2.result < 10) { if (result2.result < 10) {
Assertions.assertFalse(dice2.isCrit(result2)) Assertions.assertFalse(dice2.isCrit(result2))
@ -30,7 +29,7 @@ class AttackDiceTest {
} }
// Test crit threshold other than max // Test crit threshold other than max
val dice3 = DiceBag.critDice("2d10c8") val dice3 = Dice.critDice("2d10c8")
val result3 = dice3.roll(r) val result3 = dice3.roll(r)
if (result3.result >= 8) { if (result3.result >= 8) {
Assertions.assertTrue(dice3.isCrit(result3)) Assertions.assertTrue(dice3.isCrit(result3))
@ -44,9 +43,9 @@ class AttackDiceTest {
val trueCritResult = RollResult(1, 20, 20) val trueCritResult = RollResult(1, 20, 20)
val fakeCritResult = RollResult(1, 20, 19) val fakeCritResult = RollResult(1, 20, 19)
val defaultRoll = DiceBag.critDice("1d20") val defaultRoll = Dice.critDice("1d20")
val verboseDefaultCrit = DiceBag.critDice("1d20c20") val verboseDefaultCrit = Dice.critDice("1d20c20")
val normalModifiedCrit = DiceBag.critDice("1d20c19") val normalModifiedCrit = Dice.critDice("1d20c19")
Assertions.assertFalse(defaultRoll.isCrit(fakeCritResult)) Assertions.assertFalse(defaultRoll.isCrit(fakeCritResult))
Assertions.assertFalse(verboseDefaultCrit.isCrit(fakeCritResult)) Assertions.assertFalse(verboseDefaultCrit.isCrit(fakeCritResult))

View File

@ -4,19 +4,18 @@ import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import simulation.dice.Dice import simulation.dice.Dice
import simulation.dice.DiceBag
import simulation.dice.FlatModifier import simulation.dice.FlatModifier
import simulation.dice.RollType import simulation.dice.RollType
import java.util.* import java.util.*
internal class DiceTests { internal class DiceRollerTests {
private val random = Random(1) private val random = Random(1)
private val d20 = DiceBag.plainDice("1d20") private val d20 = Dice.regular("1d20")
@Test @Test
fun roll_normal() { fun roll_normal() {
val dice = DiceBag.plainDice("2d6") val dice = Dice.regular("2d6")
val result = dice.roll(random, RollType.Normal) val result = dice.roll(random, RollType.Normal)
val result2 = dice.roll(random) val result2 = dice.roll(random)
@ -30,7 +29,7 @@ internal class DiceTests {
@Test @Test
fun roll_advantage() { fun roll_advantage() {
val dice = DiceBag.plainDice("2d6") val dice = Dice.regular("2d6")
val result = dice.roll(random, RollType.Advantage) val result = dice.roll(random, RollType.Advantage)
assertEquals(2, result.min) assertEquals(2, result.min)
@ -41,7 +40,7 @@ internal class DiceTests {
@Test @Test
fun roll_disadvantage() { fun roll_disadvantage() {
val dice = DiceBag.plainDice("2d6") val dice = Dice.regular("2d6")
val result = dice.roll(random, RollType.Disadvantage) val result = dice.roll(random, RollType.Disadvantage)
assertEquals(2, result.min) assertEquals(2, result.min)
@ -53,7 +52,7 @@ internal class DiceTests {
fun evaluate_modifiers() { fun evaluate_modifiers() {
val mod1 = FlatModifier(1) val mod1 = FlatModifier(1)
val mod2 = FlatModifier(2) val mod2 = FlatModifier(2)
val dice = DiceBag.plainDice("1d20", arrayListOf(mod1, mod2)) val dice = Dice.regular("1d20", arrayListOf(mod1, mod2))
val bonus = dice.evaluateModifiers(random) val bonus = dice.evaluateModifiers(random)
@ -67,7 +66,7 @@ internal class DiceTests {
RollType.entries.parallelStream() RollType.entries.parallelStream()
.forEach { .forEach {
val dice = DiceBag.plainDice(rollString) val dice = Dice.regular(rollString)
val r = Random(1) val r = Random(1)
val rollType = it val rollType = it
repeat(iterations) { repeat(iterations) {
@ -99,7 +98,7 @@ internal class DiceTests {
RollType.entries.parallelStream() RollType.entries.parallelStream()
.forEach { .forEach {
val dice = DiceBag.plainDice(rollString) val dice = Dice.regular(rollString)
val r = Random(1) val r = Random(1)
for (i in 0..<iterations) { for (i in 0..<iterations) {
val res = dice.roll(r, it) val res = dice.roll(r, it)
@ -129,7 +128,7 @@ internal class DiceTests {
val expectedAverageLowerBound = ((n + (n * max)) / 2) * (1 - tolerance) val expectedAverageLowerBound = ((n + (n * max)) / 2) * (1 - tolerance)
val expectedAverageUpperBound = ((n + (n * max)) / 2) * (1 + tolerance) val expectedAverageUpperBound = ((n + (n * max)) / 2) * (1 + tolerance)
val dice = DiceBag.plainDice(rollString) val dice = Dice.regular(rollString)
var total = 0L var total = 0L
repeat(iterations) { repeat(iterations) {
total += dice.roll(random).result.toLong() total += dice.roll(random).result.toLong()
@ -153,7 +152,7 @@ internal class DiceTests {
val expectedAverageUpperBound = ((n + (n * max)) / 2) * tolerance val expectedAverageUpperBound = ((n + (n * max)) / 2) * tolerance
val dice = DiceBag.plainDice(rollString) val dice = Dice.regular(rollString)
var total = 0L var total = 0L
repeat(iterations) { repeat(iterations) {
@ -177,7 +176,7 @@ internal class DiceTests {
val expectedAverageLowerBound = ((n + (n * max)) / 2) * tolerance val expectedAverageLowerBound = ((n + (n * max)) / 2) * tolerance
val dice = DiceBag.plainDice(rollString) val dice = Dice.regular(rollString)
var total = 0L var total = 0L
@ -196,7 +195,7 @@ internal class DiceTests {
@Test @Test
fun verifyDistribution() { fun verifyDistribution() {
val size = 20 val size = 20
val dice = DiceBag.plainDice("1d20") val dice = Dice.regular("1d20")
val iterations = 10_000_000 val iterations = 10_000_000
val tolerance = 0.05 //5% wiggle on distribution val tolerance = 0.05 //5% wiggle on distribution

View File

@ -1,6 +1,6 @@
package simulation package simulation
import simulation.dice.DiceBag import simulation.dice.Dice
import simulation.dice.RollType import simulation.dice.RollType
import simulation.fifthEd.ActionRollInfo import simulation.fifthEd.ActionRollInfo
import simulation.fifthEd.AttackResult import simulation.fifthEd.AttackResult
@ -17,7 +17,7 @@ class MeleeAttackTest {
val itt = 1_000_000 val itt = 1_000_000
val simulator = Simulator.getInstance<AttackResult>(Runtime.getRuntime().availableProcessors()) val simulator = Simulator.getInstance<AttackResult>(Runtime.getRuntime().availableProcessors())
val attackAction = ActionRollInfo(DiceBag.critDice("1d20c19"), DiceBag.plainDice("1d8")) val attackAction = ActionRollInfo(Dice.critDice("1d20c19"), Dice.regular("1d8"))
val critAttack = SimpleMeleeAttackAction( val critAttack = SimpleMeleeAttackAction(
attackAction, attackAction,
10 10