Inspiration

Buckshot Roulette gave us the turn structure. Jackbox gave us the shape: the laptop is the TV, every phone is a controller, and nobody installs anything.

Then we swapped the shotgun for a confetti popper. Nobody dies at this table — you just lose money, and getting hit showers you in confetti on the big screen. It turned a grim one-on-one duel into something six people can play in a living room, and it made the stakes honest: this is a poker game wearing a party hat.

The blockchain came from a rules problem, not a sponsor list. We wanted cheating to be a legal move — you get a secret cheat card every round, and playing it is fine as long as nobody catches you. But the moment cheating is legal, the game needs evidence nobody can edit, and "trust our server" is not evidence. A game about cheating needs a referee that not even the host controls. That's the one job a chain is genuinely built for, and Thru is fast enough that a party game can put every pop on it.

What it does

Gambit Rodeo is a party game for 2–6 players, humans on phones plus AI bots.

  • Buy in for $12 and get 3 poker chips, worth $4 each. In the middle of the felt sits a confetti popper, loaded with 2–8 poppers in a secret order. The TV announces only the count: "3 LIVE · 2 BLANK", and the order's hash is committed on-chain before anyone touches it.
  • Your phone asks "Who gets popped?" — yourself or anyone still in. Then you hit the big red POP IT button, which brings up Face ID. That passkey is your wallet.
  • A LIVE popper knocks one chip off the target and into the pot. A BLANK aimed at yourself means you pop again — the risky bonus. Anything else passes the turn.
  • Every round each funded player gets one secret cheat card: 👁 PEEK (privately see if the chambered popper is live), 🔴 HOT LOAD (make it live), ⚪ DUD (make it blank), 🔀 SWAP (blindly swap it with the next one). After every pop, the chain stores one sealed envelope per seat — including decoys for honest players, so a cheater leaves no distinguishable trace.
  • The TV shows fired counts against announced counts. Go over, and it flashes "THE COUNT IS OFF." Somebody cheated; it doesn't say who.
  • Anyone can slam RIGGED! once a round and "swear on their face." The accused player's envelopes are opened on-chain and the program rules: guilty hands over their entire stack to the accuser; a wrong call costs the accuser one chip. When the popper runs empty there's a 5-second Last Call to accuse.
  • At the end of each round the pot goes to the chip leader. Run out of chips and you're broke — buy back in for another $12 if your wallet can cover it, otherwise you're 💀 cleaned out and can only sit there hitting the BOO button.
  • After the final round everyone cashes out at $4 a chip and the biggest profit wins — so the player who cheated well and bought back in twice can still lose to someone who quietly sat on their stack.
  • Then Review the Tape: a VHS-style replay that exposes every cheat nobody caught, stamps each one CAUGHT or GOT AWAY, and re-hashes every envelope in the browser against the Table account read straight from a Thru RPC node — "⛓ N/N hashes match."

