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.

pingpong.gno

2.32 Kb · 82 lines
 1// Package pingpong is a two-player game where the only move is to pass the
 2// turn. It exists to demonstrate gnotif: every turn emits TurnPlayed, and
 3// the realm declares a verified gnotif trigger that tells the next player.
 4package pingpong
 5
 6import (
 7	"chain"
 8	"strconv"
 9	"strings"
10
11	"gno.land/p/nt/avl/v0"
12	"gno.land/p/nt/seqid/v0"
13
14	"gno.land/r/g1sl6fyq6mrc3wydt8xluhxx4aqsudpp9vhv6g2a/gnotif"
15)
16
17// Game is a match between First and Second. First plays turn 1.
18type Game struct {
19	ID                  string
20	First, Second, Next address
21	Turn                int
22}
23
24var (
25	games    avl.Tree // id → *Game
26	lastGame seqid.ID
27	declared bool
28)
29
30// NewGame opens a game against opponent and returns its id. The caller plays first.
31func NewGame(cur realm, opponent address) string {
32	caller := cur.Previous().Address()
33	if opponent == caller {
34		panic("cannot play against yourself")
35	}
36	g := &Game{ID: lastGame.Next().String(), First: caller, Second: opponent, Next: caller}
37	games.Set(g.ID, g)
38	chain.Emit("GameCreated", "game", g.ID, "first", g.First.String(), "second", g.Second.String())
39	return g.ID
40}
41
42// Play takes the caller's turn in game and passes it to the other player.
43func Play(cur realm, game string) {
44	v := games.Get(game)
45	if v == nil {
46		panic("unknown game")
47	}
48	g := v.(*Game)
49	if g.Next != cur.Previous().Address() {
50		panic("not your turn")
51	}
52	if g.Next == g.First {
53		g.Next = g.Second
54	} else {
55		g.Next = g.First
56	}
57	g.Turn++
58	chain.Emit("TurnPlayed", "game", g.ID, "next", g.Next.String(), "turn", strconv.Itoa(g.Turn))
59}
60
61// DeclareTriggers declares the "your turn" trigger in the gnotif registry.
62// The registry sees this realm as the caller, so the trigger is verified.
63func DeclareTriggers(cur realm) {
64	if declared {
65		panic("triggers already declared")
66	}
67	declared = true
68	gnotif.Declare(cross(cur), cur.PkgPath(), "TurnPlayed", "", "next", "Your turn", "Game {game}, turn {turn}", "/?game={game}")
69}
70
71// Render lists the games with their players, turn and next player.
72func Render(path string) string {
73	var b strings.Builder
74	b.WriteString("# pingpong\n\n")
75	games.Iterate("", "", func(_ string, v any) bool {
76		g := v.(*Game)
77		b.WriteString("- **" + g.ID + "** " + g.First.String() + " vs " + g.Second.String() +
78			" · turn " + strconv.Itoa(g.Turn) + " · next " + g.Next.String() + "\n")
79		return false
80	})
81	return b.String()
82}