dice

package
v4.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2022 License: BSD-3-Clause Imports: 8 Imported by: 0

Documentation

Overview

Package dice provides a general facility for generating random numbers in fantasy role-playing games.

The preferred usage model is to use the higher-level abstraction provided by DieRoller, which rolls dice as described by strings. For example:

label, results, err := Roll("d20+16 | c")
label, result, err := RollOnce("15d6 + 15 fire + 1 acid")

If you need to keep the die roller itself around after the dice are rolled, to query its status, or to produce a repeatable string of die rolls given a custom seed or number generator, create a new DieRoller value and reuse that as needed:

dr, err := NewDieRoller()
label, results, err := dr.DoRoll("d20+16 | c")
label, result, err := dr.DoRollOnce("15d6 + 15 fire + 1 acid")

There is also a lower-level abstraction of dice available via the Dice type, created by the New function, if for some reason the DieRoller interface won't provide what is needed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ByDescription

func ByDescription(desc string) func(*Dice) error

ByDescription sets up a Dice value based on the text description given. This is the preferred way to make a low-level Dice value, since it is a flexible human-readable specification which likely came from the user anyway. (Although using DieRoller instead of Dice is even better.)

This text description may contain any number of nonnegative integer values and/or die‐roll expressions separated by the basic math operators “+”, “−”, “*”, and “//” which respectively add, subtract, multiply, and divide the total value so far with the value that follows the operator. Division performed with the “//” operator is integer‐only (results are immediately truncated by discarding any fractional part). There is no order of operations or parentheses supported. The expressions are simply evaluated left‐to‐right as they appear. Generally speaking, whitespace is insignificant in the description string.

On fully Unicode‐aware implementations (e.g., the Go version of this package), the character “×” (U+00D7) may be used in place of “*”, and “÷” (U+00F7) in place of “//”.

Each die‐roll expression has the general form

[>] [<n>[/<div>]] d <sides> [best|worst of <r>] [<label>]

This calls for <n> dice with the given number of <sides> (which may be a number or the character “%” which means percentile dice or d100). The optional <div> part of the expression allows a fractional number of dice: the expression “1/2d20” rolls half of a d20 (in other words, it rolls 1d20 and divides the result by 2, truncating the result). The optional qualifier “best of <r>” will cause the dice to be rolled <r> times, keeping the best result. (You may also use the word worst in place of best to take the lowest of the rolls.)

Arbitrary text (<label>) may appear at the end of the expression. It is simply reported back in the result as a label to describe that value (e.g. “1d10 + 1d6 fire + 2d6 sneak”.) If the expression begins with the character “>”, then the first die in the set is maximized: in the expression “>3d6”, the first d6 is assumed to have the maximum value (6), and the remaining two dice are rolled to produce random values.

The entire die roll expression may be followed by one or both of the following global modifiers, separated from the expression and each other by vertical bars (“|”): “min <a>” or “max <b>”.

These force the final result to be no smaller than <a> and/or no larger than <b>, where <a> and <b> are integer values. For example:

2d6 + 1d4 | min 6

which rolls 2d6 and 1d4 to get a random value between 3 and 16, but if the result is less than 6, it will return 6 anyway.

For example:

d, err := New(ByDescription("3d6 + 12"))

func ByDieType

func ByDieType(qty, sides, bonus int) func(*Dice) error

ByDieType sets the Dice up by discrete values which determine the number of dice to roll, how many sides they each have, and a bonus to add to their sum.

For example, to create a Dice value for "3d6+10", you could do:

d, err := New(ByDieType(3, 6, 10))

func WithAutoSF

func WithAutoSF(enabled bool, successMessage, failureMessage string) func(*sdrOptions)

WithAutoSF augments the operation of a StructuredDescribeRoll call by indicating that a natural 1 indicates failure regardless of the modified result's value, and a natural maximum roll similarly indicates success.

In case an automatic success is indicated, the provided successMessage string will be included in the structured description; likewise with failureMessage in case of automatic failure.

This behavior is enabled if the enabled parameter is true. Otherwise no automatic success or failure interpretation will be made.

func WithDieBonus

func WithDieBonus(n int) func(*Dice) error

WithDieBonus adds a per-die bonus of n which will be added to every single die rolled.

func WithDiv

func WithDiv(n int) func(*Dice) error

