Skip to content

Dice Rolling

Chris3606 edited this page Feb 9, 2018 · 24 revisions

The GoRogue.DiceNotation interface provides functions capable of parsing and rolling dice via full Dice Notation syntax.

Code Examples

Code examples in this section show only code in the Main function. The code provided assumes that the following "using" statements are at the top of the code file:

using GoRogue.DiceNotation;
using GoRogue.Random;

Basic Dice Rolling

Basic dice rolls are easy to perform with the Dice.Roll function. This function takes the dice notation expression to parse and (optionally) a random number generator (see Random Number Generation for details):

// Rolls 1 6-sided die.  When no RNG is specified, SingletonRandom.DefaultRNG is used.
int result = Dice.Roll("1d6");
System.Console.WriteLine("Rolling 1d6: " + result);
// Also rolls 1 6-sided die; we can omit the number of dice to roll if it should be 1.
int result2 = Dice.Roll("d6");
System.Console.WriteLine("Rolling 1d6 again: " + result2);

// Custom RNG; see Random Number Generation docs for usage details
IRandom customRNG = new DotNetRandom(); 
// Rolls 2d6 and adds 3 to the sum of the result.  We also specify a custom RNG to use
// for rolling:
int result3 = Dice.Roll("2d6+3", customRNG);
System.Console.WriteLine("Rolling 2d6+3 with custom RNG: " + result3);

// We can also do multiplication and (SOON) division in the expression

// Rolls 2d6, adds 2, multiplies that result by 3, and returns the result
int result4 = Dice.Roll("(2d6+2)*3");
System.Console.WriteLine("Rolling (2d6+2)*3: " + result4);
// Rolls 2d6,subtracts 2, divides that result by 3, and returns the result
// This division operation is not currently supported -- see issue #9
// int result5 = Dice.Roll("(2d6-2)/3");
System.Console.WriteLine("Rolling (2d6-2)/3: " + result5);

// GoRogue also supports "keep" expressions

// Rolls 3 d6, adds only the highest 2 together, and returns the result
int result6 = Dice.Roll("3d6k2");
System.Console.WriteLine("Rolling 3d6k2: " + result6);

See general information on Dice Notation syntax for details.

DiceExpression

In the case where a single dice expression, or similar dice expressions, are used multiple times, the Dice.Parse function may prove useful. It can generate a DiceExpression instance that can later be used to roll the expression one or more times, thus avoiding expensive parsing of a dice notation expression for each roll:

DiceExpression expr = Dice.Parse("2d6+2");

DiceResult result1 = Dice.Roll(); // Rolls 2d6+2 with the default RNG
IRandom rng = new DotNetRandom();
DiceResult result2 = Dice.Roll(rng); // Rolls 2d6+2, using the RNG specified.
// Prints the result of the rolls
System.Console.WriteLine("result1 result: " + result1.Value);
System.Console.WriteLine("result2 result: " + result2.Value);

Note that the return type of the Roll function is a DiceResult instance, rather than an int. The DiceResult's Value property can be used to access the result of the roll, as demonstrated above. This type is discussed in more detail below.

For convenience, DiceExpression also provides MaxRoll and MinRoll functions that allow retrieval of the maximum possible and minimum possible values that could be rolled, respectively:

DiceExpression expr = Dice.Parse("2d6+2");

DiceResult minResult = expr.MinRoll(); // Gets the minimum possible result (3)
DiceResult maxResult = expr.MaxRoll(); // Gets the maximum possible result (8)

System.Console.WriteLine("Min for 2d6+2: " + minResult.Value);
System.Console.WriteLine("Max for 2d6+2: " + maxResult.Value);

Modifying a DiceExpression After Creation

While a DiceExpression instance cannot be directly modified in terms of the expression it rolls, DiceExpression does provide various functions that can be used to generate a new DiceExpression that is effectively a copy of the original, but with additional expressions added. These functions can be used to prevent full re-parsing of an expression when dice expressions are similar, but not the same, across many rolls.

Adding a Constant Term

The Constant function can be used to add a constant term:

DiceExpression expr1 = Dice.Parse("2d6x2");
DiceExpression expr2 = expr1.Constant(5); // expr2 rolls "2d6x2+5"

System.Console.WriteLine("Rolling 2d6x2+5: " + expr2.Roll().Value);

Adding Dice Terms

We can use the Dice function to add dice terms to an existing expression:

DiceExpression expr1 = Dice.Parse("2d6+2");

// Adds 2d4 to the original expression; expr2 rolls (2d6+2)+(2d4)
DiceExpression expr2 = expr1.Dice(2, 4);
System.Console.WriteLine("Rolling (2d6+2)+(2d4): " + expr2.Roll().Value);

// Adds 3*2d4 to the original expression; expr2 now rolls (2d6+2)+(3*2d4)
expr2 = expr1.Dice(2, 4, 3);
System.Console.WriteLine("Rolling (2d6+2)+(3*2d4): " + expr2.Roll().Value);

// Adds 1*4d4k3, eg. simply 4d4k3, to the original expression; expr2 now
// rolls (2d6+2)+(4d4k3)
expr2 = expr1.Dice(2, 4, 1, 3);
System.Console.WriteLine("Rolling (2d6+2)+(4d4k3): " + expr2.Roll().Value);

In the event that we are only adding a term with only a single die, we can use the Die function as a shortcut:

DiceExpression expr1 = Dice.Parse("2d6+2");

// Adds 1d4 to the original expression; expr2 rolls (2d6+2)+(1d4)
expr2 = expr1.Die(4);
System.Console.WriteLine("Rolling (2d6+2)+(1d4): " + expr2.Roll().Value);

// Adds 2*1d4 to the original expression; expr2 now rolls (2d6+2)+(3*1d4)
expr2 = expr1.Die(4, 3);
System.Console.WriteLine("Rolling (2d6+2)+(3*1d4): " + expr2.Roll().Value);

Advanced Usage

The GoRogue.DiceNotation namespace also provides a number of interfaces and functions to enable more advanced or custom functionality with respect to dice and dice expressions.

DiceResult Term Results

The DiceResult class, in addition to providing the value of a DiceExpression.Roll result as shown above, also provides an IEnumerable of TermResult instances for each separate term:

DiceExpression expr = Dice.Parse("1d6+4");

DiceResult result = expr.Roll();
System.Console.WriteLine("Roll result: " + result.Value); // Prints the roll result

// We have two term results -- one representing the 1d6 (dice term), and one representing
// the +4 (constant term)
System.Console.WriteLine("Number of term results: " + result.Results.Count)

TermResult

TermResult instances are used to represent results of evaluating terms during a DiceExpression, and are the type of the DiceResult.Results collection demonstrated above. TermResult has three fields:

  1. Value, which indicates the numerical value of the term's result before any scalars are applied
  2. Scalar, which is a scalar to be multiplied by Value to get the total term result
  3. TermType, which is either "constant" for a constant term, or "d[sides]" for a dice term (eg, "d6" for a 6-sided die term)
DiceExpression expr = Dice.Parse("1d6+5");
DiceResult diceResult = expr.Roll();
System.Console.WriteLine("Result of roll was " + diceResult.Value);

// Print out each term -- we have a constant term with value 5, and a "d6" (dice) term
foreach (TermResult term in diceResult.Results)
    System.Console.WriteLine("Term Type: " + term.TermType + ", Result: " + term.Value + " * " + term.Scalar);

Manual Creation of a Dice Expression

While one typically retrieves a DiceExpression instance via the Dice.Parse function, it is possible to manually create them from a list of terms. Typically this might be necessary if creating a custom parser, or creating a dice expression not consistent with standard dice notation.

IDiceExpressionTerm[] terms = { new ConstantTerm(3), new DiceTerm(2, 6, 1) };
// Expression rolls a dice term with 2d6, scalar 1, and a constant term 3.
// Equivalent to expression "2d6+3"
DiceExpression expr = new DiceExpression(terms);

System.Console.WriteLine(expr.Roll().Value);

Custom Terms

In the event that you require terms that are not part of standard dice notation, you may implement the GoRogue.DiceNotation.Terms.IDiceExpressionTerm interface. This interface simply specifies how to "roll" that term and get a result, via the GetResults function. See API documentation of IDiceExpressionTerm for details. As well, the existing terms in that namespace (DiceTerm and ConstantTerm) serve as examples.

Custom Parsers

In the event that you need to implement some addition to the dice notation syntax, either to support a custom term or a custom operation, you may implement the IDiceNotation interface. It specifies a single function Parse, which takes a string to parse and returns a DiceExpression.

Once the custom DiceExpression is written, you may set the Dice.DiceParser variable to an instance of your custom implementation -- this will cause the Dice.Parse and Dice.Roll functions to use your custom implementation.