Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

results.gno

3.05 Kb · 124 lines
  1package arena
  2
  3import (
  4	"strings"
  5
  6	"gno.land/p/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/arena/payout/v0"
  7)
  8
  9// result is how a game ended, kept for readers: no payment is ever computed
 10// from it.
 11type result struct {
 12	mode     string
 13	stake    int64
 14	outcome  string // settled, cancelled or refunded
 15	players  int64  // how many played
 16	pot      int64  // their stakes
 17	fee      int64
 18	closedAt int64           // block time, Unix seconds
 19	payouts  []payout.Payout // who was paid what, in the order of the players
 20}
 21
 22const (
 23	outcomeSettled   = "settled"
 24	outcomeCancelled = "cancelled"
 25	outcomeRefunded  = "refunded"
 26)
 27
 28// maxResults is how many closed games the realm remembers.
 29const maxResults = 50
 30
 31// A result is stored as one string, its fields separated by "|", the payouts
 32// last as "address:amount" separated by ",":
 33//
 34//	mode|stake|outcome|players|pot|fee|closedAt|g1...:19500000,g1...:500000
 35func (r result) encode() string {
 36	var b strings.Builder
 37	b.WriteString(r.mode)
 38	for _, n := range []string{itoa(r.stake), r.outcome, itoa(r.players), itoa(r.pot), itoa(r.fee), itoa(r.closedAt)} {
 39		b.WriteByte('|')
 40		b.WriteString(n)
 41	}
 42	b.WriteByte('|')
 43	b.WriteString(joinPayouts(r.payouts))
 44	return b.String()
 45}
 46
 47func decodeResult(s string) result {
 48	f := strings.Split(s, "|")
 49	if len(f) != 8 {
 50		panic(errCorruptResult)
 51	}
 52	num := func(i int) int64 {
 53		n, ok := digits(f[i])
 54		if !ok {
 55			panic(errCorruptResult)
 56		}
 57		return n
 58	}
 59	r := result{mode: f[0], stake: num(1), outcome: f[2], players: num(3), pot: num(4), fee: num(5), closedAt: num(6)}
 60	if r.outcome != outcomeSettled && r.outcome != outcomeCancelled && r.outcome != outcomeRefunded {
 61		panic(errCorruptResult)
 62	}
 63	if f[7] == "" {
 64		return r
 65	}
 66	for _, entry := range strings.Split(f[7], ",") {
 67		colon := indexFrom(entry, 0, ':')
 68		if colon == 0 || colon == len(entry) {
 69			panic(errCorruptResult)
 70		}
 71		amount, ok := digits(entry[colon+1:])
 72		if !ok {
 73			panic(errCorruptResult)
 74		}
 75		r.payouts = append(r.payouts, payout.Payout{Address: address(entry[:colon]), Amount: amount})
 76	}
 77	return r
 78}
 79
 80// record keeps the result of a closed game, and forgets the oldest one past
 81// maxResults.
 82func record(id string, r result) {
 83	results.Set(id, r.encode())
 84	closed = append(closed, id)
 85	if len(closed) > maxResults {
 86		results.Remove(closed[0])
 87		next := make([]string, len(closed)-1)
 88		copy(next, closed[1:])
 89		closed = next
 90	}
 91}
 92
 93// resultOf returns the result of a closed game, if the realm still has it.
 94func resultOf(id string) (result, bool) {
 95	v := results.Get(id)
 96	if v == nil {
 97		return result{}, false
 98	}
 99	return decodeResult(v.(string)), true
100}
101
102// indexFrom returns the index of the first c in s at or after i, or len(s).
103func indexFrom(s string, i int, c byte) int {
104	for i < len(s) && s[i] != c {
105		i++
106	}
107	return i
108}
109
110// digits parses a non-negative decimal of at most 18 digits.
111func digits(s string) (int64, bool) {
112	if len(s) == 0 || len(s) > 18 {
113		return 0, false
114	}
115	n := int64(0)
116	for i := 0; i < len(s); i++ {
117		c := s[i]
118		if c < '0' || c > '9' {
119			return 0, false
120		}
121		n = n*10 + int64(c-'0')
122	}
123	return n, true
124}