poker-calculations
v1.3.2
Published
NL Hold'em hand evaluation, Monte Carlo equity, decideAction, and chip / GTO math — C++ core with prebuilt N-API addon.
Downloads
1,643
Maintainers
Readme
poker-calculations — NL Hold’em engine & odds helpers (C++ core, Node addon)
No-Limit Texas Hold’em hand evaluation, Monte Carlo equity (single- and multi-threaded), pot-odds and chip-EV helpers, and a rule-based decideAction layer—implemented in C++ and exposed to Node via N-API.
Published releases ship prebuilt native binaries (node-gyp-build), so npm install does not require CMake, a compiler, or the Windows SDK on the installing machine.
Full TypeScript types: index.d.ts.
Install
Requirements: Node.js 18+.
npm install poker-calculationsPublished tarballs include N-API prebuilds under prebuilds/<platform>-<arch>/:
node.napi.node— glibc Linux, macOS, Windows (default).node.napi.musl.node— same directory on linux-x64 / linux-arm64 when you install on Alpine / musl.
Linux glibc binaries are linked with -static-libstdc++ / -static-libgcc so they do not require as new a system libstdc++.so as a binary built on the latest Ubuntu runner (this avoids GLIBCXX_* version errors on older Linux images, including many serverless hosts).
Bundlers and Next.js
Load this package from runtime code paths (for example a lazy require() inside a route handler) if your bundler or next build evaluates server modules statically. That avoids optional build-time native resolution issues; you still need a prebuild that matches the deployment OS and libc (glibc vs musl).
Quick start
This is a Node.js library—require or import it and call functions. There are no HTTP endpoints unless you wrap it yourself.
CommonJS
const poker = require('poker-calculations');
poker.evaluateBestHand(['Ah', 'Ac', 'Kd', 'Ks', 'Qh']); // native — best 5 of 7
const equity = poker.simulateHandOutcome(['Ah', 'Kh'], ['Qh', 'Jh', 'Th'], 2000, 42, 1);
const spr = poker.spr(90, 270); // native (C++ chip math)ESM
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const poker = require('poker-calculations');Runnable samples are separate ESM scripts under examples/: one topic per file (see the header comment in each script; names align with groups in FEATURES_ADDED.md). From the repo root: node examples/<name>.mjs.
examples/native-binding-exports.mjs— print every N-API export at runtime.- Topic demos —
fold-equity.mjs,gto-frequency.mjs,hand-resolution.mjs,heuristics-draws.mjs,icm.mjs,intervals-and-odds-bridge.mjs,kelly-chubukov-jam.mjs,monte-carlo-equity.mjs,multiway.mjs,pot-chip-ev-rake.mjs,reverse-implied-geometry.mjs,side-pots.mjs,sizing-commitment.mjs,stacks-display.mjs,stats-risk.mjs,strategy-decide-action.mjs.
Cards are strings like "Ah", "Td" (ten may be "10h").
API overview
All symbols below are exported from the native addon (C++ via N-API). breakevenCallEquity(potBeforeCall, toCall) equals potOddsRatio(pot, toCall) when the same pot and call amounts are used.
| Area | Exports |
| --- | --- |
| Hands & equity | evaluateBestHand, evaluateHandStrength, evaluateHandCategory, handRankCategoryOrder, validateCardString, cardStringsHaveDuplicate, canonicalCardString, parseCompactCardList, compareBestHands, simulateHandOutcome, parallelHandSimulation, exactHuEquityVsRandomHand, straightMadeFlopToRiverExactProbability |
| Strategy | decideAction |
| Pot / EV | potOddsRatio, expectedValueCall, expectedValueCallWithRake, breakevenCallEquity, breakevenCallEquityFromPotOddsDisplayRatio, potOddsDisplayRatioFromBreakevenCallEquity, breakevenCallEquityWithRake, rakeFromPot, formatPotOddsReducedFraction, equityToWinningOddsAgainst, winningOddsAgainstToEquity |
| Stacks & display | spr, effectiveStack, normalizedStackFractions, stackInBigBlinds, potOddsRatioDisplay, formatPotOdds, harringtonM, harringtonMEffective, harringtonMEffectiveActiveAntes, harringtonQ, orbitCostChips, nlMinimumRaiseToTotal, preflopCombosFromNotation, preflopCombosFromNotationsList |
| Heuristics & draws | ruleOfFourEquity, ruleOfTwoEquity, estimatedOutsFromRuleOfTwo, estimatedOutsFromRuleOfFour, impliedBreakevenFutureWin, hypergeometricOneCardHitProbability, runnerRunnerBackdoorFlushTwoCardProbability, runnerRunnerStraightDrawHitProbability, flopToRiverAtLeastOneHitProbability, flopToRiverAtLeastOneHitUnionTwoCategories, flopToRiverAtLeastOneHitUnionThreeCategories, flopToRiverAtLeastOneHitUnionFourCategories, flopToRiverAtLeastOneHitDisjointOutsSum, duplicationAdjustedOuts |
| Reverse implied / geometry | reverseImpliedOddsMaxFutureLoss, geometricPotAfterMatchedPotFractions |
| Stats & risk | monteCarloStandardError, monteCarloTrialsForStandardErrorBound, monteCarloTrialsForHoeffdingBound, wilsonScoreInterval, agrestiCoullInterval, normalWaldBinomialInterval, riskOfRuinDiffusionApprox, bankrollForTargetRorDiffusion, betaBinomialFoldPosterior |
| Kelly & jam toys | kellyCriterionBinary, chubukovSymmetricJamBreakevenStack, chubukovSymmetricJamEv, chubukovMaxSymmetricJamStackChipsBinarySearch, chubukovMaxSymmetricJamStackBinarySearch, chubukovMaxSymmetricJamStackFromHandBinarySearch |
| GTO-style | minimumDefenseFrequency, alphaFrequency, bluffToValueRatio, valueToBluffRatio |
| Sizing & commitment | betAsPotFraction, sprAfterCall, commitmentRatio |
| Fold equity | breakevenFoldEquityPureBluff, breakevenFoldEquityPureBluffWithRake, breakevenFoldEquitySemiBluff, breakevenFoldEquitySemiBluffWithRake, twoStreetPureBluffSameFoldEquity, twoStreetPureBluffEv, breakevenFoldEquitySecondStreetPureBluff, breakevenFoldEquityFirstStreetPureBluff |
| Multiway | multiwaySymmetricBreakevenCallEquity, multiwaySymmetricBreakevenCallEquityWithShare |
| ICM | icmWinProbabilitiesHarville, icmHarvillePlacementProbabilities, icmTopKFinishProbabilities, icmLastPlaceProbabilitiesHarville, icmExpectedPayouts, icmPairwiseBubbleFactor |
| Side pots | sidePotLadderFromCommitments, layeredPotChipEvFromEquities, sidePotLayersTotalChips |
Breaking change (v1.2.0): poker-math.js was removed; require poker-calculations (or the .node binding) for all math. Rebuild native artifacts after upgrading from a git clone.
API note: chubukovMaxSymmetricJamStackBinarySearch / chubukovMaxSymmetricJamStackFromHandBinarySearch take hole cards, board (3–5 cards), dead money, and max stack; they return the largest integer jam size with nonnegative symmetric-jam EV using exact HU equity vs a random hand. The FromHand variant uses int32 coercion for maxStackChips in native code.
Responsible use
Use this for your own simulator, research, or automation you are permitted to run. It is not intended to help bypass third-party terms of service on real-money sites.
Features (engine)
| Area | What’s included |
| --- | --- |
| Cards / deck | 52-card deck, shuffle with injected std::mt19937, deal, burn on board deals in GameEngine |
| State & rules | PokerGameState, blinds, pot, per-street commits, phase machine (pre-flop → river → showdown), GameEngine::apply_action with Decision |
| Evaluation | Best five of up to seven cards, full ranking + kickers, evaluate_hand_strength scalar |
| Strategy | decide_action(..., BotConfig, OpponentModel*) using MC equity (or strength fallback when sim count is 0), pot odds, and call EV |
| Simulation | simulate_hand_outcome, parallel_hand_simulation (chunked async workers, distinct seeds) |
| Config | BotConfig::load_from_config_file / save_to_config_file (key=value, # comments) |
| Tests | GoogleTest suite (deck, engine, evaluator, card strings, poker math, ICM, side pots, exact equity, strategy, opponent model, MC, config) |
Developing from source
If you clone the repo or install from a git URL without local prebuilds, you need a C++ toolchain (CMake 3.16+, and MSVC with C++ workload on Windows, Xcode CLI tools on macOS, or GCC on Linux). Published tarballs from npm do not compile native code during install.
npm ci
npm run build:native
node scripts/stage-prebuild.js <platform-arch>Use the <platform-arch> tuple node-gyp-build expects (for example win32-x64, linux-x64, darwin-arm64). For Alpine/musl, stage with node scripts/stage-prebuild.js linux-x64 musl (writes node.napi.musl.node). On Windows, delete a stale build folder if configure fails; ensure the Windows SDK is installed if you see resource-compiler (rc) or manifest (mt) errors.
Rebuild after changing C++:
npm run build:nativeNative tests
npm testOn Windows this expects MSVC on PATH like the build steps above.
Maintainers: publishing
Publishing uses .github/workflows/npm-publish.yml. It starts automatically when package.json or package-lock.json changes on main (for example merging a version bump). The release gate still skips the native build and npm publish when that version is already on npm, so dependency-only lockfile edits do not republish. You can also re-run from Actions → npm publish → Run workflow (same branch as the failed run, usually main) without a new commit.
npm Trusted Publishing (OIDC)
Publishing uses trusted publishing — GitHub Actions proves identity to npm with OIDC; you do not store an NPM_TOKEN secret for npm publish.
- On npmjs.com → package
poker-calculations→ Settings → Trusted Publisher, connect GitHub Actions using:- Repository that matches
repository.urlinpackage.jsonexactly (npm validates at publish time; npm does not validate when you save). Current value:git+https://github.com/DevomB/Poker-Calculations.git - Workflow filename
npm-publish.yml(same casing and.ymlextension). If you previously usednative-prebuild.yml, update the trusted publisher entry on npm to this filename (or add a second allowed workflow and remove the old one).
- Repository that matches
- After proving publishes work, optionally tighten Publishing access (“Require 2FA and disallow tokens”) and revoke old automation tokens, per npm’s migration guidance.
All dependencies used during CI are public; npm ci does not need a read token. If you later add private npm dependencies, use a read-only granular token only on install steps, not for publish.
Release steps
- On
main, bumppackage.jsonversionand keeppackage-lock.jsonin sync (for examplenpm install --package-lock-onlyafter dependency or version changes), then push or merge tomain. - The npm publish workflow runs from that push. If it failed before npm accepted the package, fix and push (or use Run workflow on
mainto retry without changing files).
The release gate runs npm ci (so a broken or stale lockfile fails fast), then checks whether name@version already exists on npm. If not, it builds native targets (including musl artifacts as node.napi.musl.node), merges them under prebuilds/, and runs npm publish via OIDC. No git tags — the version field is the release input. With trusted publishing on a public repo, npm records provenance automatically. If that version is already on npm, the workflow skips build and publish.
If publish fails: fix the underlying issue, push commits without bumping version again, and re-run the same workflow until it succeeds — then increment only for the next release. That keeps npm version numbers from skipping.
Use GitHub-hosted runners for this workflow: OIDC trusted publishing does not support self-hosted runners yet (npm docs). The workflow pins Node ≥22.14 to satisfy npm’s trusted-publishing runtime requirement alongside npm CLI ≥11.5.1 in the publish job.
In the GitHub repo, under Settings → Actions → General → Workflow permissions, use the default that allows Actions to run; the publish job sets id-token: write so OIDC works for npm publish.
Manual publish: assemble binaries under prebuilds/, then npm publish. Without binaries, prepack fails unless SKIP_PREBUILD_CHECK=1.
Repository layout
include/poker/ Public headers (Card, Deck, GameEngine, HandEvaluator, poker_math, …)
src/ Implementations (`poker_math.cpp` — SPR, MDF, fold equity, …)
native/ Node-API binding (built when CMAKE_JS_INC is set by cmake-js)
tests/ Unit tests
examples/ Node ESM sample scripts (one feature area per file; see FEATURES_ADDED.md)
CMakeLists.txt Static poker_lib; optional poker_tests; optional poker_calculations.node when built by cmake-jsGeneric (single-configuration generators)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build buildRun tests:
cd build
ctest --output-on-failureWindows: MSVC with NMake (when cl is not on PATH)
Open x64 Native Tools or run vcvars64.bat, then:
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -S . -B build_nmake
cmake --build build_nmake
cd build_nmake
ctest --output-on-failureCMake options
| Option | Default | Meaning |
| --- | --- | --- |
| POKER_BUILD_TESTS | ON (non–cmake-js); OFF if CMAKE_JS_INC is set | Build poker_tests and GoogleTest. Off for fast npm addon builds. |
When cmake-js configures the project, it defines CMAKE_JS_INC and only poker_calculations.node plus poker_lib are built—tests are skipped so the addon build stays fast and does not pull GoogleTest.
Disable tests manually:
cmake -S . -B build -DPOKER_BUILD_TESTS=OFFGCC note
Tests link libstdc++fs when using GNU C++ for std::filesystem in config tests.
Configuration file (BotConfig)
# bot.txt
aggression_threshold=0.55
risk_tolerance=0.92
monte_carlo_simulations=800
monte_carlo_villains=1
raise_pot_fraction=0.55
opponent_aggression_weight=0.05
rng_seed=2463534242Load with BotConfig::load_from_config_file("bot.txt").
Quick C++ API sketch
- State & engine:
poker::PokerGameState,poker::GameEngine::start_new_hand,apply_action,advance_phase_if_ready - Hands:
poker::evaluate_best_hand,poker::evaluate_hand_strength,poker::evaluate_hand - Equity:
poker::simulate_hand_outcome,poker::parallel_hand_simulation - Decision:
poker::decide_action(state, hero_hole_cards, cfg, opponent_model, hero_seat) - Chip / GTO math:
poker::spr,poker::minimum_defense_frequency,poker::breakeven_fold_equity_pure_bluff, … (poker_math.hpp) - Integration: subclass
poker::PokerBotInterfaceor usepoker::MockPokerBotInterfacefor tests
Headers live under include/poker/. Link against poker_lib.
Contributing
Strategy thresholds and MC counts are centralized in BotConfig. After changes, run npm test or ctest; MC-heavy tests use statistical bands (e.g. AA pre-flop equity vs one random hand).
