comparing fomula

Anyone know of a foundry utility to compare two strings that might be dice formulae to tell which has the lesser deterministic value?
23 Replies
Calego
Calego•2y ago
🧵 use case: I have two strings that - might just be numbers ('3' and '4') - might be simple formula ('3 + 3' and '4') - might be nonDeterministic formula ('3 + 1d4' and '4') I want to know: - if both are deterministic formula, what is the Floor of their values - if they aren't deterministic, I'll do nothing with either value oh i can use dnd5e's simplifyRollFormula for this if the output of simplifyRollFormula is parsable as a Number, I can compare them easily
Calego
Calego•2y ago
ok wtf js
No description
Calego
Calego•2y ago
oh, i'm dumb. .floor is for rounding I'm looking for .min this is definitely excessive...
const { abilities, attributes, bonuses, details } = this.actor.system;

let spellAttackModFormula = [
new Intl.NumberFormat('en-US', {
signDisplay: 'exceptZero',
})
.format((abilities[attributes.spellcasting || 'int']?.mod ?? 0) + attributes.prof)
.toString(),
];

// apply the bonuses if they are equivalent
if (bonuses.msak.attack === bonuses.rsak.attack) {
spellAttackModFormula.push(bonuses.msak.attack);
// apply the lesser deterministic bonus
} else if (!!bonuses.msak.attack && !!bonuses.rsak.attack) {
const lesserBonus = Math.min(
dnd5e.dice.simplifyRollFormula(bonuses.msak.attack),
dnd5e.dice.simplifyRollFormula(bonuses.rsak.attack)
);

// if lesserBonus is a number, push it to the spellAttackModFormula
if (!Number.isNaN(lesserBonus)) {
spellAttackModFormula.push(lesserBonus);
}
}
sheetData.spellAttackMod =
spellAttackModFormula.length === 1
? spellAttackModFormula[0]
: dnd5e.dice.simplifyRollFormula(spellAttackModFormula.join(' + '));
const { abilities, attributes, bonuses, details } = this.actor.system;

let spellAttackModFormula = [
new Intl.NumberFormat('en-US', {
signDisplay: 'exceptZero',
})
.format((abilities[attributes.spellcasting || 'int']?.mod ?? 0) + attributes.prof)
.toString(),
];

// apply the bonuses if they are equivalent
if (bonuses.msak.attack === bonuses.rsak.attack) {
spellAttackModFormula.push(bonuses.msak.attack);
// apply the lesser deterministic bonus
} else if (!!bonuses.msak.attack && !!bonuses.rsak.attack) {
const lesserBonus = Math.min(
dnd5e.dice.simplifyRollFormula(bonuses.msak.attack),
dnd5e.dice.simplifyRollFormula(bonuses.rsak.attack)
);

// if lesserBonus is a number, push it to the spellAttackModFormula
if (!Number.isNaN(lesserBonus)) {
spellAttackModFormula.push(lesserBonus);
}
}
sheetData.spellAttackMod =
spellAttackModFormula.length === 1
? spellAttackModFormula[0]
: dnd5e.dice.simplifyRollFormula(spellAttackModFormula.join(' + '));
BUT it does do what I was looking for and covers all the edge cases I can think of Most of the time I know of, if there's a bonus, it'll be adding to both msak and rsak to represent a global increase but the data does support disparate mods, in which case, applying the lesser one is 'safe' to this display of 'spell attack modifier'
Mana
Mana•2y ago
.evaluate({min:true, async:false}) doesn't work for you? Or did I misunderstand what you were after?
Calego
Calego•2y ago
is that a method on Roll?
Mana
Mana•2y ago
Yes.
Calego
Calego•2y ago
min would return the minimum possible based on the formula provided?
Mana
Mana•2y ago
Yes. It sets the dice to minimum value they'd give instead of randomizing them and returns the result.
Calego
Calego•2y ago
gotcha, not quite what i'm looking for I'm really looking to compare two formulas, and use the 'min' one (but only if they're deterministic)
Mana
Mana•2y ago
Roll.isDeterministic is a thing It also exists for individual terms. That is available pre-eval
Calego
Calego•2y ago
so the alternative to using the dnd5e simplifyRollFormula would look something like :
const formulaA = new Roll(bonuses.msak.attack);
const formulaB = new Roll(bonuses.rsak.attack);

if (formulaA.isDeterministic() && formulaB.isDeterministic()) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate(), formulaB.evaluate());
);
}
const formulaA = new Roll(bonuses.msak.attack);
const formulaB = new Roll(bonuses.rsak.attack);

