PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • closestguess.gnogno
closestguess.gnogno
1// Package closestguess is a blind, multiplayer number-guessing puzzle.2//3// Unlike a classic higher/lower guessing game, closestguess gives NO feedback4// per guess. Each round hides a target in 1..1000 (derived deterministically5// from the block height the round started at); every address may submit6// exactly one blind Guess for the round. Whenever someone calls Reveal, the7// round closes: the target is disclosed, the guess with the smallest absolute8// distance wins (ties go to whoever guessed first), the winner is recorded on9// the leaderboard, and a fresh round starts immediately.10package closestguess1112import (13	"strconv"14	"strings"1516	"chain"17	"chain/runtime"18)1920const (21	minTarget = 122	maxTarget = 100023)2425// roundT holds the mutable state of the open round. The target stays hidden26// (never rendered, never returned) until Reveal closes the round.27type roundT struct {28	number      int29	startHeight int6430	target      int31}3233// winRecord is one closed-round result, kept for the leaderboard.34type winRecord struct {35	round    int36	winner   string37	target   int38	guess    int39	distance int40	players  int41}4243// guessEntry is one address's blind guess for the current round.44type guessEntry struct {45	addr string46	n    int47}4849var (50	current *roundT51	// entries holds one guessEntry per address for the CURRENT round only, in52	// submission order (so Reveal can break distance ties in favor of53	// whoever guessed first). Reset on every Reveal. A plain slice is fine54	// here: rounds are player-scoped and small, unlike long-lived global55	// state where avl.Tree would matter.56	entries []guessEntry57	// leaderboard is append-only across all rounds; sorted on render.58	leaderboard []winRecord59)6061func init() {62	current = newRound(1, runtime.ChainHeight())63}6465// newRound builds a fresh round whose hidden target derives deterministically66// from the start height and round number. Gno has no runtime RNG, so mixing67// height with the round number is the only available entropy, and it also68// keeps consecutive rounds at the same height from repeating a target.69func newRound(number int, height int64) *roundT {70	return &roundT{71		number:      number,72		startHeight: height,73		target:      targetFromHeight(height, number),74	}75}7677func targetFromHeight(h int64, round int) int {78	if h < 0 {79		h = -h80	}81	span := int64(maxTarget - minTarget + 1)82	mixed := h*2654435761 + int64(round)*40503 + 1234583	if mixed < 0 {84		mixed = -mixed85	}86	return int(mixed%span) + minTarget87}8889// Guess records the caller's single blind guess for the current round. It90// returns no hint about correctness — that is the whole point of the puzzle.91// Crossing function: caller invokes as Guess(cross(cur), n).92func Guess(cur realm, n int) string {93	if n < minTarget || n > maxTarget {94		panic("guess must be in " + strconv.Itoa(minTarget) + ".." + strconv.Itoa(maxTarget))95	}9697	caller := cur.Previous().Address().String()98	if hasGuessed(caller) {99		panic("you already guessed this round; wait for Reveal()")100	}101102	entries = append(entries, guessEntry{addr: caller, n: n})103104	chain.Emit("GuessSubmitted",105		"round", strconv.Itoa(current.number),106		"player", caller,107	)108109	return "guess recorded for round " + strconv.Itoa(current.number) + " (" +110		strconv.Itoa(len(entries)) + " player(s) so far) — call Reveal() to see who's closest"111}112113func hasGuessed(addr string) bool {114	for _, e := range entries {115		if e.addr == addr {116			return true117		}118	}119	return false120}121122// Reveal closes the current round: discloses the target, crowns whoever123// landed closest (ties favor the earliest guesser), records a leaderboard124// entry, and opens a fresh round. Panics if nobody has guessed yet.125// Crossing function.126func Reveal(cur realm) string {127	if len(entries) == 0 {128		panic("no guesses submitted yet this round")129	}130131	target := current.target132	winner := entries[0]133	best := distance(winner.n, target)134	for _, e := range entries[1:] {135		if d := distance(e.n, target); d < best {136			best = d137			winner = e138		}139	}140141	rec := winRecord{142		round:    current.number,143		winner:   winner.addr,144		target:   target,145		guess:    winner.n,146		distance: best,147		players:  len(entries),148	}149	leaderboard = append(leaderboard, rec)150151	chain.Emit("RoundRevealed",152		"round", strconv.Itoa(rec.round),153		"winner", winner.addr,154		"target", strconv.Itoa(target),155		"distance", strconv.Itoa(best),156	)157158	closedRound := current.number159	current = newRound(closedRound+1, runtime.ChainHeight())160	entries = nil161162	return "round " + strconv.Itoa(closedRound) + " revealed: target was " + strconv.Itoa(target) +163		" — " + shortAddr(winner.addr) + " wins, guessed " + strconv.Itoa(winner.n) +164		" (distance " + strconv.Itoa(best) + ") among " + strconv.Itoa(rec.players) +165		" player(s). Round " + strconv.Itoa(current.number) + " is now open."166}167168func distance(guess, target int) int {169	d := guess - target170	if d < 0 {171		d = -d172	}173	return d174}175176// Render produces the gnoweb Markdown view. The current round's target and177// individual guess values are never shown — only the player count — so178// rendering the page can't leak the puzzle.179func Render(path string) string {180	var b strings.Builder181182	b.WriteString("# Closest Guess\n\n")183	b.WriteString("A blind, multiplayer number-guessing puzzle. Each round hides a target in ")184	b.WriteString("**" + strconv.Itoa(minTarget) + ".." + strconv.Itoa(maxTarget) + "**. ")185	b.WriteString("Call `Guess(n)` once per round — you get no feedback. ")186	b.WriteString("Anyone can call `Reveal()` to close the round: the target is disclosed and ")187	b.WriteString("whoever landed closest wins.\n\n")188189	b.WriteString("## Current round\n\n")190	b.WriteString("- Round: **" + strconv.Itoa(current.number) + "**\n")191	b.WriteString("- Started at block height: " + strconv.FormatInt(current.startHeight, 10) + "\n")192	b.WriteString("- Players guessed so far: " + strconv.Itoa(len(entries)) + "\n")193	b.WriteString("- Status: **open** — target hidden until `Reveal()` is called.\n\n")194195	b.WriteString("## Leaderboard (closest-ever wins)\n\n")196	b.WriteString(renderLeaderboard())197198	b.WriteString("\n## How to play\n\n")199	b.WriteString("```\n")200	b.WriteString("Guess(n)   // n in 1..1000, one shot per address per round, no feedback\n")201	b.WriteString("Reveal()   // closes the round: reveals target, crowns the closest guess\n")202	b.WriteString("```\n")203204	return b.String()205}206207// renderLeaderboard formats closed rounds ranked by smallest distance (ties:208// earlier round first). Sorted on a copy so state is untouched.209func renderLeaderboard() string {210	if len(leaderboard) == 0 {211		return "_No round has been revealed yet — be the first to call `Reveal()`!_\n"212	}213214	ranked := make([]winRecord, len(leaderboard))215	copy(ranked, leaderboard)216	sortByClosest(ranked)217218	var b strings.Builder219	b.WriteString("| Rank | Player | Round | Target | Guess | Distance | Players |\n")220	b.WriteString("|---|---|---|---|---|---|---|\n")221	for i, w := range ranked {222		b.WriteString("| " + strconv.Itoa(i+1) + " | `" + shortAddr(w.winner) + "` | " +223			strconv.Itoa(w.round) + " | " + strconv.Itoa(w.target) + " | " +224			strconv.Itoa(w.guess) + " | " + strconv.Itoa(w.distance) + " | " +225			strconv.Itoa(w.players) + " |\n")226	}227	return b.String()228}229230// sortByClosest does an in-place insertion sort: smallest distance first,231// then earlier round first on ties. Small n; insertion sort keeps it232// deterministic and dependency-free.233func sortByClosest(rs []winRecord) {234	for i := 1; i < len(rs); i++ {235		j := i236		for j > 0 && closer(rs[j], rs[j-1]) {237			rs[j], rs[j-1] = rs[j-1], rs[j]238			j--239		}240	}241}242243func closer(a, b winRecord) bool {244	if a.distance != b.distance {245		return a.distance < b.distance246	}247	return a.round < b.round248}249250func shortAddr(a string) string {251	if len(a) <= 12 {252		return a253	}254	return a[:6] + "…" + a[len(a)-4:]255}256

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

  • Render(path string) string

  • Reveal(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

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.