looks like we're go for theory crafting in the morning
This commit is contained in:
parent
ac5aec6cb4
commit
20be086198
@ -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")
|
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user