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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}