diff --git a/src/D.ts b/src/D.ts index 9df9820f..b60ea3e7 100644 --- a/src/D.ts +++ b/src/D.ts @@ -26,6 +26,10 @@ class D { } roll(): Result> { + return this._rawRollResult() + } + + protected _rawRollResult(): Result> { return this.faces[this._rawRoll()] as Result> } diff --git a/src/index.ts b/src/index.ts index c881751f..edf57f2e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ export * from './D' export { roll } from './roll' -export { validateDiceNotation } from './validateDiceNotation' -export { parameterizeRollArgument } from './parameterizeRollArgument' +export * from './models' export * from './types' diff --git a/src/models/ArgumentsModel/index.ts b/src/models/ArgumentsModel/index.ts new file mode 100644 index 00000000..947d9719 --- /dev/null +++ b/src/models/ArgumentsModel/index.ts @@ -0,0 +1,96 @@ +import { + RandsumRollArgument, + DicePools, + RandsumRollOptions, + RandsumRollParameters +} from '~types' +import { uuidv7 as uuid } from 'uuidv7' +import { + isD, + isCustomSidesD, + isDicePoolOptions, + isDiceNotationArg +} from '~guards' +import { NotationModel, OptionsModel } from '~models' +import { D } from '~src/D' + +function formDicePools(args: RandsumRollArgument[]): DicePools { + return { + dicePools: args.reduce( + (acc, arg) => ({ ...acc, [uuid()]: parameterize(arg) }), + {} + ) + } +} + +function normalizeArgument(argument: RandsumRollArgument): RandsumRollOptions { + if (isD(argument)) { + return { + quantity: 1, + sides: isCustomSidesD(argument) ? argument.faces : argument.sides + } + } + + if (isDicePoolOptions(argument)) { + return argument + } + + if (isDiceNotationArg(argument)) { + return NotationModel.toOptions(argument) + } + + if (Array.isArray(argument)) { + return { + quantity: 1, + sides: argument.map(String) + } + } + + return { + quantity: 1, + sides: Number(argument || 20) + } +} + +function parameterize(argument: RandsumRollArgument): RandsumRollParameters { + const options = normalizeArgument(argument) + const die = new D(options.sides) + return { + options, + argument, + die, + notation: OptionsModel.toNotation(options), + description: OptionsModel.toDescription(options) + } +} + +function normalize(argument: RandsumRollArgument): RandsumRollOptions { + if (isD(argument)) { + return { + quantity: 1, + sides: isCustomSidesD(argument) ? argument.faces : argument.sides + } + } + + if (isDicePoolOptions(argument)) { + return argument + } + + if (isDiceNotationArg(argument)) { + return NotationModel.toOptions(argument) + } + + if (Array.isArray(argument)) { + return { + quantity: 1, + sides: argument.map(String) + } + } + + return { + quantity: 1, + sides: Number(argument || 20) + } +} + +export default { formDicePools, normalize, parameterize } diff --git a/src/roll/generateRollResult/index.ts b/src/models/DicePoolsModel/index.ts similarity index 75% rename from src/roll/generateRollResult/index.ts rename to src/models/DicePoolsModel/index.ts index e644cfa4..a5b02331 100644 --- a/src/roll/generateRollResult/index.ts +++ b/src/models/DicePoolsModel/index.ts @@ -1,12 +1,6 @@ -import { - RandsumRollParameters, - DicePoolType, - DicePools, - RandsumRollResult -} from '~types' -import { applyModifiers } from './applyModifiers' -import { generateRawRolls } from './generateRawRolls' import { isFullNumArray } from '~guards' +import { ParametersModel, RawRollsModel } from '~models' +import { DicePools, DicePoolType, RandsumRollResult } from '~types' function calculateType(dicePools: DicePools['dicePools']): DicePoolType { switch (true) { @@ -32,17 +26,15 @@ function calculateTotal(rolls: number[] | string[], bonus = 0): number { return 0 } -function generateModifiedRolls( +export function generateModifiedRolls( DicePools: DicePools, rawRolls: RandsumRollResult['rawRolls'] ): RandsumRollResult['modifiedRolls'] { return Object.fromEntries( Object.keys(DicePools.dicePools).map((key) => { - const pool = DicePools.dicePools[key] as - | RandsumRollParameters - | RandsumRollParameters + const params = DicePools.dicePools[key] const rolls = rawRolls[key] - const modified = applyModifiers(pool, rolls) + const modified = ParametersModel.applyModifiers(params, rolls) const modifiedRoll = { rolls: modified.rolls, total: calculateTotal(modified.rolls, modified.simpleMathModifier) @@ -53,7 +45,7 @@ function generateModifiedRolls( } function generateRollResult(DicePools: DicePools): RandsumRollResult { - const rawRolls = generateRawRolls(DicePools.dicePools) + const rawRolls = RawRollsModel.generate(DicePools.dicePools) const modifiedRolls = generateModifiedRolls(DicePools, rawRolls) const modifiedValues = Object.values(modifiedRolls) const rawResult = Object.values(rawRolls) @@ -71,5 +63,6 @@ function generateRollResult(DicePools: DicePools): RandsumRollResult { total } } - -export { generateRollResult } +export default { + generateRollResult +} diff --git a/src/models/NotationModel/index.ts b/src/models/NotationModel/index.ts index 2e887aa6..ea149fad 100644 --- a/src/models/NotationModel/index.ts +++ b/src/models/NotationModel/index.ts @@ -1,6 +1,13 @@ import { coreNotationPattern } from '~patterns' -import { RandsumNotation, RandsumRollOptions } from '~types' +import { + DicePoolType, + RandsumNotation, + RandsumNotationValidationResult, + RandsumRollOptions +} from '~types' import { parseCoreNotation, parseModifiers } from './optionsParsers' +import { isDiceNotationArg, isCustomSidesArg } from '~guards' +import { OptionsModel } from '~models' function toOptions( notationString: RandsumNotation @@ -14,4 +21,25 @@ function toOptions( } } -export default { toOptions } +function validate(notation: string): RandsumNotationValidationResult { + if (!isDiceNotationArg(notation)) { + return { + valid: false, + description: [] + } + } + + const digested = toOptions(notation) + + return { + valid: true, + digested, + notation: OptionsModel.toNotation(digested), + type: isCustomSidesArg(digested.sides) + ? DicePoolType.custom + : DicePoolType.numerical, + description: OptionsModel.toDescription(digested) + } +} + +export default { toOptions, validate } diff --git a/src/models/ParametersModel/index.ts b/src/models/ParametersModel/index.ts new file mode 100644 index 00000000..b81d81b5 --- /dev/null +++ b/src/models/ParametersModel/index.ts @@ -0,0 +1,117 @@ +import { isCustomParameters } from '~guards' +import { RandsumRollParameters } from '~types' +import { + applyReroll, + applyUnique, + applyReplace, + applyDrop, + applyExplode, + applySingleCap +} from './modifierApplicators' + +type RollBonuses = { + rolls: number[] + simpleMathModifier: number +} + +type ModifiedRollBonuses = { + rolls: string[] + simpleMathModifier: 0 +} +function applyModifiers( + poolParameters: RandsumRollParameters | RandsumRollParameters, + initialRolls: number[] | string[] +): RollBonuses | ModifiedRollBonuses { + if (isCustomParameters(poolParameters)) { + return { + simpleMathModifier: 0, + rolls: initialRolls as string[] + } + } + + const rollBonuses: RollBonuses = { + simpleMathModifier: 0, + rolls: initialRolls as number[] + } + + const { + options: { sides, quantity, modifiers = {} } + } = poolParameters + + const rollOne: () => number = () => poolParameters.die.roll() + + return Object.keys(modifiers).reduce((bonuses, key) => { + switch (key) { + case 'reroll': + return { + ...bonuses, + rolls: modifiers.reroll + ? applyReroll(bonuses.rolls, modifiers.reroll, rollOne) + : bonuses.rolls + } + + case 'unique': + return { + ...bonuses, + rolls: modifiers.unique + ? applyUnique( + bonuses.rolls, + { sides, quantity: quantity || 1, unique: modifiers.unique }, + rollOne + ) + : bonuses.rolls + } + + case 'replace': + return { + ...bonuses, + rolls: modifiers.replace + ? applyReplace(bonuses.rolls, modifiers.replace) + : bonuses.rolls + } + + case 'cap': + return { + ...bonuses, + rolls: modifiers.cap + ? bonuses.rolls.map(applySingleCap(modifiers.cap)) + : bonuses.rolls + } + + case 'drop': + return { + ...bonuses, + rolls: modifiers.drop + ? applyDrop(bonuses.rolls, modifiers.drop) + : bonuses.rolls + } + + case 'explode': + return { + ...bonuses, + rolls: modifiers.explode + ? applyExplode(bonuses.rolls, { sides }, rollOne) + : bonuses.rolls + } + + case 'plus': + return { + ...bonuses, + simpleMathModifier: + bonuses.simpleMathModifier + Number(modifiers.plus) + } + + case 'minus': + return { + ...bonuses, + simpleMathModifier: + bonuses.simpleMathModifier - Number(modifiers.minus) + } + + default: + throw new Error(`Unknown modifier: ${key}`) + } + }, rollBonuses) +} + +export default { applyModifiers } diff --git a/src/roll/generateRollResult/applyModifiers.ts b/src/models/ParametersModel/modifierApplicators.ts similarity index 58% rename from src/roll/generateRollResult/applyModifiers.ts rename to src/models/ParametersModel/modifierApplicators.ts index 804af70b..7d8e39d7 100644 --- a/src/roll/generateRollResult/applyModifiers.ts +++ b/src/models/ParametersModel/modifierApplicators.ts @@ -1,7 +1,5 @@ -import { isCustomParameters } from '~guards' import { RequiredCoreDiceParameters, - RandsumRollParameters, DropOptions, GreaterLessOptions, Modifiers, @@ -9,15 +7,6 @@ import { RerollOptions } from '~types' -type RollBonuses = { - rolls: number[] - simpleMathModifier: number -} - -type ModifiedRollBonuses = { - rolls: string[] - simpleMathModifier: 0 -} export class InvalidUniqueError extends Error { constructor() { super( @@ -26,7 +15,7 @@ export class InvalidUniqueError extends Error { } } -function applyUnique( +export function applyUnique( rolls: number[], { unique, @@ -58,7 +47,7 @@ function applyUnique( }) } -function applySingleCap( +export function applySingleCap( { greaterThan, lessThan }: GreaterLessOptions, value?: number ) { @@ -105,7 +94,7 @@ function rerollRoll( return roll } -function applyReroll( +export function applyReroll( rolls: number[], reroll: RerollOptions, rollOne: () => number @@ -114,7 +103,7 @@ function applyReroll( return newRolls.map((roll) => rerollRoll(roll, reroll, rollOne)) } -function applyReplace( +export function applyReplace( rolls: number[], replace: ReplaceOptions | ReplaceOptions[] ): number[] { @@ -139,7 +128,7 @@ function applyReplace( return replaceRolls } -function applyExplode( +export function applyExplode( rolls: number[], { sides }: Pick, 'sides'>, rollOne: () => number @@ -158,7 +147,7 @@ function times(iterator: number) { } } -function applyDrop( +export function applyDrop( rolls: number[], { highest, lowest, greaterThan, lessThan, exact }: DropOptions ): number[] { @@ -183,101 +172,3 @@ function applyDrop( return sortedResults } - -function applyModifiers( - poolParameters: RandsumRollParameters | RandsumRollParameters, - initialRolls: number[] | string[] -): RollBonuses | ModifiedRollBonuses { - if (isCustomParameters(poolParameters)) { - return { - simpleMathModifier: 0, - rolls: initialRolls as string[] - } - } - - const rollBonuses: RollBonuses = { - simpleMathModifier: 0, - rolls: initialRolls as number[] - } - - const { - options: { sides, quantity, modifiers = {} } - } = poolParameters - - const rollOne: () => number = () => poolParameters.die.roll() - - return Object.keys(modifiers).reduce((bonuses, key) => { - switch (key) { - case 'reroll': - return { - ...bonuses, - rolls: modifiers.reroll - ? applyReroll(bonuses.rolls, modifiers.reroll, rollOne) - : bonuses.rolls - } - - case 'unique': - return { - ...bonuses, - rolls: modifiers.unique - ? applyUnique( - bonuses.rolls, - { sides, quantity: quantity || 1, unique: modifiers.unique }, - rollOne - ) - : bonuses.rolls - } - - case 'replace': - return { - ...bonuses, - rolls: modifiers.replace - ? applyReplace(bonuses.rolls, modifiers.replace) - : bonuses.rolls - } - - case 'cap': - return { - ...bonuses, - rolls: modifiers.cap - ? bonuses.rolls.map(applySingleCap(modifiers.cap)) - : bonuses.rolls - } - - case 'drop': - return { - ...bonuses, - rolls: modifiers.drop - ? applyDrop(bonuses.rolls, modifiers.drop) - : bonuses.rolls - } - - case 'explode': - return { - ...bonuses, - rolls: modifiers.explode - ? applyExplode(bonuses.rolls, { sides }, rollOne) - : bonuses.rolls - } - - case 'plus': - return { - ...bonuses, - simpleMathModifier: - bonuses.simpleMathModifier + Number(modifiers.plus) - } - - case 'minus': - return { - ...bonuses, - simpleMathModifier: - bonuses.simpleMathModifier - Number(modifiers.minus) - } - - default: - throw new Error(`Unknown modifier: ${key}`) - } - }, rollBonuses) -} - -export { applyModifiers } diff --git a/src/roll/generateRollResult/generateRawRolls.ts b/src/models/RawRollsModel.ts similarity index 89% rename from src/roll/generateRollResult/generateRawRolls.ts rename to src/models/RawRollsModel.ts index cfaec8e5..50a9141e 100644 --- a/src/roll/generateRollResult/generateRawRolls.ts +++ b/src/models/RawRollsModel.ts @@ -1,6 +1,6 @@ import { DicePools, RandsumRollResult } from '~types' -function generateRawRolls( +function generate( dicePools: DicePools['dicePools'] ): RandsumRollResult['rawRolls'] { return Object.fromEntries( @@ -20,4 +20,4 @@ function generateRawRolls( ) } -export { generateRawRolls } +export default { generate } diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 00000000..8f2e5125 --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,6 @@ +export { default as NotationModel } from './NotationModel' +export { default as OptionsModel } from './OptionsModel' +export { default as DicePoolsModel } from './DicePoolsModel' +export { default as ArgumentsModel } from './ArgumentsModel' +export { default as RawRollsModel } from './RawRollsModel' +export { default as ParametersModel } from './ParametersModel' diff --git a/src/parameterizeRollArgument/index.ts b/src/parameterizeRollArgument/index.ts deleted file mode 100644 index d125d97b..00000000 --- a/src/parameterizeRollArgument/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RandsumRollArgument, RandsumRollParameters } from '~types' -import { D } from '~src/D' -import { normalizeArgument } from './normalizeArgument' -import OptionsModel from '~models/OptionsModel' - -function parameterizeRollArgument( - argument: RandsumRollArgument -): RandsumRollParameters { - const options = normalizeArgument(argument) - const die = new D(options.sides) - return { - options, - argument, - die, - notation: OptionsModel.toNotation(options), - description: OptionsModel.toDescription(options) - } -} - -export { parameterizeRollArgument } diff --git a/src/parameterizeRollArgument/normalizeArgument.ts b/src/parameterizeRollArgument/normalizeArgument.ts deleted file mode 100644 index 3d1ca4dd..00000000 --- a/src/parameterizeRollArgument/normalizeArgument.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - isDicePoolOptions, - isDiceNotationArg, - isD, - isCustomSidesD -} from '~guards' -import { RandsumRollArgument, RandsumRollOptions } from '~types' -import NotationModel from '~models/NotationModel' - -function normalizeArgument(argument: RandsumRollArgument): RandsumRollOptions { - if (isD(argument)) { - return { - quantity: 1, - sides: isCustomSidesD(argument) ? argument.faces : argument.sides - } - } - - if (isDicePoolOptions(argument)) { - return argument - } - - if (isDiceNotationArg(argument)) { - return NotationModel.toOptions(argument) - } - - if (Array.isArray(argument)) { - return { - quantity: 1, - sides: argument.map(String) - } - } - - return { - quantity: 1, - sides: Number(argument || 20) - } -} - -export { normalizeArgument } diff --git a/src/roll.ts b/src/roll.ts new file mode 100644 index 00000000..fc853aee --- /dev/null +++ b/src/roll.ts @@ -0,0 +1,9 @@ +import { ArgumentsModel, DicePoolsModel } from '~models' +import { RandsumRollArgument, RandsumRollResult } from '~types' + +function roll(...args: RandsumRollArgument[]): RandsumRollResult { + const dicePools = ArgumentsModel.formDicePools(args) + return DicePoolsModel.generateRollResult(dicePools) +} + +export { roll } diff --git a/src/roll/formDicePools.ts b/src/roll/formDicePools.ts deleted file mode 100644 index 9192d2c5..00000000 --- a/src/roll/formDicePools.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RandsumRollArgument, DicePools } from '~types' -import { uuidv7 as uuid } from 'uuidv7' -import { parameterizeRollArgument } from '~src/parameterizeRollArgument' - -function formDicePools(args: RandsumRollArgument[]): DicePools { - return { - dicePools: args.reduce( - (acc, arg) => ({ ...acc, [uuid()]: parameterizeRollArgument(arg) }), - {} - ) - } -} - -export { formDicePools } diff --git a/src/roll/index.ts b/src/roll/index.ts deleted file mode 100644 index 61ab8643..00000000 --- a/src/roll/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RandsumRollArgument, RandsumRollResult } from '~types' -import { generateRollResult } from './generateRollResult' -import { formDicePools } from './formDicePools' - -function roll(...args: RandsumRollArgument[]): RandsumRollResult { - const dicePools = formDicePools(args) - return generateRollResult(dicePools) -} - -export { roll } diff --git a/src/validateDiceNotation/index.ts b/src/validateDiceNotation/index.ts deleted file mode 100644 index a28bebbf..00000000 --- a/src/validateDiceNotation/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DicePoolType, RandsumNotationValidationResult } from '~types' -import { isCustomSidesArg, isDiceNotationArg } from '~guards' -import NotationModel from '~models/NotationModel' -import OptionsModel from '~models/OptionsModel' - -function validateDiceNotation( - notation: string -): RandsumNotationValidationResult { - if (!isDiceNotationArg(notation)) { - return { - valid: false, - description: [] - } - } - - const digested = NotationModel.toOptions(notation) - - return { - valid: true, - digested, - notation: OptionsModel.toNotation(digested), - type: isCustomSidesArg(digested.sides) - ? DicePoolType.custom - : DicePoolType.numerical, - description: OptionsModel.toDescription(digested) - } -} - -export { validateDiceNotation } diff --git a/tests/parameterizeRollArgument.test.ts b/tests/ArgumentsModel.test.ts similarity index 85% rename from tests/parameterizeRollArgument.test.ts rename to tests/ArgumentsModel.test.ts index db6720d4..3f91ed01 100644 --- a/tests/parameterizeRollArgument.test.ts +++ b/tests/ArgumentsModel.test.ts @@ -1,15 +1,20 @@ import { describe, expect, test } from 'bun:test' +import { ArgumentsModel } from '~models' import { D } from '~src/D' -import { parameterizeRollArgument } from '~src/parameterizeRollArgument' -import { RandsumNotation } from '~types' +import { RandsumNotation, RandsumRollParameters, DicePools } from '~types' -describe('parameterizeRollArgument', () => { +const extractDicePoolValues = (params: DicePools): RandsumRollParameters[] => { + const pools = Object.entries(params.dicePools) + return pools.map(([, value]) => value) +} + +describe('ArgumentModel.parameterize', () => { describe('given a number', () => { const argument = 2 test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -25,7 +30,7 @@ describe('parameterizeRollArgument', () => { const argument = ['h', 't'] test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -45,7 +50,7 @@ describe('parameterizeRollArgument', () => { } test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -72,7 +77,7 @@ describe('parameterizeRollArgument', () => { } test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -96,7 +101,7 @@ describe('parameterizeRollArgument', () => { } test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -131,7 +136,7 @@ describe('parameterizeRollArgument', () => { } test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -162,7 +167,7 @@ describe('parameterizeRollArgument', () => { const argument = new D(6) test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -181,7 +186,7 @@ describe('parameterizeRollArgument', () => { const argument = new D(['r', 'a', 'n', 'd', 's', 'u', 'm']) test('returns a RollParameter matching the argument', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -205,7 +210,7 @@ describe('parameterizeRollArgument', () => { const argument = coreTestString test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -223,7 +228,7 @@ describe('parameterizeRollArgument', () => { const customSides = ['+', '+', '-', '-', ' ', ' '] test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -241,7 +246,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}H` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -260,7 +265,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}H2` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -281,7 +286,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}L` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -300,7 +305,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}L2` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -320,7 +325,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}D{<2,>5,2,4}` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -346,7 +351,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}C{<2,>5}` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -370,7 +375,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}-2` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -389,7 +394,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}+2` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -408,7 +413,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}R{5,20,<6,>2}3` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -438,7 +443,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}U{5,6}` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -460,7 +465,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}U` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -480,7 +485,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}!` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -500,7 +505,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}V{1=2,>2=6}` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -529,7 +534,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `${coreTestString}V{<2=6}` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -553,7 +558,7 @@ describe('parameterizeRollArgument', () => { describe('like an ordered dice notation', () => { test('it produces proper organized parameters', () => { const explodeFirstString: RandsumNotation = '4d6!H' - const explodeParams = parameterizeRollArgument(explodeFirstString) + const explodeParams = ArgumentsModel.parameterize(explodeFirstString) expect(explodeParams).toMatchObject({ argument: explodeFirstString, @@ -571,7 +576,7 @@ describe('parameterizeRollArgument', () => { }) const dropFirstString: RandsumNotation = '4d6H!' - const dropFirstParams = parameterizeRollArgument(dropFirstString) + const dropFirstParams = ArgumentsModel.parameterize(dropFirstString) expect(dropFirstParams).toMatchObject({ argument: dropFirstString, @@ -595,7 +600,7 @@ describe('parameterizeRollArgument', () => { const argument: RandsumNotation = `10d20 H2 L V{1=2,>2=6} D{<2,>5,2,4} C{<2,>18} R{5,2}3 U{5} R{<6} ! +2 -5 +3` test('returns a RollParameter matching the notation', () => { - const params = parameterizeRollArgument(argument) + const params = ArgumentsModel.parameterize(argument) expect(params).toMatchObject({ argument, @@ -648,3 +653,32 @@ describe('parameterizeRollArgument', () => { }) }) }) + +describe('ArgumentsModel.formDicePools', () => { + describe('given a single argument', () => { + const argument = 20 + + test('returns DicePools with one key', () => { + const params = ArgumentsModel.formDicePools([argument]) + const dicePools = extractDicePoolValues(params) + + expect(dicePools.length).toBe(1) + expect(dicePools[0].argument).toBe(argument) + }) + }) + + describe('given an array of arguments', () => { + const argument: [number, RandsumNotation, string[]] = [2, '4d6', ['h', 't']] + + test('returns a DicePools matching the argument', () => { + const params = ArgumentsModel.formDicePools(argument) + const dicePools = extractDicePoolValues(params) + + expect(dicePools.length).toBe(3) + + expect(dicePools[0].argument).toBe(argument[0]) + expect(dicePools[1].argument).toBe(argument[1]) + expect(dicePools[2].argument).toBe(argument[2]) + }) + }) +}) diff --git a/tests/generateResult.test.ts b/tests/DicePoolsModel.test.ts similarity index 80% rename from tests/generateResult.test.ts rename to tests/DicePoolsModel.test.ts index 6414bb66..2ca0ff73 100644 --- a/tests/generateResult.test.ts +++ b/tests/DicePoolsModel.test.ts @@ -1,12 +1,11 @@ import { describe, expect, spyOn, test } from 'bun:test' +import { DicePoolsModel, RawRollsModel } from '~models' import { D } from '~src/D' -import { generateRollResult } from '~src/roll/generateRollResult' -import { InvalidUniqueError } from '~src/roll/generateRollResult/applyModifiers' -import * as GenerateRawRolls from '~src/roll/generateRollResult/generateRawRolls' +import { InvalidUniqueError } from '~src/models/ParametersModel/modifierApplicators' import { RandsumNotation, DicePoolType, DicePools } from '~types' -describe('generateRollResult', () => { +describe('DicePoolsModel.generateResult', () => { const testRollSet = [1, 2, 3, 4] const coreRawRolls = { 'test-roll-id': testRollSet @@ -34,11 +33,9 @@ describe('generateRollResult', () => { } test('it returns the sum total of the quantity and the roll total', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) - expect(generateRollResult(coreParameters)).toMatchObject({ + expect(DicePoolsModel.generateRollResult(coreParameters)).toMatchObject({ ...coreParameters, rawRolls: coreRawRolls, type: DicePoolType.numerical, @@ -72,22 +69,24 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': uniqueRolls } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce(rawRolls) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(generateRollResult(uniqueParameters)).toMatchObject({ - ...uniqueParameters, - rawRolls, - modifiedRolls: { - 'test-roll-id': { - rolls: [1, 200, 2, 3], - total: 206 - } - }, - type: DicePoolType.numerical, - total: 206, - result: [[1, 200, 2, 3]], - rawResult: [[1, 1, 2, 3]] - }) + expect(DicePoolsModel.generateRollResult(uniqueParameters)).toMatchObject( + { + ...uniqueParameters, + rawRolls, + modifiedRolls: { + 'test-roll-id': { + rolls: [1, 200, 2, 3], + total: 206 + } + }, + type: DicePoolType.numerical, + total: 206, + result: [[1, 200, 2, 3]], + rawResult: [[1, 1, 2, 3]] + } + ) }) describe('when given a "notUnique" array', () => { @@ -112,11 +111,11 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': uniqueRolls } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - rawRolls - ) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(generateRollResult(notUniqueParameters)).toMatchObject({ + expect( + DicePoolsModel.generateRollResult(notUniqueParameters) + ).toMatchObject({ ...notUniqueParameters, rawRolls, modifiedRolls: { @@ -156,13 +155,11 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': overflowRollTotals } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - rawRolls - ) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(() => generateRollResult(overflowParameters)).toThrow( - new InvalidUniqueError() - ) + expect(() => + DicePoolsModel.generateRollResult(overflowParameters) + ).toThrow(new InvalidUniqueError()) }) }) }) @@ -190,9 +187,11 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': customSidesRoll } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce(rawRolls) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(generateRollResult(customSidesParameters)).toMatchObject({ + expect( + DicePoolsModel.generateRollResult(customSidesParameters) + ).toMatchObject({ ...customSidesParameters, rawRolls, modifiedRolls: { @@ -239,9 +238,9 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': longerRollTotals } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce(rawRolls) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(generateRollResult(dropParameters)).toMatchObject({ + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject({ ...dropParameters, rawRolls, modifiedRolls: { @@ -276,23 +275,23 @@ describe('generateRollResult', () => { } test('it returns the total with all values replaced according to the provided rules', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) - expect(generateRollResult(dropParameters)).toMatchObject({ - ...dropParameters, - rawRolls: coreRawRolls, - type: DicePoolType.numerical, - modifiedRolls: { - 'test-roll-id': { - rolls: [2, 2, 3, 4], - total: 11 - } - }, - total: 11, - result: [[2, 2, 3, 4]] - }) + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject( + { + ...dropParameters, + rawRolls: coreRawRolls, + type: DicePoolType.numerical, + modifiedRolls: { + 'test-roll-id': { + rolls: [2, 2, 3, 4], + total: 11 + } + }, + total: 11, + result: [[2, 2, 3, 4]] + } + ) }) }) @@ -319,23 +318,23 @@ describe('generateRollResult', () => { } test('it returns the total with all values replaced according to the provided rules', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) - expect(generateRollResult(dropParameters)).toMatchObject({ - ...dropParameters, - type: DicePoolType.numerical, - rawRolls: coreRawRolls, - modifiedRolls: { - 'test-roll-id': { - rolls: [2, 2, 3, 6], - total: 13 - } - }, - total: 13, - result: [[2, 2, 3, 6]] - }) + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject( + { + ...dropParameters, + type: DicePoolType.numerical, + rawRolls: coreRawRolls, + modifiedRolls: { + 'test-roll-id': { + rolls: [2, 2, 3, 6], + total: 13 + } + }, + total: 13, + result: [[2, 2, 3, 6]] + } + ) }) }) }) @@ -363,9 +362,11 @@ describe('generateRollResult', () => { const rawRolls = { 'test-roll-id': explodeRollTotals } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce(rawRolls) + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) - expect(generateRollResult(explodeParameters)).toMatchObject({ + expect( + DicePoolsModel.generateRollResult(explodeParameters) + ).toMatchObject({ ...explodeParameters, rawRolls, type: DicePoolType.numerical, @@ -400,10 +401,8 @@ describe('generateRollResult', () => { } test('it stops at 99 rerolls and returns the total with all values matching the queries rerolled', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(reDicePools)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(reDicePools)).toMatchObject({ ...reDicePools, rawRolls: coreRawRolls, modifiedRolls: { @@ -439,10 +438,8 @@ describe('generateRollResult', () => { } test('it returns the total with all values matching the queries rerolled', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(reDicePools)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(reDicePools)).toMatchObject({ ...reDicePools, rawRolls: coreRawRolls, modifiedRolls: { @@ -478,10 +475,8 @@ describe('generateRollResult', () => { } test('it returns the total with all values matching the queries rerolled', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(reDicePools)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(reDicePools)).toMatchObject({ ...reDicePools, rawRolls: coreRawRolls, modifiedRolls: { @@ -516,10 +511,8 @@ describe('generateRollResult', () => { } test('it returns the total with all values greaterThan greaterThan and lessThan lessThan replaced with their respective comparitor and the roll total', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(dropParameters)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject({ ...dropParameters, rawRolls: coreRawRolls, modifiedRolls: { @@ -553,10 +546,8 @@ describe('generateRollResult', () => { } test('it returns the total plus the "plus" modifier, and the roll total', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(dropParameters)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject({ ...dropParameters, rawRolls: coreRawRolls, modifiedRolls: { @@ -589,10 +580,8 @@ describe('generateRollResult', () => { } test('it returns the total minus the "minus" modifier, and the roll total', () => { - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce( - coreRawRolls - ) - expect(generateRollResult(dropParameters)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(coreRawRolls) + expect(DicePoolsModel.generateRollResult(dropParameters)).toMatchObject({ ...dropParameters, rawRolls: coreRawRolls, modifiedRolls: { @@ -633,8 +622,8 @@ describe('generateRollResult', () => { 'test-roll-id-2': testRollSet } - spyOn(GenerateRawRolls, 'generateRawRolls').mockReturnValueOnce(rawRolls) - expect(generateRollResult(parameters)).toMatchObject({ + spyOn(RawRollsModel, 'generate').mockReturnValueOnce(rawRolls) + expect(DicePoolsModel.generateRollResult(parameters)).toMatchObject({ ...parameters, rawRolls, modifiedRolls: { @@ -672,7 +661,7 @@ describe('generateRollResult', () => { } as unknown as DicePools test('Throws an error', () => { - expect(() => generateRollResult(dropParameters)).toThrow( + expect(() => DicePoolsModel.generateRollResult(dropParameters)).toThrow( 'Unknown modifier: foo' ) }) diff --git a/tests/validateNotation.test.ts b/tests/NotationModel.test.ts similarity index 83% rename from tests/validateNotation.test.ts rename to tests/NotationModel.test.ts index a3f52ec0..46e35215 100644 --- a/tests/validateNotation.test.ts +++ b/tests/NotationModel.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it } from 'bun:test' -import { validateDiceNotation } from '~src/validateDiceNotation' +import { NotationModel } from '~models' import { RandsumNotation, DicePoolType } from '~types' -describe('validateDiceNotation', () => { +describe('NotationModel.validate', () => { describe('when the notation is completely invalid', () => { const notation = 'invalid-notation' it('returns an error result', () => { - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: false, @@ -20,7 +20,7 @@ describe('validateDiceNotation', () => { const notation: RandsumNotation = '2d5XXddf' it('returns an error result', () => { - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: false, @@ -40,7 +40,7 @@ describe('validateDiceNotation', () => { notations.forEach((notation) => { it(`returns an error result for ${notation}`, () => { - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: false, @@ -54,7 +54,7 @@ describe('validateDiceNotation', () => { const notation: RandsumNotation = '2d5D' it('returns an error result', () => { - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: false, @@ -66,7 +66,7 @@ describe('validateDiceNotation', () => { describe('when given a numerical notation', () => { it('returns a valid result', () => { const notation = '2d6' - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: true, @@ -77,7 +77,7 @@ describe('validateDiceNotation', () => { }) const notation2 = '1D20' - const result2 = validateDiceNotation(notation2) + const result2 = NotationModel.validate(notation2) expect(result2).toEqual({ valid: true, @@ -92,7 +92,7 @@ describe('validateDiceNotation', () => { describe('when given a custom sides notation', () => { it('returns a valid result', () => { const notation = '2d{ht}' - const result = validateDiceNotation(notation) + const result = NotationModel.validate(notation) expect(result).toEqual({ valid: true, diff --git a/tests/RawRollsModel.test.ts b/tests/RawRollsModel.test.ts new file mode 100644 index 00000000..e93106d1 --- /dev/null +++ b/tests/RawRollsModel.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, test } from 'bun:test' +import { RawRollsModel } from '~models' + +import { RandsumRollParameters, DicePools } from '~types' + +describe('RawRollsModel', () => { + describe('RawRollsModel.generate', () => { + describe('when given a DicePools object', () => { + const dicePoolParams: DicePools = { + dicePools: { + 'first-id': { + argument: 20, + notation: '20d4', + description: ['roll 20d4'], + options: { + quantity: 4, + sides: 20 + }, + die: { + roll: () => 1 + } as unknown as RandsumRollParameters['die'] + }, + 'second-id': { + argument: ['h', 't'], + notation: '20d{ht}', + description: ['roll 20 dice with sides h and t'], + options: { + quantity: 1, + sides: ['h', 't'] + }, + die: { + roll: () => 'h' + } as unknown as RandsumRollParameters['die'] + } + } + } + + test('returns an object with the same keys and an array of rolls equal to the desired quality with the provided die', () => { + const result = RawRollsModel.generate(dicePoolParams.dicePools) + + expect(result).toMatchObject({ + 'first-id': [1, 1, 1, 1], + 'second-id': ['h'] + }) + }) + }) + }) +}) diff --git a/tests/formDicePools.test.ts b/tests/formDicePools.test.ts deleted file mode 100644 index 50bc08fb..00000000 --- a/tests/formDicePools.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, test } from 'bun:test' - -import { formDicePools } from '~src/roll/formDicePools' -import { RandsumNotation, RandsumRollParameters, DicePools } from '~types' - -const extractDicePoolValues = (params: DicePools): RandsumRollParameters[] => { - const pools = Object.entries(params.dicePools) - return pools.map(([, value]) => value) -} - -describe('formDicePools', () => { - describe('given a single argument', () => { - const argument = 20 - - test('returns DicePools with one key', () => { - const params = formDicePools([argument]) - const dicePools = extractDicePoolValues(params) - - expect(dicePools.length).toBe(1) - expect(dicePools[0].argument).toBe(argument) - }) - }) - - describe('given an array of arguments', () => { - const argument: [number, RandsumNotation, string[]] = [2, '4d6', ['h', 't']] - - test('returns a DicePools matching the argument', () => { - const params = formDicePools(argument) - const dicePools = extractDicePoolValues(params) - - expect(dicePools.length).toBe(3) - - expect(dicePools[0].argument).toBe(argument[0]) - expect(dicePools[1].argument).toBe(argument[1]) - expect(dicePools[2].argument).toBe(argument[2]) - }) - }) -}) diff --git a/tests/generateRawRolls.test.ts b/tests/generateRawRolls.test.ts deleted file mode 100644 index be8f7a43..00000000 --- a/tests/generateRawRolls.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, expect, test } from 'bun:test' - -import { RandsumRollParameters, DicePools } from '~types' -import { generateRawRolls } from '~src/roll/generateRollResult/generateRawRolls' - -describe('generateRawRolls', () => { - describe('when given a DicePools object', () => { - const dicePoolParams: DicePools = { - dicePools: { - 'first-id': { - argument: 20, - notation: '20d4', - description: ['roll 20d4'], - options: { - quantity: 4, - sides: 20 - }, - die: { - roll: () => 1 - } as unknown as RandsumRollParameters['die'] - }, - 'second-id': { - argument: ['h', 't'], - notation: '20d{ht}', - description: ['roll 20 dice with sides h and t'], - options: { - quantity: 1, - sides: ['h', 't'] - }, - die: { - roll: () => 'h' - } as unknown as RandsumRollParameters['die'] - } - } - } - - test('returns an object with the same keys and an array of rolls equal to the desired quality with the provided die', () => { - const result = generateRawRolls(dicePoolParams.dicePools) - - expect(result).toMatchObject({ - 'first-id': [1, 1, 1, 1], - 'second-id': ['h'] - }) - }) - }) -}) diff --git a/tsconfig.json b/tsconfig.json index a96953cf..ecd65635 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "paths": { - "~models/*": ["./src/models/*"], + "~models": ["./src/models/index.ts"], "~patterns": ["./src/patterns.ts"], "~guards": ["./src/guards.ts"], "~types": ["./src/types.ts"],