if (formulaA.isDeterministic() && formulaB.isDeterministic()) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate(), formulaB.evaluate());
);
}
Mana
Mana•2y ago
Almost, .isDeterministic is a getter. And .evaluate() returns Roll instance, so you'd need to add .total there. Also eval is async unless you tell it to be non-async.
Calego
Calego•2y ago
that async:false is being deprecated right? I could probably DIY that actually if isDeterministic is true jk not as easy i expected in js
Mana
Mana•2y ago
People keep saying it's not. The requirement for it is, because async is becoming the default. And there's no warning in it that sync option is going away, just that it won't be the default.
Calego
Calego•2y ago
goootcha
Mana
Mana•2y ago
Anyway, you'd have this:
if (formulaA.isDeterministic && formulaB.isDeterministic) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate({async:false}).total, formulaB.evaluate({async:false}.total));
);
}
if (formulaA.isDeterministic && formulaB.isDeterministic) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate({async:false}).total, formulaB.evaluate({async:false}.total));
);
}
Calego
Calego•2y ago
so then the question i have is 'is the overhead of creating two Rolls to compare these strings greater than or less than using simplifyRollFormula?' odds are, less than, simplifyRollFormula is a heavy function now that I look closer
Mana
Mana•2y ago
I'd favor whichever makes the intent of the code clearer.
Calego
Calego•2y ago
// apply the bonuses if they are equivalent
if (bonuses.msak.attack === bonuses.rsak.attack) {
spellAttackModFormula.push(bonuses.msak.attack);
} else if (!!bonuses.msak.attack && !!bonuses.rsak.attack) {
const formulaA = new Roll(bonuses.msak.attack);
const formulaB = new Roll(bonuses.rsak.attack);

// apply the lesser deterministic bonus
if (formulaA.isDeterministic && formulaB.isDeterministic) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate({ async: false }).total, formulaB.evaluate({ async: false }.total))
);
}
}
// apply the bonuses if they are equivalent
if (bonuses.msak.attack === bonuses.rsak.attack) {
spellAttackModFormula.push(bonuses.msak.attack);
} else if (!!bonuses.msak.attack && !!bonuses.rsak.attack) {
const formulaA = new Roll(bonuses.msak.attack);
const formulaB = new Roll(bonuses.rsak.attack);

// apply the lesser deterministic bonus
if (formulaA.isDeterministic && formulaB.isDeterministic) {
spellAttackModFormula.push(
Math.min(formulaA.evaluate({ async: false }).total, formulaB.evaluate({ async: false }.total))
);
}
}
suspect this will never be clear 😛 but this is a solidly simpler solution and doesn't rely on 5e code thanks @manaflower
Leo The League Lion
Leo The League Lion•2y ago
@calego gave vote LeaguePoints™ to @manaflower (#15 • 203)
Calego
Calego•2y ago
🎉
No description
LukeAbby
LukeAbby•2y ago
out of interest why do you only want to figure out which is better if it's deterministic? is it effectively a MVP or do you genuinely never think it will be a non-number I'm mostly asking because if it's always gonna be a number I'd expect to store it as a number but it could be an external constraint.
Calego
Calego•2y ago
External constraint, could be a simple formula like "+1 +1 +2"