WithDiv causes the total die-roll to be divided by n (as an integer division, truncated toward zero).

DEPRECATED in favor of using WithDescription("... // n") or WithDescription("... ÷ n")

func WithFactor

func WithFactor(n int) func(*Dice) error

WithFactor causes the total die-roll to be multiplied by n.

DEPRECATED in favor of using WithDescription("... * n") or WithDescription("... × n")

func WithGenerator

func WithGenerator(source rand.Source) func(*Dice) error

WithGenerator sets up the Dice value to use a random number generator created by the caller and passed in to this option.

func WithRollBonus

func WithRollBonus(bonus int) func(*sdrOptions)

WithRollBonus augments the operation of a StructuredDescribeRoll call by indicating that this roll included an extra bonus (not indicated by the user).

This is not done if the bonus is 0 (zero).

func WithSeed

func WithSeed(s int64) func(*Dice) error

WithSeed sets up the Dice value to use a random number generator with the given seed value. (Per rand, this generator will not be safe for concurrent use by multiple goroutines.)

Types

type Dice

type Dice struct {
	// Constrained minimum and maximum values for the roll.
	// Specify 0 if there should be no minimum and/or maximum.
	MinValue int
	MaxValue int

	// The result of the last roll of this die. LastValue has a value
	// that can be used if Rolled is true.
	LastValue int

	// Have we actually rolled the dice yet to get a result?
	Rolled bool
	// contains filtered or unexported fields
}

Dice is an abstraction of the real-world concept of a set of dice. When constructing a new Dice value, specify the number of dice and number of sides.

This is a low-level representation of dice.

See the DieRoller type for a higher-level abstraction which is the recommended type to use instead of this one, for most purposes.

func New

func New(options ...func(*Dice) error) (*Dice, error)

New creates a new set of dice (using the low-level representation Dice type; for a more user-friendly interface use NewDieRoller instead).

By default, this creates a d20 you can roll. For other kinds of die rolls, pass the option(s) ByDescription(description), ByDieType(qty, sides, bonus), WithDieBonus(n), WithDiv(n), WithFactor(n), WithGenerator(source), and/or WithSeed(s).

For example,

d, err := New()
d, err := New(ByDescription("15d6 + 12"))
d, err := New(ByDieType(15, 6, 12))

func (*Dice) Description

func (d *Dice) Description() (desc string)

Description produces a human-readable description of the die roll specification represented by the Dice object.

func (*Dice) MaxRoll

func (d *Dice) MaxRoll() (int, error)

MaxRoll is an alternative to Roll where instead of rolling the dice, it just assumes they all came up at their maximum possible values. This does NOT set up for subsequent critical rolls.

func (*Dice) MaxRollToConfirm

func (d *Dice) MaxRollToConfirm(bonus int) (int, error)

MaxRollToConfirm is the analog to RollToConfirm but just assumes all dice come up with their maximum values rather than rolling anything.

func (*Dice) Roll

func (d *Dice) Roll() (int, error)

Roll rolls the dice which this Dice instance represents. The result is returned as an integer value. Each time this is called, the dice are rerolled to get a new result. The Dice value’s internal state reflects the last call to this method.

func (*Dice) RollToConfirm

func (d *Dice) RollToConfirm(confirm bool, threat int, bonus int) (int, error)

RollToConfirm rolls the dice specified in the Dice value, with support for making critical confirmation rolls.

If confirm is true, we're rolling to confirm a critical threat. In this case, the previous-rolled value is checked against the provided threat value. If that previous roll is less than the threat value, nothing further is done, and 0 is returned.

If the previous roll was ≥ threat, then we make another roll, adding the provided bonus modifier to that roll. This roll's total is returned, and becomes the new most-recent roll for this Dice value.

If threat is less than or equal to zero, the default threat of a natural maximum roll (e.g., 20 on a d20, or 10 on a d10) is used.

Calling d.RollToConfirm(false, 0, 0) is equivalent to calling d.Roll(). Confirm a critical roll by rolling again against the normal to-hit target.

func (*Dice) StructuredDescribeRoll

func (d *Dice) StructuredDescribeRoll(options ...func(*sdrOptions)) ([]StructuredDescription, error)

StructuredDescribeRoll produces a detailed structured description of the result of rolling the Dice, in a way that a caller can format as they see fit.

