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, 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, 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 results, 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, err } for _, result := range results[alreadyRolled:] { if result.Result == result.Size { neededExplosions++ } } } } return results, 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 }