summaryrefslogtreecommitdiff
path: root/roll.go
blob: 5a02138aca512ccb37f04fa0c7641d38bedb53d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* Copyright (C) 2011, 2013-2014, 2017 Luke Shumaker <lukeshu@sbcglobal.net> */
package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"os"
	"regexp"
	"strconv"
	"strings"
)

func usage() {
	fmt.Printf("Usage: %s <SPECSLIST>...\n", os.Args[0])
	fmt.Println("SPECLIST = [-]SPEC[<<+|->SPEC>...]")
	fmt.Println("SPEC = [<COUNT>]d<SIZE> | <MOD>")
}

func rollDie(size int) int {
	num, err := rand.Int(rand.Reader, big.NewInt(int64(size)))
	if err != nil {
		panic(err)
	}
	return int(num.Int64()) + 1
}

var parser = regexp.MustCompile("^(([0-9]*)d)?([0-9]+)$")

func rollSpec(spec string) (string, int) {
	parts := parser.FindStringSubmatch(spec)
	if len(parts) < 4 {
		usage()
		os.Exit(1)
	}

	var str string
	var num int
	if parts[1] == "" {
		str = parts[3]
		num, _ = strconv.Atoi(str)
	} else {
		dice, _ := strconv.Atoi(parts[2])
		die_size, _ := strconv.Atoi(parts[3])
		if dice < 1 {
			dice = 1
		}

		var rolls []string
		for i := 0; i < dice; i++ {
			v := rollDie(die_size)
			rolls = append(rolls, strconv.Itoa(v))
			num += v
		}
		str = "(" + strings.Join(rolls, "+") + ")"
	}
	return str, num
}

func roll(speclist string) {
	var strs []string
	var total int

	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]
		}
		str, num := rollSpec(spec)
		if neg {
			str = "-" + str
			num = -num
		}
		strs = append(strs, str)
		total += num

		if sep < 0 {
			break
		}
		neg = speclist[sep] == '-'
		speclist = speclist[sep+1:]
	}

	fmt.Printf("%s = %d\n", strings.Join(strs, " + "), total)
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	for _, arg := range os.Args[1:] {
		roll(arg)
	}
}