diff options
-rw-r--r-- | __tests__/dice.test.js | 45 | ||||
-rw-r--r-- | __tests__/lexer.test.js | 12 | ||||
-rw-r--r-- | __tests__/parser.test.js | 16 | ||||
-rw-r--r-- | index.js | 2 | ||||
-rw-r--r-- | src/dice.js | 14 | ||||
-rw-r--r-- | src/lexer.js | 1 | ||||
-rw-r--r-- | src/parser.js | 2 |
7 files changed, 91 insertions, 1 deletions
diff --git a/__tests__/dice.test.js b/__tests__/dice.test.js index d397d4c..6a9ba46 100644 --- a/__tests__/dice.test.js +++ b/__tests__/dice.test.js @@ -16,7 +16,8 @@ const { againAbove, againUnder, thresholdHigh, - thresholdLow + thresholdLow, + repeat } = require('../src/dice.js') const defaultNumberRolls = 500 @@ -692,3 +693,45 @@ describe('threshold', () => { }) }) }) + +describe('repeat', () => { + describe('1d6 x 2', () => { + const die = repeat(d(constant(1), constant(6)), constant(2)) + + testDie(die, { + diceCount: 2, + average: { + average: 7 + }, + variance: { + variance: 5.83 + }, + bounds: { + low: 2, + high: 12, + expectLow: true, + expectHigh: true + } + }) + }) + + describe('1d6 x 1d4', () => { + const die = repeat(d(constant(1), constant(6)), d(constant(1), constant(4))) + + testDie(die, { + variableDiceCount: { + min: 1, + max: 4 + }, + average: { + average: 8.75 + }, + bounds: { + low: 1, + high: 24, + expectLow: true, + expectHigh: false + } + }) + }) +}) diff --git a/__tests__/lexer.test.js b/__tests__/lexer.test.js index 0c615d5..6745715 100644 --- a/__tests__/lexer.test.js +++ b/__tests__/lexer.test.js @@ -284,4 +284,16 @@ describe('lex', () => { ]) }) }) + + describe('repeat', () => { + test('1d6 x 3', () => { + expect(lex('1d6 x 3')).toEqual([ + { type: 'constant', value: 1 }, + { type: 'd' }, + { type: 'constant', value: 6 }, + { type: ' x ' }, + { type: 'constant', value: 3 } + ]) + }) + }) }) diff --git a/__tests__/parser.test.js b/__tests__/parser.test.js index bef11c5..d696b3e 100644 --- a/__tests__/parser.test.js +++ b/__tests__/parser.test.js @@ -285,6 +285,22 @@ describe('parse', () => { }) }) + it('parses dice with repeat', () => { + expect(parse('1d6 x 1d4')).toEqual({ + type: 'repeat', + left: { + type: 'd', + left: { type: 'constant', value: 1 }, + right: { type: 'constant', value: 6 } + }, + right: { + type: 'd', + left: { type: 'constant', value: 1 }, + right: { type: 'constant', value: 4 } + } + }) + }) + describe('parsing parentheses', () => { test('(1d6)d6', () => { expect(parse('(1d6)d6')).toEqual({ @@ -37,6 +37,8 @@ const interpret = tree => { return D.bonusAdd(interpret(tree.left), interpret(tree.right)) case 'bonusSubtract': return D.bonusSubtract(interpret(tree.left), interpret(tree.right)) + case 'repeat': + return D.repeat(interpret(tree.left), interpret(tree.right)) } } diff --git a/src/dice.js b/src/dice.js index d47455e..5a484b0 100644 --- a/src/dice.js +++ b/src/dice.js @@ -193,6 +193,19 @@ const thresholdLow = (die1, die2) => { return threshold(lessThanOrEqual, die1, die2) } +const repeat = (die1, die2) => { + return () => { + const times = roll(die2) + + let results = [] + for (let i = 0; i < times; i++) { + results = results.concat(die1()) + } + + return results + } +} + exports.pool = pool exports.roll = roll exports.constant = constant @@ -212,3 +225,4 @@ exports.againAbove = againAbove exports.againUnder = againUnder exports.thresholdHigh = thresholdHigh exports.thresholdLow = thresholdLow +exports.repeat = repeat diff --git a/src/lexer.js b/src/lexer.js index 59fba16..248a9b2 100644 --- a/src/lexer.js +++ b/src/lexer.js @@ -32,6 +32,7 @@ newLexemeType('A', 'A') newLexemeType('a', 'a') newLexemeType('T', 'T') newLexemeType('t', 't') +newLexemeType(' x ', ' x ') const lex = (expressionString) => { let lexemes = [] diff --git a/src/parser.js b/src/parser.js index fd0fd59..b1f6ebb 100644 --- a/src/parser.js +++ b/src/parser.js @@ -82,6 +82,8 @@ newSymbol('minus', (parser) => { } }) +newInfix(' x ', 24, { type: 'repeat' }) + newSymbol('end', null, -1) const newParser = (tokens) => { |