m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--__tests__/dice.test.js45
-rw-r--r--__tests__/lexer.test.js12
-rw-r--r--__tests__/parser.test.js16
-rw-r--r--index.js2
-rw-r--r--src/dice.js14
-rw-r--r--src/lexer.js1
-rw-r--r--src/parser.js2
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({
diff --git a/index.js b/index.js
index 37883cc..cc768cb 100644
--- a/index.js
+++ b/index.js
@@ -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) => {