Transaction
1E8832CC4075E1…66AE71B2E3E0
Block 26,121 · index 0 · indexed
Summary
- Hash
- 1E8832CC4075E13A92142373122CAA805E92B367101E349378CE66AE71B2E3E0
- Block
- 26,121
- Size
- 7803 bytes
- Gas used
- 12,515,161 / 15,018,253
- Fee
- 1000000 ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1tictactoe
- #2README.md
- #3# Tic-Tac-Toe > ⚠️ **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 2-player tic-tac-toe realm for gno.land. The game creator plays **X**, the named opponent plays **O**. Moves enforce strict turn order by caller address and reject cells that are already taken. The realm detects wins (rows, columns, diagonals) and draws, and `Render` draws the 3×3 board with `X` / `O` / `·`, whose turn it is, and the final result. Realm path: `gno.land/r/REPLACE_ADDR/tictactoe` ## Example calls ``` # Alice creates a game against Bob — Alice is X, Bob is O. Returns the game id (e.g. 1). NewGame(g1pxk...bob) # Play a cell (0-8), indexed left-to-right, top-to-bottom: # 0 | 1 | 2 # 3 | 4 | 5 # 6 | 7 | 8 Move(1, 0) # X (creator) plays top-left Move(1, 3) # O (opponent) plays middle-left Move(1, 1) # X plays top-middle Move(1, 4) # O plays center Move(1, 2) # X completes the top row and wins # View a single game board: Render("/1") # List all games: Render("") ``` <!-- 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/tictactoe/v0" gno = "0.9"
- #6tictactoe.gno
- #7package tictactoe import ( "chain" "chain/runtime/unsafe" "errors" "strconv" "strings" "gno.land/p/nt/avl/v0" ) // Cell states. const ( empty = 0 markX = 1 markO = 2 ) // Game holds a single tic-tac-toe match. type Game struct { id int playerX address // caller of NewGame playerO address // opponent board [9]int // 0 empty, 1 X, 2 O moves int // number of moves played turn int // markX or markO — whose turn it is winner int // 0 none, 1 X, 2 O draw bool finished bool } var ( games = avl.NewTree() // id (padded string) -> *Game nextID = 1 ) func pad(id int) string { s := strconv.Itoa(id) for len(s) < 12 { s = "0" + s } return s } // NewGame creates a game. Caller is X, opponent is O. Returns the game id. func NewGame(cur realm, opponent address) int { caller := unsafe.PreviousRealm().Address() if opponent == caller { panic("opponent must differ from caller") } if !opponent.IsValid() { panic("invalid opponent address") } id := nextID nextID++ g := &Game{ id: id, playerX: caller, playerO: opponent, turn: markX, } games.Set(pad(id), g) chain.Emit("GameCreated", "id", strconv.Itoa(id), "x", caller.String(), "o", opponent.String()) return id } // Move plays cell (0-8) for the calling player. Enforces turn order and rejects taken cells. func Move(cur realm, gameID int, cell int) { v := games.Get(pad(gameID)) if v == nil { panic("game not found") } g := v.(*Game) if g.finished { panic("game already finished") } if cell < 0 || cell > 8 { panic("cell out of range (0-8)") } if g.board[cell] != empty { panic("cell already taken") } caller := unsafe.PreviousRealm().Address() var mark int switch caller { case g.playerX: mark = markX case g.playerO: mark = markO default: panic("caller is not a player in this game") } if mark != g.turn { panic("not your turn") } g.board[cell] = mark g.moves++ if won(g.board, mark) { g.winner = mark g.finished = true chain.Emit("GameWon", "id", strconv.Itoa(gameID), "winner", markName(mark)) } else if g.moves == 9 { g.draw = true g.finished = true chain.Emit("GameDraw", "id", strconv.Itoa(gameID)) } else { if g.turn == markX { g.turn = markO } else { g.turn = markX } chain.Emit("Move", "id", strconv.Itoa(gameID), "cell", strconv.Itoa(cell), "mark", markName(mark)) } } var lines = [8][3]int{ {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // rows {0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // cols {0, 4, 8}, {2, 4, 6}, // diagonals } func won(b [9]int, mark int) bool { for _, l := range lines { if b[l[0]] == mark && b[l[1]] == mark && b[l[2]] == mark { return true } } return false } func markName(m int) string { switch m { case markX: return "X" case markO: return "O" default: return "-" } } func cellGlyph(m int) string { switch m { case markX: return "X" case markO: return "O" default: return "·" } } // GetGame returns a game by id (read helper for tests / callers). func GetGame(id int) (*Game, error) { v := games.Get(pad(id)) if v == nil { return nil, errors.New("game not found") } return v.(*Game), nil } func renderBoard(b [9]int) string { var sb strings.Builder for r := 0; r < 3; r++ { sb.WriteString("| ") for c := 0; c < 3; c++ { sb.WriteString(cellGlyph(b[r*3+c])) sb.WriteString(" | ") } sb.WriteString("\n") } return sb.String() } func gameStatus(g *Game) string { if g.finished { if g.draw { return "Draw." } return "Winner: " + markName(g.winner) } return "Turn: " + markName(g.turn) } // Render draws the list of games, or a single game board at path "/<id>". func Render(path string) string { p := strings.TrimPrefix(path, "/") if p == "" { return renderList() } id, err := strconv.Atoi(p) if err != nil { return "# Tic-Tac-Toe\n\nInvalid game id: " + p + "\n" } g, gerr := GetGame(id) if gerr != nil { return "# Tic-Tac-Toe\n\nGame " + p + " not found.\n" } var sb strings.Builder sb.WriteString("# Tic-Tac-Toe — Game #") sb.WriteString(strconv.Itoa(g.id)) sb.WriteString("\n\n") sb.WriteString("- X: `") sb.WriteString(g.playerX.String()) sb.WriteString("`\n- O: `") sb.WriteString(g.playerO.String()) sb.WriteString("`\n\n") sb.WriteString(renderBoard(g.board)) sb.WriteString("\n**") sb.WriteString(gameStatus(g)) sb.WriteString("**\n\nCell indices:\n\n") sb.WriteString("| 0 | 1 | 2 |\n| 3 | 4 | 5 |\n| 6 | 7 | 8 |\n") return sb.String() } func renderList() string { var sb strings.Builder sb.WriteString("# Tic-Tac-Toe\n\n") sb.WriteString("2-player tic-tac-toe. Call `NewGame(opponent)` to start; view a game at `/<id>`.\n\n") if games.Size() == 0 { sb.WriteString("_No games yet._\n") return sb.String() } sb.WriteString("| # | X | O | Status |\n|---|---|---|--------|\n") games.Iterate("", "", func(key string, value interface{}) bool { g := value.(*Game) sb.WriteString("| ") sb.WriteString(strconv.Itoa(g.id)) sb.WriteString(" | `") sb.WriteString(g.playerX.String()) sb.WriteString("` | `") sb.WriteString(g.playerO.String()) sb.WriteString("` | ") sb.WriteString(gameStatus(g)) sb.WriteString(" |\n") return false }) return sb.String() }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]