JSPM

  • Created
  • Published
  • Downloads 40
  • Score
    100M100P100Q87632F
  • License MIT

TypeScript-first dice notation engine for tabletop RPGs, game development, and probability simulations

Package Exports

  • @randsum/roller
  • @randsum/roller/docs
  • @randsum/roller/errors
  • @randsum/roller/package.json
  • @randsum/roller/roll
  • @randsum/roller/tokenize
  • @randsum/roller/trace
  • @randsum/roller/validate

Readme

Randsum Logo. A Dotted D6 rolled a 6 with the dots arranged to look like an R.

@randsum/roller

A Zero Dependency, Typescript-First, Bun-Native Dice Notation and Rolling Engine

Throw Dice, Not Exceptions.

npm version bundle size Types License Downloads codecov

A Zero Dependency, Typescript-First, Bun-Native Dice Notation and Rolling Engine. Throw Dice, Not Exceptions.

Installation

npm install @randsum/roller
# or
bun add @randsum/roller

Usage

import { roll } from "@randsum/roller"

// Three ways to roll: number, notation string, or options object
roll(20) // Roll 1d20
roll("4d6L") // Roll 4d6, drop lowest
roll({ sides: 6, quantity: 4, modifiers: { drop: { lowest: 1 } } })

// Complex modifiers
roll("2d20L") // Advantage (drop lowest, keep highest)
roll("4d6!R{<3}") // Exploding dice, reroll below 3
roll("1d20+5", "2d6+3") // Multiple rolls combined

CLI

npx @randsum/cli 2d20    # Roll two d20s
npx @randsum/cli 4d6L    # Roll 4d6, drop lowest
npx @randsum/cli 3d8+2   # Roll 3d8 and add 2

API

roll(...args)

The main function accepts numbers, notation strings, or options objects.

const result = roll("2d6+3")

result.total // Final total after all modifiers
result.values // Array of individual die values
result.rolls // Full roll records with modifier history

roll() throws on invalid input. Wrap calls in try/catch:

import { roll, RandsumError } from "@randsum/roller"

try {
  const result = roll(userInput)
  console.log(result.total)
} catch (e) {
  if (e instanceof RandsumError) {
    console.error(e.message)
  }
}

Notation Reference

Notation Description
4d6 Roll 4 six-sided dice
4d6+2 Add 2 to total
4d6L Drop lowest
4d6H Drop highest
2d20L Drop lowest (advantage)
2d20H Drop highest (disadvantage)
4d6! Exploding dice
4d6R{<3} Reroll values below 3
4d6U Unique rolls only

See RANDSUM_DICE_NOTATION.md for the complete notation guide.

Other Exports

import {
  // Validation
  validateNotation,
  isDiceNotation,
  validateRollOptions,

  // Conversion utilities
  optionsToNotation,
  optionsToDescription,

  // Error types
  RandsumError,
  NotationParseError,
  ValidationError
} from "@randsum/roller"

Subpath Exports

Use these subpaths to import only what you need without pulling in the full roll engine.

@randsum/roller/docs — Static modifier documentation. Zero dependencies, safe for any environment.

import { MODIFIER_DOCS } from "@randsum/roller/docs"
import type { ModifierDoc } from "@randsum/roller/docs"

// Keyed by notation shorthand
const doc = MODIFIER_DOCS["L"] // Drop Lowest
const doc = MODIFIER_DOCS["!"] // Explode
const doc = MODIFIER_DOCS["R{..}"] // Reroll

@randsum/roller/trace — Transform a RollRecord into a step-by-step display trace.

import { traceRoll, formatAsMath } from "@randsum/roller/trace"
import type { RollTraceStep } from "@randsum/roller/trace"

const result = roll("4d6L")
const steps = traceRoll(result.rolls[0]!)
// [
//   { kind: 'rolls', label: 'Rolled', unchanged: [...], removed: [], added: [] },
//   { kind: 'rolls', label: 'Drop Lowest 1', unchanged: [...], removed: [2], added: [] },
//   { kind: 'finalRolls', rolls: [...], arithmeticDelta: 0 }
// ]

formatAsMath([3, 4, 5]) // "3 + 4 + 5"
formatAsMath([3, 4, 5], -1) // "3 + 4 + 5 - 1"

RollTraceStep is a discriminated union on kind: 'rolls' | 'divider' | 'arithmetic' | 'finalRolls'.

@randsum/roller/tokenize — Notation tokenizer without the roll engine.

import { tokenize } from "@randsum/roller/tokenize"

@randsum/roller/validate — Validation utilities only.

import { validateNotation, isDiceNotation } from "@randsum/roller/validate"
Made with 👹 by RANDSUM