Return flat modifier in dice roll Use log.Print instead of log.Fatal where makes sense
176 lines
3.9 KiB
Go
176 lines
3.9 KiB
Go
package dice
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/rand/v2"
|
|
"regexp"
|
|
"strconv"
|
|
)
|
|
|
|
type DieExp struct {
|
|
Size int
|
|
Count int
|
|
Add int
|
|
KeepHigh int
|
|
KeepLow int
|
|
Explode bool
|
|
}
|
|
|
|
type DieResult struct {
|
|
Size int
|
|
Result int
|
|
}
|
|
|
|
func (d DieExp) Roll() ([]DieResult, int, error) {
|
|
results := make([]DieResult, 0)
|
|
var err error
|
|
|
|
for i := 0; i < d.Count; i++ {
|
|
results, err = addDice(d, results)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
}
|
|
|
|
if d.KeepHigh != 0 {
|
|
highResults := make([]DieResult, 0)
|
|
for i := 0; i < d.KeepHigh; i++ {
|
|
var high int
|
|
for j, val := range results {
|
|
if results[high].Result > val.Result {
|
|
fmt.Printf("Result %v higher than %v\n", results[high].Result, results[j].Result)
|
|
high = j
|
|
} else {
|
|
fmt.Printf("Result %v lower than %v\n", results[high].Result, results[j].Result)
|
|
}
|
|
}
|
|
highResults = append(highResults, results[high])
|
|
results = append(results[:high], results[high+1:]...)
|
|
}
|
|
results = highResults
|
|
}
|
|
|
|
if d.KeepLow > len(results) {
|
|
return nil, 0, errors.New("keep low higher than remaining results")
|
|
}
|
|
|
|
if d.KeepLow != 0 {
|
|
lowResults := make([]DieResult, 0)
|
|
for i := 0; i < d.KeepLow; i++ {
|
|
var low int
|
|
for j, val := range results {
|
|
if results[low].Result < val.Result {
|
|
fmt.Printf("Result %v lower than %v\n", results[low].Result, results[j].Result)
|
|
low = j
|
|
} else {
|
|
fmt.Printf("Result %v higher than %v\n", results[low].Result, results[j].Result)
|
|
}
|
|
}
|
|
lowResults = append(lowResults, results[low])
|
|
results = append(results[:low], results[low+1:]...)
|
|
}
|
|
results = lowResults
|
|
}
|
|
|
|
if d.Explode {
|
|
var neededExplosions int
|
|
for _, result := range results {
|
|
if result.Result == result.Size {
|
|
neededExplosions++
|
|
}
|
|
}
|
|
for explosions := 0; explosions < neededExplosions; explosions++ {
|
|
alreadyRolled := len(results)
|
|
results, err = addDice(d, results)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for _, result := range results[alreadyRolled:] {
|
|
if result.Result == result.Size {
|
|
neededExplosions++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return results, d.Add, nil
|
|
}
|
|
|
|
func CreateFromExp(exp string) (DieExp, error) {
|
|
d := DieExp{
|
|
Count: 1,
|
|
KeepHigh: 0,
|
|
KeepLow: 0,
|
|
Explode: false,
|
|
}
|
|
match := regexp.MustCompile("[0-9]*d[0-9]+([hl][0-9]+)*e?([+-][0-9]+)?")
|
|
countMatch := regexp.MustCompile("^[0-9]*")
|
|
sizeMatch := regexp.MustCompile("d[0-9]+")
|
|
highMatch := regexp.MustCompile("h[0-9]+")
|
|
lowMatch := regexp.MustCompile("l[0-9]+")
|
|
explodeMatch := regexp.MustCompile("e")
|
|
addMatch := regexp.MustCompile("[+\\-][0-9]+")
|
|
match.Longest()
|
|
countMatch.Longest()
|
|
sizeMatch.Longest()
|
|
highMatch.Longest()
|
|
lowMatch.Longest()
|
|
matched := match.FindString(exp)
|
|
if len(matched) != len(exp) {
|
|
return DieExp{}, errors.New("dice expression invalid")
|
|
}
|
|
|
|
countString := countMatch.FindString(exp)
|
|
if len(countString) != 0 {
|
|
count, _ := strconv.Atoi(countString)
|
|
d.Count = count
|
|
}
|
|
|
|
sizeString := sizeMatch.FindString(exp)
|
|
if len(sizeString) != 0 {
|
|
size, _ := strconv.Atoi(sizeString[1:])
|
|
d.Size = size
|
|
}
|
|
|
|
highString := highMatch.FindString(exp)
|
|
if len(highString) != 0 {
|
|
high, _ := strconv.Atoi(highString[1:])
|
|
d.KeepHigh = high
|
|
}
|
|
|
|
lowString := lowMatch.FindString(exp)
|
|
if len(lowString) != 0 {
|
|
low, _ := strconv.Atoi(lowString[1:])
|
|
d.KeepLow = low
|
|
}
|
|
|
|
if explodeMatch.MatchString(exp) {
|
|
d.Explode = true
|
|
}
|
|
|
|
addString := addMatch.FindString(exp)
|
|
if len(addString) != 0 {
|
|
add, _ := strconv.Atoi(addString[1:])
|
|
if addString[0] == '-' {
|
|
add = 0 - add
|
|
}
|
|
d.Add = add
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func addDice(d DieExp, results []DieResult) ([]DieResult, error) {
|
|
if d.Size < 2 {
|
|
return nil, errors.New("dice size is too small. Should be at least 2")
|
|
}
|
|
if len(results) > 1000 {
|
|
return nil, errors.New("too many dice rolled. This may be caused by a dice explosion that got out of hand")
|
|
}
|
|
result := DieResult{
|
|
Size: d.Size,
|
|
Result: rand.IntN(d.Size) + 1,
|
|
}
|
|
return append(results, result), nil
|
|
}
|