UNPKG

@randsum/roller

Version:

A flexible, type-safe dice roller for tabletop RPGs, game development, and probability simulations

61 lines 62.5 kB
{ "version": 3, "sources": ["../src/lib/comparisonUtils/matchesComparison.ts", "../src/lib/comparisonUtils/formatHumanList.ts", "../src/lib/comparisonUtils/formatComparisonDescription.ts", "../src/lib/comparisonUtils/formatComparisonNotation.ts", "../src/lib/modifiers/transformers/applyCap.ts", "../src/lib/modifiers/description/formatDropDescription.ts", "../src/lib/modifiers/description/formatRerollDescription.ts", "../src/lib/modifiers/description/formatReplaceDescription.ts", "../src/lib/modifiers/description/descriptionHandlers.ts", "../src/lib/modifiers/modifierToDescription.ts", "../src/lib/modifiers/transformers/formatDropNotation.ts", "../src/lib/modifiers/transformers/formatRerollNotation.ts", "../src/lib/modifiers/transformers/formatReplaceNotation.ts", "../src/lib/modifiers/transformers/notationHandlers.ts", "../src/lib/modifiers/modifierToNotation.ts", "../src/lib/modifiers/transformers/applyCapping.ts", "../src/lib/modifiers/transformers/applyDropping.ts", "../src/lib/modifiers/transformers/applyExploding.ts", "../src/lib/modifiers/transformers/applyReplacing.ts", "../src/lib/modifiers/transformers/applyRerolling.ts", "../src/lib/modifiers/transformers/applyUnique.ts", "../src/lib/modifiers/logging/createFrequencyMap.ts", "../src/lib/modifiers/logging/createModifierLog.ts", "../src/lib/modifiers/logging/mergeLogs.ts", "../src/lib/modifiers/transformers/modifierHandlers.ts", "../src/lib/modifiers/applyModifiers.ts", "../src/lib/modifiers/constants.ts", "../src/lib/patterns/modifierPatterns.ts", "../src/lib/modifiers/parsing/parseArithmeticModifiers.ts", "../src/lib/modifiers/parsing/parseCapModifier.ts", "../src/lib/modifiers/parsing/parseDropModifiers.ts", "../src/lib/modifiers/parsing/parseExplodeModifier.ts", "../src/lib/modifiers/parsing/parseReplaceModifier.ts", "../src/lib/modifiers/parsing/parseRerollModifier.ts", "../src/lib/modifiers/parsing/parseUniqueModifier.ts", "../src/lib/modifiers/parseModifiers.ts", "../src/lib/modifiers/processors/processModifierDescriptions.ts", "../src/lib/modifiers/processors/processModifierNotations.ts", "../src/lib/transformers/optionsToSidesFaces.ts", "../src/lib/transformers/optionsToDescription.ts", "../src/lib/patterns/coreNotationPattern.ts", "../src/lib/patterns/completeRollPattern.ts", "../src/isDiceNotation.ts", "../src/lib/transformers/optionsToNotation.ts", "../src/lib/notation/listOfNotations.ts", "../src/lib/notation/singleNotationToOptions.ts", "../src/lib/notation/notationToOptions.ts", "../src/roll/argToParameter/optionsFromArgument.ts", "../src/roll/argToParameter/index.ts", "../src/lib/random/coreRandom.ts", "../src/lib/random/coreSpreadRolls.ts", "../src/roll/generateRollRecord/generateHistory.ts", "../src/roll/generateRollRecord/index.ts", "../src/roll/index.ts", "../src/validateNotation.ts"], "sourcesContent": [ "import type { ComparisonOptions } from '../../types'\n\nexport function matchesComparison(\n value: number,\n { greaterThan, lessThan, exact }: ComparisonOptions & { exact?: number[] }\n): boolean {\n if (exact?.includes(value)) return true\n if (greaterThan !== undefined && value > greaterThan) return true\n if (lessThan !== undefined && value < lessThan) return true\n\n return false\n}\n", "export function formatHumanList(values: number[]): string {\n if (!values.length) return ''\n if (values.length === 1) return `[${values[0]}]`\n\n const items = values.map(item => `[${item}]`)\n const last = items.pop()\n return `${items.join(' ')} and ${last}`\n}\n", "import type { ComparisonOptions } from '../../types'\nimport { formatHumanList } from './formatHumanList'\n\nexport function formatComparisonDescription({\n greaterThan,\n lessThan,\n exact\n}: ComparisonOptions & { exact?: number[] }): string[] {\n const descriptions: string[] = []\n\n if (exact?.length) descriptions.push(formatHumanList(exact))\n if (greaterThan !== undefined) descriptions.push(`greater than [${greaterThan}]`)\n if (lessThan !== undefined) descriptions.push(`less than [${lessThan}]`)\n\n return descriptions\n}\n", "import type { ComparisonOptions } from '../../types'\n\nexport function formatComparisonNotation({\n greaterThan,\n lessThan,\n exact\n}: ComparisonOptions & { exact?: number[] }): string[] {\n const notations: string[] = []\n\n if (exact?.length) notations.push(...exact.map(String))\n if (greaterThan !== undefined) notations.push(`>${greaterThan}`)\n if (lessThan !== undefined) notations.push(`<${lessThan}`)\n\n return notations\n}\n", "import type { ComparisonOptions } from '../../../types'\n\nexport function applyCap(\n value: number,\n { greaterThan, lessThan }: ComparisonOptions,\n replacementValue?: number\n): number {\n let result = value\n\n if (greaterThan !== undefined && result > greaterThan) {\n result = replacementValue ?? greaterThan\n }\n\n if (lessThan !== undefined && result < lessThan) {\n result = replacementValue ?? lessThan\n }\n\n return result\n}\n", "import type { DropOptions } from '../../../types'\nimport { formatHumanList } from '../../comparisonUtils'\n\nexport function formatDropDescription({\n highest,\n lowest,\n greaterThan,\n lessThan,\n exact\n}: DropOptions): string[] {\n const descriptions: string[] = []\n\n if (highest) {\n if (highest > 1) {\n descriptions.push(`Drop highest ${highest}`)\n } else {\n descriptions.push('Drop highest')\n }\n }\n\n if (lowest) {\n if (lowest > 1) {\n descriptions.push(`Drop lowest ${lowest}`)\n } else {\n descriptions.push('Drop lowest')\n }\n }\n\n if (exact) {\n descriptions.push(`Drop ${formatHumanList(exact)}`)\n }\n\n if (greaterThan !== undefined) {\n descriptions.push(`Drop greater than [${greaterThan}]`)\n }\n\n if (lessThan !== undefined) {\n descriptions.push(`Drop less than [${lessThan}]`)\n }\n\n return descriptions\n}\n", "import type { RerollOptions } from '../../../types'\nimport { formatHumanList } from '../../comparisonUtils'\n\nexport function formatRerollDescription({\n exact,\n greaterThan,\n lessThan,\n max\n}: RerollOptions): string[] {\n const rerollList: string[] = []\n\n if (exact) {\n exact.forEach(roll => rerollList.push(`${roll}`))\n }\n\n const greaterLessList: string[] = []\n if (greaterThan !== undefined) {\n greaterLessList.push(`greater than [${greaterThan}]`)\n }\n if (lessThan !== undefined) {\n greaterLessList.push(`less than [${lessThan}]`)\n }\n\n const exactList = formatHumanList(rerollList.map(Number))\n const greaterLess = greaterLessList.join(' and ')\n\n const conditions = [exactList, greaterLess].filter(Boolean).join(', ')\n if (!conditions) return []\n\n const maxText = max !== undefined ? ` (up to ${max} times)` : ''\n return [`Reroll ${conditions}${maxText}`]\n}\n", "import type { ReplaceOptions } from '../../../types'\nimport { formatComparisonDescription } from '../../comparisonUtils'\n\nexport function formatReplaceDescription(options: ReplaceOptions | ReplaceOptions[]): string[] {\n const rules = Array.isArray(options) ? options : [options]\n return rules.map(({ from, to }) => {\n if (typeof from === 'object') {\n const comparisons = formatComparisonDescription(from)\n return `Replace ${comparisons.join(' and ')} with [${to}]`\n }\n return `Replace [${from}] with [${to}]`\n })\n}\n", "import type {\n ComparisonOptions,\n DropOptions,\n ModifierOptions,\n ReplaceOptions,\n RerollOptions,\n UniqueOptions\n} from '../../../types'\nimport { formatComparisonDescription, formatHumanList } from '../../comparisonUtils'\nimport { formatDropDescription } from './formatDropDescription'\nimport { formatRerollDescription } from './formatRerollDescription'\nimport { formatReplaceDescription } from './formatReplaceDescription'\nimport type { DescriptionHandler } from '../types'\n\nexport const DESCRIPTION_HANDLERS: ReadonlyMap<keyof ModifierOptions, DescriptionHandler> = new Map<\n keyof ModifierOptions,\n DescriptionHandler\n>([\n ['plus', options => [`Add ${options as number}`]],\n ['minus', options => [`Subtract ${options as number}`]],\n [\n 'cap',\n options =>\n formatComparisonDescription(options as ComparisonOptions).map(str => `No Rolls ${str}`)\n ],\n ['drop', options => formatDropDescription(options as DropOptions)],\n ['reroll', options => formatRerollDescription(options as RerollOptions)],\n ['explode', () => ['Exploding Dice']],\n [\n 'unique',\n options => {\n if (typeof options === 'boolean') {\n return ['No Duplicate Rolls']\n }\n return [`No Duplicates (except ${formatHumanList((options as UniqueOptions).notUnique)})`]\n }\n ],\n ['replace', options => formatReplaceDescription(options as ReplaceOptions | ReplaceOptions[])]\n])\n", "import type { ModifierOptions } from '../../types'\nimport { DESCRIPTION_HANDLERS } from './description/descriptionHandlers'\n\nexport function modifierToDescription(\n type: keyof ModifierOptions,\n options: ModifierOptions[keyof ModifierOptions]\n): string[] | undefined {\n if (options === undefined) return undefined\n\n const handler = DESCRIPTION_HANDLERS.get(type)\n return handler ? handler(options) : undefined\n}\n", "import type { DropOptions } from '../../../types'\n\nexport function formatDropNotation({\n highest,\n lowest,\n greaterThan,\n lessThan,\n exact\n}: DropOptions): string | undefined {\n const parts: string[] = []\n\n if (highest) {\n parts.push(highest === 1 ? 'H' : `H${highest}`)\n }\n\n if (lowest) {\n parts.push(lowest === 1 ? 'L' : `L${lowest}`)\n }\n\n const dropList: string[] = []\n\n if (greaterThan !== undefined) {\n dropList.push(`>${greaterThan}`)\n }\n\n if (lessThan !== undefined) {\n dropList.push(`<${lessThan}`)\n }\n\n if (exact) {\n exact.forEach(roll => dropList.push(`${roll}`))\n }\n\n if (dropList.length > 0) {\n parts.push(`D{${dropList.join(',')}}`)\n }\n\n return parts.length ? parts.join('') : undefined\n}\n", "import type { RerollOptions } from '../../../types'\nimport { formatComparisonNotation } from '../../comparisonUtils'\n\nexport function formatRerollNotation(options: RerollOptions): string | undefined {\n const parts = formatComparisonNotation(options)\n if (!parts.length) return undefined\n\n const maxSuffix = options.max ? `${options.max}` : ''\n return `R{${parts.join(',')}}${maxSuffix}`\n}\n", "import type { ReplaceOptions } from '../../../types'\nimport { formatComparisonNotation } from '../../comparisonUtils'\n\nexport function formatReplaceNotation(\n options: ReplaceOptions | ReplaceOptions[]\n): string | undefined {\n const rules = Array.isArray(options) ? options : [options]\n const notations = rules.map(({ from, to }) => {\n if (typeof from === 'object') {\n const comparisons = formatComparisonNotation(from)\n return comparisons.map(comp => `${comp}=${to}`).join(',')\n }\n return `${from}=${to}`\n })\n\n return notations.length ? `V{${notations.join(',')}}` : undefined\n}\n", "import type {\n ComparisonOptions,\n DropOptions,\n ModifierOptions,\n ReplaceOptions,\n RerollOptions,\n UniqueOptions\n} from '../../../types'\nimport { formatComparisonNotation } from '../../comparisonUtils'\nimport { formatDropNotation } from './formatDropNotation'\nimport { formatRerollNotation } from './formatRerollNotation'\nimport { formatReplaceNotation } from './formatReplaceNotation'\nimport type { NotationHandler } from '../types'\n\nexport const NOTATION_HANDLERS: ReadonlyMap<keyof ModifierOptions, NotationHandler> = new Map<\n keyof ModifierOptions,\n NotationHandler\n>([\n [\n 'plus',\n options => {\n const numOptions = options as number\n if (numOptions < 0) {\n return `-${Math.abs(numOptions)}`\n }\n return `+${numOptions}`\n }\n ],\n ['minus', options => `-${options as number}`],\n [\n 'cap',\n options => {\n const capList = formatComparisonNotation(options as ComparisonOptions)\n return capList.length ? `C{${capList.join(',')}}` : undefined\n }\n ],\n ['drop', options => formatDropNotation(options as DropOptions)],\n ['reroll', options => formatRerollNotation(options as RerollOptions)],\n ['explode', () => '!'],\n [\n 'unique',\n options => {\n if (typeof options === 'boolean') return 'U'\n return `U{${(options as UniqueOptions).notUnique.join(',')}}`\n }\n ],\n ['replace', options => formatReplaceNotation(options as ReplaceOptions | ReplaceOptions[])]\n])\n", "import type { ModifierOptions } from '../../types'\nimport { NOTATION_HANDLERS } from './transformers/notationHandlers'\n\nexport function modifierToNotation(\n type: keyof ModifierOptions,\n options: ModifierOptions[keyof ModifierOptions]\n): string | undefined {\n if (options === undefined) return undefined\n\n const handler = NOTATION_HANDLERS.get(type)\n return handler ? handler(options) : undefined\n}\n", "import type { ComparisonOptions } from '../../../types'\nimport { applyCap } from './applyCap'\n\nexport function applyCapping(rolls: number[], options: ComparisonOptions): number[] {\n return rolls.map(roll => applyCap(roll, options))\n}\n", "import type { DropOptions } from '../../../types'\n\nexport function applyDropping(rolls: number[], options: DropOptions): number[] {\n const { highest, lowest, greaterThan, lessThan, exact } = options\n\n const exactSet = exact ? new Set(exact) : null\n let result = rolls.filter(roll => {\n if (greaterThan !== undefined && roll > greaterThan) return false\n if (lessThan !== undefined && roll < lessThan) return false\n if (exactSet?.has(roll)) return false\n return true\n })\n\n if (highest !== undefined || lowest !== undefined) {\n const indexedRolls = result.map((roll, index) => ({ roll, index }))\n indexedRolls.sort((a, b) => a.roll - b.roll)\n\n const indicesToDrop = new Set<number>()\n\n if (lowest !== undefined) {\n for (let i = 0; i < Math.min(lowest, indexedRolls.length); i++) {\n const roll = indexedRolls[i]\n if (roll) {\n indicesToDrop.add(roll.index)\n }\n }\n }\n\n if (highest !== undefined) {\n for (let i = indexedRolls.length - 1; i >= Math.max(0, indexedRolls.length - highest); i--) {\n const roll = indexedRolls[i]\n if (roll) {\n indicesToDrop.add(roll.index)\n }\n }\n }\n\n result = result.filter((_, index) => !indicesToDrop.has(index))\n\n if (lowest !== undefined && highest === undefined) {\n result.sort((a, b) => a - b)\n }\n }\n\n return result\n}\n", "import type { RequiredNumericRollParameters } from '../../../types'\n\nexport function applyExploding(\n rolls: number[],\n { sides }: RequiredNumericRollParameters,\n rollOne: () => number\n): number[] {\n let explodeCount = 0\n\n for (const roll of rolls) {\n if (roll === sides) {\n explodeCount++\n }\n }\n\n if (explodeCount === 0) {\n return rolls\n }\n\n const explodedRolls = [...rolls]\n for (let i = 0; i < explodeCount; i++) {\n explodedRolls.push(rollOne())\n }\n\n return explodedRolls\n}\n", "import type { ReplaceOptions } from '../../../types'\nimport { applyCap } from './applyCap'\n\nexport function applyReplacing(\n rolls: number[],\n options: ReplaceOptions | ReplaceOptions[]\n): number[] {\n const replaceRules = Array.isArray(options) ? options : [options]\n let result = [...rolls]\n\n for (const { from, to } of replaceRules) {\n result = result.map(roll => {\n if (typeof from === 'object') {\n // Comparison-based replacement\n return applyCap(roll, from, to)\n } else {\n // Exact value replacement\n return roll === from ? to : roll\n }\n })\n }\n\n return result\n}\n", "import type { RerollOptions } from '../../../types'\nimport { matchesComparison } from '../../comparisonUtils'\n\nexport function applyRerolling(\n rolls: number[],\n options: RerollOptions,\n rollOne: () => number\n): number[] {\n const { max } = options\n let globalRerollCount = 0\n\n return rolls.map(roll => {\n if (max !== undefined && globalRerollCount >= max) {\n return roll // Don't reroll if we've hit the global limit\n }\n\n const result = rerollSingle(roll, options, rollOne)\n if (result !== roll) {\n globalRerollCount++\n }\n return result\n })\n}\n\nfunction rerollSingle(\n roll: number,\n options: RerollOptions,\n rollOne: () => number,\n attempt = 0\n): number {\n if (attempt >= 99) {\n return roll\n }\n\n if (matchesComparison(roll, options)) {\n return rerollSingle(rollOne(), options, rollOne, attempt + 1)\n }\n\n return roll\n}\n", "import type { RequiredNumericRollParameters } from '../../../types'\nimport type { UniqueOptions } from '../../../types'\n\nexport function applyUnique(\n rolls: number[],\n options: boolean | UniqueOptions,\n { sides }: RequiredNumericRollParameters,\n rollOne: () => number\n): number[] {\n if (rolls.length > sides) {\n throw new Error('Cannot have more rolls than sides when unique is enabled')\n }\n\n const notUnique = typeof options === 'object' ? options.notUnique : []\n const notUniqueSet = new Set(notUnique)\n const seenValues = new Set<number>()\n const uniqueRolls: number[] = []\n\n for (const roll of rolls) {\n if (notUniqueSet.has(roll) || !seenValues.has(roll)) {\n // This value is allowed to repeat or hasn't been seen yet\n uniqueRolls.push(roll)\n seenValues.add(roll)\n } else {\n // Need to reroll for uniqueness\n let newRoll: number\n let attempts = 0\n const maxAttempts = sides * 10 // Safety limit\n\n do {\n newRoll = rollOne()\n attempts++\n if (attempts > maxAttempts) {\n // Fallback: use the original roll to avoid infinite loop\n newRoll = roll\n break\n }\n } while (seenValues.has(newRoll) && !notUniqueSet.has(newRoll))\n\n uniqueRolls.push(newRoll)\n seenValues.add(newRoll)\n }\n }\n\n return uniqueRolls\n}\n", "export function createFrequencyMap(values: number[]): Map<number, number> {\n const freq = new Map<number, number>()\n\n for (const value of values) {\n freq.set(value, (freq.get(value) ?? 0) + 1)\n }\n\n return freq\n}\n", "import type { ModifierConfig, ModifierLog } from '../../../types'\nimport { createFrequencyMap } from './createFrequencyMap'\n\nexport function createModifierLog(\n modifier: string,\n options: ModifierConfig | undefined,\n initialRolls: number[],\n newRolls: number[]\n): ModifierLog {\n const baseLog = { modifier, options }\n\n if (initialRolls === newRolls) {\n return { ...baseLog, added: [], removed: [] }\n }\n\n if (initialRolls.length === 0) {\n return { ...baseLog, added: [...newRolls], removed: [] }\n }\n\n if (newRolls.length === 0) {\n return { ...baseLog, added: [], removed: [...initialRolls] }\n }\n\n const initialFreq = createFrequencyMap(initialRolls)\n const newFreq = createFrequencyMap(newRolls)\n\n const added: number[] = []\n const removed: number[] = []\n\n const allValues = new Set([...initialRolls, ...newRolls])\n\n for (const value of allValues) {\n const initialCount = initialFreq.get(value) ?? 0\n const newCount = newFreq.get(value) ?? 0\n const diff = newCount - initialCount\n\n if (diff > 0) {\n // More of this value in new array - it was added\n for (let i = 0; i < diff; i++) {\n added.push(value)\n }\n } else if (diff < 0) {\n // Fewer of this value in new array - it was removed\n for (let i = 0; i < Math.abs(diff); i++) {\n removed.push(value)\n }\n }\n }\n\n return { ...baseLog, added, removed }\n}\n", "import type { ModifierLog } from '../../../types'\n\nexport function mergeLogs(existingLogs: ModifierLog[], newLog: ModifierLog): ModifierLog[] {\n return [...existingLogs, newLog]\n}\n", "import type {\n ComparisonOptions,\n DropOptions,\n ModifierOptions,\n ReplaceOptions,\n RerollOptions,\n UniqueOptions\n} from '../../../types'\n\nimport { applyCapping } from './applyCapping'\nimport { applyDropping } from './applyDropping'\nimport { applyExploding } from './applyExploding'\nimport { applyReplacing } from './applyReplacing'\nimport { applyRerolling } from './applyRerolling'\nimport { applyUnique } from './applyUnique'\nimport type { ModifierHandler } from '../types'\nimport { createModifierLog } from '../logging/createModifierLog'\nimport { mergeLogs } from '../logging/mergeLogs'\n\nexport const MODIFIER_HANDLERS: ReadonlyMap<keyof ModifierOptions, ModifierHandler> = new Map<\n keyof ModifierOptions,\n ModifierHandler\n>([\n [\n 'plus',\n (bonus, options) => ({\n rolls: bonus.rolls,\n simpleMathModifier: Number(options),\n logs: bonus.logs\n })\n ],\n [\n 'minus',\n (bonus, options) => ({\n rolls: bonus.rolls,\n simpleMathModifier: -Number(options),\n logs: bonus.logs\n })\n ],\n [\n 'cap',\n (bonus, options) => {\n const initialRolls = [...bonus.rolls]\n const newRolls = applyCapping(bonus.rolls, options as ComparisonOptions)\n const log = createModifierLog('cap', options as ComparisonOptions, initialRolls, newRolls)\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ],\n [\n 'drop',\n (bonus, options) => {\n const initialRolls = [...bonus.rolls]\n const newRolls = applyDropping(bonus.rolls, options as DropOptions)\n const log = createModifierLog('drop', options as DropOptions, initialRolls, newRolls)\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ],\n [\n 'reroll',\n (bonus, options, rollOne) => {\n if (!rollOne) throw new Error('rollOne function required for reroll modifier')\n const initialRolls = [...bonus.rolls]\n const newRolls = applyRerolling(bonus.rolls, options as RerollOptions, rollOne)\n const log = createModifierLog('reroll', options as RerollOptions, initialRolls, newRolls)\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ],\n [\n 'explode',\n (bonus, options, rollOne, context) => {\n if (!rollOne || !context) throw new Error('rollOne and context required for explode modifier')\n const initialRolls = [...bonus.rolls]\n const newRolls = applyExploding(bonus.rolls, context, rollOne)\n const log = createModifierLog('explode', options as boolean, initialRolls, newRolls)\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ],\n [\n 'unique',\n (bonus, options, rollOne, context) => {\n if (!rollOne || !context) throw new Error('rollOne and context required for unique modifier')\n const initialRolls = [...bonus.rolls]\n const newRolls = applyUnique(\n bonus.rolls,\n options as boolean | UniqueOptions,\n context,\n rollOne\n )\n const log = createModifierLog(\n 'unique',\n options as boolean | UniqueOptions,\n initialRolls,\n newRolls\n )\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ],\n [\n 'replace',\n (bonus, options) => {\n const initialRolls = [...bonus.rolls]\n const newRolls = applyReplacing(bonus.rolls, options as ReplaceOptions | ReplaceOptions[])\n const log = createModifierLog(\n 'replace',\n options as ReplaceOptions | ReplaceOptions[],\n initialRolls,\n newRolls\n )\n return {\n rolls: newRolls,\n simpleMathModifier: bonus.simpleMathModifier,\n logs: mergeLogs(bonus.logs, log)\n }\n }\n ]\n])\n", "import type { ModifierOptions, NumericRollBonus, RequiredNumericRollParameters } from '../../types'\nimport { MODIFIER_HANDLERS } from './transformers/modifierHandlers'\n\nexport function applyModifiers(\n type: keyof ModifierOptions,\n options: ModifierOptions[keyof ModifierOptions],\n bonus: NumericRollBonus,\n context?: RequiredNumericRollParameters,\n rollOne?: () => number\n): NumericRollBonus {\n if (options === undefined) {\n return bonus\n }\n\n const handler = MODIFIER_HANDLERS.get(type)\n if (!handler) {\n throw new Error(`Unknown modifier type: ${type}`)\n }\n\n return handler(bonus, options, rollOne, context)\n}\n", "import type { ModifierOptions } from '../../types'\n\nexport const MODIFIER_ORDER: (keyof ModifierOptions)[] = [\n 'cap',\n 'drop',\n 'replace',\n 'reroll',\n 'explode',\n 'unique',\n 'plus',\n 'minus'\n]\n", "/* eslint-disable @typescript-eslint/no-inferrable-types */\nexport const dropHighestPattern: RegExp = /[Hh](\\d+)?/\nexport const dropLowestPattern: RegExp = /[Ll](\\d+)?/\nexport const dropConstraintsPattern: RegExp = /[Dd]\\{([^}]{1,50})\\}/\nexport const explodePattern: RegExp = /!/\nexport const uniquePattern: RegExp = /[Uu](\\{([^}]{1,50})\\})?/\nexport const replacePattern: RegExp = /[Vv]\\{([^}]{1,50})\\}/\nexport const rerollPattern: RegExp = /[Rr]\\{([^}]{1,50})\\}(\\d+)?/\nexport const capPattern: RegExp = /[Cc]\\{([^}]{1,50})\\}/\nexport const plusPattern: RegExp = /\\+(\\d+)/\nexport const minusPattern: RegExp = /-(\\d+)/\n", "import type { ModifierOptions } from '../../../types'\nimport { minusPattern, plusPattern } from '../../patterns/modifierPatterns'\n\nexport function parseArithmeticModifiers(\n notation: string\n): Pick<ModifierOptions, 'plus' | 'minus'> {\n const result: { plus?: number; minus?: number } = {}\n\n const plusMatches = Array.from(notation.matchAll(new RegExp(plusPattern.source, 'g')))\n if (plusMatches.length > 0) {\n const total = plusMatches.reduce((sum, match) => sum + Number(match[1]), 0)\n result.plus = total\n }\n\n const minusMatches = Array.from(notation.matchAll(new RegExp(minusPattern.source, 'g')))\n if (minusMatches.length > 0) {\n const total = minusMatches.reduce((sum, match) => sum + Number(match[1]), 0)\n result.minus = total\n }\n\n return result\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { capPattern } from '../../patterns/modifierPatterns'\n\nexport function parseCapModifier(notation: string): Pick<ModifierOptions, 'cap'> {\n const match = notation.match(capPattern)\n if (!match) return {}\n\n const cap: {\n greaterThan?: number\n lessThan?: number\n } = {}\n const conditions = match[1]\n if (!conditions) return {}\n\n const parts = conditions.split(',').map(s => s.trim())\n\n for (const part of parts) {\n if (part.startsWith('>')) {\n cap.greaterThan = Number(part.slice(1))\n } else if (part.startsWith('<')) {\n cap.lessThan = Number(part.slice(1))\n }\n }\n\n return { cap }\n}\n", "import type { ModifierOptions } from '../../../types'\nimport {\n dropConstraintsPattern,\n dropHighestPattern,\n dropLowestPattern\n} from '../../patterns/modifierPatterns'\n\nexport function parseDropModifiers(notation: string): Pick<ModifierOptions, 'drop'> {\n const drop: {\n highest?: number\n lowest?: number\n greaterThan?: number\n lessThan?: number\n exact?: number[]\n } = {}\n\n const highestMatch = notation.match(dropHighestPattern)\n if (highestMatch) {\n drop.highest = highestMatch[1] ? Number(highestMatch[1]) : 1\n }\n\n const lowestMatch = notation.match(dropLowestPattern)\n if (lowestMatch) {\n drop.lowest = lowestMatch[1] ? Number(lowestMatch[1]) : 1\n }\n\n const constraintsMatch = notation.match(dropConstraintsPattern)\n if (constraintsMatch) {\n const constraints = constraintsMatch[1]\n if (constraints) {\n const parts = constraints.split(',').map(s => s.trim())\n\n for (const part of parts) {\n if (part.startsWith('>')) {\n drop.greaterThan = Number(part.slice(1))\n } else if (part.startsWith('<')) {\n drop.lessThan = Number(part.slice(1))\n } else if (/^\\d+$/.test(part)) {\n drop.exact ??= []\n drop.exact.push(Number(part))\n }\n }\n }\n }\n\n return Object.keys(drop).length > 0 ? { drop } : {}\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { explodePattern } from '../../patterns/modifierPatterns'\n\nexport function parseExplodeModifier(notation: string): Pick<ModifierOptions, 'explode'> {\n return explodePattern.test(notation) ? { explode: true } : {}\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { replacePattern } from '../../patterns/modifierPatterns'\n\nexport function parseReplaceModifier(notation: string): Pick<ModifierOptions, 'replace'> {\n const match = notation.match(replacePattern)\n if (!match) return {}\n\n const content = match[1]\n if (!content) return {}\n const parts = content.split(',').map(s => s.trim())\n\n const replacements = parts.map(part => {\n const [fromPart, toPart] = part.split('=')\n if (!fromPart || !toPart) return { from: 0, to: 0 }\n\n let from: number | { greaterThan: number } | { lessThan: number }\n if (fromPart.startsWith('>')) {\n from = { greaterThan: Number(fromPart.slice(1)) }\n } else if (fromPart.startsWith('<')) {\n from = { lessThan: Number(fromPart.slice(1)) }\n } else {\n from = Number(fromPart)\n }\n\n return { from, to: Number(toPart) }\n })\n\n return { replace: replacements }\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { rerollPattern } from '../../patterns/modifierPatterns'\n\nexport function parseRerollModifier(notation: string): Pick<ModifierOptions, 'reroll'> {\n const matches = Array.from(notation.matchAll(new RegExp(rerollPattern.source, 'g')))\n if (matches.length === 0) return {}\n\n const reroll: {\n max?: number\n greaterThan?: number\n lessThan?: number\n exact?: number[]\n } = {}\n\n for (const match of matches) {\n const conditions = match[1]\n const maxCount = match[2] ? Number(match[2]) : undefined\n\n if (maxCount) {\n reroll.max = maxCount\n }\n\n if (conditions) {\n const parts = conditions.split(',').map(s => s.trim())\n\n for (const part of parts) {\n if (part.startsWith('>')) {\n reroll.greaterThan = Number(part.slice(1))\n } else if (part.startsWith('<')) {\n reroll.lessThan = Number(part.slice(1))\n } else if (/^\\d+$/.test(part)) {\n reroll.exact ??= []\n reroll.exact.push(Number(part))\n }\n }\n }\n }\n\n return Object.keys(reroll).length > 0 ? { reroll } : {}\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { uniquePattern } from '../../patterns/modifierPatterns'\n\nexport function parseUniqueModifier(notation: string): Pick<ModifierOptions, 'unique'> {\n const match = notation.match(uniquePattern)\n if (!match) return {}\n\n if (!match[2]) {\n return { unique: true }\n }\n\n const notUnique = match[2].split(',').map(s => Number(s.trim()))\n return { unique: { notUnique } }\n}\n", "import type { ModifierOptions } from '../../types'\nimport { parseArithmeticModifiers } from './parsing/parseArithmeticModifiers'\nimport { parseCapModifier } from './parsing/parseCapModifier'\nimport { parseDropModifiers } from './parsing/parseDropModifiers'\nimport { parseExplodeModifier } from './parsing/parseExplodeModifier'\nimport { parseReplaceModifier } from './parsing/parseReplaceModifier'\nimport { parseRerollModifier } from './parsing/parseRerollModifier'\nimport { parseUniqueModifier } from './parsing/parseUniqueModifier'\n\nexport function parseModifiers(notation: string): ModifierOptions {\n return {\n ...parseDropModifiers(notation),\n ...parseExplodeModifier(notation),\n ...parseUniqueModifier(notation),\n ...parseReplaceModifier(notation),\n ...parseRerollModifier(notation),\n ...parseCapModifier(notation),\n ...parseArithmeticModifiers(notation)\n }\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { MODIFIER_ORDER } from '../constants'\nimport { modifierToDescription } from '../modifierToDescription'\n\nexport function processModifierDescriptions(modifiers: ModifierOptions | undefined): string[] {\n if (!modifiers) return []\n\n return MODIFIER_ORDER.map(type => modifierToDescription(type, modifiers[type]))\n .flat()\n .filter((desc): desc is string => typeof desc === 'string')\n .filter(desc => desc.length > 0)\n}\n", "import type { ModifierOptions } from '../../../types'\nimport { MODIFIER_ORDER } from '../constants'\nimport { modifierToNotation } from '../modifierToNotation'\n\nexport function processModifierNotations(modifiers: ModifierOptions | undefined): string {\n if (!modifiers) return ''\n\n return MODIFIER_ORDER.map(type => modifierToNotation(type, modifiers[type]))\n .filter((notation): notation is string => typeof notation === 'string')\n .join('')\n}\n", "import type { RollOptions } from '../../types'\n\nexport function optionsToSidesFaces<T>({ sides }: RollOptions<T>): {\n sides: number\n faces?: T[]\n} {\n if (Array.isArray(sides)) {\n return { sides: sides.length, faces: sides }\n }\n return { sides }\n}\n", "import type { RollOptions } from '../../types'\nimport { processModifierDescriptions } from '../modifiers'\nimport { optionsToSidesFaces } from './optionsToSidesFaces'\n\nexport function optionsToDescription<T>(options: RollOptions<T>): string[] {\n const { modifiers, quantity = 1, arithmetic } = options\n const { sides, faces = [] } = optionsToSidesFaces(options)\n const descriptor = quantity === 1 ? 'die' : 'dice'\n const coreDescription = `Roll ${quantity} ${sides}-sided ${descriptor}`\n const customCoreDescription = `Roll ${quantity} Dice with the following sides: ${faces.join(', ')}`\n const modifierDescription = processModifierDescriptions(modifiers)\n const arithmeticDescription = arithmetic === 'subtract' ? 'and Subtract the result' : ''\n\n return [\n faces.length ? customCoreDescription : coreDescription,\n ...modifierDescription,\n arithmeticDescription\n ].filter(Boolean)\n}\n", "export const coreNotationPattern: RegExp = /[+-]?\\d+[Dd]\\d+/ satisfies RegExp\n", "import { coreNotationPattern } from './coreNotationPattern'\nimport {\n capPattern,\n dropConstraintsPattern,\n dropHighestPattern,\n dropLowestPattern,\n explodePattern,\n minusPattern,\n plusPattern,\n replacePattern,\n rerollPattern,\n uniquePattern\n} from './modifierPatterns'\n\nconst completeRollPatternSource = [\n coreNotationPattern.source,\n dropHighestPattern.source,\n dropLowestPattern.source,\n dropConstraintsPattern.source,\n explodePattern.source,\n uniquePattern.source,\n replacePattern.source,\n rerollPattern.source,\n capPattern.source,\n plusPattern.source,\n minusPattern.source\n].join('|')\n\nexport const completeRollPattern: RegExp = new RegExp(\n completeRollPatternSource,\n 'g'\n) satisfies RegExp\n", "import { completeRollPattern, coreNotationPattern } from './lib/patterns'\nimport type { DiceNotation } from './types'\n\nexport function isDiceNotation(argument: unknown): argument is DiceNotation {\n if (typeof argument !== 'string') return false\n const trimmedArg = argument.trim()\n const basicTest = !!coreNotationPattern.test(trimmedArg)\n if (!basicTest) return false\n\n const cleanArg = trimmedArg.replace(/\\s/g, '')\n const remaining = cleanArg.replaceAll(completeRollPattern, '')\n return remaining.length === 0\n}\n", "import { isDiceNotation } from '../../isDiceNotation'\nimport type { DiceNotation, RollOptions } from '../../types'\nimport { processModifierNotations } from '../modifiers'\nimport { optionsToSidesFaces } from './optionsToSidesFaces'\n\nexport function optionsToNotation<T>(options: RollOptions<T>): DiceNotation {\n const { modifiers, quantity = 1, arithmetic } = options\n const { sides } = optionsToSidesFaces(options)\n const arithmeticPrefix = arithmetic === 'subtract' ? '-' : ''\n const modifierSuffix = processModifierNotations(modifiers)\n const proposed = `${arithmeticPrefix}${quantity}d${sides}${modifierSuffix}`\n\n if (!isDiceNotation(proposed)) {\n throw new Error(`Invalid notation generated: ${proposed}`)\n }\n return proposed\n}\n", "export function listOfNotations(notationString: string, coreMatches: RegExpMatchArray[]): string[] {\n const completeExpressions: string[] = []\n\n for (const [i, currentMatch] of coreMatches.entries()) {\n const nextMatch = coreMatches[i + 1]\n\n if (currentMatch.index === undefined) continue\n\n let startPos: number\n\n if (i === 0) {\n startPos = 0\n } else {\n const prevMatch = coreMatches[i - 1]\n const prevEndPos = prevMatch ? Number(prevMatch.index) + prevMatch[0].length : 0\n const textBetween = notationString.slice(prevEndPos, currentMatch.index)\n const arithmeticMatch = /([+-])/.exec(textBetween)\n\n if (arithmeticMatch?.[1]) {\n startPos = prevEndPos + textBetween.indexOf(arithmeticMatch[1])\n } else {\n startPos = currentMatch.index\n }\n }\n\n const endPos = nextMatch?.index ?? notationString.length\n\n const expression = notationString.slice(startPos, endPos).trim()\n if (expression) {\n completeExpressions.push(expression)\n }\n }\n return completeExpressions\n}\n", "import type { RollOptions } from '../../types'\nimport { coreNotationPattern } from '../patterns'\nimport { parseModifiers } from '../modifiers'\n\nexport function singleNotationToOptions<T>(notationString: string): RollOptions<T> {\n const trimmedNotationString = notationString.trim()\n const coreNotationMatch = trimmedNotationString.match(coreNotationPattern)?.at(0) ?? ''\n const modifiersString = trimmedNotationString.replace(coreNotationMatch, '')\n const [quantityNot, sidesNotation = ''] = coreNotationMatch.split(/[Dd]/)\n\n const core = {\n quantity: Math.abs(Number(quantityNot)),\n arithmetic: Number(quantityNot) < 0 ? ('subtract' as const) : ('add' as const),\n sides: Number(sidesNotation)\n }\n\n if (modifiersString.length === 0) {\n return core\n }\n\n return {\n ...core,\n modifiers: parseModifiers(modifiersString)\n }\n}\n", "import type { DiceNotation, RollOptions } from '../../types'\nimport { coreNotationPattern } from '../patterns'\nimport { listOfNotations } from './listOfNotations'\nimport { singleNotationToOptions } from './singleNotationToOptions'\n\nconst globalCoreNotationPattern = new RegExp(coreNotationPattern.source, 'g')\n\nexport function notationToOptions<T = string>(notationString: DiceNotation): RollOptions<T>[] {\n const coreMatches = Array.from(notationString.matchAll(globalCoreNotationPattern))\n\n if (coreMatches.length <= 1) {\n return [singleNotationToOptions(notationString)]\n }\n\n return listOfNotations(notationString, coreMatches).map(singleNotationToOptions<T>)\n}\n", "import { isDiceNotation } from '../../isDiceNotation'\nimport { notationToOptions } from '../../lib/notation'\nimport type { RollArgument, RollOptions } from '../../types'\n\nexport function optionsFromArgument<T>(argument: RollArgument<T>): RollOptions<T>[] {\n if (isDiceNotation(argument)) {\n return [...notationToOptions<T>(argument)]\n }\n\n if (typeof argument === 'string' || typeof argument === 'number') {\n return [{ quantity: 1, sides: Number(argument) }]\n }\n\n return [argument]\n}\n", "import {\n optionsToDescription,\n optionsToNotation,\n optionsToSidesFaces\n} from '../../lib/transformers'\nimport type { RollArgument, RollParams } from '../../types'\nimport { optionsFromArgument } from './optionsFromArgument'\n\nexport function argToParameter<T>(argument: RollArgument<T>, position: number): RollParams<T>[] {\n const allOptions = optionsFromArgument(argument)\n return allOptions.map((options, index) => {\n const indexLabel = index === 0 ? '' : `-${index + 1}`\n const {\n quantity = 1,\n arithmetic = 'add',\n modifiers = {},\n key = `Roll ${position}${indexLabel}`\n } = options\n return {\n ...options,\n ...optionsToSidesFaces(options),\n key,\n modifiers,\n quantity,\n arithmetic,\n argument,\n notation: optionsToNotation(options),\n description: optionsToDescription(options)\n }\n })\n}\n", "export function coreRandom(max: number): number {\n return (Math.random() * max) | 0\n}\n", "import { coreRandom } from './coreRandom'\n\nexport function coreSpreadRolls(quantity: number, max: number): number[] {\n if (quantity <= 0) return []\n\n return Array.from({ length: quantity }, () => coreRandom(max) + 1)\n}\n", "import { coreRandom } from '../../lib/random'\nimport { MODIFIER_ORDER } from '../../lib/modifiers/constants'\nimport type { NumericRollBonus, RollParams, RollRecord } from '../../types'\nimport { applyModifiers } from '../../lib/modifiers'\n\nexport function generateHistory<T>(\n { sides, quantity = 1, modifiers = {} }: RollParams<T>,\n rolls: RollRecord<T>['modifierHistory']['initialRolls']\n): RollRecord<T>['modifierHistory'] {\n const hasModifiers = MODIFIER_ORDER.some(key => modifiers[key] !== undefined)\n\n if (!hasModifiers) {\n const modifiedRolls = Array.from(rolls, roll => Number(roll))\n\n return {\n total: rolls.reduce((acc, cur) => Number(acc) + cur, 0),\n initialRolls: rolls,\n modifiedRolls,\n logs: []\n }\n }\n\n const rollOne = (): number => coreRandom(sides)\n\n const rollParams = { sides, quantity }\n\n const initialRollsAsNumbers = Array.from(rolls, roll => Number(roll))\n\n const bonuses: NumericRollBonus = {\n simpleMathModifier: 0,\n rolls: initialRollsAsNumbers,\n logs: []\n }\n\n for (const modifierKey of MODIFIER_ORDER) {\n if (modifiers[modifierKey]) {\n const result = applyModifiers(\n modifierKey,\n modifiers[modifierKey],\n bonuses,\n rollParams,\n rollOne\n )\n\n bonuses.rolls = result.rolls\n bonuses.simpleMathModifier = result.simpleMathModifier\n bonuses.logs = result.logs\n }\n }\n\n return {\n modifiedRolls: bonuses.rolls,\n initialRolls: rolls,\n total: bonuses.rolls.reduce((acc, cur) => Number(acc) + cur, bonuses.simpleMathModifier),\n logs: bonuses.logs\n }\n}\n", "import { coreSpreadRolls } from '../../lib/random'\nimport type { RollParams, RollRecord } from '../../types'\nimport { generateHistory } from './generateHistory'\n\nexport function generateRollRecord<T>(parameters: RollParams<T>): RollRecord<T> {\n const { sides, quantity = 1, faces, arithmetic, description } = parameters\n const initialRolls = coreSpreadRolls(quantity, sides)\n const isNegative = arithmetic === 'subtract'\n const customResults = faces\n ? { customResults: initialRolls.map(roll => faces[roll - 1] as T) }\n : {}\n const modifierHistory = generateHistory(parameters, initialRolls)\n\n return {\n ...customResults,\n parameters,\n description,\n modifierHistory,\n rolls: modifierHistory.modifiedRolls,\n appliedTotal: isNegative ? -modifierHistory.total : modifierHistory.total,\n total: modifierHistory.total\n }\n}\n", "import type { RollArgument, RollerRollResult } from '../types'\nimport { argToParameter } from './argToParameter'\nimport { generateRollRecord } from './generateRollRecord'\n\nexport function roll<T = string>(...args: RollArgument<T>[]): RollerRollResult<T> {\n const parameters = args.flatMap((arg, index) => argToParameter(arg, index + 1))\n const rolls = parameters.map(parameter => generateRollRecord(parameter))\n const total = rolls.reduce((acc, cur) => {\n const factor = cur.parameters.arithmetic === 'subtract' ? -1 : 1\n return acc + cur.total * factor\n }, 0)\n\n const isCustom = rolls.every(roll => roll.customResults)\n const result = rolls.flatMap(roll =>\n isCustom ? (roll.customResults ?? []) : (roll.rolls.map(String) as T)\n )\n\n return {\n rolls,\n result,\n total\n }\n}\n", "import { isDiceNotation } from './isDiceNotation'\nimport type { ValidationResult } from './types'\nimport { optionsToDescription, optionsToNotation } from './lib/transformers'\nimport { notationToOptions } from './lib/notation'\n\nexport function validateNotation(notation: string): ValidationResult {\n if (!isDiceNotation(notation)) {\n return {\n valid: false,\n argument: notation\n }\n }\n\n const options = notationToOptions(notation)\n return {\n valid: true,\n argument: notation,\n options,\n notation: options.map(o => optionsToNotation(o)),\n description: options.map(o => optionsToDescription(o))\n }\n}\n" ], "mappings": "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