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

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • quizstreak.gnogno
quizstreak.gnogno
1// Package quizstreak is a multiple-choice trivia quiz where every player2// runs their own independent streak instead of racing others for a single3// shared question.4//5// Each address gets its own current question, picked deterministically from6// the address and the block height of its first answer so different players7// don't all start on the same question. A correct answer extends the8// player's streak and advances them to their next question; a wrong answer9// breaks the streak back to zero but leaves the same question live so they10// can retry. The realm tracks each player's best-ever streak on a global11// leaderboard.12package quizstreak1314import (15	"strconv"16	"strings"1718	"chain"19	"chain/runtime"2021	"gno.land/p/nt/avl/v0"22)2324// question is one fixed trivia entry.25type question struct {26	text       string27	choices    [4]string28	correctIdx int29}3031// playerState is one address's progress through the question bank.32type playerState struct {33	qIdx    int34	streak  int35	best    int36	correct int37	wrong   int38}3940var (41	questions = []question{42		{"What is the smallest prime number?", [4]string{"0", "1", "2", "3"}, 2},43		{"Which gas does a plant primarily absorb for photosynthesis?", [4]string{"Oxygen", "Nitrogen", "Carbon dioxide", "Hydrogen"}, 2},44		{"How many continents are there on Earth?", [4]string{"5", "6", "7", "8"}, 2},45		{"In binary, what is 1 + 1?", [4]string{"2", "10", "11", "0"}, 1},46		{"What does 'HTTP' stand for?", [4]string{"HyperText Transfer Protocol", "High Transfer Text Protocol", "Home Tool Transfer Protocol", "HyperText Transmission Path"}, 0},47		{"Which planet is known as the Red Planet?", [4]string{"Venus", "Jupiter", "Mars", "Saturn"}, 2},48		{"What is the time complexity of binary search?", [4]string{"O(n)", "O(log n)", "O(n^2)", "O(1)"}, 1},49		{"Which ocean is the largest by surface area?", [4]string{"Atlantic", "Indian", "Arctic", "Pacific"}, 3},50		{"What does 'CPU' stand for?", [4]string{"Central Processing Unit", "Computer Personal Unit", "Central Program Utility", "Core Processing Unicode"}, 0},51		{"How many bits are in a byte?", [4]string{"4", "8", "16", "32"}, 1},52	}5354	players = avl.NewTree() // addr(string) -> *playerState55)5657// startIndex picks a deterministic starting question for a brand-new player,58// spreading players across the bank using their address and the height of59// their first answer as entropy.60func startIndex(addr string, height int64) int {61	if height < 0 {62		height = -height63	}64	var sum int6465	for i := 0; i < len(addr); i++ {66		sum += int64(addr[i])67	}68	n := int64(len(questions))69	return int((sum + height) % n)70}7172// getPlayer returns addr's state, lazily creating it on first contact.73func getPlayer(addr string) *playerState {74	if v, ok := players.Get(addr).(*playerState); ok {75		return v76	}77	p := &playerState{qIdx: startIndex(addr, runtime.ChainHeight())}78	players.Set(addr, p)79	return p80}8182// nextIndex advances a solved player to a fresh-feeling next question: the83// step grows with the streak so a long run doesn't loop through the bank in84// a visibly fixed order.85func nextIndex(cur, streak, n int) int {86	return (cur + 1 + streak) % n87}8889// Answer submits a choice (0..3) for the caller's current question.90// Crossing function: caller invokes as Answer(cross(cur), choiceIdx).91func Answer(cur realm, choiceIdx int) string {92	if !cur.IsCurrent() {93		panic("spoofed realm")94	}95	if choiceIdx < 0 || choiceIdx >= len(questions[0].choices) {96		panic("choice must be in 0..3")97	}9899	addr := cur.Previous().Address().String()100	p := getPlayer(addr)101	q := questions[p.qIdx]102103	if choiceIdx != q.correctIdx {104		brokeStreak := p.streak105		p.streak = 0106		p.wrong++107		if brokeStreak > 0 {108			return "Wrong — streak of " + strconv.Itoa(brokeStreak) + " broken. Same question stays live, try again."109		}110		return "Wrong — try again."111	}112113	p.streak++114	p.correct++115	if p.streak > p.best {116		p.best = p.streak117	}118	p.qIdx = nextIndex(p.qIdx, p.streak, len(questions))119120	chain.Emit("QuizAnswered",121		"player", addr,122		"streak", strconv.Itoa(p.streak),123		"best", strconv.Itoa(p.best),124	)125126	return "Correct! Streak is now " + strconv.Itoa(p.streak) + ". Next question is live."127}128129// leaderboardEntry is a snapshot row used only for rendering, sorted by best130// streak descending.131type leaderboardEntry struct {132	addr string133	best int134}135136func leaderboard() []leaderboardEntry {137	var rows []leaderboardEntry138	players.Iterate("", "", func(addr string, v any) bool {139		rows = append(rows, leaderboardEntry{addr: addr, best: v.(*playerState).best})140		return false141	})142	for i := 1; i < len(rows); i++ {143		j := i144		for j > 0 && rows[j-1].best < rows[j].best {145			rows[j-1], rows[j] = rows[j], rows[j-1]146			j--147		}148	}149	return rows150}151152func renderQuestion(b *strings.Builder, p *playerState) {153	q := questions[p.qIdx]154	letters := [4]string{"A", "B", "C", "D"}155	b.WriteString("**" + q.text + "**\n\n")156	for i, c := range q.choices {157		b.WriteString("- **" + letters[i] + "** (" + strconv.Itoa(i) + "): " + c + "\n")158	}159	b.WriteString("\n- Current streak: " + strconv.Itoa(p.streak) + "\n")160	b.WriteString("- Best streak: " + strconv.Itoa(p.best) + "\n")161	b.WriteString("- Correct / wrong lifetime: " + strconv.Itoa(p.correct) + " / " + strconv.Itoa(p.wrong) + "\n\n")162}163164// Render produces the gnoweb Markdown view. The root path shows the rules165// and the leaderboard; a path of a bech32 address shows that player's166// current question and stats.167func Render(path string) string {168	var b strings.Builder169170	b.WriteString("# Quiz Streak\n\n")171	b.WriteString("Everyone plays their own trivia run at their own pace. Call ")172	b.WriteString("`Answer(choiceIdx)` with 0-3 — a correct answer extends your streak ")173	b.WriteString("and moves you to your next question; a wrong answer breaks your ")174	b.WriteString("streak but leaves the same question live so you can retry.\n\n")175176	if path != "" {177		if v, ok := players.Get(path).(*playerState); ok {178			b.WriteString("## Your question (`" + path + "`)\n\n")179			renderQuestion(&b, v)180		} else {181			b.WriteString("## `" + path + "`\n\n_No answers submitted yet — call `Answer` to get your first question._\n\n")182		}183	}184185	b.WriteString("## Best-streak leaderboard\n\n")186	rows := leaderboard()187	if len(rows) == 0 {188		b.WriteString("_No one has played yet — be the first!_\n\n")189	} else {190		b.WriteString("| Player | Best streak |\n|---|---|\n")191		for _, r := range rows {192			b.WriteString("| `" + r.addr + "` | " + strconv.Itoa(r.best) + " |\n")193		}194		b.WriteString("\n")195	}196197	b.WriteString("## How to play\n\n")198	b.WriteString("```\n")199	b.WriteString("gnokey maketx call -pkgpath gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/quizstreak \\\n")200	b.WriteString("  -func Answer -args <0|1|2|3> ...\n")201	b.WriteString("```\n\n")202	b.WriteString("View your own dashboard at `.../quizstreak:<your-address>`.\n")203204	return b.String()205}206

Functions

  • Answer(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}, choiceIdx int) 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.