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

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • wordle.gnogno
wordle.gnogno
1// Package wordle is a daily five-letter word puzzle realm for gno.land.2//3// The day's secret word is picked deterministically from the current block4// height (a "day index"), so every player faces the same word for the same5// span of blocks. Players submit guesses with Guess; each guess is scored6// letter-by-letter with emoji feedback (🟩 correct spot, 🟨 wrong spot,7// ⬜ absent). Render shows the caller's board, attempts left, and rules.8package wordle910import (11	"chain/runtime"12	"chain/runtime/unsafe"13	"strings"14)1516// maxAttempts is the number of guesses a player gets per day.17const maxAttempts = 61819// wordLen is the fixed length of every word / guess.20const wordLen = 52122// blocksPerDay controls how many blocks share the same secret word. Testnet23// blocks are fast, so this is a pragmatic "day" for a demo puzzle rather than24// a wall-clock day. Deterministic: derived only from block height.25const blocksPerDay = 172802627// words is the built-in dictionary of candidate secret words. All entries are28// exactly wordLen lowercase letters.29var words = []string{30	"crane",31	"slate",32	"pride",33	"ghost",34	"lunar",35	"mango",36	"vivid",37	"quilt",38	"brave",39	"flame",40	"storm",41	"pearl",42	"trout",43	"zebra",44	"cabin",45}4647// board holds one player's guesses for a given day.48type board struct {49	day     int6450	guesses []string // raw lowercase guessed words, in order51	won     bool52}5354// players maps a caller address string to that player's current board.55// A plain map is fine here: keys are addresses, iteration order is never56// used for rendering (we only ever look up the caller's own board).57var players = map[string]*board{}5859// dayIndex returns the deterministic day index for a given block height.60func dayIndex(height int64) int64 {61	if height < 0 {62		height = 063	}64	return height / blocksPerDay65}6667// wordForDay returns the secret word for the given day index. Deterministic:68// a pure function of the day index and the built-in dictionary.69func wordForDay(day int64) string {70	n := int64(len(words))71	idx := day % n72	if idx < 0 {73		idx += n74	}75	return words[idx]76}7778// currentDay returns today's day index from the chain height.79func currentDay() int64 {80	return dayIndex(runtime.ChainHeight())81}8283// score compares a guess against the secret and returns wordLen emoji tiles:84// 🟩 right letter right spot, 🟨 right letter wrong spot, ⬜ letter absent.85// Standard Wordle two-pass scoring so duplicate letters are handled correctly.86func score(guess, secret string) string {87	g := []rune(guess)88	s := []rune(secret)8990	tiles := make([]string, wordLen)91	// counts of each secret letter still available to match as 🟨.92	counts := map[rune]int{}9394	// First pass: greens.95	for i := 0; i < wordLen; i++ {96		if g[i] == s[i] {97			tiles[i] = "🟩"98		} else {99			counts[s[i]]++100		}101	}102	// Second pass: yellows / greys.103	for i := 0; i < wordLen; i++ {104		if tiles[i] == "🟩" {105			continue106		}107		if counts[g[i]] > 0 {108			tiles[i] = "🟨"109			counts[g[i]]--110		} else {111			tiles[i] = "⬜"112		}113	}114	return strings.Join(tiles, "")115}116117// isFiveLetters reports whether w is exactly wordLen ASCII lowercase letters.118func isFiveLetters(w string) bool {119	if len(w) != wordLen {120		return false121	}122	for i := 0; i < len(w); i++ {123		c := w[i]124		if c < 'a' || c > 'z' {125			return false126		}127	}128	return true129}130131// Guess records a five-letter guess for the caller against today's word.132//133// It is a crossing (state-mutating) function: callers invoke it as134// Guess(cross(cur), "crane"). Identity is taken from the live crossing frame135// after the IsCurrent authentication check.136func Guess(cur realm, word string) {137	if !cur.IsCurrent() {138		panic("spoofed realm")139	}140	caller := cur.Previous().Address().String()141142	word = strings.ToLower(strings.TrimSpace(word))143	if !isFiveLetters(word) {144		panic("guess must be exactly 5 lowercase letters a-z")145	}146147	day := currentDay()148	b := players[caller]149	if b == nil || b.day != day {150		// New player, or a new day rolled over: reset the board.151		b = &board{day: day}152		players[caller] = b153	}154155	if b.won {156		panic("you already solved today's word")157	}158	if len(b.guesses) >= maxAttempts {159		panic("no attempts left today")160	}161162	b.guesses = append(b.guesses, word)163	if word == wordForDay(day) {164		b.won = true165	}166}167168// Render returns the caller's board as Markdown. It is NOT a crossing function169// (no cur realm parameter). It reads the viewer via the stack-walking170// unsafe.PreviousRealm(); an unknown viewer simply sees an empty board.171func Render(path string) string {172	viewer := unsafe.PreviousRealm().Address().String()173	day := currentDay()174175	var sb strings.Builder176	sb.WriteString("# 🟩 Daily Word Puzzle\n\n")177	sb.WriteString("A Wordle-like game. Everyone gets the same secret 5-letter word each ")178	sb.WriteString("\"day\" (a span of blocks). You have ")179	sb.WriteString(itoa(maxAttempts))180	sb.WriteString(" attempts.\n\n")181182	sb.WriteString("**Day #")183	sb.WriteString(i64toa(day))184	sb.WriteString("**\n\n")185186	b := players[viewer]187	if b == nil || b.day != day {188		sb.WriteString("_No guesses yet today._\n\n")189		writeRules(&sb)190		return sb.String()191	}192193	secret := wordForDay(day)194	sb.WriteString("## Your board\n\n")195	for _, g := range b.guesses {196		sb.WriteString(score(g, secret))197		sb.WriteString("  `")198		sb.WriteString(strings.ToUpper(g))199		sb.WriteString("`\n\n")200	}201202	left := maxAttempts - len(b.guesses)203	switch {204	case b.won:205		sb.WriteString("🎉 **You solved it!** Come back next day for a new word.\n\n")206	case left <= 0:207		sb.WriteString("💀 **Out of attempts.** The word was `")208		sb.WriteString(strings.ToUpper(secret))209		sb.WriteString("`. Try again next day.\n\n")210	default:211		sb.WriteString("**Attempts left:** ")212		sb.WriteString(itoa(left))213		sb.WriteString("\n\n")214	}215216	writeRules(&sb)217	return sb.String()218}219220// writeRules appends the legend / how-to-play block.221func writeRules(sb *strings.Builder) {222	sb.WriteString("## Rules\n\n")223	sb.WriteString("- Guess the 5-letter word in ")224	sb.WriteString(itoa(maxAttempts))225	sb.WriteString(" tries: `Guess(\"crane\")`.\n")226	sb.WriteString("- 🟩 correct letter, correct spot.\n")227	sb.WriteString("- 🟨 correct letter, wrong spot.\n")228	sb.WriteString("- ⬜ letter not in the word.\n")229	sb.WriteString("- The word changes every day (deterministic from block height).\n")230}231232// itoa formats a small non-negative int (attempt counts) without importing233// strconv, keeping the dependency surface minimal.234func itoa(n int) string {235	return i64toa(int64(n))236}237238// i64toa formats an int64 as decimal.239func i64toa(n int64) string {240	if n == 0 {241		return "0"242	}243	neg := n < 0244	if neg {245		n = -n246	}247	var digits []byte248	for n > 0 {249		digits = append(digits, byte('0'+n%10))250		n /= 10251	}252	// reverse253	for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {254		digits[i], digits[j] = digits[j], digits[i]255	}256	if neg {257		return "-" + string(digits)258	}259	return string(digits)260}261

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}, word 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.