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

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • bullscows.gnogno
bullscows.gnogno
1// Package bullscows is a shared on-chain Bulls & Cows puzzle: one secret2// 4-digit code (distinct digits) per round, guessed cooperatively/competitively3// by anyone who calls Guess. Bulls = right digit, right spot. Cows = right4// digit, wrong spot. Whoever hits 4 bulls wins the round and a leaderboard5// entry; a fresh secret is drawn immediately after.6package bullscows78import (9	"chain/runtime"10	"strconv"11	"strings"12)1314const (15	maxHistory = 1016	maxRecords = 517)1819type guessLog struct {20	player   string21	guess    string22	bulls    int23	cows     int24}2526type record struct {27	player   string28	attempts int29	round    int30}3132var (33	secret   [4]int34	round    int35	attempts int36	history  []guessLog37	records  []record38)3940func init() {41	round = 142	secret = newSecret(seedFor(round))43}4445// seedFor derives a per-round PRNG seed from the current chain height so46// nobody can precompute the next secret before the round actually starts.47func seedFor(r int) int64 {48	return runtime.ChainHeight()*1000003 + int64(r)*7919 + 1749}5051// nextRand is a minimal LCG (glibc constants) — no crypto strength needed52// for a casual puzzle, just enough spread across digits.53func nextRand(seed int64) int64 {54	return (seed*1103515245 + 12345) & 0x7fffffff55}5657func newSecret(seed int64) [4]int {58	var used [10]bool59	var digits [4]int60	s := seed61	for i := 0; i < 4; i++ {62		for {63			s = nextRand(s)64			d := int(s % 10)65			if !used[d] {66				used[d] = true67				digits[i] = d68				break69			}70		}71	}72	return digits73}7475// parseGuess validates a 4-digit, distinct-digit guess string.76func parseGuess(guess string) ([4]int, bool) {77	var out [4]int78	if len(guess) != 4 {79		return out, false80	}81	var used [10]bool82	for i := 0; i < 4; i++ {83		c := guess[i]84		if c < '0' || c > '9' {85			return out, false86		}87		d := int(c - '0')88		if used[d] {89			return out, false90		}91		used[d] = true92		out[i] = d93	}94	return out, true95}9697// score returns (bulls, cows) for guess against target, assuming both have98// distinct digits (guaranteed by parseGuess / newSecret).99func score(target, guess [4]int) (int, int) {100	bulls, cows := 0, 0101	for i := 0; i < 4; i++ {102		if guess[i] == target[i] {103			bulls++104			continue105		}106		for j := 0; j < 4; j++ {107			if i != j && guess[i] == target[j] {108				cows++109				break110			}111		}112	}113	return bulls, cows114}115116func addHistory(l guessLog) {117	history = append(history, l)118	if len(history) > maxHistory {119		history = history[len(history)-maxHistory:]120	}121}122123// addRecord keeps the top maxRecords fastest solves, ascending by attempts.124func addRecord(r record) {125	records = append(records, r)126	for i := len(records) - 1; i > 0 && records[i].attempts < records[i-1].attempts; i-- {127		records[i], records[i-1] = records[i-1], records[i]128	}129	if len(records) > maxRecords {130		records = records[:maxRecords]131	}132}133134func startNewRound() {135	round++136	attempts = 0137	history = nil138	secret = newSecret(seedFor(round))139}140141// Guess submits a 4-distinct-digit code against the current round's secret.142// Returns the bulls/cows feedback, or "solved!" text when it's a win.143func Guess(cur realm, guess string) string {144	digits, ok := parseGuess(guess)145	if !ok {146		panic("guess must be 4 digits, 0-9, no repeats (e.g. \"1972\")")147	}148149	player := cur.Previous().Address().String()150	attempts++151	bulls, cows := score(secret, digits)152	addHistory(guessLog{player: player, guess: guess, bulls: bulls, cows: cows})153154	if bulls == 4 {155		wonRound := round156		wonAttempts := attempts157		addRecord(record{player: player, attempts: wonAttempts, round: wonRound})158		startNewRound()159		return "*** SOLVED *** " + guess + " was it — round " + strconv.Itoa(wonRound) +160			" cracked in " + strconv.Itoa(wonAttempts) + " guesses. New round " +161			strconv.Itoa(round) + " has begun, good luck!"162	}163164	return guess + " -> " + strconv.Itoa(bulls) + " bulls, " + strconv.Itoa(cows) + " cows"165}166167// GiveUp reveals the current secret and starts a fresh round without168// awarding a leaderboard record.169func GiveUp(cur realm) string {170	revealed := digitsToString(secret)171	oldRound := round172	startNewRound()173	return "round " + strconv.Itoa(oldRound) + "'s code was " + revealed +174		" — round " + strconv.Itoa(round) + " is live now."175}176177func digitsToString(d [4]int) string {178	var sb strings.Builder179	for _, v := range d {180		sb.WriteString(strconv.Itoa(v))181	}182	return sb.String()183}184185func Render(path string) string {186	var sb strings.Builder187	sb.WriteString("# Bulls & Cows\n\n")188	sb.WriteString("One shared secret 4-digit code (no repeated digits). Call `Guess(\"1972\")` ")189	sb.WriteString("to get **bulls** (right digit, right spot) and **cows** (right digit, wrong spot). ")190	sb.WriteString("First to 4 bulls wins the round; `GiveUp()` reveals the code and starts over.\n\n")191192	sb.WriteString("## Round " + strconv.Itoa(round) + "\n\n")193	sb.WriteString("Attempts so far: **" + strconv.Itoa(attempts) + "**\n\n")194195	if len(history) == 0 {196		sb.WriteString("_No guesses yet this round — be the first._\n\n")197	} else {198		sb.WriteString("### Recent guesses\n\n")199		sb.WriteString("| player | guess | bulls | cows |\n|---|---|---|---|\n")200		for i := len(history) - 1; i >= 0; i-- {201			h := history[i]202			sb.WriteString("| " + shortAddr(h.player) + " | " + h.guess + " | " +203				strconv.Itoa(h.bulls) + " | " + strconv.Itoa(h.cows) + " |\n")204		}205		sb.WriteString("\n")206	}207208	sb.WriteString("## Leaderboard (fastest solves)\n\n")209	if len(records) == 0 {210		sb.WriteString("_Nobody has cracked a code yet._\n")211	} else {212		sb.WriteString("| rank | player | attempts | round |\n|---|---|---|---|\n")213		for i, r := range records {214			sb.WriteString("| " + strconv.Itoa(i+1) + " | " + shortAddr(r.player) + " | " +215				strconv.Itoa(r.attempts) + " | " + strconv.Itoa(r.round) + " |\n")216		}217	}218219	return sb.String()220}221222func shortAddr(a string) string {223	if len(a) <= 12 {224		return a225	}226	return a[:6] + "…" + a[len(a)-4:]227}228

Functions

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

  • 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}, guess 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.