If you wish to interpret the die roll in light of a rule which allows for a natural 1 to indicate automatic failure and a natural maximum die face (e.g., natural 20 on a d20) to indicate automatic success, then add a WithAutoSF option function call to the end of the argument list.

If this die roll included a bonus added to it for some reason (e.g., a confirmation bonus on dice rolled to confirm a critical threat), then add a WithRollBonus function call to the end of the argument list.

type DieRoller

type DieRoller struct {

	// If we need to repeatedly roll dice, we will either do so RepeatFor
	// times (if > 0), or until the result meets or exceeds RepeatUntil
	// (again, if > 0)
	RepeatUntil int
	RepeatFor   int

	// If DC > 0 we're trying to meet a difficulty class for the
	// roll to be "successful".
	DC int

	// User-defined label for this entire die-roll specification, such as
	// "Knowledge Skill Check".
	LabelText string

	// Messages to report if the roll can be categorized as "successful" or "failed".
	SuccessMessage string
	FailMessage    string

	// Template for permuted roll pattern substitution.
	Template string

	// Values to be substituted into the Template
	Permutations [][]interface{}

	// If PctChance ≥ 0 then our target to be "successful" is a score
	// or at least PctChance on a percentile die roll. In that case
	// we can also set a label in PctLabel for that roll.
	PctChance int
	PctLabel  string

	// Are we supposed to confirm potential critical rolls?
	Confirm bool

	// DoMax is true if we are supposed to maximize all die rolls rather than
	// using random numbers.
	DoMax bool
	// contains filtered or unexported fields
}

The DieRoller type provides a higher-level view of die-roll generation, and should be generally preferred instead of the lower-level Dice type.

This allows the caller to deal with die rolls in a friendly but expressive syntax as a string of characters such as "2d12 + 5 bonus - 1 size | dc 12" which game players are more accustomed to using, and then simply roll that die-roll expression as needed.

Note that it is not expected for the user to set or query these structures directly. Use the provided functions instead.

func NewDieRoller

func NewDieRoller(options ...func(*Dice) error) (*DieRoller, error)

NewDieRoller creates a new DieRoller value, which provides the recommended higher-level interface for rolling dice. This value can then be used for as many die rolls as needed by calling its DoRoll or DoRollOnce methods.

You may pass zero or more option specifiers to this function as already described for the New constructor, although the only ones which apply here are WithSeed(s) and WithGenerator(s).

Initially it is set up to roll a single d20, but this can be changed with each DoRoll call.

func (*DieRoller) DoRoll

func (d *DieRoller) DoRoll(spec string) (string, []StructuredResult, error)

DoRoll rolls dice as described by the specification string. If this string is empty, it re-rolls the previously-used specification. Initially, "1d20" is assumed.

Returns the user-specified die-roll label (if any), the result of the roll, and an error if one occurred.

In this more comprehensive interface, the spec string is a string of the form

[<title>=] <expression> [|<options>...]

or

[<title>=] <chance>% [<success>[/<fail>]] [|<options>...]

where []s indicate optional parameters, the given words in angle brackets (<title>, etc) represent values to be placed into the string, and the other characters are to be taken literally.

The <title> (which, if given, is separated from the rest of the spec with an equals sign (“=”)) is optional and will be included as a comment in the result list to indicate what the purpose of the die roll was for.

Note that this module does not interpret the <title> value further, but by convention two special characters are significant to some clients:

‖ (U+2016) separates multiple titles in the <title> string
≡ (U+2261) separates the title text on the left with a color on the right.

This means that a <title> string of "monster≡blue‖damage≡red" will display a title for the die roll as two separate title values, "monster" in blue and "damage" in red.

<expression> can be anything that can be given as the description string to the New constructor (q.v.). At the end of the spec string there may be zero or more options, each beginning with a vertical bar (“|”).

These options may be any of the following:

| min <n>

The result will be at least <n>.

| max <n>

The result will be no more than <n>.

| c[<t>[±<b>]]

This indicates that the roll may need a critical confirmation roll to follow it. This will appear as an additional result in the list of results returned from the DoRoll and DoRollOnce methods. If the <t> parameter is given, a natural roll equal to or greater than <t> is assumed to be a critical threat. If a plus or minus sign followed by a number <b> is appended to the option, then this value is added to the confirmation die roll as a confirmation bonus. (The notation "±" here means either a "-" or "+" may appear at that position in the string.)

