Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline rolls #206

Open
manlok876 opened this issue Feb 18, 2021 · 2 comments
Open

Inline rolls #206

manlok876 opened this issue Feb 18, 2021 · 2 comments

Comments

@manlok876
Copy link

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) produces Invalid notation; Expected "%", "*", "**", "+", "-", "/", "^", [.], or [0-9] but "d" found. error.
Input (1d6)d6 produces Invalid notation; Expected "%", "*", "**", "+", "-", "/", "^", or end of input but "d" found. error.
etc.
Inputs such as 1d4*((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 of 1d6 results in 4, then the overall result would be equal to rolling 4d6. If a 2 is rolled on 1d6, then overall result will be determined on roll of 2d6. 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 roll
  • 3d(1d6) would be the same as rolling a d6, and then roll 3d1, 3d2, 3d3, 3d4, 3d5 or 3d6, depending on result of first roll
  • Example output format: ((((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:
(((4d6>=4)d6>=3)d6<5)d6: 
4d3: [1, 1, 2, 1] = 5
5d6>=4: [3, 4*, 4*, 1, 6*] = 3
3d6>=3: [4*, 1, 3*] = 2
2d6<5: [2*, 3*] = 2
2d6: [5, 6] = 11

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

  • May be difficult to incorporate into current notation grammar
  • Hard to come up with intuitive output format
  • Some sort of parenthesis will probably have to be required in order to avoid ambiguity of notation
@GreenImp
Copy link
Collaborator

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;

Storing the variables

You could roll each part individually:

// (1d6)d6
const qty = DiceRoll.roll('1d6');
const roll = DiceRoll.roll(`${qty.total}d6`);

Do it inline

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:
Screenshot 2021-04-16 at 10 59 50

Hovering over calculated quantity:
Screenshot 2021-04-16 at 10 55 07

@maliut
Copy link

maliut commented Oct 21, 2022

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
  • but 10d6>3 may result in 0 and 0d6 will cause syntax error

You 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

3 participants