Multiplayer apps
You are looking at an old version of this page: revision #846 by zoobc.how, 2026-10-26 22:31 UTC. See the current version
The board games and dice games on ZooBC are played by two to four people, and every move is a transaction that every node checks against the same rules. The stakes form a pot, which the protocol pays out when the game ends, minus a 1 % rake for the apps pool unless the game ends in a draw.
Opening and joining
A player opens a game with CreateApp, choosing the type and 2 to 4 seats (battleship always has exactly 2), and pays the first stake. The game is Open. Each JoinApp pays an equal stake, and when the last seat is filled the game becomes Active. An optional named opponent turns the game into a direct challenge that only that account may join.
A seat needs a 36-byte account, such as a native ZooBC account. Accounts with 24-byte addresses, the Ethereum and Bitcoin-hash types, cannot take a seat, even though the error message says "multiplayer apps require a ZBC account" (keys and account types). Solo bets accept any account.
Who moves first
Who starts can matter, so nobody can know it or steer it in advance. When the last seat is filled, the turn is pending: nobody is on the clock, and every move is a no-op. Two blocks later the resolver draws the opening seat, as a hash of that block's seed and the app's id taken modulo the number of seats. The producer of the block with the last join knows its own seed but not the seed two blocks later, so it cannot include or leave out the join according to who would start. Neither the creator nor the last joiner starts by rule. Battleship is the exception: the joiner moves first, by committing its board.
In the four dice games (ludo, pig, race and monopoly-lite) a move is not applied in the block that includes it. It is stored as pending and applied by the next block, with that block's seed and keyed by the move's transaction id, so the producer that included the move never sees the roll. One pending move is allowed at a time, and a move that has become illegal in the meantime is dropped.
The move clock
When the last seat joins, and again after every accepted move, the deadline is set 240 blocks ahead, about an hour. The player whose turn it is must move before then, or send an AppMove with an empty move: a paid keep-alive that pushes the deadline another 240 blocks ahead. A move from a player who is not on the clock, or an illegal move, is refused when submitted; if one reaches a block, it is a no-op that costs no fee.
How a game ends
| Ending | How | Payout |
|---|---|---|
| Win | a move wins the game | the pot minus the 1 % rake, to the winner |
| Draw | a move ends the game drawn | every seat gets its stake back; no rake |
| Resignation | a player sends ResignApp | the pot minus the rake, shared equally among the other seats, any remainder to the first of them |
| Timeout | a seated player not on the clock sends ClaimAppTimeout, from the deadline + 1 | the pot minus the rake, shared among every seat but the one on the clock |
The rake is floor(pot × 100 ÷ 10,000), 1 % of the pot: two players staking 1 ZBC each make a pot of 2 ZBC, and the winner receives 1.98 ZBC in the block of the winning move. The block producer takes nothing from the pot. Nothing ends on its own: after a missed deadline the game waits until a player chooses to send the claim, and nobody, the ZooBC Foundation included, can cancel a game.
The engines
Each game's engine is part of the node, with a few simplifications worth knowing:
- chess has no castling and no en passant, and pawns always promote to queens;
- checkers forces captures and continues multi-jumps;
- reversi passes a seat that has no legal move;
- dots-and-boxes gives another move after a box is completed;
- pig is won at 100, and race, the snakes-and-ladders game, finishes at 49;
- ludo moves four tokens per seat;
- monopoly-lite starts each seat with 1,500 and pays 200 for passing GO.
Battleship: secrets on a public chain
Battleship needs hidden information on a chain where everything is public, and it gets it by commit and reveal. Before play, each player picks a secret 32-byte salt for each of the 100 cells of a 10 × 10 board and publishes SHA3-256(is_ship ‖ salt) for every cell: 100 hashes, 3,200 bytes in all. The creator sends its commitment with CreateApp, and the joiner sends its own as its first move. Because every salt is random, the hashes reveal nothing.
A player fires with the move [0, cell]. The owner of that board must reveal the pending cell with [1, is_ship, salt], and every node checks that these hash to the committed value. A lie gives a different hash and is refused, so the owner can only tell the truth, or run out of time and lose to a ClaimAppTimeout. The first player to hit 17 ship cells wins. Committing fewer ship cells gains nothing: once the firer has fired all 100 shots and fewer than 17 ship cells were revealed, the firer wins.