← WorkCase file · LK-002
Sikhu Poker
Anywhere. Anytime. A table. A Khmer card game for three or four players, rebuilt inside Telegram — with the deck, the turn order and the ledger owned by the server.
- Role
- Product, design, build
- Scope
- End-to-end, three runtimes
- Stack
- Flutter · Java 25 · Spring Boot · PostgreSQL · Valkey
- Year
- 2026
01Context
Three friends, four phones, no deck.
Sikhu is a local Cambodian card game: 52 cards, three or four players, eight cards in hand, four pairs to win. Red cards carry the score; the table settles who owes whom, and money never enters the app. The friends already knew the rules. The problem was the table itself — someone forgot the deck, or the cards needed shuffling again. The brief was not to invent a new game. It was to make the existing one work on three or four phones inside Telegram.
02Model
The rules became the state machine.
Everything that can be disputed lives on the server: the deck, the wall, every hand, the turn, the deadlines, the claim window. The client renders the snapshot addressed to that player and sends commands back; it does not judge a card. One discard runs the same sequence every time: who can win on it, a 15-second claim window, then the next player may take the card or draw. A taken card must be followed by a discard, which reopens the sequence. Claims are ordered by seat, not by who tapped first; a race would reward latency, not the card. The judge is a pure function — eight cards into four pairs — checked against all 752,538,150 possible hands. The 1,731-line rulebook came before the code. That is what made the rest possible.
03Build
Three runtimes, one table.
The player app is Flutter Web inside a Telegram Mini App, with native shells reserved for later. The server is Java 25 + Spring Boot 4.1.1: REST for the lobby, a WebSocket for the table, PostgreSQL 18.6 for members, rooms, settled rounds and transfers, Valkey 9.1.2 for sessions, entry tickets and live hands. A small Vue 3 admin reads members, rooms and ledgers. The whole thing ships in Docker behind nginx. Friend tables and bot tables share one engine; only the seats, the stop rule and the ledger label change. The build is 11.9k lines across client, server and admin, plus 2.3k lines of server tests. The rulebook and the tests decided what the code was allowed to do.
04Result
A playable table, not a prototype.
v1 has friend rooms for three or four players, a reusable invite link, a host-only start, a unanimous stop, bot tables for practice, a 15-second reconnect grace, and a ledger that nets a round into the fewest transfers. It runs in Chinese, English and Khmer, with the Khmer face bundled so the script shapes correctly. There is no matchmaking, no spectator mode, no wallet and no ads. The table is live through @sikhu_bot, inside Telegram.
05Retro
The rulebook came before the code.
What worked: writing the rules and edge cases before the UI; making the server authoritative; using one engine for friend and bot tables; checking the judge exhaustively; keeping the ledger separate from money. What we would change: add UI automation, not only unit tests; test Khmer shaping on more real Telegram WebViews; ship the native shells; give the bots another level. The 1,731-line rulebook and the test suite are why it held.
3–4players
752.5Mhands verified
11.9klines of app code
3languages
2.3k lines of server tests · 7 migrations · 13 Java test classes, 2 Flutter tests.