PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/x/daily/rps/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / rps
Files
3 (README)(gnomod.toml)
Exported functions
2
Module
gno.land/r/moul/x/daily/rps/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • rps.gnogno
rps.gnogno
1package rps23import (4	"chain"5	"chain/runtime"6	"chain/runtime/unsafe"7	"strconv"8	"strings"910	"gno.land/p/nt/avl/v0"11)1213// Moves.14const (15	rock     = 016	paper    = 117	scissors = 218)1920var moveNames = [3]string{"rock", "paper", "scissors"}2122// Outcomes.23const (24	outLose = 0 // player loses25	outDraw = 126	outWin  = 2 // player wins27)2829// tally holds a single player's cumulative record.30type tally struct {31	wins   int32	losses int33	draws  int34	plays  int // total rounds this player has played (drives entropy)35}3637// round is one recorded game (for the recent-rounds table).38type round struct {39	player   address40	height   int6441	player_m int // player move42	house_m  int // house move43	outcome  int44}4546// State.47var (48	players = avl.NewTree() // address string -> *tally49	rounds  []round         // append-only history; Render shows the tail5051	// Global tally (caller-agnostic).52	totalWins   int // player wins53	totalLosses int // player losses54	totalDraws  int55	totalRounds int56)5758const maxRecent = 85960// parseChoice maps a choice string to a move index; ok=false if invalid.61func parseChoice(choice string) (int, bool) {62	switch strings.ToLower(strings.TrimSpace(choice)) {63	case "rock", "r":64		return rock, true65	case "paper", "p":66		return paper, true67	case "scissors", "s":68		return scissors, true69	}70	return 0, false71}7273// houseMove derives the house move deterministically from the chain height74// and the caller's play count (so repeated calls in the same block differ).75func houseMove(height int64, playCount int) int {76	x := height + int64(playCount)*777	m := x % 378	if m < 0 {79		m += 380	}81	return int(m)82}8384// decide returns the outcome from the player's perspective.85func decide(playerMove, houseM int) int {86	if playerMove == houseM {87		return outDraw88	}89	// player beats (player+1)%3 ... rock(0) beats scissors(2), etc.90	if (playerMove+2)%3 == houseM {91		return outWin92	}93	return outLose94}9596func getTally(key string) *tally {97	if v := players.Get(key); v != nil {98		return v.(*tally)99	}100	return nil101}102103// Play plays one round against the chain. choice is "rock"|"paper"|"scissors"104// (single-letter shortcuts accepted). It panics on an invalid choice.105func Play(cur realm, choice string) {106	mv, ok := parseChoice(choice)107	if !ok {108		panic("invalid choice: use rock, paper, or scissors")109	}110111	caller := unsafe.PreviousRealm().Address()112	key := caller.String()113114	t := getTally(key)115	if t == nil {116		t = &tally{}117		players.Set(key, t)118	}119120	height := runtime.ChainHeight()121	hm := houseMove(height, t.plays)122	outcome := decide(mv, hm)123124	t.plays++125	switch outcome {126	case outWin:127		t.wins++128		totalWins++129	case outLose:130		t.losses++131		totalLosses++132	default:133		t.draws++134		totalDraws++135	}136	totalRounds++137138	rounds = append(rounds, round{139		player:   caller,140		height:   height,141		player_m: mv,142		house_m:  hm,143		outcome:  outcome,144	})145146	chain.Emit(147		"RoundPlayed",148		"player", key,149		"choice", moveNames[mv],150		"house", moveNames[hm],151		"outcome", outcomeName(outcome),152	)153}154155func outcomeName(o int) string {156	switch o {157	case outWin:158		return "win"159	case outLose:160		return "lose"161	default:162		return "draw"163	}164}165166func winRate(wins, plays int) string {167	if plays == 0 {168		return "0%"169	}170	return strconv.Itoa(wins*100/plays) + "%"171}172173// Render returns a Markdown dashboard: global tally, per-player table, and the174// last few rounds. It is caller-agnostic (no cur).175func Render(path string) string {176	var b strings.Builder177178	b.WriteString("# Rock-Paper-Scissors — play vs the chain\n\n")179	b.WriteString("Call `Play(cur, \"rock\"|\"paper\"|\"scissors\")`. ")180	b.WriteString("The house move is derived deterministically from the current block height ")181	b.WriteString("and your personal play count.\n\n")182183	// Global tally.184	b.WriteString("## Global tally\n\n")185	b.WriteString("| Rounds | Player wins | Player losses | Draws | Player win rate |\n")186	b.WriteString("|---|---|---|---|---|\n")187	b.WriteString("| " + strconv.Itoa(totalRounds))188	b.WriteString(" | " + strconv.Itoa(totalWins))189	b.WriteString(" | " + strconv.Itoa(totalLosses))190	b.WriteString(" | " + strconv.Itoa(totalDraws))191	b.WriteString(" | " + winRate(totalWins, totalRounds) + " |\n\n")192193	// Per-player table (deterministic order via avl iteration).194	b.WriteString("## Players\n\n")195	if players.Size() == 0 {196		b.WriteString("_No games played yet. Be the first!_\n\n")197	} else {198		b.WriteString("| Player | W | L | D | Rounds | Win rate |\n")199		b.WriteString("|---|---|---|---|---|---|\n")200		players.Iterate("", "", func(key string, v interface{}) bool {201			t := v.(*tally)202			b.WriteString("| " + shortAddr(key))203			b.WriteString(" | " + strconv.Itoa(t.wins))204			b.WriteString(" | " + strconv.Itoa(t.losses))205			b.WriteString(" | " + strconv.Itoa(t.draws))206			b.WriteString(" | " + strconv.Itoa(t.plays))207			b.WriteString(" | " + winRate(t.wins, t.plays) + " |\n")208			return false209		})210		b.WriteString("\n")211	}212213	// Recent rounds (tail).214	b.WriteString("## Recent rounds\n\n")215	if len(rounds) == 0 {216		b.WriteString("_None yet._\n")217	} else {218		b.WriteString("| Height | Player | Choice | House | Result |\n")219		b.WriteString("|---|---|---|---|---|\n")220		start := len(rounds) - maxRecent221		if start < 0 {222			start = 0223		}224		// Show newest first.225		for i := len(rounds) - 1; i >= start; i-- {226			r := rounds[i]227			b.WriteString("| " + strconv.FormatInt(r.height, 10))228			b.WriteString(" | " + shortAddr(r.player.String()))229			b.WriteString(" | " + moveNames[r.player_m])230			b.WriteString(" | " + moveNames[r.house_m])231			b.WriteString(" | " + outcomeName(r.outcome) + " |\n")232		}233	}234235	return b.String()236}237238// shortAddr abbreviates an address string for display.239func shortAddr(a string) string {240	if len(a) <= 12 {241		return a242	}243	return a[:8] + "…" + a[len(a)-4:]244}245

Functions

  • Play(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, choice string)

  • Render(path string) string

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.