From 606a177f0d0fd03fb8ac8899e67c0d4083b93ba6 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Sun, 9 Jul 2017 16:32:02 -0400 Subject: Implement basic dice --- __tests__/dice.test.js | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dice.js | 18 ++++++++ 2 files changed, 137 insertions(+) create mode 100644 __tests__/dice.test.js create mode 100644 src/dice.js diff --git a/__tests__/dice.test.js b/__tests__/dice.test.js new file mode 100644 index 0000000..9df156f --- /dev/null +++ b/__tests__/dice.test.js @@ -0,0 +1,119 @@ +const { d } = require('../src/dice.js') + +const defaultNumberRolls = 100 + +expect.extend({ + toBeOnAverage(received, expected, margin = 0.3) { + const average = received.reduce(plus) / received.length + const pass = average > expected - margin && average < expected + margin + + return { + pass, + message: () => ( + `expected average to${pass ? ' not ' : ' '}be around ${expected} (margin: ${margin}), got ${average}` + ) + } + }, + toBeBetween(received, low, high) { + const pass = received.reduce((allOk, value) => { + return allOk && value >= low && value <= high + }, true) + + return { + pass, + message: () => ( + `expected all to${pass ? ' not ' : ' '}be between ${low} and ${high}` + ) + } + }, + toAllBe(received, expected) { + const pass = received.reduce((allOk, value) => { + return allOk && value === expected + }) + + return { + pass, + message: () => ( + `expected all to${pass ? ' not ' : ' '}be equal to ${expected}` + ) + } + } +}) + +const plus = (a, b) => (a + b) + +const rollForTest = (die, numberRolls) => { + let pools = [] + let rolls = [] + + for (let i = 0; i < numberRolls; i++) { + let rolled = die() + pools.push(rolled) + rolls.push(rolled.reduce(plus)) + } + + return { pools, rolls } +} + +const testDie = (die, numberRolls, testSpecs) => { + let { pools, rolls } = rollForTest(die, numberRolls) + + if ('diceCount' in testSpecs) { + it(`rolls ${testSpecs.diceCount} dice`, () => { + expect(pools.map((pool) => (pool.length))).toAllBe(testSpecs.diceCount) + }) + } + + if ('average' in testSpecs) { + let { average, margin } = testSpecs.average + + it(`has an expected value of ${average}`, () => { + expect(rolls).toBeOnAverage(average, margin) + }) + } + + if ('bounds' in testSpecs) { + let { low, high } = testSpecs.bounds + + it(`rolls between ${low} and ${high}`, () => { + expect(rolls).toBeBetween(low, high) + }) + + it('attains its minimum', () => { + expect(rolls).toContain(low) + }) + + it('attains its maximum', () => { + expect(rolls).toContain(high) + }) + } +} + +const describeBasicDie = (number, sides, numberRolls = defaultNumberRolls) => { + describe(`${number}d${sides}`, () => { + const die = d(number, sides) + const min = number + const max = number * sides + + const testSpecs = { + diceCount: number, + average: { + average: (min + max) / 2, + }, + bounds: { + low: min, + high: max, + expectLow: true, + expectHigh: true + } + } + + testDie(die, numberRolls, testSpecs) + }) +} + +describe('basic dice', () => { + describeBasicDie(1, 6) + describeBasicDie(2, 8) + describeBasicDie(20, 1) +}) diff --git a/src/dice.js b/src/dice.js new file mode 100644 index 0000000..2914317 --- /dev/null +++ b/src/dice.js @@ -0,0 +1,18 @@ +const d = (number, sides) => { + return () => { + let pool = [] + + for (let i = 0; i < number; i++) { + pool.push(1 + Math.floor(Math.random() * sides)) + } + + return pool + } +} + +const roll = (die) => { + return die().reduce((a, b) => (a + b)) +} + +exports.d = d +exports.roll = roll -- cgit v1.2.3