| dc <n>

This is a roll against a known difficulty class <n>. If the result is at least <n>, the roll is "successful".

| sf [<success>[/<fail>]]

Auto-success/fail: the roll, which must involve only a single die, will be considered successful if it's a natural maximum value (e.g., 20 on a d20 before modifiers are applied), or a failure if a natural 1 was rolled. Optionally, messages to report to the user to indicate what success and failure mean may be specified. Suitable defaults will be used or derived if one or both of those strings is not given.

| until <n>

Continue making die rolls, adding their results to the returned output, until a result of at least <n> is obtained.

| repeat <n>

Make <n> die rolls, reporting their results.

| maximized

Assume all dice roll at their maximum possible values. For example, the spec "3d6 | maximized" will always return the result 18, as if all three dice rolled sixes.

To prevent getting caught in an infinite loop, a maximum of 100 rolls will be made regardless of repeat and until options.

Anywhere in the string you may introduce a combination specifier in curly braces as “{<a>/<b>/<c>/...}”. This will repeat the overall die roll expression once for each of the values <a>, <b>, <c>, etc., substituting each in turn for the braced list. If multiple specifiers appear, they’ll all repeat so you get the Cartesian product of all the sets of values. This allows, for example, multiple attack rolls in a single click. For example, “Attack=d20+{17/12/7}” would roll three attack rolls: d20+17, d20+12, and d20+7.

In the second form for the spec string, <chance> gives the percentage chance of something occurring, causing percentile dice to be rolled. The result will be a the integer value 1 if the d100 roll was less than or equal to <chance>, or 0 otherwise. By default, the result is reported in the detailed description as “success” or “fail”. If a success label is given in the die‐roll string, that is reported in case of a successful roll, and “did not <success>” otherwise. If an explicit fail label is also given, that is used for unsuccessful rolls instead. As a special case, if <success> is “miss” then <fail> is assumed to be “hit” and vice versa.

For percentile rolls, only the until, repeat, and maximized global options may be used. Permutations (“{...}”) are also disallowed with percentile rolls.

This method returns values title, resultSet, and error representing the results of rolling the dice. The title is the title specified in the dice string, or an empty string if one was not given. The resultSet is a slice of StructuredResult structures, one for each roll of the dice that was performed.

Example die-roll specifications:

"d20"             Roll 1d20.
"3d6"             Roll 3d6 (3 six-sided dice, adding their values).
"15d6+15"         Roll 15d6, add 15 to their sum.
"1d10+5*10"       Roll 1d10, add 5, then multiply the result by 10.
"1/2 d6"          Roll 1d6, divide result by 2 (truncating toward zero).
"2d10+3d6+12"     Roll 2d10, 3d6, add their results and add 12 to the sum.
"d20+15|c"        Roll d20+15, automatically rolling to confirm on a natural 20.
"d20+15|c19+2"    Roll d20+15, rolling to confirm on natural 19 or 20 with +2 bonus.
"d%"              Roll percentile dice, giving result 1-100.
"40%"             Roll percentile dice, giving result 1 with 40% probability.
"d20+12|max20"    Roll d20+12 but any result > 20 is capped at 20.
"d20 best of 2"   Roll d20 twice, discarding the worse result.
"d20+4|dc 10"     Roll d20+4, signalling success if the result is 10 or greater.
"3d6 fire+1d4 acid+2 bonus"
                  Roll 3d6+1d4+2. In the structured results, it will show the values
                  rolled for the 3d6 fire, 1d4 acid, and 2 bonus individually.
"40% hit"         Reports success ("hit") with a 40% probability; otherwise reports
                  failure ("miss").
"13% red/blue"    Reports success ("red") with a 13% probability; otherwise reports
                  failure ("blue").
"2d10+3|until 19" Repeatedly rolls 2d10+3, adding each result to the set of die rolls
                  returned, until a roll totals at least 19.

func (*DieRoller) DoRollOnce

func (d *DieRoller) DoRollOnce(spec string) (string, StructuredResult, error)

DoRollOnce is just like the DoRoll method but adds the constraint that there may only be one result returned, in the same way the RollOnce function differs from the Roll() function.

func (*DieRoller) IsNatural1

func (d *DieRoller) IsNatural1() (result bool)

