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

render.gno

5.61 Kb · 159 lines
  1package gnodice
  2
  3import (
  4	"strconv"
  5	"strings"
  6)
  7
  8// Render affiche une page lisible sur gnoweb (le site officiel de gno.land).
  9//   - chemin ""               : règles, banque et dernières parties
 10//   - chemin "player/<g1...>" : statistiques et historique d'un joueur
 11//   - chemin "game/<numéro>"  : une partie, avec de quoi vérifier le tirage
 12//
 13// Le chemin est choisi par le visiteur : on ne l'affiche jamais tel quel, on
 14// n'utilise qu'une adresse ou un numéro validés.
 15func Render(path string) string {
 16	switch {
 17	case strings.HasPrefix(path, "player/"):
 18		player := address(strings.TrimPrefix(path, "player/"))
 19		if !player.IsValid() {
 20			return "# GNO-DICE\n\nAdresse invalide.\n"
 21		}
 22		return renderPlayer(player)
 23	case strings.HasPrefix(path, "game/"):
 24		id, err := strconv.ParseInt(strings.TrimPrefix(path, "game/"), 10, 64)
 25		if err != nil || getGame(id) == nil {
 26			return "# GNO-DICE\n\nPartie introuvable.\n"
 27		}
 28		return renderGame(getGame(id))
 29	}
 30	return renderHome()
 31}
 32
 33func renderHome() string {
 34	var b strings.Builder
 35	b.WriteString("# 🎲 GNO-DICE\n\n")
 36	b.WriteString("Choisis un chiffre de 1 à 6, mise entre 1 et 10 GNOT. ")
 37	b.WriteString("Si le dé tombe sur ton chiffre, tu gagnes **5 fois ta mise**. ")
 38	b.WriteString("Un lancer toutes les 10 minutes.\n\n")
 39	b.WriteString("Le jeu est **prouvablement équitable** : ton chiffre reste caché (empreinte SHA-256) pendant que le croupier tire le dé, ")
 40	b.WriteString("puis il est dévoilé et vérifié par le contrat. Chaque tirage publie sa graine : `game/<numéro>` pour le vérifier.\n\n")
 41
 42	if paused {
 43		b.WriteString("> ⏸️ Le jeu est actuellement en pause.\n\n")
 44	}
 45
 46	b.WriteString("## Banque\n\n")
 47	b.WriteString("- Solde : " + formatGnot(Bankroll()) + " GNOT (dont " + formatGnot(reserved) + " réservés aux parties en cours)\n")
 48	b.WriteString("- Mise maximale acceptée en ce moment : " + formatGnot(MaxCoverableBet()) + " GNOT\n")
 49	b.WriteString("- Parties : " + itoa64(totalGames) + " (" + strconv.Itoa(totalWins) + " gagnées, " + strconv.Itoa(openGames.Size()) + " en cours)\n")
 50	b.WriteString("- Total misé : " + formatGnot(totalWagered) + " GNOT, total payé : " + formatGnot(totalPaid) + " GNOT\n\n")
 51
 52	b.WriteString("## Dernières parties\n\n")
 53	writeGamesTable(&b, recent, true)
 54	return b.String()
 55}
 56
 57func renderPlayer(player address) string {
 58	var b strings.Builder
 59	b.WriteString("# 🎲 GNO-DICE : joueur\n\n")
 60	b.WriteString("`" + player.String() + "`\n\n")
 61
 62	p := getPlayer(player)
 63	if p == nil {
 64		b.WriteString("Aucune partie jouée pour l'instant.\n")
 65		return b.String()
 66	}
 67	b.WriteString("- Parties terminées : " + strconv.Itoa(p.Played) + " (" + strconv.Itoa(p.Wins) + " gagnées), en cours : " + strconv.Itoa(p.Open) + "\n")
 68	b.WriteString("- Total misé : " + formatGnot(p.Wagered) + " GNOT, total gagné : " + formatGnot(p.Paid) + " GNOT\n\n")
 69	b.WriteString("## Dernières parties\n\n")
 70	writeGamesTable(&b, p.History, false)
 71	return b.String()
 72}
 73
 74func renderGame(g *Game) string {
 75	var b strings.Builder
 76	b.WriteString("# 🎲 GNO-DICE : partie n° " + itoa64(g.ID) + "\n\n")
 77	b.WriteString("- Joueur : `" + g.Player.String() + "`\n")
 78	b.WriteString("- Mise : " + formatGnot(g.Bet) + " GNOT, bloc " + itoa64(g.PlacedHeight) + "\n")
 79	b.WriteString("- Statut : " + g.Status.String() + "\n")
 80	b.WriteString("- Empreinte du chiffre : `" + g.Commitment + "`\n")
 81	if g.Seed != "" {
 82		b.WriteString("- Graine du croupier : `" + g.Seed + "`\n")
 83		b.WriteString("- Dé : **" + strconv.Itoa(g.Roll) + "**\n")
 84	}
 85	if g.Guess != 0 {
 86		b.WriteString("- Chiffre dévoilé : **" + strconv.Itoa(g.Guess) + "**\n")
 87	}
 88	if g.Payout > 0 {
 89		b.WriteString("- Versé au joueur : " + formatGnot(g.Payout) + " GNOT\n")
 90	}
 91	b.WriteString("\n### Vérifier ce tirage\n\n")
 92	b.WriteString("dé = 1 + (8 premiers octets de sha256(\"gnodice|v1|roll|\" + graine + \"|\" + empreinte + \"|\" + numéro)) mod 6\n")
 93	return b.String()
 94}
 95
 96func writeGamesTable(b *strings.Builder, ids []int64, withPlayer bool) {
 97	if len(ids) == 0 {
 98		b.WriteString("Aucune partie terminée pour l'instant.\n")
 99		return
100	}
101	if withPlayer {
102		b.WriteString("| # | Bloc | Joueur | Mise | Chiffre | Dé | Résultat |\n")
103		b.WriteString("|---|---|---|---|---|---|---|\n")
104	} else {
105		b.WriteString("| # | Bloc | Mise | Chiffre | Dé | Résultat |\n")
106		b.WriteString("|---|---|---|---|---|---|\n")
107	}
108	for i := len(ids) - 1; i >= 0; i-- {
109		g := mustGetGame(ids[i])
110		b.WriteString("| [" + itoa64(g.ID) + "](:game/" + itoa64(g.ID) + ") | " + itoa64(g.PlacedHeight) + " | ")
111		if withPlayer {
112			b.WriteString(shortAddress(g.Player) + " | ")
113		}
114		b.WriteString(formatGnot(g.Bet) + " GNOT | " + digitOrDash(g.Guess) + " | " + digitOrDash(g.Roll) + " | ")
115		switch g.Status {
116		case StatusWon:
117			b.WriteString("✅ +" + formatGnot(g.Payout) + " GNOT |\n")
118		case StatusRefunded:
119			b.WriteString("↩️ remboursé |\n")
120		case StatusExpired:
121			b.WriteString("⌛ expiré |\n")
122		default:
123			b.WriteString("❌ perdu |\n")
124		}
125	}
126}
127
128func digitOrDash(v int) string {
129	if v == 0 {
130		return "-"
131	}
132	return strconv.Itoa(v)
133}
134
135// shortAddress raccourcit une adresse : g1abcd...wxyz
136func shortAddress(a address) string {
137	s := a.String()
138	if len(s) <= 12 {
139		return s
140	}
141	return s[:6] + "..." + s[len(s)-4:]
142}
143
144// formatGnot convertit des ugnot en GNOT lisibles : 1500000 -> "1.5"
145func formatGnot(ugnot int64) string {
146	sign := ""
147	if ugnot < 0 {
148		sign = "-"
149		ugnot = -ugnot
150	}
151	whole := ugnot / UgnotPerGnot
152	frac := ugnot % UgnotPerGnot
153	if frac == 0 {
154		return sign + itoa64(whole)
155	}
156	fracStr := itoa64(frac + UgnotPerGnot)[1:] // complète avec des zéros : 500000 -> "500000"
157	fracStr = strings.TrimRight(fracStr, "0")
158	return sign + itoa64(whole) + "." + fracStr
159}