How we built it

  • apps/web (Next.js on Vercel): the TV screen, the QR join flow, and the phone controller. It holds no secrets — the TV and each phone only ever receive the slice of state they're allowed to see.
  • apps/server (Node + Socket.IO): the rules engine as a pure reducer, step(state, action) → { state, effects }, where effects are chain calls, timers, animations and private messages.
  • programs/referee (C on Thru's RISC-V VM): commitments, sealed envelopes, accusations, verdicts, chips and pots, in a ~75 KB program-derived Table account.
  • Face ID on-chain via @thru/passkey: the passkey-manager program checks the signature and CPIs into our referee, so the pop is signed by the player's face, not by our server. The same face signs the $12 buy-in.
  • The money is real money on-chain — play money, but a real token. BCUSD is created on first boot through Thru's bootstrap Token Program. Dollars move in the Token Program, chips move in the referee, and the tape links the two by transaction signature.
  • The referee sits behind an interface. MockReferee encodes the real instruction bytes and runs them through program.ts, a line-by-line TypeScript mirror of blank_check.c — so a laptop with no blockchain still obeys the chain's rules, and gameplay work was never blocked on chain work.
  • Bots and the Pit Boss run on Jev: four personalities (🧮 The Accountant, pure odds; 🍺 Uncle Gary, cheats every round and accuses everyone; 🙏 Sister Mercy, never cheats, mostly; 🐣 The Intern, does whatever the model says). They pick from pre-written taunts rather than free-writing prose, and fall back to heuristics with no API key.
  • All sound is synthesized with Web Audio (no audio assets), and the art is original.

Challenges we ran into

  • Making C and TypeScript agree byte for byte. Commit-reveal only works if both sides hash the identical preimage. The SDK's SHA-256 uses RISC-V Zknh instructions, so our host test compiles blank_check.c with gcc against the real Thru SDK headers and a pinned SHA-256 source, swapping only those four macros.
  • Two languages sharing one 74,850-byte struct. The layout is defined once in packages/shared/src/table.ts, static-asserted in blank_check.h so the build fails if it drifts, and parsed a third time by the browser for the tape.
  • The 4 KiB program stack on the Thru VM: no large locals anywhere in the C referee.
  • Teaching the chain about money, not lives. Moving from hearts to chips meant every verdict became a transfer, and every transfer has to be conserved. Chips can only move between a stack and the pot, so chips + pot = buy-ins × 3 has to hold after every single transaction, including a guilty verdict that hands a whole stack to an accuser.
  • iOS WebAuthn must be called inside the tap the user made. So the pop is two taps: picking a target prefetches the challenge, and POP IT calls WebAuthn directly.
  • Latency in a party game. House transactions share a global fee-payer queue and cache chain id, slot and nonce so a pop costs one round trip, every chain wait is capped at 4s, and a failed transaction shows red on the ticker while play continues.
  • A pre-1.0 chain. Thru's APIs move, so we verified every call against the live docs and pinned versions the moment something worked. Its C toolchain is Linux/macOS only, which meant developing on Windows through WSL2.
  • Being honest about trust. In this MVP the server is the dealer: it knows the popper order and writes the envelopes. The chain stops it from rewriting history, not from lying at the moment of sealing. Two things narrow the gap — the tape must reproduce every reported pop from the committed order plus the revealed cheats, and REVEAL_SHELLS refuses a reveal whose live count doesn't match what was announced.

Accomplishments that we're proud of

  • The chain's rules are provable, not asserted. 60 tests cover the rules, the money and the view boundary; 10 randomized full games (2–6 seats, with buy-backs) run through the referee mirror and check that chips never appear out of nowhere; and a host test replays 212 recorded instructions — 3 full games plus deliberate refusals like wrong turn, forged reveal, missing Face ID, not the host, buying in with chips already on the table — demanding that results, revert codes, emitted events and the final Table account bytes all match the C program exactly.
  • No seed phrase, no extension, no app install. You scan a QR code and your face is your wallet — and that same face pays the $12.
  • Decoy envelopes, so the on-chain record of a cheat is indistinguishable from the record of an honest turn until somebody accuses.
  • A replay you can actually check. The tape re-hashes in the browser and compares against the chain, not against us, with every item linking to scan.thru.org.
  • A popper that can't be un-fired, and a table that can't be broken. If the chain or the AI key is missing the game falls back and keeps playing, and a player who walks away doesn't freeze the table.
  • Measured alphanet round trips of 161 ms (chain id) and 84 ms (block height / proof); per-transaction latency from our on-chain smoke test: .

What we learned

  • Commit-reveal is the whole design, and the interesting part isn't the hash — it's the decoys, and sealing before anyone is allowed to accuse.
  • Turning lives into money made the game better. Losing a heart ends your night; losing chips gives you a decision — buy back in and chase, or fold and protect your profit.
  • Mirroring the on-chain program in TypeScript was the single best decision we made. It let all the gameplay work happen with no chain attached, then became the test oracle for the C program.
  • Passkeys are a genuinely good wallet UX, but they're bound to an origin — keep one stable domain, or you throw away everyone's wallet on every deploy.
  • Pin a shared binary layout with static asserts in both languages, or the two sides drift and you debug it at 3am as a hash mismatch.
  • Judges respect a stated trust boundary far more than a hand-wave.

What's next for Gambit Rodeo

  • Close the dealer gap: phones seal their own envelopes with a session key, so not even we can know a cheat before it's revealed. The design is already written up in the spec.
  • Ship the stretch cheat cards, PALM and COPYCAT, which are already reserved in the hash preimage and the on-chain format.
  • Rebuild the tape from @thru/replay instead of our transaction list, so the replay needs nothing at all from our server.
  • More table: spectators who can bet on the outcome from the sidelines instead of just booing, more bot personalities, and tournament mode across several tables.
  • A real BCUSD faucet and a permanently hosted table, so anyone can pull up a chair.

Built With

Share this project:

Updates

Submission history