89 lines
1.9 KiB
Go
89 lines
1.9 KiB
Go
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
|
|
}
|