dice

module
v0.0.0-...-7afa4bb Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2020 License: BSD-2-Clause

README

Polyhedral dice

Copyright © 2019, Ramsey Dow. All rights reserved.

polyhedral is a Go package that implements classic polyhedral dice. All of the dice required for a game of D&D® are supported, along with a rich API. The polyhedral.Die API enables you to roll a die any number of times, even adding modifiers. You can get just the sum of a throw or the sum and a slice of the individual dice scores. For example:

d6 := polyhedral.NewD6(nil)
score := d6.Roll(1)
fmt.Println(d6, "→", score)

The nil passed into the NewD6 initializer instructed the API to seed the pseudorandom number generator (PRNG) using the current Unix time, expressed as a timestamp with nanosecond resolution. If you want to specify your own seed (e.g., 6941961420900260635) you would do so in the initializer. The canonical example would be for your app to generate a one-time, universal, high-entropy seed using the crypto/rand package. The API will not do this for you because it has a commitment to guaranteeing that dice initializers never fail. As a result, you can initialize any of the classic polyhedral dice — d4, d6, d8, d10, d12, d20, and d100 — and roll them as you like without having to worry about errors. The rand.Int API from crypto/rand can fail (and even panic), so managing its use is up to the caller. If you choose to use rand.Int, pass the seed value into the die's initializer function at construction time. Regardless, such configuration should not present a barrier. To get started right away, initialize a die with nil (or 0) and the PRNG will be seeded with the current Unix time. polyhedral.Die supports the Stringer interface, so you can easily print out dice instances. Additionally, you can get the number of sides or the seed of a given die using the Sides and Seed methods, respectively.

Now for a more robust example… It is a straightforward task to implement Method I of generating ability scores from the AD&D® 1E Dungeon Masters Guide, p. 11. The following code fragment will roll 4d6 and drop the lowest result, yielding a Strength score according to the Nancy boy method:

import (
  "fmt"
  "sort"

  "github.com/yesmar/dice/polyhedral"
)

d6 := polyhedral.NewD6(nil)
_, xi := d6.RollWithScores(4)
sort.Ints(xi)
xi = xi[1:] // Drop the lowest score.
str := 0
for i, _ := range xi {
  str += xi[i]
}
fmt.Println("Strength", str)

This sample makes use of the die.RollWithScores method, which returns a score and a slice of individual dice scores. Since we are throwing away the lowest score, we ignore the returned sum. We sort the slice, omit the lowest value, and sum the remaining scores, yielding the final ability score.

The polyhedral.Die API also supports dice expressions, allowing for the parsing and evaluation of strings that look like 3d6, 2d10+2, or 4d10–4 using the expression.Parse method. The structure of the input must match the following regular expression:

^\s*(\d+)?\s*[dD]{1}(\d+)\s*([\+\-–\*×]?\d+)?\s*$

Input is expected to take the form qdsom, where q is an optional quantity, s is the number of sides, and o and m are the optional operator and operand, respectively. The operators +, - (including the Unicode 'MINUS SIGN' U+2212), and * (including the Unicode 'MULTIPLICATION SIGN' U+00D7) are supported. Note that expression.Parse tolerates some whitespace, but inputs such as d 6, - 1, and the like will be rejected. An example:

dx, err := expression.Parse("1d4×10", nil)
if err != nil {
  log.Fatal(err)
}
score, _ := dx.Eval()
fmt.Println(dx, "→", score)

The above code fragment will print out a number in the range 10–40. Note the use of the Unicode 'MULTIPLICATION SIGN' U+00D7 in the expression. Use of the literal * will result in the same evaluation.

Of course, if you have the quantity, polyhedral.Die, and modifier as distinct variables, you can bypass expression.Parse and call expression.New directly, which of course perform better in terms of execution speed.

dx, err := expression.New(3, polyhedral.NewD10(nil), nil) // 3d10
// The remainder of the code follows from the previous fragment…

The ability to create modifiers (e.g., +1, –3, ×4) is provided by the polyhedral.Modifier API in support of the polyhedral.Expression API. You can create polyhedral.Modifier instances and use them directly with the polyhedral.Die Roll API. For example:

d4 := polyhedral.NewD4(nil)
m, err := modifier.New(modifier.Bonus, 1)
if err != nil {
  // Handle error…
}
fmt.Println(m.Eval(d4.Eval()))

Note that if you strictly use modifier.Penalty, modifier.Bonus, and modifier.Multiplier in calls to modifier.New (i.e. no untrusted input is passed in) then a result of no error is guaranteed. In such cases you may safely ignore the error return value using the _ keyword. (Even so, a comment indicating this special condition is warranted.)

The cmd/roll program is provided as a self-contained sample that illustrates how to use the polyhedral/expression package. Usage is straightforward:

$ go run cmd/roll/main.go 1d4*10 3d6 2d10+2 1d20 1d100
1d4×10 10
3d6 [3 1 2] 6
2d10+2 [3 3] 8
1d20 13
1d100 33

The user-supplied expressions will be parsed, evaluated, and their respective scores returned. In the case of an expression with multiple scores, e.g., 3d6, each individual die score will be shown along with the total score. Modifiers, if any, will only be applied to the total score.

If the -verbose flag is specified then the PRNG seed will be displayed. If you pass this value back in using the -seed flag then you will get the same results.

Directories

Path Synopsis
cmd
roll command

Jump to

Keyboard shortcuts

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