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/numguess/v0

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • numguess.gnogno
numguess.gnogno
1// Package numguess is a Number Guessing Game realm.2//3// A hidden target in 1..100 is derived deterministically from the block height4// at which the round started. Players call Guess(n) to receive "higher",5// "lower" or "correct". When a round is solved the winner is recorded on the6// leaderboard (ranked by fewest guesses) and anyone can start a fresh round.7package numguess89import (10	"strconv"11	"strings"1213	"chain"14	"chain/runtime"1516	"gno.land/p/nt/avl/v0"17)1819// round holds the mutable state of the active game round.20type round struct {21	number      int    // 1-based round counter22	startHeight int64  // block height the round began at (entropy source)23	target      int    // hidden value in 1..10024	solved      bool   // set once someone guesses correctly25	lastHint    string // "higher" / "lower" / "correct" / "" for none yet26	winner      string // address of the solver, once solved27	winnerTries int    // attempts the winner needed28}2930// winRecord is one leaderboard entry.31type winRecord struct {32	addr   string33	round  int34	tries  int35	target int36}3738var (39	current *round40	// attempts counts guesses per address for the CURRENT round only.41	attempts = avl.NewTree() // addr(string) -> *int42	// leaderboard is append-only across all rounds; sorted on render.43	leaderboard []winRecord44)4546func init() {47	current = newRound(1, runtime.ChainHeight())48}4950// newRound builds a fresh round whose target derives deterministically from51// the start height. Gno has no runtime RNG, so height is the only entropy.52func newRound(number int, height int64) *round {53	return &round{54		number:      number,55		startHeight: height,56		target:      targetFromHeight(height),57		solved:      false,58		lastHint:    "",59	}60}6162// targetFromHeight maps a block height to a hidden number in 1..100.63// Deterministic and cheap: a small integer mix so consecutive rounds don't64// land on obviously adjacent targets.65func targetFromHeight(h int64) int {66	if h < 0 {67		h = -h68	}69	mixed := (h*2654435761 + 12345)70	if mixed < 0 {71		mixed = -mixed72	}73	return int(mixed%100) + 174}7576// Guess submits a guess for the current round. It records the caller's attempt77// count and returns "higher", "lower" or "correct". Crossing function: caller78// invokes as Guess(cross(cur), n).79func Guess(cur realm, n int) string {80	if !cur.IsCurrent() {81		panic("spoofed realm")82	}83	if n < 1 || n > 100 {84		panic("guess must be in 1..100")85	}86	if current.solved {87		panic("round already solved; call NewRound to start a fresh one")88	}8990	caller := cur.Previous().Address().String()91	incAttempt(caller)9293	var hint string94	switch {95	case n < current.target:96		hint = "higher"97	case n > current.target:98		hint = "lower"99	default:100		hint = "correct"101		current.solved = true102		current.winner = caller103		current.winnerTries = attemptsOf(caller)104		leaderboard = append(leaderboard, winRecord{105			addr:   caller,106			round:  current.number,107			tries:  current.winnerTries,108			target: current.target,109		})110		chain.Emit("RoundSolved",111			"round", strconv.Itoa(current.number),112			"winner", caller,113			"tries", strconv.Itoa(current.winnerTries),114		)115	}116117	current.lastHint = hint118	return hint119}120121// NewRound resets the game with a fresh target when the current round is122// solved. Panics if the current round is still open. Crossing function.123func NewRound(cur realm) string {124	if !cur.IsCurrent() {125		panic("spoofed realm")126	}127	if !current.solved {128		panic("current round is not solved yet")129	}130131	next := current.number + 1132	current = newRound(next, runtime.ChainHeight())133	attempts = avl.NewTree() // per-round attempt counters reset134	chain.Emit("NewRound", "round", strconv.Itoa(next))135	return "round " + strconv.Itoa(next) + " started"136}137138// incAttempt bumps the caller's attempt counter for the current round.139func incAttempt(addr string) {140	if v := attempts.Get(addr); v != nil {141		p := v.(*int)142		*p++143		return144	}145	one := 1146	attempts.Set(addr, &one)147}148149// attemptsOf returns how many guesses addr has made this round.150func attemptsOf(addr string) int {151	if v := attempts.Get(addr); v != nil {152		return *(v.(*int))153	}154	return 0155}156157// totalAttempts sums all attempts made in the current round.158func totalAttempts() int {159	total := 0160	attempts.Iterate("", "", func(_ string, v any) bool {161		total += *(v.(*int))162		return false163	})164	return total165}166167// Render produces the gnoweb Markdown view of the current game state.168func Render(path string) string {169	var b strings.Builder170171	b.WriteString("# Number Guessing Game\n\n")172	b.WriteString("Guess the hidden number between **1** and **100**. ")173	b.WriteString("Each round's target is derived deterministically from the block height at which it started.\n\n")174175	b.WriteString("## Current round\n\n")176	b.WriteString("- Round: **" + strconv.Itoa(current.number) + "**\n")177	b.WriteString("- Started at block height: " + strconv.FormatInt(current.startHeight, 10) + "\n")178	b.WriteString("- Attempts so far (this round): " + strconv.Itoa(totalAttempts()) + "\n")179180	hint := current.lastHint181	if hint == "" {182		hint = "_no guesses yet_"183	} else {184		hint = "`" + hint + "`"185	}186	b.WriteString("- Last hint: " + hint + "\n")187188	if current.solved {189		b.WriteString("- Status: **solved** by `" + current.winner + "` in " +190			strconv.Itoa(current.winnerTries) + " guess(es). ")191		b.WriteString("Call `NewRound()` to play again.\n")192	} else {193		b.WriteString("- Status: **open** — call `Guess(n)` to play.\n")194	}195	b.WriteString("\n")196197	b.WriteString("## Leaderboard\n\n")198	b.WriteString(renderLeaderboard())199200	b.WriteString("\n## How to play\n\n")201	b.WriteString("```\n")202	b.WriteString("Guess(n)     // n in 1..100 -> \"higher\" | \"lower\" | \"correct\"\n")203	b.WriteString("NewRound()   // start a fresh round once the current one is solved\n")204	b.WriteString("```\n")205206	return b.String()207}208209// renderLeaderboard formats winners ranked by fewest guesses (ties: earlier210// round first). Sorted on a copy so state is untouched.211func renderLeaderboard() string {212	if len(leaderboard) == 0 {213		return "_No winners yet — be the first!_\n"214	}215216	ranked := make([]winRecord, len(leaderboard))217	copy(ranked, leaderboard)218	sortByFewestTries(ranked)219220	var b strings.Builder221	b.WriteString("| Rank | Player | Round | Guesses | Target |\n")222	b.WriteString("|---|---|---|---|---|\n")223	for i, w := range ranked {224		b.WriteString("| " + strconv.Itoa(i+1) + " | `" + w.addr + "` | " +225			strconv.Itoa(w.round) + " | " + strconv.Itoa(w.tries) + " | " +226			strconv.Itoa(w.target) + " |\n")227	}228	return b.String()229}230231// sortByFewestTries does an in-place insertion sort: fewer tries first, then232// earlier round first on ties. Small n; insertion sort keeps it deterministic233// and dependency-free.234func sortByFewestTries(rs []winRecord) {235	for i := 1; i < len(rs); i++ {236		j := i237		for j > 0 && less(rs[j], rs[j-1]) {238			rs[j], rs[j-1] = rs[j-1], rs[j]239			j--240		}241	}242}243244func less(a, b winRecord) bool {245	if a.tries != b.tries {246		return a.tries < b.tries247	}248	return a.round < b.round249}250

Functions

  • Guess(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}, n int) string

  • NewRound(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}) 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.