From 1c30be467d6f0b430e51c701f918cd98a6ba9330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=90=99PiperYxzzy?= Date: Sun, 10 Mar 2024 10:29:06 +0200 Subject: [PATCH] Initial setup of Roll --- .gitignore | 2 + go.mod | 3 + roll.go | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 go.mod create mode 100644 roll.go diff --git a/.gitignore b/.gitignore index 3b735ec..bade807 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ # Go workspace file go.work + +roll \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..891b9b7 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/yxzzy-wtf/roll + +go 1.19 diff --git a/roll.go b/roll.go new file mode 100644 index 0000000..2021fad --- /dev/null +++ b/roll.go @@ -0,0 +1,166 @@ +package main + +import ( + "crypto/rand" + "fmt" + "math/big" + "os" + "strconv" + "time" +) + +func globalRand(n int) int { + r, err := rand.Int(rand.Reader, big.NewInt(int64(n))) + if err != nil { + panic(err) + } + return int(r.Int64()) +} + +func main() { + if len(os.Args) == 0 { + panic("No arguments (d2, d4, d6, d8, d10, d12, d20, d100)") + } + die := os.Args[1] + rolls := 1 + + if len(os.Args) == 3 { + r, err := strconv.Atoi(os.Args[2]) + if err != nil { + panic(err) + } + if r < 1 { + panic("Cannot roll fewer than 1 times") + } + + rolls = r + } + + var dieFunc func() int + var critGood = []int{} + var critBad = []int{} + var critNeut = []int{} + switch die { + case "d2": + dieFunc = d2 + case "d4": + dieFunc = d4 + case "d6": + dieFunc = d6 + case "d8": + dieFunc = d8 + case "d10": + dieFunc = d10 + critGood = []int{10} + critBad = []int{1} + case "d12": + dieFunc = d12 + critGood = []int{12} + critBad = []int{1} + case "d20": + dieFunc = d20 + critGood = []int{20} + critBad = []int{1} + case "d100": + dieFunc = d100 + critNeut = []int{11, 22, 33, 44, 55, 66, 77, 88, 99} + critBad = []int{100} + critGood = []int{1} + default: + panic("Invalid die choice (d2, d4, d6, d8, d10, d12, d20, d100)") + } + + sum := 0 + for r := 0; r < rolls; r++ { + bouncesMin := 2 + bouncesMax := 5 + + if rolls == 1 { + bouncesMin = 5 + bouncesMax = 11 + } + sum += simulate(dieFunc, bouncesMin, bouncesMax, critNeut, critGood, critBad) + } + + if rolls > 1 { + fmt.Printf("Roll total: %v\n", sum) + } + +} + +func simulate(dieFunc func() int, bouncesMin int, bouncesMax int, critNeut []int, critGood []int, critBad []int) int { + bounces := globalRand(bouncesMax-bouncesMin) + bouncesMin + + fmt.Print("") + + for b := 0; b < bounces; b++ { + preroll := dieFunc() + fmt.Printf("\r>\033[34m%3d\033[0m", preroll) // purple while rolling + + rwait := globalRand(50) + 25 + time.Sleep(time.Millisecond * time.Duration(rwait)) + } + + actualRoll := dieFunc() + colour := "\033[34m" // Blue + if isIn(critNeut, actualRoll) { + colour += "\033[1m\033[35m" // Purple + } else if isIn(critBad, actualRoll) { + colour = "\033[1m\033[31m" // Red + } else if isIn(critGood, actualRoll) { + colour = "\033[1m\033[32m" // Green + } + + fmt.Printf("\r%v%4d\033[0m\n", colour, actualRoll) + + return actualRoll +} + +func isIn(slice []int, item int) bool { + for _, i := range slice { + if i == item { + return true + } + } + return false +} + +func d2() int { + return globalRand(2) + 1 +} + +func d4() int { + return globalRand(4) + 1 +} + +func d6() int { + return globalRand(6) + 1 +} + +func d8() int { + return globalRand(8) + 1 +} + +func d10() int { + return globalRand(10) + 1 +} + +func d12() int { + return globalRand(12) + 1 +} + +func d20() int { + return globalRand(20) + 1 +} + +func d100() int { + roll10 := globalRand(10) + roll100 := globalRand(10) * 10 + roll := roll100 + roll10 + + if roll == 0 { + roll = 100 + } + + return roll +}