UNPKG

@randsum/notation

Version:

Dice notation parser and types for the @randsum ecosystem

318 lines (317 loc) 12.3 kB
interface ComparisonOptions { /** Threshold for "greater than" comparisons (strict: roll > N) */ greaterThan?: number; /** Threshold for "greater than or equal to" comparisons (roll >= N) */ greaterThanOrEqual?: number; /** Threshold for "less than" comparisons (strict: roll < N) */ lessThan?: number; /** Threshold for "less than or equal to" comparisons (roll <= N) */ lessThanOrEqual?: number; /** Exact values to match */ exact?: number[]; } interface DropOptions extends ComparisonOptions { /** Number of highest dice to drop */ highest?: number; /** Number of lowest dice to drop */ lowest?: number; } interface KeepOptions { /** Number of highest dice to keep */ highest?: number; /** Number of lowest dice to keep */ lowest?: number; } interface RerollOptions extends ComparisonOptions { /** Maximum number of rerolls allowed */ max?: number; } interface ReplaceOptions { /** Value or comparison to match for replacement */ from: number | ComparisonOptions; /** Value to replace matched rolls with */ to: number; } interface UniqueOptions { /** Values that are allowed to repeat */ notUnique: number[]; } interface SuccessCountOptions { /** Threshold for counting successes (rolls >= this value) */ threshold: number; /** Optional: threshold for counting botches/failures (rolls <= this value) */ botchThreshold?: number; } type ModifierConfig = number | boolean | ComparisonOptions | DropOptions | KeepOptions | ReplaceOptions | ReplaceOptions[] | RerollOptions | UniqueOptions | SuccessCountOptions; interface ModifierOptions { /** Cap roll values to a range */ cap?: ComparisonOptions; /** Drop dice from the result */ drop?: DropOptions; /** Keep dice from the result (complement to drop) */ keep?: KeepOptions; /** Replace specific values */ replace?: ReplaceOptions | ReplaceOptions[]; /** Reroll dice matching conditions */ reroll?: RerollOptions; /** Ensure unique values (true or options) */ unique?: boolean | UniqueOptions; /** Exploding dice: reroll and add on max value */ explode?: boolean | number; /** Compounding exploding: add to triggering die instead of creating new dice */ compound?: boolean | number; /** Penetrating exploding: subtract 1 from each subsequent explosion */ penetrate?: boolean | number; /** Count successes instead of summing (for dice pool systems) */ countSuccesses?: SuccessCountOptions; /** Multiply dice result (before +/- arithmetic) */ multiply?: number; /** Add a fixed value to the total */ plus?: number; /** Subtract a fixed value from the total */ minus?: number; /** Multiply final total (after all other modifiers) */ multiplyTotal?: number; } /** * The result of parsing a dice notation string. * Similar to RollOptions but with sides always numeric and * quantity/arithmetic always present. */ interface ParsedNotationOptions { /** Number of dice to roll */ quantity: number; /** How this roll combines with others */ arithmetic: "add" | "subtract"; /** Number of sides on each die */ sides: number; /** Modifiers to apply to the roll */ modifiers?: ModifierOptions; } /** * Template literal type for dice notation strings. */ type DiceNotation = `${number}${"d" | "D"}${number}${string}`; /** * Configuration options for a dice roll. * * @template T - Type for custom dice faces (defaults to string) */ interface RollOptions<T = string> { /** Number of dice to roll (default: 1) */ quantity?: number; /** How this roll combines with others: 'add' or 'subtract' (default: 'add') */ arithmetic?: "add" | "subtract"; /** Number of sides, or array of custom face values */ sides: number | T[]; /** Modifiers to apply to the roll (drop, reroll, explode, etc.) */ modifiers?: ModifierOptions; /** * Optional identifier for this roll in multi-roll expressions. * Default keys are auto-generated as "Roll 1", "Roll 2", etc. */ key?: string | undefined; } /** * Successful validation result. */ interface ValidValidationResult { /** Indicates successful validation */ valid: true; /** Original input as DiceNotation */ argument: DiceNotation; /** Human-readable descriptions for each roll */ description: string[][]; /** Parsed roll options for each roll */ options: ParsedNotationOptions[]; /** Notation strings for each roll */ notation: DiceNotation[]; /** No error on success */ error: null; } /** * Error information from validation. */ interface ValidationErrorInfo { /** Description of what's wrong */ message: string; /** The input that failed validation */ argument: string; } /** * Failed validation result. */ interface InvalidValidationResult { /** Indicates failed validation */ valid: false; /** Original input string */ argument: string; /** Error information */ error: ValidationErrorInfo; } /** * Result of notation validation. */ type ValidationResult = ValidValidationResult | InvalidValidationResult; /** * Notation-only half of a modifier definition. * * Covers pure string transformation concerns: * - Pattern matching * - Parsing notation into options * - Converting options back to notation * - Human-readable description generation * * @template TOptions - The type of options this modifier's notation represents */ interface NotationSchema<TOptions = unknown> { /** Unique identifier matching the key in ModifierOptions */ name: keyof ModifierOptions; /** * Execution priority (lower = earlier). * Shared with ModifierDefinition to determine parse ordering. */ priority: number; /** * Regex pattern to match this modifier in a notation string. * Should NOT include 'g' flag - that is handled by the registry. */ pattern: RegExp; /** * Parse notation string into modifier options. * Receives the full notation string to allow finding multiple matches. * Returns a partial ModifierOptions with just this modifier's key. */ parse: (notation: string) => Partial<ModifierOptions>; /** * Convert options to notation string (e.g., "C{>5}"). * Returns undefined if options would produce empty notation. */ toNotation: (options: TOptions) => string | undefined; /** * Convert options to human-readable description strings. * Returns array of descriptions (some modifiers produce multiple lines). */ toDescription: (options: TOptions) => string[]; } /** * Define a notation schema with type checking. * This is a type-safe factory for creating notation schemas. * * @param schema - Complete notation schema * @returns The schema unchanged (identity function for type checking) */ declare function defineNotationSchema<TOptions>(schema: NotationSchema<TOptions>): NotationSchema<TOptions>; declare function notationToOptions(notationString: string): ParsedNotationOptions[]; declare function listOfNotations(notationString: string, coreMatches: RegExpMatchArray[]): string[]; /** * Type guard that checks if a value is valid dice notation. * * @param argument - Value to check * @returns True if argument is valid dice notation, false otherwise * * @example * ```ts * if (isDiceNotation("4d6L")) { * // TypeScript knows this is DiceNotation here * } * ``` */ declare function isDiceNotation(argument: unknown): argument is DiceNotation; /** * Error thrown when a string is not valid dice notation. */ declare class NotationParseError extends Error { readonly code: "INVALID_NOTATION"; readonly suggestion: string | undefined; constructor(notation: string, reason: string, suggestion?: string); } /** * Validates a string as DiceNotation, throwing if invalid. * * @param input - String to validate * @returns The input narrowed to DiceNotation * @throws NotationParseError if input is not valid dice notation */ declare function notation(input: string): DiceNotation; /** * Validates dice notation and returns parsed information. * * @param notation - String to validate as dice notation * @returns ValidationResult with valid flag and error (if invalid) */ declare function validateNotation(notation: string): ValidationResult; /** * Convert modifier options to a notation string suffix. * Processes modifiers in priority order (cap → multiplyTotal). */ declare function modifiersToNotation(modifiers: ModifierOptions | undefined): string; /** * Convert modifier options to an array of human-readable description strings. * Processes modifiers in priority order (cap → multiplyTotal). */ declare function modifiersToDescription(modifiers: ModifierOptions | undefined): string[]; declare function optionsToSidesFaces<T>({ sides }: RollOptions<T>): { sides: number; faces?: T[]; }; /** * Converts roll options to RANDSUM dice notation string. * * @template T - Type for custom dice faces * @param options - Roll options to convert * @returns Dice notation string (e.g., "4d6L", "2d20H+5") * @throws NotationParseError if generated notation is invalid */ declare function optionsToNotation<T = string>(options: RollOptions<T>): DiceNotation; /** * Converts roll options to a human-readable description. * * @template T - Type for custom dice faces * @param options - Roll options to describe * @returns Array of description strings */ declare function optionsToDescription<T = string>(options: RollOptions<T>): string[]; /** * Detects common typos in dice notation. * * @param notation - Invalid notation to check * @returns Suggested correction, or undefined if no suggestion */ declare function suggestNotationFix(notation: string): string | undefined; declare const coreNotationPattern: RegExp; /** * Parses a condition string into comparison options. */ declare function parseComparisonNotation(conditionString: string): ComparisonOptions; /** * Checks if a comparison options object has any conditions defined. */ declare function hasConditions(options: ComparisonOptions): boolean; declare function formatComparisonDescription({ greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, exact }: ComparisonOptions): string[]; declare function formatComparisonNotation({ greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, exact }: ComparisonOptions): string[]; type TokenType = "core" | "dropLowest" | "dropHighest" | "dropCondition" | "keepHighest" | "keepLowest" | "reroll" | "explode" | "compound" | "penetrate" | "cap" | "replace" | "unique" | "countSuccesses" | "plus" | "minus" | "multiply" | "multiplyTotal" | "unknown"; interface Token { readonly text: string; readonly type: TokenType; readonly start: number; readonly end: number; readonly description: string; } declare function tokenize(notation: string): readonly Token[]; declare function formatHumanList(values: number[]): string; declare const capSchema: NotationSchema<ComparisonOptions>; declare const dropSchema: NotationSchema<DropOptions>; declare const keepSchema: NotationSchema<KeepOptions>; declare const replaceSchema: NotationSchema<ReplaceOptions | ReplaceOptions[]>; declare const rerollSchema: NotationSchema<RerollOptions>; declare const explodeSchema: NotationSchema<boolean | number>; declare const compoundSchema: NotationSchema<boolean | number>; declare const penetrateSchema: NotationSchema<boolean | number>; declare const uniqueSchema: NotationSchema<boolean | UniqueOptions>; declare const countSuccessesSchema: NotationSchema<SuccessCountOptions>; declare const multiplySchema: NotationSchema<number>; declare const plusSchema: NotationSchema<number>; declare const minusSchema: NotationSchema<number>; declare const multiplyTotalSchema: NotationSchema<number>; export { validateNotation, uniqueSchema, tokenize, suggestNotationFix, rerollSchema, replaceSchema, plusSchema, penetrateSchema, parseComparisonNotation, optionsToSidesFaces, optionsToNotation, optionsToDescription, notationToOptions, notation, multiplyTotalSchema, multiplySchema, modifiersToNotation, modifiersToDescription, minusSchema, listOfNotations, keepSchema, isDiceNotation, hasConditions, formatHumanList, formatComparisonNotation, formatComparisonDescription, explodeSchema, dropSchema, defineNotationSchema, countSuccessesSchema, coreNotationPattern, compoundSchema, capSchema, ValidationResult, ValidationErrorInfo, ValidValidationResult, UniqueOptions, TokenType, Token, SuccessCountOptions, RollOptions, RerollOptions, ReplaceOptions, ParsedNotationOptions, NotationSchema, NotationParseError, ModifierOptions, ModifierConfig, KeepOptions, InvalidValidationResult, DropOptions, DiceNotation, ComparisonOptions };