IsNatural1 returns true if the DieRoller has been rolled already, and contains but a single die in its specification, and that die was rolled as a natural 1.

It returns false if that was not true, even if that was for reasons that it could not possibly be true (no die was rolled, the die-roll spec contained multiple dice, etc.)

func (*DieRoller) IsNaturalMax

func (d *DieRoller) IsNaturalMax() (result bool)

IsNaturalMax returns true if the DieRoller has been rolled already, and contains but a single die in its specification, and that die was rolled to the maximum possible value (i.e., a 20 on a d20).

It returns false if that was not true, even if that was for reasons that it could not possibly be true (no die was rolled, the die-roll spec contained multiple dice, etc.)

func (*DieRoller) RandFloat64

func (d *DieRoller) RandFloat64() float64

RandFloat64 generates a pseudorandom number in the range [0.0, 1.0) using the same random number generator as used for die rolls. This means that it affects the outcome of subsequent die rolls just as other die rolls do.

func (*DieRoller) RandIntn

func (d *DieRoller) RandIntn(n int) int

RandIntn generates a pseudorandom integer in the range [0, n) using the same random number generator as used for die rolls. This means that it affects the outcome of subsequent die rolls just as other die rolls do.

type StructuredDescription

type StructuredDescription struct {
	// A text label describing what the value means in the context
	// of the die-roll result. Typical type labels are documented
	// in dice(3).
	Type string

	// The value (as a string, since the intent here is just to report
	// these values to a human with various kinds of formatting) for this
	// part of the description.
	Value string
}

StructuredDescription values are used to report die-roll results as a structured description list.

type StructuredDescriptionSet

type StructuredDescriptionSet []StructuredDescription

A StructuredDescriptionSet is simply a collection of StructuredDescriptions.

func (StructuredDescriptionSet) Text

func (sr StructuredDescriptionSet) Text() (string, error)

Text produces a simple plain-text rendering of the information in a StructuredDescription slice. This is useful for showing the full details of a die roll to a human, or a log file, etc., if fancier formatting isn't needed.

type StructuredResult

type StructuredResult struct {
	// Total final result of the expression.
	Result int

	// Breakdown of how the result was obtained.
	Details StructuredDescriptionSet
}

StructuredResult provides a full, detailed report of the die-roll operation. See the documentation in dice(3) for full details.

For example, making a die roll such as Roll("1d20+3|min 5|c") might result in the following StructuredResult slice, which contains a fully-described breakdown of how those results were arrived at:

 []StructuredResult{{
   Result: 23,
   Details: StructuredDescriptionSet{
     {Type: "result",    Value: "23"},
     {Type: "success",   Value: "HIT"},
     {Type: "separator", Value: "="},
     {Type: "diespec",   Value: "1d20"},
     {Type: "roll",      Value: "20"},
     {Type: "operator",  Value: "+"},
     {Type: "constant",  Value: "3"},
     {Type: "moddelim",  Value: "|"},
     {Type: "min",       Value: "5"},
     {Type: "moddelim",  Value: "|"},
     {Type: "critspec",  Value: "c"},
   }
  }, {
   Result: 13,
   Details: StructuredDescriptionSet{
     {Type: "critlabel", Value: "Confirm:"},
     {Type: "result",    Value: "13"},
     {Type: "separator", Value: "="},
     {Type: "diespec",   Value: "1d20"},
     {Type: "roll",      Value: "10"},
     {Type: "operator",  Value: "+"},
     {Type: "constant",  Value: "3"},
     {Type: "moddelim",  Value: "|"},
     {Type: "min",       Value: "5"},
  }
}}

func Roll

func Roll(spec string) (string, []StructuredResult, error)

Roll rolls the dice specified by the specification string, without requiring a separate step to create a DieRoller first.

Calling Roll(spec) is equivalent to the sequence

dr = NewDieRoller()
dr.DoRoll(spec)

func RollOnce

func RollOnce(spec string) (string, StructuredResult, error)

RollOnce is just like Roll but adds the constraint that there may only be one result returned (no confirmation rolls, no repeated rolls, etc., although multiple dice such as "3d6+4d10" or "best of N" kinds of things are allowed). It is an error if the die roll spec generates multiple results.

The return value differs from Roll in that only a single StructuredResult is returned rather than a slice of them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL