# Poker Night A Discord-Poker-Night-style No-Limit Texas Hold'em table that runs in HexDroid, played between HexDroid users over an encrypted `#game` channel. ## Architecture ``` poker.hex (launcher / flavour) : /poker, sidebar, event reactions │ uiIntent / signals PokerTable composable in ChatScreen : renders state; sends moves │ reads PokerTableState from _state ; emits actions PokerGame engine (native Kotlin) :rules, betting, side pots, hand eval │ moves <-> bytes (PokerMove) AgeChannel (+AGE) :sign + encrypt every move over #game ``` ## `+AGE` Every move is a `PokerMove` -> `encode()` -> bytes _> `AgeChannel.encrypt(moveBytes, ad)` -> `AGE MSG` on `#game`. Peers `decrypt` (which verifies the sender's Ed25519 signature), `PokerMove.decode`, and apply it to their own copy of the deterministic `PokerGame`. ## Shuffle & dealing, the trust model players commit shuffle nonces; the dealer commits a secret salt; nonces are revealed; `seed = H(salt ‖ nonces)`. Only the dealer can compute the order (knows salt), so it deals hole cards by **sealing each player's two cards to their `+AGE` key** (`AgeSeal`). At showdown the dealer reveals the salt, and everyone recomputes the deck and verifies the commit, the community cards, and each shown hand. - The dealer cannot stack (player nonces feed the seed *after* the dealer commits salt). - The dealer cannot change cards mid-hand (the salt commit binds the whole order). - The dealer can peek* at everyone's cards this hand: rotate the dealer each hand ```kotlin data class PokerTableState( val seats: List, // name, stack, betThisStreet, folded, allIn, statusLabel ("CHECK"/"BET $196"/"HIGH CARD") val community: List, // the flop/turn/river (5♣ 3♥ 9♠ …) val hole: List, // our two cards (10♥ 6♥) val pot: Int, // 246 val dealerSeat: Int, // the D button val toActSeat: Int, // highlight ring val legal: PokerGame.LegalActions?, // drives MIN / ¼ / ½ / ¾ / ALL-IN + amount + CONFIRM ) ``` The bet-sizing row is `PokerGame.potFractionRaiseTo(0.25/0.5/0.75)`, `legal.minRaiseTo` ("MIN"), and `legal.maxRaiseTo` ("ALL IN"). CONFIRM sends `Action.Raise(amount)` -> `PokerMove.Act` -> `AgeChannel`.