Open manlok876 opened 3 years ago
This is a brilliant idea, and your examples are really clear, thank you. This is something that I did try to implement a while ago, when I rewrote the whole parsing system. Unfortunately, I hit some issues that just made it very difficult to do, and so I parked the idea.
It wasn't parsing the notation that was difficult, that bit is actually quite simple, but the parsing them into die objects. Die objects need to be provided basic numbers for the quantity and sides. Die objects can't parse equations and dice rolls, and it proved incredibly difficult to make them able to do so.
You can do things like this:
(3*4)d6
4d(2+6)
(sqrt(4*2))d10
And the parser runs calculates the result to a raw number, before creating the Die object. I would like to see if I can get it working, as a built in function, but it will be a long way down the line.
There are a few ways around this though;
You could roll each part individually:
// (1d6)d6
const qty = DiceRoll.roll('1d6');
const roll = DiceRoll.roll(`${qty.total}d6`);
You could modify the above example, to do it all inline:
// (1d6)d6
const roll = DiceRoll.roll(`${DiceRoll.roll('1d6').total}d6`);
I know neither of these are a perfect solution to what you're trying to achieve, but perhaps they're good enough for the time being?
For reference, I've just checked how Roll20 outputs a roll like this: Results:
Hovering over calculated quantity:
I think you could achieve it by RegExp string replacement:
const regex = /\[([^[\]]+)\]/
function parseExpression(expression, roller) {
if (regex.test(expression)) {
expression = expression.replace(regex, (_, notation) => {
const singleRoll = roller.roll(notation.trim())
return String(singleRoll.total)
})
return parseExpression(expression, roller)
} else {
return expression
}
}
const diceRoller = new DiceRoller()
const expression = /* your expression */
const finalExpression = parseExpression(expression, diceRoller)
console.log('firstly:', diceRoller.output)
console.log('finally:', new DiceRoll(finalExpression).output)
examples:
expression: [1d6]d6
firstly: 1d6: [4] = 4
finally: 4d6: [4, 5, 1, 1] = 11
expression: d[d[d10]]
firstly: d10: [4] = 4; d4: [1] = 1
finally: d1: [1] = 1
expression: [10d6>3]d6
firstly: 10d6>3: [1, 6*, 5*, 6*, 1, 5*, 2, 6*, 1, 5*] = 6
finally: 6d6: [2, 4, 3, 1, 5, 2] = 17
10d6>3
may result in 0 and 0d6
will cause syntax errorYou can also introduce variables to use the results of previous rolls, or any other datasource.
function parseExpression(expression, roller) {
if (regex.test(expression)) {
expression = expression.replace(regex, (_, notation) => {
notation = notation.replace(/\$(\d+)/, (_, variable) => {
return String(roller.log[Number(variable) - 1].total) // $1 will refer to roller.log[0]
})
const singleRoll = roller.roll(notation.trim())
return String(singleRoll.total)
})
return parseExpression(expression, roller)
} else {
return expression
}
}
and then:
expression: [d10]+[$1]
firstly: d10: [3] = 3; 3: 3 = 3
finally: 3+3: 3+3 = 6
A complex example: how to roll [10d6>3]d6
safely
run: sign([10d6>3])*[max(1,$1)]d6
if 10d6>3
result in 0, it becomes sign(0)*1d6 = 0
if 10d6>3
result in 5(for example), it becomes sign(5)*5d6
which is equivalent to 5d6
Feature request Provide the ability to treat roll results as dice count or number of dice sides. A similar feature is implemented in Roll20: https://wiki.roll20.net/Dice_Reference#Inline_Dice_Rolls
Current behaviour Input
4d(1d6)
producesInvalid notation; Expected "%", "*", "**", "+", "-", "/", "^", [.], or [0-9] but "d" found.
error. Input(1d6)d6
producesInvalid notation; Expected "%", "*", "**", "+", "-", "/", "^", or end of input but "d" found.
error. etc. Inputs such as1d4*((3d10)*(4d6/2))
produce desired results.Desired behaviour I would like to treat dice roll results in a way similar to equations, so they would be computed and used in notation as literal numbers. Examples:
(1d6)d6
would be the same as rolling a d6, then using the result as number of d6 to roll on the next step. E.g. inline roll of1d6
results in 4, then the overall result would be equal to rolling4d6
. If a 2 is rolled on1d6
, then overall result will be determined on roll of2d6
. And so on(10d6>3)d6
would be the same as rolling a 10d6, then roll d6 equal to the amount of successes (> 3) in the first roll3d(1d6)
would be the same as rolling a d6, and then roll3d1
,3d2
,3d3
,3d4
,3d5
or3d6
, depending on result of first roll((((4d3)d6>=4)d6>=3)d6<5)d6: ( ( ( ( ([1, 1, 2, 1] = 5) -> [3, 4*, 4*, 1, 6*] = 3) -> [4*, 1, 3*] = 2 ) -> [2*, 3*] = 2 ) -> [5, 6] = 11
or a multi-lined, step-by-step format:Benefits I play Warhammer 40,000. A common case of dice roll for Warhammer players (and probably in other wargames too) is to roll attacks in several steps ("To Hit" -> "To Wound" -> "Attempt saves against attacks" -> "Calculate damage" -> "Atttempt to ignore damage", etc.), using number of successes sum of rolled dice as dice pool for new rolls. A description of the rules can be found here. Obviously, just saving simple dice rolls such as
24d6
does not help much, as number of dice rolled on each step is randomized. Saving the whole chain, like(((24d6>=4)d6>=3)d6<3
, or at least typing it in a single roll instead of rolling each step separately, is something I have been looking for in a dice rolling app for a long time.Concerns