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

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • tictactoe.gnogno
tictactoe.gnogno
1package tictactoe23import (4	"chain"5	"chain/runtime/unsafe"6	"errors"7	"strconv"8	"strings"910	"gno.land/p/nt/avl/v0"11)1213// Cell states.14const (15	empty = 016	markX = 117	markO = 218)1920// Game holds a single tic-tac-toe match.21type Game struct {22	id       int23	playerX  address // caller of NewGame24	playerO  address // opponent25	board    [9]int  // 0 empty, 1 X, 2 O26	moves    int     // number of moves played27	turn     int     // markX or markO — whose turn it is28	winner   int     // 0 none, 1 X, 2 O29	draw     bool30	finished bool31}3233var (34	games  = avl.NewTree() // id (padded string) -> *Game35	nextID = 136)3738func pad(id int) string {39	s := strconv.Itoa(id)40	for len(s) < 12 {41		s = "0" + s42	}43	return s44}4546// NewGame creates a game. Caller is X, opponent is O. Returns the game id.47func NewGame(cur realm, opponent address) int {48	caller := unsafe.PreviousRealm().Address()49	if opponent == caller {50		panic("opponent must differ from caller")51	}52	if !opponent.IsValid() {53		panic("invalid opponent address")54	}55	id := nextID56	nextID++57	g := &Game{58		id:      id,59		playerX: caller,60		playerO: opponent,61		turn:    markX,62	}63	games.Set(pad(id), g)64	chain.Emit("GameCreated", "id", strconv.Itoa(id), "x", caller.String(), "o", opponent.String())65	return id66}6768// Move plays cell (0-8) for the calling player. Enforces turn order and rejects taken cells.69func Move(cur realm, gameID int, cell int) {70	v := games.Get(pad(gameID))71	if v == nil {72		panic("game not found")73	}74	g := v.(*Game)75	if g.finished {76		panic("game already finished")77	}78	if cell < 0 || cell > 8 {79		panic("cell out of range (0-8)")80	}81	if g.board[cell] != empty {82		panic("cell already taken")83	}84	caller := unsafe.PreviousRealm().Address()8586	var mark int87	switch caller {88	case g.playerX:89		mark = markX90	case g.playerO:91		mark = markO92	default:93		panic("caller is not a player in this game")94	}95	if mark != g.turn {96		panic("not your turn")97	}9899	g.board[cell] = mark100	g.moves++101102	if won(g.board, mark) {103		g.winner = mark104		g.finished = true105		chain.Emit("GameWon", "id", strconv.Itoa(gameID), "winner", markName(mark))106	} else if g.moves == 9 {107		g.draw = true108		g.finished = true109		chain.Emit("GameDraw", "id", strconv.Itoa(gameID))110	} else {111		if g.turn == markX {112			g.turn = markO113		} else {114			g.turn = markX115		}116		chain.Emit("Move", "id", strconv.Itoa(gameID), "cell", strconv.Itoa(cell), "mark", markName(mark))117	}118}119120var lines = [8][3]int{121	{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // rows122	{0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // cols123	{0, 4, 8}, {2, 4, 6}, // diagonals124}125126func won(b [9]int, mark int) bool {127	for _, l := range lines {128		if b[l[0]] == mark && b[l[1]] == mark && b[l[2]] == mark {129			return true130		}131	}132	return false133}134135func markName(m int) string {136	switch m {137	case markX:138		return "X"139	case markO:140		return "O"141	default:142		return "-"143	}144}145146func cellGlyph(m int) string {147	switch m {148	case markX:149		return "X"150	case markO:151		return "O"152	default:153		return "·"154	}155}156157// GetGame returns a game by id (read helper for tests / callers).158func GetGame(id int) (*Game, error) {159	v := games.Get(pad(id))160	if v == nil {161		return nil, errors.New("game not found")162	}163	return v.(*Game), nil164}165166func renderBoard(b [9]int) string {167	var sb strings.Builder168	for r := 0; r < 3; r++ {169		sb.WriteString("| ")170		for c := 0; c < 3; c++ {171			sb.WriteString(cellGlyph(b[r*3+c]))172			sb.WriteString(" | ")173		}174		sb.WriteString("\n")175	}176	return sb.String()177}178179func gameStatus(g *Game) string {180	if g.finished {181		if g.draw {182			return "Draw."183		}184		return "Winner: " + markName(g.winner)185	}186	return "Turn: " + markName(g.turn)187}188189// Render draws the list of games, or a single game board at path "/<id>".190func Render(path string) string {191	p := strings.TrimPrefix(path, "/")192	if p == "" {193		return renderList()194	}195	id, err := strconv.Atoi(p)196	if err != nil {197		return "# Tic-Tac-Toe\n\nInvalid game id: " + p + "\n"198	}199	g, gerr := GetGame(id)200	if gerr != nil {201		return "# Tic-Tac-Toe\n\nGame " + p + " not found.\n"202	}203	var sb strings.Builder204	sb.WriteString("# Tic-Tac-Toe — Game #")205	sb.WriteString(strconv.Itoa(g.id))206	sb.WriteString("\n\n")207	sb.WriteString("- X: `")208	sb.WriteString(g.playerX.String())209	sb.WriteString("`\n- O: `")210	sb.WriteString(g.playerO.String())211	sb.WriteString("`\n\n")212	sb.WriteString(renderBoard(g.board))213	sb.WriteString("\n**")214	sb.WriteString(gameStatus(g))215	sb.WriteString("**\n\nCell indices:\n\n")216	sb.WriteString("| 0 | 1 | 2 |\n| 3 | 4 | 5 |\n| 6 | 7 | 8 |\n")217	return sb.String()218}219220func renderList() string {221	var sb strings.Builder222	sb.WriteString("# Tic-Tac-Toe\n\n")223	sb.WriteString("2-player tic-tac-toe. Call `NewGame(opponent)` to start; view a game at `/<id>`.\n\n")224	if games.Size() == 0 {225		sb.WriteString("_No games yet._\n")226		return sb.String()227	}228	sb.WriteString("| # | X | O | Status |\n|---|---|---|--------|\n")229	games.Iterate("", "", func(key string, value interface{}) bool {230		g := value.(*Game)231		sb.WriteString("| ")232		sb.WriteString(strconv.Itoa(g.id))233		sb.WriteString(" | `")234		sb.WriteString(g.playerX.String())235		sb.WriteString("` | `")236		sb.WriteString(g.playerO.String())237		sb.WriteString("` | ")238		sb.WriteString(gameStatus(g))239		sb.WriteString(" |\n")240		return false241	})242	return sb.String()243}244

Functions

  • GetGame(id int) (*gno.land/r/moul/x/daily/tictactoe/v0.Game, interface {Error func() string})

  • Move(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}, gameID int, cell int)

  • NewGame(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}, opponent string) int

  • 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.