Transaction

3653B8E7DD2144…13442E02A72A

Block 25,999 · index 0 · indexed

Summary

Hash
3653B8E7DD214440D2554343311F3EBFD68BF369CF01EF7B54E813442E02A72A
Block
25,999
Size
9679 bytes
Gas used
15,787,032 / 18,944,498
Fee
1000000ugnot
Memo
gnopublish
Status
success

Messages

Arguments · 9

  1. #1closestguess
  2. #2README.md
  3. #3# Closest Guess > ⚠️ **Experimental — generated with no human supervision** by the daily MCP pipeline to exercise gno tooling. Not audited. See [r/moul/x/daily](https://github.com/moul/gno-contracts/blob/main/r/moul/x/daily/README.md). --- A blind, multiplayer number-guessing puzzle: submit one silent guess per round in 1..1000, then anyone calls Reveal() to disclose the hidden target and crown whoever landed closest. No hints, no iterative narrowing — just one shot and a leaderboard ranked by smallest distance across all-time rounds. It's a deliberately different twist on the genre from higher/lower or bulls-and-cows: the suspense is in not knowing until reveal. Built for sapphire (gno 0.9). Live demo (agent address): https://sapphire.testnets.gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/closestguess <!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) --> --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md). <!-- END GNOCONTRACTS FOOTER -->
  4. #4closestguess.gno
  5. #5// Package closestguess is a blind, multiplayer number-guessing puzzle. // // Unlike a classic higher/lower guessing game, closestguess gives NO feedback // per guess. Each round hides a target in 1..1000 (derived deterministically // from the block height the round started at); every address may submit // exactly one blind Guess for the round. Whenever someone calls Reveal, the // round closes: the target is disclosed, the guess with the smallest absolute // distance wins (ties go to whoever guessed first), the winner is recorded on // the leaderboard, and a fresh round starts immediately. package closestguess import ( "strconv" "strings" "chain" "chain/runtime" ) const ( minTarget = 1 maxTarget = 1000 ) // roundT holds the mutable state of the open round. The target stays hidden // (never rendered, never returned) until Reveal closes the round. type roundT struct { number int startHeight int64 target int } // winRecord is one closed-round result, kept for the leaderboard. type winRecord struct { round int winner string target int guess int distance int players int } // guessEntry is one address's blind guess for the current round. type guessEntry struct { addr string n int } var ( current *roundT // entries holds one guessEntry per address for the CURRENT round only, in // submission order (so Reveal can break distance ties in favor of // whoever guessed first). Reset on every Reveal. A plain slice is fine // here: rounds are player-scoped and small, unlike long-lived global // state where avl.Tree would matter. entries []guessEntry // leaderboard is append-only across all rounds; sorted on render. leaderboard []winRecord ) func init() { current = newRound(1, runtime.ChainHeight()) } // newRound builds a fresh round whose hidden target derives deterministically // from the start height and round number. Gno has no runtime RNG, so mixing // height with the round number is the only available entropy, and it also // keeps consecutive rounds at the same height from repeating a target. func newRound(number int, height int64) *roundT { return &roundT{ number: number, startHeight: height, target: targetFromHeight(height, number), } } func targetFromHeight(h int64, round int) int { if h < 0 { h = -h } span := int64(maxTarget - minTarget + 1) mixed := h*2654435761 + int64(round)*40503 + 12345 if mixed < 0 { mixed = -mixed } return int(mixed%span) + minTarget } // Guess records the caller's single blind guess for the current round. It // returns no hint about correctness — that is the whole point of the puzzle. // Crossing function: caller invokes as Guess(cross(cur), n). func Guess(cur realm, n int) string { if n < minTarget || n > maxTarget { panic("guess must be in " + strconv.Itoa(minTarget) + ".." + strconv.Itoa(maxTarget)) } caller := cur.Previous().Address().String() if hasGuessed(caller) { panic("you already guessed this round; wait for Reveal()") } entries = append(entries, guessEntry{addr: caller, n: n}) chain.Emit("GuessSubmitted", "round", strconv.Itoa(current.number), "player", caller, ) return "guess recorded for round " + strconv.Itoa(current.number) + " (" + strconv.Itoa(len(entries)) + " player(s) so far) — call Reveal() to see who's closest" } func hasGuessed(addr string) bool { for _, e := range entries { if e.addr == addr { return true } } return false } // Reveal closes the current round: discloses the target, crowns whoever // landed closest (ties favor the earliest guesser), records a leaderboard // entry, and opens a fresh round. Panics if nobody has guessed yet. // Crossing function. func Reveal(cur realm) string { if len(entries) == 0 { panic("no guesses submitted yet this round") } target := current.target winner := entries[0] best := distance(winner.n, target) for _, e := range entries[1:] { if d := distance(e.n, target); d < best { best = d winner = e } } rec := winRecord{ round: current.number, winner: winner.addr, target: target, guess: winner.n, distance: best, players: len(entries), } leaderboard = append(leaderboard, rec) chain.Emit("RoundRevealed", "round", strconv.Itoa(rec.round), "winner", winner.addr, "target", strconv.Itoa(target), "distance", strconv.Itoa(best), ) closedRound := current.number current = newRound(closedRound+1, runtime.ChainHeight()) entries = nil return "round " + strconv.Itoa(closedRound) + " revealed: target was " + strconv.Itoa(target) + " — " + shortAddr(winner.addr) + " wins, guessed " + strconv.Itoa(winner.n) + " (distance " + strconv.Itoa(best) + ") among " + strconv.Itoa(rec.players) + " player(s). Round " + strconv.Itoa(current.number) + " is now open." } func distance(guess, target int) int { d := guess - target if d < 0 { d = -d } return d } // Render produces the gnoweb Markdown view. The current round's target and // individual guess values are never shown — only the player count — so // rendering the page can't leak the puzzle. func Render(path string) string { var b strings.Builder b.WriteString("# Closest Guess\n\n") b.WriteString("A blind, multiplayer number-guessing puzzle. Each round hides a target in ") b.WriteString("**" + strconv.Itoa(minTarget) + ".." + strconv.Itoa(maxTarget) + "**. ") b.WriteString("Call `Guess(n)` once per round — you get no feedback. ") b.WriteString("Anyone can call `Reveal()` to close the round: the target is disclosed and ") b.WriteString("whoever landed closest wins.\n\n") b.WriteString("## Current round\n\n") b.WriteString("- Round: **" + strconv.Itoa(current.number) + "**\n") b.WriteString("- Started at block height: " + strconv.FormatInt(current.startHeight, 10) + "\n") b.WriteString("- Players guessed so far: " + strconv.Itoa(len(entries)) + "\n") b.WriteString("- Status: **open** — target hidden until `Reveal()` is called.\n\n") b.WriteString("## Leaderboard (closest-ever wins)\n\n") b.WriteString(renderLeaderboard()) b.WriteString("\n## How to play\n\n") b.WriteString("```\n") b.WriteString("Guess(n) // n in 1..1000, one shot per address per round, no feedback\n") b.WriteString("Reveal() // closes the round: reveals target, crowns the closest guess\n") b.WriteString("```\n") return b.String() } // renderLeaderboard formats closed rounds ranked by smallest distance (ties: // earlier round first). Sorted on a copy so state is untouched. func renderLeaderboard() string { if len(leaderboard) == 0 { return "_No round has been revealed yet — be the first to call `Reveal()`!_\n" } ranked := make([]winRecord, len(leaderboard)) copy(ranked, leaderboard) sortByClosest(ranked) var b strings.Builder b.WriteString("| Rank | Player | Round | Target | Guess | Distance | Players |\n") b.WriteString("|---|---|---|---|---|---|---|\n") for i, w := range ranked { b.WriteString("| " + strconv.Itoa(i+1) + " | `" + shortAddr(w.winner) + "` | " + strconv.Itoa(w.round) + " | " + strconv.Itoa(w.target) + " | " + strconv.Itoa(w.guess) + " | " + strconv.Itoa(w.distance) + " | " + strconv.Itoa(w.players) + " |\n") } return b.String() } // sortByClosest does an in-place insertion sort: smallest distance first, // then earlier round first on ties. Small n; insertion sort keeps it // deterministic and dependency-free. func sortByClosest(rs []winRecord) { for i := 1; i < len(rs); i++ { j := i for j > 0 && closer(rs[j], rs[j-1]) { rs[j], rs[j-1] = rs[j-1], rs[j] j-- } } } func closer(a, b winRecord) bool { if a.distance != b.distance { return a.distance < b.distance } return a.round < b.round } func shortAddr(a string) string { if len(a) <= 12 { return a } return a[:6] + "…" + a[len(a)-4:] }
  6. #6gnomod.toml
  7. #7module = "gno.land/r/moul/x/daily/closestguess/v0" gno = "0.9"
  8. #8/gno.MemPackageType
  9. #9 MPUserProd

Result log

msg:0,success:true,log:,events:[]

← Back to block 25,999