2023-05-14 03:13:05 +00:00
|
|
|
package koboldsim
|
|
|
|
|
2024-09-28 04:56:13 +00:00
|
|
|
import (
|
2024-09-29 19:41:49 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2024-09-28 04:56:13 +00:00
|
|
|
"math"
|
|
|
|
|
|
|
|
"golang.org/x/exp/constraints"
|
|
|
|
)
|
2023-05-14 03:13:05 +00:00
|
|
|
|
|
|
|
// Generic helper functions not directly attached to Card Sim Engine mechanics.
|
|
|
|
|
|
|
|
// Mean calculates the mathematical mean of its arguments (the sum, divided
|
|
|
|
// by the number of elements). If it is called with no arguments, it returns
|
|
|
|
// NaN ("not a number"). If there are contradictory infinities among the
|
|
|
|
// arguments, it also returns NaN. Overflowing or underflowing can create an
|
|
|
|
// infinity.
|
|
|
|
func Mean(vals ...float64) float64 {
|
|
|
|
if len(vals) == 0 {
|
|
|
|
return math.NaN()
|
|
|
|
}
|
|
|
|
|
|
|
|
total := 0.0
|
|
|
|
for _, x := range vals {
|
|
|
|
total += x
|
|
|
|
}
|
|
|
|
return total / float64(len(vals))
|
|
|
|
}
|
2024-09-28 04:56:13 +00:00
|
|
|
|
2024-09-29 19:09:11 +00:00
|
|
|
// clamp returns the middle of the three provided values. It doesn't
|
|
|
|
// matter which order the values are in. This function is known as `mid` in
|
|
|
|
// Pico-8's library, among others. It is usually used to clamp a value to a
|
|
|
|
// range, and it doesn't care which order the range is written in.
|
|
|
|
func clamp[T constraints.Ordered](a, b, c T) T {
|
|
|
|
if a <= b && a <= c {
|
|
|
|
// `a` is least; mid is lower of `b` or `c`
|
|
|
|
if b <= c {
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
return c
|
2024-09-28 04:56:13 +00:00
|
|
|
}
|
2024-09-29 19:09:11 +00:00
|
|
|
if a >= b && a >= c {
|
|
|
|
// `a` is most; mid is greater of `b` or `c`
|
|
|
|
if b >= c {
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
return c
|
2024-09-28 04:56:13 +00:00
|
|
|
}
|
2024-09-29 19:09:11 +00:00
|
|
|
// `a` is neither most nor least; therefore, `a` is mid
|
|
|
|
return a
|
2024-09-28 04:56:13 +00:00
|
|
|
}
|
2024-09-29 19:41:49 +00:00
|
|
|
|
|
|
|
var ErrWrappedPanic = errors.New("panic")
|
|
|
|
|
|
|
|
// try catches a panic in the provided func and demotes it to an error, if any
|
|
|
|
// panic occurs. The returned error, if any, wraps `ErrWrappedPanic`. If the
|
|
|
|
// panic argument is itself an error, it is also wrapped; otherwise, it is
|
|
|
|
// stringified into the error message using `%v`.
|
|
|
|
func try(f func()) (finalErr error) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
if e, ok := r.(error); ok {
|
|
|
|
finalErr = fmt.Errorf("%w: %w", ErrWrappedPanic, e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
finalErr = fmt.Errorf("%w: %v", ErrWrappedPanic, r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
f()
|
|
|
|
return nil
|
|
|
|
}
|