poker-lan-cli
v0.3.0
Published
Texas Hold'em in your terminal (Ink) with a browser GUI — single player vs equity-based bots, or LAN multiplayer with save/resume.
Maintainers
Readme
poker-lan-cli
No-limit Texas Hold'em in your terminal, built with Ink. Every game also opens a browser GUI on localhost that mirrors the table and lets you act from there. The GUI shows the table from above in one of three scenes, picked at random each time you open it (a live-TV arena with a square stage, cameras, cheering fans, sponsor billboards and a casino hallway; a ski lodge with a fireplace and round windows onto snowy mountains; or a little garden lawn on the beach with deck chairs and a busy boardwalk). It also has chip and card animations, sound effects, and a cartoon avatar in sunglasses for every player.
- Single Player — you against 4 bots that decide with equity, pot odds and hand ranges
- Host — run a table other people on your LAN can join, plus up to 4 bots. Progress is saved so you can resume unfinished games
- Join on LAN — hosts on your network are found automatically, or you can type an address
Install
With npm (needs Node.js 22+):
npx poker-lan-cli
# or
npm install -g poker-lan-cli
poker-lanWithout npm or Node (Linux and macOS). This downloads a standalone executable from the GitHub releases:
curl -fsSL https://raw.githubusercontent.com/samnayak1/lan-cli-poker/main/install.sh | shIt installs to ~/.local/bin/poker-lan. Use PREFIX=/usr/local to choose another location, or VERSION=v0.1.0 for a specific release.
From source with make (needs Bun, or npm as a fallback):
git clone https://github.com/samnayak1/lan-cli-poker && cd lan-cli-poker
make install # builds bin/poker-lan and copies it to ~/.local/bin
make release # executables for every platform, in release/The executables bundle the runtime, the game and the browser GUI into one file (60–85 MB), so they run with nothing else installed.
Options
-n, --name <name> Your player name (skips the name prompt)
--port <port> Port to host LAN games on (default 47777)
--web-port <port> Port for the local browser GUI (default 8347)
--no-browser Serve the browser GUI but don't open it automatically
--no-gui Terminal onlyControls
There are only three actions, in the terminal and in the browser:
| Key | Action |
| --- | --- |
| F | Fold |
| C | Call (this checks when there's nothing to call) |
| R | Raise. Type an amount or use ←/→ (±1 BB) and ↑/↓ (±5 BB), then Enter. Raising your whole stack is going all in |
Q quits to the menu (hosts: the game is saved) and O reopens the browser GUI. The next hand is dealt automatically a few seconds after the last one ends.
In the lobby the host presses B to add a bot (at most 4), X to remove one, and S to start.
LAN play
- One person picks Host a LAN game. The lobby shows addresses such as
192.168.1.20:47777. - Everyone else picks Join a LAN game. Tables on the network are found through UDP broadcast on port 47778. If none appears, choose Enter address manually.
- The host can add bots to fill seats and then starts the game.
The host's machine is the source of truth. Clients only ever receive their own hole cards, so a modified client can't see other players' hands. If a player's connection drops, their seat checks or folds until they rejoin with the same name. Each turn has a 60-second limit.
Firewall: the host must allow inbound TCP 47777 (game) and UDP 47778 (discovery). On WSL2, LAN broadcast usually doesn't reach Linux, so joiners should type the Windows host's IP. The host may also need to set up port forwarding or mirrored networking.
Saved games
Hosted games are saved to ~/.poker-lan-cli/saves/ after every hand and when the host quits. You can change the location with POKER_LAN_HOME. If a game is closed mid-hand, that hand is replayed from its starting stacks.
To resume, choose Host → Resume "…". Saved players get their seats and chips back when they rejoin with the same name. The host can press F to give unclaimed seats to bots. Seats beyond the 4-bot limit are closed. A save is deleted when its game finishes. Press D in the host menu to delete one manually.
The bots
Every bot plays the same strong, GTO-inspired strategy and adapts to how you play. Each decision uses only information a real player would have:
- Preflop charts: solver-style opening, re-raising and calling ranges for each seat, with mixed frequencies for borderline hands.
- Postflop play: the bot estimates its equity with a Monte Carlo simulation (about 700 run-outs) against what each opponent likely holds. Facing a bet, it calls whenever that beats the pot odds. Otherwise it value-bets at mixed sizes, semi-bluffs draws, bluffs rivers at a measured rate, and sometimes slow-plays monsters.
- Learning the table: a shared tracker records how every player plays: how often they enter pots, raise, re-raise, bet after the flop, and fold to bets. The bots bluff players who fold too much, value-bet players who call too much, call down players who bet too much, and widen or tighten their preflop charts to match.
In simulated heads-up play the current bot beats the previous version, wins 2–4× as much as before from players who always call or always fold, and defends against players who raise and bet every hand instead of folding to them.
The full walkthrough, with worked examples and every tuning constant, is in docs/BOT.md.
How hands are ranked (with bitmasks for straights and flushes) is explained in docs/HAND_EVALUATOR.md.
Architecture
src/
engine/ pure game rules, no I/O
Card.ts rank/suit, codes like "As"
Deck.ts shuffle (crypto RNG), draw
HandEvaluator.ts best 5 of 7 → comparable score + name
Player.ts stack and per-hand betting state
Pot.ts main/side pot construction
GameState.ts all table data, per-viewer views, snapshots for saving
Game.ts betting rounds, streets, showdown (a state machine)
ai/ ranges.ts · equity.ts · Bot.ts
TableSession.ts runs a table: seats, bot turns, timeouts, pacing, saving
net/ protocol, HostServer (WebSocket), RemoteConnection, UDP discovery
web/WebServer.ts localhost GUI server (token-protected WebSocket)
persistence/ SaveManager
ui/ Ink screens
public/index.html the browser GUI (vanilla JS, no build step)The terminal UI and the browser GUI both talk to a TableConnection. That connection is either local (single player or host, where the table runs in this process) or remote (join, over WebSocket), so each screen is written only once.
Development
npm install
npm run dev # run from source with tsx
npm test # engine, AI, and network integration tests
npm run build # compile to dist/
npm publish # runs typecheck + tests + build first
make release # standalone executables for every platformPushing a version tag (npm version patch && git push --follow-tags) runs .github/workflows/release.yml, which builds the executables and attaches them to a GitHub release. install.sh downloads from those releases.
Contributing
Any problems or want to contribute? Contact [email protected]
License
MIT
