func Drop
crossing ActionDrop places the caller's disc into the given column (0-6). Enforces turn order by caller, rejects full columns, and detects a 4-in-a-row win or a draw.
⚠️ 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
A two-player Connect Four realm on a 7-wide × 6-tall board. The game creator plays 🔴 and moves first; the named opponent plays 🟡. Drop enforces turn order by caller, rejects full and out-of-range columns, and detects a horizontal, vertical, or diagonal 4-in-a-row (or a draw). Render draws the board with 🔴🟡· and shows whose turn it is or who won.
Realm path: gno.land/r/REPLACE_ADDR/connect4
# create a game against another address (you are 🔴, returns the game id)
NewGame(g1zabc...opponent) # -> 1
# drop discs into columns 0-6 (turns alternate, enforced by caller)
Drop(1, 3) # 🔴 drops in column 3
Drop(1, 3) # 🟡 drops in column 3 (called from opponent's key)
# view state
render: # lists all games
render:1 # shows board for game 1
Two ports against v0, which stays live and untouched.
Identical behaviour to v0 apart from rendering, which now goes
through p/moul/kit/ui instead of the local short helper.
One address format. Eleven realms carried their own shortAddr with four different truncation rules, so the same account rendered differently depending on which realm you opened. ui.Addr is that decision made once: 8 leading characters, an ellipsis, 4 trailing, in backticks.
State moved from a hand-rolled avl.Tree + nextID + zero-padding helper to
p/moul/kit/store, which keys entries by
seqid instead of a fixed-width decimal string.
v0 padded ids to width 12. Past that width the padding stops and the tree
orders "1000000000000" before "999999999999", so every list this realm
renders would be wrong from that entry on. store keys are 8 big-endian bytes
whose order is numeric for every uint64, so there is no width left to outgrow.
Game loses its ID field, which the store now owns, so Drop emits with the
gameID it was already given and renderGame takes the id alongside the game.
Drop's trailing games.Set(...) is gone as well: it re-stored a pointer that
was already in the tree, and mutating through it persists on its own, which is
how the rest of this realm already worked.
Ids stay plain integers and the rendered output of the storage port is
unchanged. Each port changes something v0 promised, one the rendered output
and one the storage layout, so under this repo's versioning rule each is a
compatibility change and neither could be an edit to v0 in place.
They land in the same new version because v1 was never published. A
version number is a tag on something that exists on a chain, and until it does
there is nothing for a second number to avoid disturbing, so the right move is
to keep editing the version you have. gnopm unbump is what folded the second
port back down into this one.
Part of 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.
Drop places the caller's disc into the given column (0-6). Enforces turn order by caller, rejects full columns, and detects a 4-in-a-row win or a draw.
NewGame creates a match between the caller (🔴) and opponent (🟡). Returns the new game id.
Render draws the board with 🔴🟡· and shows whose turn / the winner. path "" lists all games; path "<id>" shows a single board.
Game holds the full state of one Connect Four match. board is indexed board[row][col]; row 0 is the BOTTOM row. It carries no ID field: the id belongs to the store, which hands it back on lookup and iteration.