dice

package module
v0.0.0-...-4e95258 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2024 License: MIT Imports: 16 Imported by: 1

README

Dice

GoDoc Go Report Card

Dice is a Golang library and CLI solution for your dice-rolling needs. The Go source is split into two main parts:

  • Package dice implements virtualized standard polyhedral and specialty game dice. The dice roll calculations are intended to be cryptographically pseudo-random through use of crypto/rand by default, but the entropy source used by the package is configurable.
  • Package main in cmd/dice is a CLI utility for dice rolling and expression evaluation.

Install

You need Go installed. To fetch just the main CLI, build it, and place it in your GOPATH:

go get -u github.com/travis-g/dice/cmd/dice

Build

To fetch the source and dependencies and place everything in your GOPATH:

go get -u github.com/travis-g/dice/...

The actual main package is defined in cmd/dice. To test everything and build the CLI:

make build

See the Makefile for more.

Tips

  • Alias dice eval as roll in your shell if you get sick of specifying the subcommand.

    alias roll="dice eval"
    

Documentation

Overview

Package dice implements virtualized standard polyhedral and specialty game dice. The dice roll calculations are intended to be cryptographically pseudo-random through use of crypto/rand, but the entropy source used by the package is globally configurable.

Dice Notation

Dice notation is an algebra-like system for indicating dice rolls in games. Dice rolls are usually given in the form AdX+B, where A is the number of X-sided dice to roll, with an optional modifier B. B could be an integer or potentially another dice notation string. Additionally, A can be omitted if the number of X-sided dice to roll is 1: 1dX can be written as simply dX.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	CtxKeyTotalRolls = &contextKey{name: "total rolls"}
	CtxKeyMaxRolls   = &contextKey{name: "max rolls"}
	CtxKeyParameters = &contextKey{name: "parameters"}
)
View Source
var (
	// ErrImpossibleDie is returned when an attempt to create a die with a
	// negative number of sides is made.
	ErrImpossibleDie = errors.New("die cannot have negative sides")

	// ErrNilDie is returned when a die's passed reference is nil.
	ErrNilDie = errors.New("nil die passed")

	// ErrMaxRolls is returned when a maximum number of rolls/rerolls has been
	// met or surpassed.
	ErrMaxRolls = errors.New("max rolls reached")

	// ErrUnrolled is returned when an operation that requires a rolled die is
	// preformed on an unrolled die
	ErrUnrolled = errors.New("die is unrolled")

	// ErrRolled is returned when an attempt is made to roll a die that had been
	// rolled already
	ErrRolled = errors.New("die already rolled")

	// ErrRerolled is returned when it must be noted that a die's value has
	// changed due to being rerolled.
	ErrRerolled = errors.New("die rerolled")

	// ErrInvalidExpression is returned when a dice expression is invalid or
	// there is a general issue evaluating it.
	ErrInvalidExpression = errors.New("invalid expression")

	// ErrImpossibleRoll is returned when a given dice roll is deemed
	// impossible, illogical, or will never be able to yield a result. As an
	// example, a roll of "3d6r<6" should return this error at some stage in its
	// evaluation, as no die in the set will ever be able to settle with the
	// given reroll modifier.
	ErrImpossibleRoll = errors.New("impossible roll")

	// ErrContextKeyMissing is returned if a context key is not found in a
	// request context.
	ErrContextKeyMissing = errors.New("context key missing")

	// ErrMaxDepth is returned if a replacement operation recurses beyond
	// MaxDepth.
	ErrMaxDepth = errors.New("max replacement depth")
)
View Source
var (
	// DiceNotationPattern is the base XdY notation pattern for matching dice
	// strings.
	DiceNotationPattern = `(?i)(?P<count>\d+)?d(?P<size>\d{1,}|f|F)`

	// DiceNotationRegex is the compiled RegEx for parsing supported dice
	// notations.
	DiceNotationRegex = regexp.MustCompile(DiceNotationPattern)

	// ComparePointpattern is the base pattern that matches compare points
	// within dice modifiers.
	ComparePointPattern = `(?P<compare>[=<>])?(?P<point>\d+)`

	// ComparePointRegex is the compiled RegEx for parsing supported dice
	// modifiers' core compare points.
	ComparePointRegex = regexp.MustCompile(ComparePointPattern)
)

