/* Copyright (C) 2011, 2013-2014, 2017, 2019 Luke Shumaker */ package main import ( "math" "fmt" "os" "regexp" "strconv" "strings" ) func usage() { fmt.Printf("Usage: %s ...\n", os.Args[0]) fmt.Println("SPECLIST = [-]SPEC[<<+|->SPEC>...]") fmt.Println("SPEC = []d | ") } var parser = regexp.MustCompile("^(([0-9]*)d)?([0-9]+)$") func rollSpec(spec string) (mean float64, variance float64) { parts := parser.FindStringSubmatch(spec) if len(parts) < 4 { usage() os.Exit(1) } if parts[1] == "" { mean, _ := strconv.Atoi(parts[3]) return float64(mean), 0 } else { dice, _ := strconv.Atoi(parts[2]) die_size, _ := strconv.Atoi(parts[3]) if dice < 1 { dice = 1 } // - Let 'd' be the die_size // - Let 'X' be the the uniform random variable that is the result of a 1d${d} roll // - E(X) = (1+d)/2 mean = float64(1 + die_size)/2.0 // - Var(X) = E(X²)-E(X)² // - E(X²) = (Σi² for i=1..d) / d // = ¹/₆d(d+1)(2d+1) / d // = d(d+1)(2d+1) / (6d) // ∴ Var(X) = (d(d+1)(2d+1) / (6d)) - ((1+d)/2)² // = ¹/₁₂(d+1)(d-1) variance = float64((die_size+1)*(die_size-1))/12.0 mean *= float64(dice) variance *= float64(dice) return } } func roll(speclist string) { var mean, variance float64 neg := strings.HasPrefix(speclist, "-") speclist = strings.TrimPrefix(speclist, "-") for { sep := strings.IndexAny(speclist, "+-") var spec string if sep < 0 { spec = speclist } else { spec = speclist[:sep] } _mean, _variance := rollSpec(spec) if neg { _mean = -_mean } mean += _mean variance += _variance if sep < 0 { break } neg = speclist[sep] == '-' speclist = speclist[sep+1:] } fmt.Printf("µ=%v σ=%.2v\n", mean, math.Sqrt(variance)) } func main() { if len(os.Args) < 2 { usage() } for _, arg := range os.Args[1:] { roll(arg) } }