renamed some things and snuck in some testing code
This commit is contained in:
parent
a985417f83
commit
483e67bb16
@ -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 }
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
@ -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)
|
|
||||||
|
|
||||||
|
|
||||||
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 critDice(rollString: String, modifiers: List<DiceModifier<Int>> = ArrayList()): CritDiceRoller {
|
||||||
private fun advantageRoll(r: Random, nDice: Int, range: Int): Int {
|
return CritDiceImpl(rollString, modifiers)
|
||||||
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 {
|
fun rerollDice(
|
||||||
val roll1 = r.nextInt(range + 1)
|
rollString: String,
|
||||||
val roll2 = r.nextInt(range + 1)
|
rerollThreshold: Int,
|
||||||
|
modifiers: List<DiceModifier<Int>> = ArrayList()
|
||||||
return min(roll1, roll2) + nDice
|
): RerollDiceRoller {
|
||||||
}
|
return RerollDiceImpl(rollString, rerollThreshold, modifiers)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
111
src/main/kotlin/simulation/dice/DiceRoller.kt
Normal file
111
src/main/kotlin/simulation/dice/DiceRoller.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user