Regexes for parsing basic dice notation strings.

View Source
var DiceWithModifiersExpressionRegex = regexp.MustCompile(
	DiceNotationPattern + `(?P<modifiers>[!a-zA-Z=<>\d]*)`)

DiceWithModifiersExpressionRegex is the compiled RegEx for parsing a dice notation with modifier strings appended.

View Source
var MaxDepth int = 3

MaxDepth is the maximum number of replacement iterations allowed for a parse.

View Source
var MaxRolls uint64 = math.MaxUint64

MaxRolls is the maximum number of rolls allowed for a request.

View Source
var RollerFactoryMap = map[DieType]RollerFactory{
	TypePolyhedron: NewDieWithParent,
	TypeFudge:      NewDieWithParent,
}

RollerFactoryMap is the package-wide mapping of die types and the function to use to create a new die of that type. This map can be modified to create dice using different functions or to implement new die types.

View Source
var Source *rand.Rand

Source is the dice package's global RNG source. Source uses the system's native cryptographically secure pseudorandom number generator by default.

Source must be safe for concurrent use: to use something akin to math/rand's thread safe global reader try binding a Source64 with a Mutex. See math/rand's globalRand variable source code for an example.

Functions

func All

func All(vs []Roller, f func(Roller) bool) bool

All is a helper function that returns true if all Rollers of a slice match a predicate. All will return false on the first failure.

func CryptoInt64

func CryptoInt64() (int64, error)

CryptoInt64 is a convenience function that returns a cryptographically random int64 using the system's CSPRNG. If there is a problem generating enough entropy it will return a non-nil error.

This function was designed to seed math/rand Sources with uniform random values. It will not use the package's global Source.

func CryptoIntn

func CryptoIntn(max int) (n int, err error)

CryptoIntn is a convenience wrapper for emulating rand.Intn using crypto/rand. Panics if max <= 0, and any other errors encountered when generating the integer are passed through by err.

CryptoIntn does not use the package's global Source, it uses crypto.Reader.

func CtxMaxRolls

func CtxMaxRolls(ctx context.Context) uint64

CtxMaxRolls returns the context's maximum allowed number of rolls, or the default.

func CtxParameters

func CtxParameters(ctx context.Context) map[string]interface{}

CtxParameters returns the context's arbitrary parameters.

func CtxTotalRolls

func CtxTotalRolls(ctx context.Context) *uint64

CtxTotalRolls returns the pointer to total number of rolls made by the context.

func FindNamedCaptureGroups

func FindNamedCaptureGroups(exp *regexp.Regexp, in string) map[string]string

FindNamedCaptureGroups finds string submatches within an input string based on a compiled Regexp and returns a map of the named capture groups with their captured submatches.

func MustCtxTotalRolls

func MustCtxTotalRolls(ctx context.Context) *uint64

func NewContextFromContext

func NewContextFromContext(ctx context.Context) context.Context

NewContextFromContext makes a child context from a given context, including setting the context's maximum rolls and adding a roll counter if not present.

func Ptr

func Ptr[T any](v T) *T

Ptr returns a pointer to the passed value.

Types

type CompareOp

type CompareOp int

CompareOp is an comparison operator usable in modifiers.

const (
	EMPTY CompareOp = iota

	EQL // =
	LSS // <
	GTR // >
	LEQ // <=
	GEQ // >=

)

Comparison operators.

func LookupCompareOp

func LookupCompareOp(s string) CompareOp

LookupCompareOp returns the CompareOp that is represented by a given string.

func (*CompareOp) MarshalJSON

func (c *CompareOp) MarshalJSON() ([]byte, error)

MarshalJSON ensures the CompareOp is encoded as its string representation.

func (CompareOp) String

func (c CompareOp) String() string

func (*CompareOp) UnmarshalJSON

func (c *CompareOp) UnmarshalJSON(data []byte) error

UnmarshalJSON enables JSON encoded string versions of CompareOps to be converted to their appropriate counterparts.

type CompareTarget

type CompareTarget struct {
	Compare CompareOp `json:"compare,omitempty"`
	Target  int       `json:"target"`
}

CompareTarget is the base comparison

type CriticalFailureModifier

type CriticalFailureModifier struct {
	*CompareTarget
}

A CriticalFailureModifier shifts or sets the compare point/range used to classify a die's result as a critical failure.

func (*CriticalFailureModifier) String

func (m *CriticalFailureModifier) String() string

type CriticalSuccessModifier

type CriticalSuccessModifier struct {
	*CompareTarget
}

A CriticalSuccessModifier shifts or sets the compare point/range used to classify a die's result as a critical success.

func (*CriticalSuccessModifier) String

func (m *CriticalSuccessModifier) String() string

type Die

type Die struct {
	// Generic properties
	Type DieType `json:"type,omitempty" mapstructure:"type"`
	Size int     `json:"size" mapstructure:"size"`

	Rerolls int `json:"rerolls" mapstructure:"rerolls"`
	*Result `json:"result,omitempty" mapstructure:"result"`

	Modifiers ModifierList `json:"modifiers,omitempty" mapstructure:"modifiers"`
	// contains filtered or unexported fields
}

Die represents an internally-typed die. If Result is a non-nil pointer, it is considered rolled.

Example
die := &Die{
	Size: 20,
}
fateDie := &Die{
	Type: TypeFudge,
	Size: 1,
}
fmt.Println(die, fateDie)
Output:

d20 dF

func (*Die) Add

func (d *Die) Add(r Roller)

Add causes a panic as a single Die cannot have a descendent.

func (*Die) FullRoll

func (d *Die) FullRoll(ctx context.Context) error

FullRoll rolls the Die. The die will be reset if it had been rolled previously.

func (*Die) Parent

func (d *Die) Parent() Roller

Parent returns the Die's parent, which will be nil if an orphan.

func (*Die) Reroll

func (d *Die) Reroll(ctx context.Context) error

Reroll performs a reroll after resetting a Die.

func (*Die) Roll

func (d *Die) Roll(ctx context.Context) error

Roll rolls a die based on the die's size and type and calculates a value.

func (*Die) SetParent

func (d *Die) SetParent(r Roller)

func (*Die) String

func (d *Die) String() string

String returns an expression-like representation of a rolled die or its type, if it has not been rolled.

func (*Die) ToGraphviz

func (d *Die) ToGraphviz() string

func (*Die) Total

func (d *Die) Total(ctx context.Context) (float64, error)

Total implements the Total method. An ErrUnrolled error will be returned if the die has not been rolled.

func (*Die) Value

func (d *Die) Value(ctx context.Context) (float64, error)

Value returns the Result.Value of a Die, regardless of whether the Die was dropped.

type DieType

type DieType string

DieType is the enum of types that a die or dice can be

const (
	// Concrete dice types: these can be used to instantiate a new rollable.
	TypePolyhedron DieType = ""
	TypeFudge      DieType = "fudge"

	// Meta dice types: these are used to classify rollable groups and unknown
	// dice.
	TypeUnknown DieType = "unknown"
)

Types of dice/dice groups.

func (DieType) String

func (t DieType) String() string

type DropKeepMethod

type DropKeepMethod string

A DropKeepMethod is a method to use when evaluating a drop/keep modifier against a dice group.

const (
	DropKeepMethodUnknown     DropKeepMethod = ""
	DropKeepMethodDrop        DropKeepMethod = "d"
	DropKeepMethodDropLowest  DropKeepMethod = "dl"
	DropKeepMethodDropHighest DropKeepMethod = "dh"
	DropKeepMethodKeep        DropKeepMethod = "k"
	DropKeepMethodKeepLowest  DropKeepMethod = "kl"
	DropKeepMethodKeepHighest DropKeepMethod = "kh"
)

Drop/keep methods.

type DropKeepModifier

type DropKeepModifier struct {
	Method DropKeepMethod `json:"op,omitempty"`
	Num    int            `json:"num"`
}

A DropKeepModifier is a modifier to drop the highest or lowest Num dice within a group by marking them as Dropped. The Method used to apply the modifier defines if the dice are dropped or kept (meaning the Num highest dice are not dropped).

func (*DropKeepModifier) Apply

func (d *DropKeepModifier) Apply(ctx context.Context, r Roller) error

Apply executes a DropKeepModifier against a Roller. If the Roller is not a Group an error is returned.

func (*DropKeepModifier) String

func (d *DropKeepModifier) String() string

type ErrNotImplemented

type ErrNotImplemented struct {
	// contains filtered or unexported fields
}

ErrNotImplemented is an error returned when a feature is not yet implemented.

func NewErrNotImplemented

func NewErrNotImplemented(message string) *ErrNotImplemented

NewErrNotImplemented returns a new not implemented error.

func (*ErrNotImplemented) Error

func (e *ErrNotImplemented) Error() string

type ErrParseError

type ErrParseError struct {
	Notation     string
	NotationElem string
	ValueElem    string
	Message      string
}

ErrParseError is an error encountered when parsing a string into dice notation.

func (*ErrParseError) Error

func (e *ErrParseError) Error() string

type ExplodeModifier

type ExplodeModifier struct {
	*CompareTarget
	Once bool `json:"once,omitempty"`
}

func (*ExplodeModifier) Apply

func (m *ExplodeModifier) Apply(ctx context.Context, r Roller) error

func (*ExplodeModifier) String

func (m *ExplodeModifier) String() string

func (*ExplodeModifier) Valid

func (m *ExplodeModifier) Valid(ctx context.Context, r Roller) (bool, error)

type Group

type Group []Roller

A Group is a slice of rollables.

func (Group) Add

func (g Group) Add(r Roller)

func (Group) Copy

func (g Group) Copy() []Roller

Copy returns a copy of the dice within the group

func (Group) Drop

func (g Group) Drop(_ context.Context, _ bool)

Drop is (presently) a noop on the group.

func (Group) Expression

func (g Group) Expression() string

Expression returns an expression to represent the group's total. Dice in the group that are unrolled are replaced with their roll notations and dropped dice results are omitted.

func (Group) FullRoll

func (g Group) FullRoll(ctx context.Context) (err error)

FullRoll implements the Roller interface's FullRoll method by rolling each object/Roller within the group.

func (Group) IsDropped

func (g Group) IsDropped(_ context.Context) bool

IsDropped returns whether the Group is dropped. It always returns false.

func (Group) Len

func (g Group) Len() int

Len returns the number of elements in a Group.

func (Group) Less

func (g Group) Less(i, j int) bool

Less determines the sort order of Rollers in a Group.

func (Group) Parent

func (g Group) Parent() Roller

Parent returns the parent object of the Group, which should be nil.

func (Group) Reroll

func (g Group) Reroll(ctx context.Context) (err error)

Reroll implements the Reroll method by rerolling each object in the group.

func (Group) Roll

func (g Group) Roll(ctx context.Context) (err error)

Roll rolls each of the dice in the group without applying their modifiers.

func (Group) SetParent

func (g Group) SetParent(Roller)

Parent returns the parent object of the Group, which should be nil.

func (Group) String

func (g Group) String() string

func (Group) Swap

func (g Group) Swap(i, j int)

Swap swaps the positions of two Rollers in a Group. This method is not thread safe.

func (Group) ToGraphviz

func (g Group) ToGraphviz() string

func (Group) Total

func (g Group) Total(ctx context.Context) (total float64, err error)

Total implements the Total method and sums a dice group's totals, excluding values of dropped dice.

func (Group) Value

func (g Group) Value(ctx context.Context) (float64, error)

Value returns the total value of a Group for sorting purposes. It should return the Group's Total still.

type Modifier

type Modifier interface {
	// Apply executes a modifier against a Die.
	Apply(context.Context, Roller) error
	fmt.Stringer
}

A Modifier is a dice modifier that can apply to a set or a single die.

type ModifierList

type ModifierList []Modifier

ModifierList is a slice of modifiers that implements Stringer.

func (ModifierList) String

func (m ModifierList) String() string

type RerollModifier

type RerollModifier struct {
	*CompareTarget
	Once bool `json:"once,omitempty"`
}

RerollModifier is a modifier that rerolls a Die if a comparison against the compare target is true.

func (*RerollModifier) Apply

func (m *RerollModifier) Apply(ctx context.Context, r Roller) error

Apply executes a RerollModifier against a Roller. The modifier may be slightly modified the first time it is applied to ensure property consistency.

The full roll needs to be recalculated in the event that one result may be acceptable for one reroll criteria, but not for one that was already evaluated. An ErrRerolled error will be returned if the die was rerolled in case other modifiers on the die need to be reapplied. Impossible rerolls and impossible combinations of rerolls may cause a stack overflow from recursion without a safeguard like MaxRerolls.

func (*RerollModifier) MarshalJSON

func (m *RerollModifier) MarshalJSON() ([]byte, error)

MarshalJSON marshals the modifier into JSON and includes an internal type property.

func (*RerollModifier) String

func (m *RerollModifier) String() string

func (*RerollModifier) Valid

func (m *RerollModifier) Valid(ctx context.Context, r Roller) (bool, error)

Valid checks if the supplied die is valid against the modifier. If not valid the reroll modifier should be applied, unless there is an error.

type Result

type Result struct {
	Value       float64 `json:"value"`
	Dropped     bool    `json:"dropped,omitempty"`
	CritSuccess bool    `json:"crit,omitempty"`
	CritFailure bool    `json:"fumble,omitempty"`
}

A Result is a value a die has rolled. By default, CritSuccess and CritFailure should be set to true if the maximum or minimum value of a die is rolled respectively, but the range in which a critical success/failure must be overridable through modifiers.

func NewResult

func NewResult(result float64) *Result

NewResult returns a new un-dropped, non-critical Result.

func (*Result) Drop

func (r *Result) Drop(_ context.Context, drop bool)

Drop marks a Result as dropped.

func (*Result) IsDropped

func (r *Result) IsDropped(_ context.Context) bool

IsDropped returns whether a Result was dropped.

func (*Result) Total

func (r *Result) Total(_ context.Context) (float64, error)

Total returns the Result's value or 0 if the result was dropped.

type Roller

type Roller interface {
	// FullRoll rolls the object at the macro level, inclusive of testing and
	// applying modifiers.
	FullRoll(context.Context) error

	// Roll rolls and records the object's Result. Roll should not apply
	// modifiers. However, it should always increment the appropriate roll
	// count context key.
	Roll(context.Context) error

	// Reroll resets the object and should re-roll the core die by calling Roll.
	// Methods used by Reroll should not call FullRoll without safeguards to
	// prevent a stack overflow.
	Reroll(context.Context) error

	// Total returns the summed results, omitting any dropped results.
	Total(context.Context) (float64, error)

	// Value returns the rolled face value of the Roller, regardless of whether
	// the Roller was dropped. Value should be used when sorting.
	Value(context.Context) (float64, error)

	// Drop marks the object dropped based on a provided boolean.
	Drop(context.Context, bool)

	// IsDropped returns the dropped status of the Roller.
	IsDropped(context.Context) bool

	// Parent returns the parent of the Roller, or nil.
	Parent() Roller

	// SetParent sets the parent of the Roller.
	SetParent(Roller)

	// Add associates a Roller as a child.
	Add(Roller)

	// Must implement a String method; if the object has not been rolled String
	// should return a stringified representation of that can be re-parsed to
	// yield an equivalent property set.
	fmt.Stringer

	ToGraphviz() string
}

Roller must be implemented for an object to be considered rollable. Internally, a Roller and should maintain a "total rolls" count.

func Filter

func Filter(vs []Roller, f func(Roller) bool) []Roller

Filter is a helper function that returns a slice of Rollers that match a predicate out of an input slice.

func MustNewDie

func MustNewDie(props *RollerProperties) Roller

MustNewDie creates a new die off of a properties list. It will tweak the properties list to better suit reuse. Panics on a non-nil error.

func MustNewRoller

func MustNewRoller(props *RollerProperties) Roller

MustNewRoller creates a new Roller from a properties set using NewRoller and panics if NewRoller returns an error.

func NewDie

func NewDie(props *RollerProperties) (Roller, error)

NewDie creates a new die off of a properties list. It will tweak the properties list to better suit reuse.

func NewDieWithParent

func NewDieWithParent(props *RollerProperties, parent Roller) (Roller, error)

NewDieWithParent creates a new die off of a properties list. It will tweak the properties list to better suit reuse.

func NewRoller

func NewRoller(props *RollerProperties) (Roller, error)

NewRoller wraps NewRollerWithParent but a parent is not bound to the Roller.

Example
ctx := context.Background()
roll, _ := NewRoller(&RollerProperties{
	Type: TypePolyhedron,
	Size: 6,
})
die := roll.(*Die)
fmt.Println(die)
_ = roll.FullRoll(ctx)
fmt.Println(die)
Output:

func NewRollerWithParent

func NewRollerWithParent(props *RollerProperties, parent Roller) (Roller, error)

NewRollerWithParent creates a new Die to roll off of a supplied property set. The property set is modified/linted to better suit defaults in the event a properties list is reused.

New dice created with this function are created by the per-DieType factories declared within the package-level RollerFactoryMap.

type RollerFactory

type RollerFactory func(*RollerProperties, Roller) (Roller, error)

A RollerFactory is a function that takes a properties object and returns a valid rollable die based off of the properties list. If there is an error creating a die off of the properties list an error should be returned.

type RollerGroup

type RollerGroup struct {
	Group     `json:"group" mapstructure:"group"`
	Modifiers ModifierList `json:"modifiers,omitempty" mapstructure:"modifiers"`
	// contains filtered or unexported fields
}

RollerGroup is a wrapper around a Group that implements Roller. The Modifiers supplied at this level should be group-level modifiers, like drop/keep modifiers.

func MustNewRollerGroup

func MustNewRollerGroup(props *RollerProperties) *RollerGroup

MustNewRollerGroup creates a new RollerGroup from properties using NewRollerGroup and panics if the method returns an error.

func NewRollerGroup

func NewRollerGroup(props *RollerProperties) (*RollerGroup, error)

NewRollerGroup creates a new dice group with the count provided by the properties list. If a count of dice was not specified within the properties list it will default to a count of 1 and tweak the provided properties object accordingly.

func (*RollerGroup) Add

func (d *RollerGroup) Add(r Roller)

Add adds a Roller to the RollerGroup's embedded Group and sets this as the Roller's parent.

func (*RollerGroup) FullRoll

func (d *RollerGroup) FullRoll(ctx context.Context) error

FullRoll rolls each die embedded in the dice group.

func (*RollerGroup) Reroll

func (d *RollerGroup) Reroll(ctx context.Context) error

Reroll re-rolls each die within the dice group.

func (*RollerGroup) ToGraphviz

func (d *RollerGroup) ToGraphviz() string

type RollerProperties

type RollerProperties struct {
	Type   DieType `json:"type,omitempty" mapstructure:"type"`
	Size   int     `json:"size,omitempty" mapstructure:"size"`
	Result *Result `json:"result,omitempty" mapstructure:"result"`
	Count  int     `json:"count,omitempty" mapstructure:"count"`

	// Modifiers for the dice or parent set
	DieModifiers   ModifierList `json:"die_modifiers,omitempty" mapstructure:"die_modifiers"`
	GroupModifiers ModifierList `json:"group_modifiers,omitempty" mapstructure:"group_modifiers"`
}

A RollerProperties object is the set of properties (usually extracted from a notation) that should be used to define a Die or group of like dice (a slice of multiple Die).

This may be best broken into two properties types, a RollerProperties and a RollerGroupProperties.

func ParseNotation

func ParseNotation(ctx context.Context, notation string) (RollerProperties, error)

ParseNotation parses the provided notation with updated regular expressions that also extract dice group modifiers.

type SortDirection

type SortDirection uint8

SortDirection is a possible direction for sorting dice.

const (
	SortDirectionAscending SortDirection = iota
	SortDirectionDescending
)

Sort directions for sorting modifiers.

type SortModifier

type SortModifier struct {
	Direction SortDirection `json:"direction"`
}

SortModifier is a modifier that will sort the Roller group.

func (*SortModifier) Apply

func (s *SortModifier) Apply(ctx context.Context, r Roller) error

Apply applies a sort to a Roller.

func (*SortModifier) String

func (s *SortModifier) String() string

Directories

Path Synopsis
cmd
dice
Package dice defines a CLI for the package.
Package dice defines a CLI for the package.
dice/command
Package command defines the subcommands available to the dice CLI.
Package command defines the subcommands available to the dice CLI.
Package math implements dice expression mathematics and functions.
Package math implements dice expression mathematics and functions.

Jump to

Keyboard shortcuts

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