/// <summary> /// Rolls dice according to the given dice expression and configuration. Please see /// the documentation for more details on the formatting for this string. /// </summary> /// <param name="diceExpr">Dice expression to roll.</param> /// <param name="config">Configuration to use. If null, the DefaultConfig is used instead.</param> /// <param name="data">Additional data that is scoped to this roll.</param> /// <returns>A RollResult containing the details of the roll.</returns> public static RollResult Roll(string diceExpr, RollerConfig config, RollData data) { if (diceExpr == null) { throw new ArgumentNullException(nameof(diceExpr)); } if (data == null) { data = new RollData(); } if (config == null) { config = DefaultConfig; } if (config.MaxDice < 1 || config.MaxRecursionDepth < 0 || config.MaxRerolls < 0 || config.MaxSides < 1) { throw new InvalidOperationException("RollerConfig is invalid, cannot have negative values for maximums."); } data.Config = config; var root = Parse(diceExpr, data); return(Roll(root, data)); }
internal FunctionContext(FunctionScope scope, string name, IReadOnlyList <DiceAST> arguments, RollData data) { Scope = scope; Name = name; Arguments = arguments; Expression = null; Value = Decimal.MinValue; ValueType = ResultType.Total; Values = null; Data = data; }
internal RollResult(RollData data, DiceAST rollRoot, int numRolls) { RollRoot = rollRoot; // cache some commonly-referenced information directly in this class instead of requiring // the user to drill down into RollRoot for everything (and because RollRoot isn't available // if deserializing). ResultType = rollRoot.ValueType; Value = rollRoot.Value; Values = rollRoot.Values; NumRolls = numRolls; Expression = rollRoot.ToString(); Metadata = data.Metadata; AllRolls = data.InternalContext.AllRolls; AllMacros = data.InternalContext.AllMacros; }
/// <summary> /// Parses the diceExpr into an AST without evaluating it. /// </summary> /// <param name="diceExpr"></param> /// <param name="data"></param> /// <returns></returns> internal static DiceAST Parse(string diceExpr, RollData data) { // parse diceExpr var inputStream = new AntlrInputStream(diceExpr); var lexer = new DiceGrammarLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new DiceGrammarParser(tokenStream); var walker = new ParseTreeWalker(); var listener = new DiceGrammarListener(data); lexer.AddErrorListener(new DiceErrorListener()); parser.AddErrorListener(new DiceErrorListener()); var context = parser.input(); walker.Walk(listener, context); return(listener.Root); }
internal MacroContext(string param, RollData data) { Value = Decimal.MinValue; Values = null; ValueType = ResultType.Total; Param = param; Data = data; // parse Param Param = Param.Trim(); string[] args = Param.Split(':'); for (int i = 0; i < args.Length; i++) { args[i] = args[i].Trim(); } Name = args[0]; Arguments = args; }
/// <summary> /// Evaluates the dice expression using the default configuration, /// and fixing all dice to roll their maximum value. /// </summary> /// <param name="diceExpr">Dice expression to evaluate</param> /// <param name="config">Configuration to use. If null, the DefaultConfig is used instead.</param> /// <param name="data">Additional data that is scoped to this roll.</param> /// <returns>A RollResult containing the details of the roll.</returns> public static RollResult Max(string diceExpr, RollerConfig config, RollData data) { if (config == null) { config = DefaultConfig; } var savedRollDie = config.RollDie; config.RollDie = (min, max) => max; try { return(Roll(diceExpr, config, data)); } finally { config.RollDie = savedRollDie; } }
/// <summary> /// Evaluates the dice expression using the default configuration, /// and fixing all dice to roll their average value. /// </summary> /// <param name="diceExpr">Dice expression to evaluate</param> /// <param name="config">Configuration to use. If null, the DefaultConfig is used instead.</param> /// <param name="data">Additional data that is scoped to this roll.</param> /// <returns>A RollResult containing the details of the roll.</returns> public static RollResult Average(string diceExpr, RollerConfig config, RollData data) { if (config == null) { config = DefaultConfig; } if (data == null) { data = new RollData(); } var savedRollDie = config.RollDie; config.RollDie = (min, max) => { // we round down if we're doing an odd roll and up on an even roll. // e.g. in 2d6 the first die is 3 and the second is 4. var avg = (min + max) / 2.0; if (data.InternalContext.AllRolls.Count % 2 == 0) { // we've completed an even number of rolls, which means the current roll is odd. Round down. return((int)Math.Floor(avg)); } else { return((int)Math.Ceiling(avg)); } }; try { return(Roll(diceExpr, config, data)); } finally { config.RollDie = savedRollDie; } }
/// <summary> /// Evaluates the root of the tree, returning the RollResult. /// </summary> /// <param name="root"></param> /// <param name="data"></param> /// <returns></returns> internal static RollResult Roll(DiceAST root, RollData data) { var numRolls = root.Evaluate(data, root, 0); return(new RollResult(data, root, (int)numRolls)); }