Transaction
ACE86915DE997A…6E895A96C45B
Block 26,359 · index 0 · indexed
Summary
- Hash
- ACE86915DE997ADF531886BF54A5A7A7BF818D099C3E7157D1926E895A96C45B
- Block
- 26,359
- Size
- 9385 bytes
- Gas used
- 13,746,265 / 16,495,578
- Fee
- 1000000ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1kingofdice
- #2README.md
- #3# 👑 King of the Dice > ⚠️ **Experimental — generated with no human supervision.** This realm was > produced automatically by an MCP-driven agent to exercise the gno MCP server > and tooling, and to generate test content for gno compilers, linters and > formatters. **Not audited. Not for production.** Full context & folder README: > [r/moul/x/daily](https://github.com/moul/gno-contracts/blob/main/r/moul/x/daily/README.md) --- A "king of the hill" dice game for gno.land (sapphire / gno 0.9). Call `Roll()` to throw a deterministic 1-100 die; beat the reigning king's roll and you dethrone them, becoming king yourself. Unlike a typical leaderboard that just sums points, this one ranks players by **accumulated reign length in blocks** — a single lucky high roll only pays off for as long as nobody beats it, so outlasting the field matters more than the roll itself. **Realm path:** `gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/kingofdice` ## Public API | Function | Kind | Description | | --- | --- | --- | | `Roll(cur realm)` | crossing (tx) | Roll 1-100; dethrones the king on a higher roll. | | `Render(path string) string` | view | Current king + reign-length leaderboard. | ## Example calls ```sh gnokey maketx call \ -pkgpath "gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/kingofdice" \ -func Roll \ -gas-fee 1000000ugnot -gas-wanted 2000000 \ -broadcast -chainid sapphire-1 -remote <rpc> <keyname> ``` View the dashboard in gnoweb at `/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/kingofdice`, or query it: ```sh gnokey query "vm/qrender" -data "gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/kingofdice:" -remote <rpc> ``` ## Toolchain status Verified locally: `gno test .` passes (built against a `gnolang/gno` checkout as `GNOROOT`, since the machine's configured `GNOROOT` pointed at a stale, deleted path). Tests avoid `gno.land/p/nt/uassert/v0` — the cached copy of that package uses the bare `cross` builtin as a function value, which the locally installed `gno` (master) rejects; plain `testing.T` calls sidestep that unrelated cache/toolchain drift. <!-- 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. **Dependency graph:**  > 🧪 **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 -->
- #4gnomod.toml
- #5module = "gno.land/r/moul/x/daily/kingofdice/v0" gno = "0.9"
- #6kingofdice.gno
- #7// Package kingofdice is a "king of the hill" dice game. Anyone can Roll a // pseudo-random 1-100 die; beat the reigning king's roll and you dethrone // them, becoming king yourself. The leaderboard doesn't rank raw points — it // ranks accumulated *reign length*, measured in blocks held as king, so // camping on a lucky high roll pays off more than a single spike. package kingofdice import ( "sort" "strconv" "chain/runtime" "gno.land/p/nt/avl/v0" ) // playerStats is the persisted record for one address. type playerStats struct { addr address rolls int bestRoll int reigns int totalReignBlocks int64 // blocks held as king across all past reigns } var ( players avl.Tree // address string -> *playerStats king address kingRoll int reignSince int64 // chain height at which the current king took the throne nonce int totalRolls int ) // get returns the stored *playerStats for addr, creating one if absent. func get(addr address) *playerStats { key := addr.String() if v := players.Get(key); v != nil { return v.(*playerStats) } ps := &playerStats{addr: addr} players.Set(key, ps) return ps } // pseudoRoll derives a deterministic 1-100 value from the caller's address, // a per-call nonce, and the current chain height, so no two calls in the // same block from the same address collide, yet the result can't be // pre-computed off-chain before the triggering transaction lands. func pseudoRoll(addr address, nonce int, height int64) int { s := addr.String() var h int64 for i := 0; i < len(s); i++ { h = h*31 + int64(s[i]) } h += int64(nonce)*104729 + height*7919 if h < 0 { h = -h } return int(h%100) + 1 } // Roll throws the die for the caller. Rolling higher than the current king's // roll dethrones them (crediting their finished reign in blocks) and crowns // the caller as the new king. func Roll(cur realm) string { if !cur.IsCurrent() { panic("spoofed realm") } caller := cur.Previous().Address() nonce++ totalRolls++ height := runtime.ChainHeight() roll := pseudoRoll(caller, nonce, height) ps := get(caller) ps.rolls++ if roll > ps.bestRoll { ps.bestRoll = roll } if !king.IsValid() || roll > kingRoll { if king.IsValid() { prev := get(king) prev.totalReignBlocks += height - reignSince } king = caller kingRoll = roll reignSince = height ps.reigns++ return "you rolled " + strconv.Itoa(roll) + " and seized the throne!" } if king == caller { return "you rolled " + strconv.Itoa(roll) + " — you're still king (need to beat " + strconv.Itoa(kingRoll) + ")" } return "you rolled " + strconv.Itoa(roll) + " — not enough to dethrone the king's " + strconv.Itoa(kingRoll) } // effectiveReignBlocks returns a player's accumulated reign length, including // the currently-in-progress reign if they are the sitting king. It does not // mutate state — it's a display-time projection. func effectiveReignBlocks(ps *playerStats, height int64) int64 { if king.IsValid() && ps.addr == king { return ps.totalReignBlocks + (height - reignSince) } return ps.totalReignBlocks } // byReign implements sort.Interface, ranking players by effective reign // length descending, ties broken by best roll then address so the order is // fully deterministic. type byReign struct { stats []*playerStats blocks []int64 } func (r byReign) Len() int { return len(r.stats) } func (r byReign) Swap(i, j int) { r.stats[i], r.stats[j] = r.stats[j], r.stats[i] r.blocks[i], r.blocks[j] = r.blocks[j], r.blocks[i] } func (r byReign) Less(i, j int) bool { if r.blocks[i] != r.blocks[j] { return r.blocks[i] > r.blocks[j] } if r.stats[i].bestRoll != r.stats[j].bestRoll { return r.stats[i].bestRoll > r.stats[j].bestRoll } return r.stats[i].addr.String() < r.stats[j].addr.String() } // snapshot collects all players ranked by effective reign length descending. func snapshot(height int64) ([]*playerStats, []int64) { stats := make([]*playerStats, 0, players.Size()) players.Iterate("", "", func(_ string, v any) bool { stats = append(stats, v.(*playerStats)) return false }) blocks := make([]int64, len(stats)) for i, ps := range stats { blocks[i] = effectiveReignBlocks(ps, height) } sort.Stable(byReign{stats: stats, blocks: blocks}) return stats, blocks } // medal returns the emoji for a given zero-based rank, or "" past the podium. func medal(rank int) string { switch rank { case 0: return "🥇" case 1: return "🥈" case 2: return "🥉" default: return "" } } // display returns a shortened address for table display. func display(addr address) string { s := addr.String() if len(s) > 12 { return s[:8] + "…" + s[len(s)-4:] } return s } // Render shows the current king and a reign-length leaderboard. func Render(path string) string { height := runtime.ChainHeight() out := "# 👑 King of the Dice\n\n" out += "Roll higher than the reigning king to take the throne. The leaderboard " + "ranks total blocks held as king, not raw roll count — outlasting beats " + "getting lucky once.\n\n" if !king.IsValid() { out += "_No king yet. Call `Roll()` to make the first claim._\n\n" } else { out += "**Current king:** `" + display(king) + "` with a roll of **" + strconv.Itoa(kingRoll) + "**, reigning for **" + strconv.Itoa(int(height-reignSince)) + "** blocks so far.\n\n" } out += "Total rolls: **" + strconv.Itoa(totalRolls) + "**\n\n" stats, blocks := snapshot(height) if len(stats) == 0 { return out } out += "| Rank | Player | Blocks reigned | Reigns | Best roll | Rolls |\n" out += "| ---: | :--- | ---: | ---: | ---: | ---: |\n" for i, ps := range stats { rankCell := medal(i) if rankCell == "" { rankCell = strconv.Itoa(i + 1) } out += "| " + rankCell + " | " + display(ps.addr) + " | " + strconv.Itoa(int(blocks[i])) + " | " + strconv.Itoa(ps.reigns) + " | " + strconv.Itoa(ps.bestRoll) + " | " + strconv.Itoa(ps.rolls) + " |\n" } return out }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]