looks like we're go for theory crafting in the morning

This commit is contained in:
dtookey 2023-09-02 23:49:11 -04:00
parent ac5aec6cb4
commit 20be086198
4 changed files with 47 additions and 14 deletions

View File

@ -13,17 +13,24 @@ fun doSimulation(){
val itt = 10_000_000 val itt = 10_000_000
val simulator = Simulator.getInstance<AttackResult>(Runtime.getRuntime().availableProcessors()) val simulator = Simulator.getInstance<AttackResult>(Runtime.getRuntime().availableProcessors())
val critAttack = SimpleMeleeAttack( val attackModifiers = listOf(FlatModifier(4), DiceBonusModifier("1d4"))
actionRoll = AttackDice("1d20"),
damageRoll = Dice.makeDice("1d8"), RollType.entries.parallelStream()
10 .forEach{
) val normalAttack = SimpleMeleeAttack(
actionRoll = AttackDice("1d20", it, attackModifiers),
damageRoll = Dice.makeDice("1d8"),
19
)
val normalAttackModel = AttackSimulatorModel(itt, normalAttack)
val normalResults = simulator.doSimulation(normalAttackModel)
AttackResult.printSimulationStatistics(normalResults, "Normal Attack (${it.name})")
}
val normalAttackModel = AttackSimulatorModel(itt, critAttack)
val normalResults = simulator.doSimulation(normalAttackModel)
AttackResult.printSimulationStatistics(normalResults, "Normal Attack")
} }

View File

@ -2,11 +2,33 @@ package simulation
import java.util.* import java.util.*
/**
* Attack defines the interface for performing an attack action.
* It handles rolling attack dice, calculating modifiers, checking for hits/crits, and triggering damage calculation.
*/
interface Attack { interface Attack {
/**
* We can't call this an 'attack' roll because it could be the case that we're attacking by forcing a DC. So whoever
* is taking the positive action makes this roll. For melee attacks, this is a normal attack. For spell checks this
* would be the save roll.
*/
val actionRoll: AttackDice val actionRoll: AttackDice
val passValue: Int
/**
* Similar to [actionRoll], we cannot call this 'defense', because it might be a spell DC. This is the value of the
* responder to the action. In a melee attack, this is AC. For a spell check, this would be the DC.
*/
val responseValue: Int
/**
* calculateDamage calculates the damage for an attack by:
* - Rolling the relevant action
* - Evaluating action modifiers
* - Checking if action hits by comparing roll + modifiers to response value
* - Calling onCriticalHit, onNormalHit or onMiss based on hit result
* - Returning the AttackResult
*/
fun calculateDamage(r: Random): AttackResult { fun calculateDamage(r: Random): AttackResult {
val attackResult = actionRoll.roll(r) val attackResult = actionRoll.roll(r)
@ -24,9 +46,9 @@ interface Attack {
} }
} }
private fun isHit(roll: RollResult, attackBonus: Int): Boolean { private fun isHit(roll: RollResult, actionBonus: Int): Boolean {
//ties go to the roller //ties go to the roller
return (roll.result + attackBonus) >= passValue return (roll.result + actionBonus) >= responseValue
} }
fun onNormalHit(r: Random): AttackResult fun onNormalHit(r: Random): AttackResult
@ -37,6 +59,10 @@ interface Attack {
} }
/**
* AttackSimulatorModel simulates attacks by running [sampleSize] simulations using the provided [attack] instance.
* It implements [SimulationModel] to run the simulations and return [AttackResult].
*/
class AttackSimulatorModel(override val sampleSize: Int, private val attack: Attack) : SimulationModel<AttackResult> { class AttackSimulatorModel(override val sampleSize: Int, private val attack: Attack) : SimulationModel<AttackResult> {
override fun simulate(r: Random): AttackResult { override fun simulate(r: Random): AttackResult {
return attack.calculateDamage(r) return attack.calculateDamage(r)

View File

@ -19,7 +19,7 @@ package simulation
class AttackDice( class AttackDice(
override val rollString: String, override val rollString: String,
override val rollType: RollType = RollType.Normal, override val rollType: RollType = RollType.Normal,
override val modifiers: ArrayList<Modifier<Int>> = ArrayList() override val modifiers: List<Modifier<Int>> = ArrayList()
) : Dice { ) : Dice {
override val nDice: Int override val nDice: Int
override val dieSize: Int override val dieSize: Int

View File

@ -7,12 +7,12 @@ import java.util.*
* *
* @param actionRoll The dice roll used to determine if an attack hits. * @param actionRoll The dice roll used to determine if an attack hits.
* @param damageRoll The dice roll used to determine damage if attack hits. * @param damageRoll The dice roll used to determine damage if attack hits.
* @param passValue The defense value the attack must exceed to hit. * @param responseValue The defense value the attack must exceed to hit.
*/ */
class SimpleMeleeAttack( class SimpleMeleeAttack(
override val actionRoll: AttackDice, override val actionRoll: AttackDice,
val damageRoll: Dice, val damageRoll: Dice,
override val passValue: Int override val responseValue: Int
) : Attack { ) : Attack {
override fun onNormalHit(r: Random): AttackResult { override fun onNormalHit(r: Random): AttackResult {