package core import ( "crypto/rand" "crypto/sha256" "encoding/base64" "fmt" "math" "math/big" ) func RandomDecimal(bits int) (string, error) { max := new(big.Int).Lsh(big.NewInt(1), uint(bits)) n, err := rand.Int(rand.Reader, max) if err != nil { return "", err } return n.String(), nil } func RandomSeed() (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err } return base64.RawURLEncoding.EncodeToString(b), nil } func ExpectedGuess(taskID, seed, clientID string, seq int64, bits int) string { h := sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%s|%d", taskID, seed, clientID, seq))) n := new(big.Int).SetBytes(h[:]) mod := new(big.Int).Lsh(big.NewInt(1), uint(bits)) n.Mod(n, mod) return n.String() } func Distance(a, b string) (*big.Int, error) { x, ok := new(big.Int).SetString(a, 10) if !ok { return nil, fmt.Errorf("invalid integer") } y, ok := new(big.Int).SetString(b, 10) if !ok { return nil, fmt.Errorf("invalid integer") } d := new(big.Int).Sub(x, y) d.Abs(d) return d, nil } func log2Big(x *big.Int) float64 { if x.Sign() <= 0 { return 0 } n := x.BitLen() if n <= 53 { return math.Log2(float64(x.Uint64())) } shift := n - 53 top := new(big.Int).Rsh(new(big.Int).Set(x), uint(shift)) return math.Log2(float64(top.Uint64())) + float64(shift) } func Score(distance *big.Int, bits int) float64 { if distance.Sign() == 0 { return 100 } s := 100 * (1 - log2Big(new(big.Int).Add(distance, big.NewInt(1)))/float64(bits)) if s < 0 { return 0 } if s > 100 { return 100 } return s } func Position(clientID string, score float64) (float64, float64, float64) { h := sha256.Sum256([]byte(clientID)) f := func(i int) float64 { v := uint16(h[i])<<8 | uint16(h[i+1]); return float64(v)/65535*2 - 1 } x, y, z := f(0), f(2), f(4) norm := math.Sqrt(x*x + y*y + z*z) if norm < 0.001 { norm = 1 } r := 0.25 + 13*(1-score/100) return r * x / norm, r * y / norm, r * z / norm }