morgul / rpgdice Goto Github PK
View Code? Open in Web Editor NEWA generic RPG dice roller syntax and library.
A generic RPG dice roller syntax and library.
Please fill in https://github.com/Morgul/rpgdice/wiki/Syntax-Documentation for reference.
As comparison:
Starting a separate issue to further talk about proposed change 1 in #7.
mrfigg:
1. Add logical support 1. Add `if(conditionExpr, thenExpr[, elseExpr])` function to default scope, internally it would use `if(conditionExpr.value !== 0) { value = thenExpr.value } else { value = /* elseExpr.value or 0 */ }` 2. Add `equal`, `notEqual`, `greaterThan`, `lessThan`, `greaterThanOrEqualTo`, `lessThanOrEqualTo`, `or`, `and`, & `not` operators This can be done in two ways; 1. Via functions in default scope `if(greaterThanOrEqualTo(1d20+5, enemyArmor), 2d8+3)` This is simpler to implement, but not by much, and it is uglier 2. Via operators in peg file `if(1d20+5 >= enemyArmor, 2d8+3)` The `not` operator (`!expr`) probably needs its own expression, but the rest would fit nicely in the `Operation` expression I would pick option b, but both are reasonable.
Morgul:
1. My first thought was, "If I've implemented a Turing complete Domain Specific Language for my dice roller, I'm doing something wrong." Now, that's not _entirely_ true; but it does feel like you're trying to put game specific logic into a DSL, when more than likely it should be implemented in side a 'real' language, like Javascript. So as far as `if`, `else`, `not`, `and`, `or` go, I'm rather against it. _However_, all of that being said, I'm open to the idea of expanding the parser to support user extensions for these use cases. Here's a couple of ideas: 1. **Compromise:** Support the `?` operator. 1. Yes, I know what I said above, but I can definitely see some places where something like `attackBonus > 5 ? 2d8 + 1 : 2d6 + 1` could be really handy. However, it would have the following caveats: it would use the short form javascript ternary, to discourage chained usage, and only `<, >, =` (and combinations) would be implemented, probably as their own `BooleanOperation` class. There would be _no_ `and`, `or`, `is`, or `not` implemented as operators. _(More on that below.)_ 2. We _might_ need to implement `!`, and combinations like `!=`, depending on how awkward it is to use `not` with one of the other proposals below. We would **not** implement `===` or `!==`, instead `==` would be equivalent to `===` and `!=` would be equivalent to `!==`. (I do not want javascript's weird implicit casting part of this.). 2. **Implicit / Operator functions:** We could make the following valid: `not 3d6` where `not` is a function on the scope that takes one argument, basically `not 3d6 === not(3d6)`. _I have not thought through the parser implications of this!_ It would get us much closer to being able to have user defined operator functions like: `1d10 > 5 and 3d6 < 5`. In order to do that, however, we have to make a few definitions: 1. An "implicit" function (I'd rather call them 'operator functions') could only be of arity one, or arity two. 2. If an operator function is of arity one, its signature would be: `op(right)`. 3. If an operator function is of arity two, its signature would be: `op(right, left)`. 4. If `Variable` evaluates to a function, it will be called with `fun(right, left)`, where `left` could be undefined. 5. I'm honestly not sure that the complexity of this is worth it. Even as I was typing this out I became concerned. 3. **Pipelines:** We could implement the minimal pipeline operator instead of the implicit operators. It would mean the following would be valid: `attackBonus |> greaterThan(3) ? 3d8 : 2d6`, where `greaterThan` was defined as `greaterThan(n) { return (m) => n > m; }`. This would also likely mean we'd need to ensure we support currying: `add(2)(3) === add: n => m => n + m`. I would also be open to a `lodash.fp.flow` way of doing custom operations: `flow(double(3(5d6 + 1)), negate, add(30))`. That's probably overkill, but as long as we support currying, a user could implement that. 4. **RPGDice Extras:** If we have all the base required support in the main lib, I'd be open to adding some of the more controversial features into a secondary library. It would, basically, be an expanded default scope, with all the function versions of the features you were suggesting. That way, if someone really wants those, they can have a nice, supported version, but for people who just want to roll some dice (my main use case), we keep the core lib nice and small.
Parentheses are not preserved in the parse tree, which limits the ability to create a proper breakdown of the dice roll and results in Expression.render()
returning an incorrect value. (For anyone reading this in the future, the broken math in this issue is being addressed in #4.)
Expression.render()
output: 3 - { 1d20: [ 8 ] } + 5 + 2
{
"type": "subtract",
"left": {
"type": "number",
"value": 3
},
"right": {
"type": "add",
"left": {
"type": "add",
"left": {
"type": "roll",
"count": 1,
"sides": 20,
"results": [
8
],
"value": 8
},
"right": {
"type": "number",
"value": 5
},
"value": 13
},
"right": {
"type": "number",
"value": 2
},
"value": 15
},
"value": -12
}
Maybe add a new parentheses
Expression
type?
{
"type": "parentheses",
"content": {},
"value": 1234
}
Let's assume I have the following scope:
{
Weapon: {
Damage: '1d6'
}
}
I cannot do the following: 'Weapon.Damage' + 1
; Weapon.Damage
evaluates to a string, and I get '1d61'
out.
I ran a couple of basic order of operations tests with version 2.0.0-rc.2
and got some illogical results.
Input: 2+8-2+8
Expected output: 16
Generated output: 0
Input: 1-(1+1)+1
Expected output: 0
Generated output: -2
Both tests seem to be going outside-in instead of left-to-right.
So I noticed what seems to be an exponential performance drop while doing another stress test. I remade my fork to illustrate the issue here and made a few optimization tweaks to the side-associative helper functions to (unsuccessfully) try to help alleviate the issue.
When you get to around 6 nested parentheses, ((((((6))))))
, it takes about 830ms to parse the expression, at least on my machine. Adding one more for 7 nested parentheses bumps that all the way up to about 5000ms.
When testing in 2.0.0-rc.2
the same performance drop is there, it just takes around 8 nested parentheses before getting to be about 800ms. 9 nested parentheses gets up to around 4100ms.
I don't know how severe you consider this issue to be right now, but it seems like it will only get worse once/if we proceed with adding conditionals, as that would probably add another 5 or 6 peg rules.
I could be too inexperienced with PEG.js to be coming to this conclusion given that I only became aware of it two weeks ago, but this seems to be an issue on their end as your peg file seems to match the format of the examples they give. I'm really not sure what we can do about this one.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.