Getting Started

This guide walks you through installing d20Tek.DiceNotation and using its core features to roll dice in your .NET application.

Prerequisites

  • A .NET project targeting .NET 9 or later.
  • A package source configured for NuGet (the default nuget.org source is sufficient).

Installation

Install the NuGet package using whichever workflow you prefer.

Package Manager Console:

PM > Install-Package d20tek-dicenotation

.NET CLI:

dotnet add package d20tek-dicenotation

You can also install it through the Visual Studio NuGet Package Manager UI by searching for "d20tek-dicenotation".

Your First Roll

The primary entry point is the IDice interface and its default implementation, Dice. Create a Dice instance and call Roll with a dice notation string.

using d20Tek.DiceNotation;
using d20Tek.DiceNotation.DieRoller;
using d20Tek.DiceNotation.Results;

IDice dice = new Dice();
DiceResult result = dice.Roll("d20+4", new RandomDieRoller());

Console.WriteLine($"Roll result = {result.Value}");

If you omit the die roller argument, the roll uses the default die roller configured on the instance:

IDice dice = new Dice();
DiceResult result = dice.Roll("d20+4");

Two Ways to Build Expressions

The library supports two equivalent approaches for describing what to roll. Both produce the same kind of DiceResult.

1. Dice Notation Strings

Parse a string that follows the dice notation language. This is the most compact approach and is ideal when expressions come from configuration, user input, or game data.

IDice dice = new Dice();
DiceResult result = dice.Roll("4d6k3 + d8 + 5", new RandomDieRoller());
Console.WriteLine($"Roll result = {result.Value}");

2. The Fluent Programmatic API

Build the expression in code by chaining operations with DiceExpression. This approach is strongly typed and convenient when you assemble expressions dynamically.

IDice dice = new Dice();

// Equivalent of dice expression: 4d6k3 + d8 + 5
DiceExpression expression = DiceExpression.Create()
										  .AddDice(6, 4, choose: 3)
										  .AddDice(8)
										  .AddConstant(5);

DiceResult result = dice.Roll(expression, new RandomDieRoller());
Console.WriteLine($"Roll result = {result.Value}");

Working With the Result

Every roll returns a DiceResult that contains more than just the total. The most commonly used members are shown below.

DiceResult result = dice.Roll("3d6+2", new RandomDieRoller());

Console.WriteLine(result.Value);            // The final calculated total.
Console.WriteLine(result.DiceExpression);   // The normalized expression that was rolled.
Console.WriteLine(result.DieRollerUsed);    // The die roller type that produced the roll.
Console.WriteLine(result.RollsDisplayText); // A friendly string of the individual dice rolls.

foreach (TermResult term in result.Results)
{
	Console.WriteLine($"{term.Type}: {term.Value} (scalar {term.Scalar})");
}

Handling Errors

When an expression cannot be parsed or evaluated, the result reports the failure rather than throwing during normal use. Check HasError before relying on the value.

DiceResult result = dice.Roll("not-a-valid-expression");

if (result.HasError)
{
	Console.WriteLine($"Could not roll: {result.Error}");
}
else
{
	Console.WriteLine($"Roll result = {result.Value}");
}

Choosing a Die Roller

All roll operations use an IDieRoller to generate random values. The library provides several implementations for different needs.

Die roller Use it when
RandomDieRoller You want general-purpose randomness backed by System.Random.
CryptoDieRoller You need cryptographically strong random numbers.
MathNetDieRoller You want an advanced random source from Math.NET Numerics.
ConstantDieRoller You need deterministic, repeatable values (ideal for unit tests).
// Deterministic rolls for a test: every die returns 4.
IDice dice = new Dice();
DiceResult result = dice.Roll("3d6", new ConstantDieRoller(4));
// result.Value == 12

If none of these suit your needs, implement the IDieRoller interface to provide your own random number generation strategy.

Configuring Default Behavior

You can customize default dice behavior by supplying an IDiceConfiguration when constructing Dice. The configuration controls the default number of die sides, whether results are bounded by a minimum value, and which die roller is used when no explicit roller is passed.

var config = new DiceConfiguration();
config.SetDefaultDieSides(6);
config.SetHasBoundedResult(true);
config.SetBoundedMinimumResult(1);
config.SetDefaultDieRoller(new CryptoDieRoller());

IDice dice = new Dice(config);

// Uses the configured default die roller and default die sides.
DiceResult result = dice.Roll("3d");

Tracking Roll Statistics

To collect statistics about the dice you roll, attach a tracker to a die roller. The tracker records every roll and can produce frequency distributions or serialize the data to JSON.

using d20Tek.DiceNotation.DieRoller;

IDieRollTracker tracker = new DieRollTracker();
IDieRoller roller = new RandomDieRoller(tracker);
IDice dice = new Dice();

for (int i = 0; i < 100; i++)
{
	dice.Roll("d20", roller);
}

// Aggregate frequency data across all recorded rolls.
IList<AggregateDieTrackingData> frequencies = await tracker.GetFrequencyDataViewAsync();
foreach (AggregateDieTrackingData entry in frequencies)
{
	Console.WriteLine($"d{entry.DieSides} => {entry.Result}: {entry.Count} times ({entry.Percentage}%)");
}

// Persist and reload the tracked data.
string json = await tracker.ToJsonAsync();
await tracker.LoadFromJsonAsync(json);

For a lightweight, synchronous alternative that only keeps aggregate counts, use AggregateRollTracker with its GetFrequencyDataView, ToJson, and LoadFromJson methods.

Next Steps

  • Review the full API Reference for every public type and member.
  • Read the Introduction for an overview of why the library exists and what it offers.
An unhandled error has occurred. Reload 🗙