Closed ArtemGr closed 8 years ago
So I've used Scrivener to brainstorm this. Yikes!
Here's what Scrivener compiled from my notes:
The funny thing about this task is that we don't need the parser. We already has the "> X" parser in place.
What we need is for the "> X" dice to 1) Change the logic (the modifiers have to be applied to every dice roll and not to the sum of them). And for that - we do have to extend the parser! 2) Change the output - to print the number of successes instead of (or in addition to) the sum of the faces.
So "/r 3d6+2>1" should return the number of successes by rolling d6 three times and applying the "+2" modifier to each roll.
Now how do we do that?
In order to be able to parse and handle the modifiers 1) We need the modifiers to be recognized by the parser. This will magically make the parser eat the modifiers instead of leaving them to the outer evaluator. 2) We need the modifiers to be parsed in a way that lets us tackle them when we do the rolls. They should be a part of dice specification maybe. 3) And finally, we need this to happen only if there's a ">1" condition at the end of the line! E.g. "3d6+2" should emit the usual "(d6+d6+d6)+2" whereas "3d6+2>1" should emit "(modified_d6+modified_d6+modified_d6)". 4) Wait, the last one wasn't a final condition even. This one is. Thing is, we should be able to do math with the dice! And it's only logical if we could do the math with the number of successes as well! So, "3d6>1 * 3" should multiply the number of successes by three, for example! At the same time, it would be nice to print the number of successes with the "successes" suffix. Means we should remove this suffix before the outer evaluation.
Make the parser eat the modifiers.
It should chew on "3d6+2".
By default the dice evaluator should spew the modifiers unmodified.
E.g. "3d6+2" should evaluate to "(d6+d6+d6)+2".
When we roll, we should look ahead and see if there is a success number condition.
E.g. check if the first Dice::ops is a TargetNumber.
I think we might be conservative here and check only the first Dice::ops. For example, "3d6+2>5" is evaluated as (modified_d6+modified_d6+modified_d6)>5, but "3d6+2 d1 >5"... huh. I don't know how it's evaluated.
Well, shucks, I've checked roll20 and they just ignore the drop in this case. E.g. "/roll 3d6 +2 dl 1" they evaluate to (d6+d6+d6)+2.
I beg to differ there. If we want to drop some rolls then we might as well take the modifiers into account.
The question is... how do we unit test this? Because unit testing all those cases seems more and more important.
One way to unit test the dice is to implement a mock roller. E.g. tell the roller what it should roll instead of a truly random number.
And another nifty thing to have would be unit testing through Discord. E.g. "/r test" should do the unit tests and print the results. Capish?
So... when we roll, we should check ahead and see whether we have any ops on the dice. Because if we have then it doesn't make sense to leave the modifier to the outer evaluator. "3d6 +modifier $any_op" should apply the modifier inline. "3d6 +modifier" should leave the modifier alone (spew it back for the outer evaluator to chew on). Something like that.
As a developer or even a curious user I should be able to run the unit tests right from the discord UI with, say, "/r test", to save the hassle of going into a different terminal UI just to run the tests.
Alternatively, I might see the tests being run right after a fresh build, in the editor. This seems even better.
"/r 3d6 +2 dl 1" should work as follows: roll d6 and modify, thrice, apply drop lowest. E.g. the modifiers should be handled right away.
On the other hand, "/r 3d6 +2" should work as before: roll d6 thrice and then modify.
We should test that "/r 3d6+2>5" evaluates to the proper number of successes.
We should test that we can do math on the number of successes. "3d6>5+2" (remember: +2 is not parsed into the dice specification here).
"3d6>5" should print the number of successes.
In order to do this I think it's enough to recognize in the printer that the last op is the TargetNumber op.
If it is, then instead of (d+d+d) we print (x successes).
Should replace "(x successes)" with "(x)" before handling it to the outer evaluator.
There should be a modifier that makes the dice bot return the number of passed rolls instead of the sum of them.
https://www.reddit.com/r/discordapp/comments/4vfbqi/looking_for_a_dice_bot/d780297
=> Note that the number of successes might be negative in some cases. E.g. an oWoD botch.