Back to the catalog

pkcore Knowledge Bundle

Bundle OKF 0.1 · 13 conceitos · ImperialBower/pkcore

Open source Repository Open in the app JSON README (API)

About

# pkcore Knowledge Bundle

Curated knowledge about the pkcore poker library — start with
[Getting started](getting-started.md).

# Root Concepts

* [Getting started](getting-started.md) - How to navigate this bundle and where each kind of pkcore knowledge lives.
* [pkcore crate](crate.md) - Core poker library — cards, hand evaluation, equity, GTO analysis, bots, and full game simulation.
* [Cactus Kev lookup core](cactus-kev-lookup.md) - How pkcore packs cards into u32 values and evaluates 5-card hands via prime products and embedded lookup tables.
* [GTO combos](gto-combos.md) - How poker range notation strings parse into combo sets and how hero blockers prune villain combinations.
* [PLO rules](plo-rules.md) - PLO's exactly-2-hole / exactly-3-board evaluation rule, the 60-combination loop, and its zero-allocation constraints.
* [Stud rules](stud-rules.md) - Stud's five boardless streets, ante + bring-in forced bets, visible-hand action ordering, and its free showdown via the standard

Details

Kind
OKF bundles
Topic
Media, design & games
Publisher
imperialbower
Origin
okf_github
Category
dados
Version
0.1
Stars
6
Forks
1
Last push
2026-09-05T01:58:43Z
Repository state
ativo
Language
Rust
License
Apache-2.0
Added
2026-09-09 05:03:59
Updated
2026-09-09 05:03:59
Origin id
ImperialBower/pkcore:.okf/index.md

README

# pkcore

[![Crates.io](https://img.shields.io/crates/v/pkcore.svg)](https://crates.io/crates/pkcore)
[![CI](https://github.com/ImperialBower/pkcore/actions/workflows/basic.yaml/badge.svg)](https://github.com/ImperialBower/pkcore/actions/workflows/basic.yaml)
[![Security audit](https://github.com/ImperialBower/pkcore/actions/workflows/audit.yml/badge.svg)](https://github.com/ImperialBower/pkcore/actions/workflows/audit.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE-MIT)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE-APACHE)
[![AI BOM](https://img.shields.io/badge/AI--BOM-tracked-blueviolet)](./AI-BOM.md)

🚧 **Work In Progress** 🚧

[Rust](https://www.rust-lang.org/) poker library. Code inspired by [Cactus Kev's](https://suffe.cool)
[work in C](https://suffe.cool/poker/code/). An isolated version of the core hand evaluation library is available at [ckc-rs](https://github.com/ContractBridge/ckc-rs).

Supports No-Limit and Fixed-Limit [Hold'em](https://en.wikipedia.org/wiki/Texas_hold_%27em),
[Pot-Limit Omaha](https://en.wikipedia.org/wiki/Omaha_hold_%27em), Seven-Card Stud Hi, and
[Razz](https://en.wikipedia.org/wiki/Razz_(poker)), with more variants planned.

This code is a complete rewrite from scratch of my [Fudd](https://github.com/ImperialBower/fudd) crate. Changes:

* Folded [ckc-rs](https://github.com/ContractBridge/ckc-rs) crate into the repo.
* ~~Folded [wincounter](https://github.com/ImperialBower/wincounter) crate into the repo.~~ 
* ~~Removed [cardpack.rs](https://github.com/ImperialBower/cardpack.rs) dependency~~
  * Added it back in for some utility functions, but it's not a core dependency of the library.

## Setup

This program uses [cargo make](https://github.com/sagiegurari/cargo-make) to manage tasks. Install it with:

```shell
cargo install cargo-make
```

The default `cargo make` runs the following tasks:

* `cargo fmt`
* `cargo clean`
* `cargo build`
* `cargo test`
* `cargo clippy` with `clippy::pedantic` lint settings
* `cargo doc --no-deps`

```shell
❯ cargo make
````

To open the generated docs in your browser:

```shell
❯ cargo make docs
```

### Configuration

The optional SQLite HUP store reads its path from the `HUPS_DB_PATH` environment
variable (default: `generated/hups.db`). Export it in your shell before running
the examples that use the store:

```shell
export HUPS_DB_PATH=generated/hups.db
```

(`pkcore` no longer loads a `.env` file — the `dotenvy` dependency was dropped in
0.2.0, so set the variable in your environment directly.)

## Data files

Most of `pkcore` works out of the box from a plain `cargo add pkcore`: hand evaluation,
heads-up preflop odds, and the equity engine all rely on lookup tables that ship embedded in
the crate. A couple of APIs are backed by large, self-generated data files that are **not**
published — those APIs report the absence explicitly instead of panicking.

| API surface | Backing data | Ships in the crate? | How to enable |
| --- | --- | --- | --- |
| 5/7-card evaluation, equity engine | `src/lookups/*` | Yes | works out of the box |
| Heads-up preflop odds / GTO | `generated/hups.bin` (embedded) | Yes | works out of the box |
| BCM APIs (`SortedHeadsUp::wins`, `StartingHands` case evals) | `generated/bcm.zst` (~403 MB) | No | self-generate with `SevenFiveBCM::generate_bin(path)` and point `PKCORE_75BCM_PATH` at it — otherwise the calls return `Err(PKError::BcmUnavailable)` |
| Distinct 5-card hand enumeration (`UNIQUE_HANDS`) | `generated/5card_distinct_hands.txt` | No | historical generator input; build with `--features generators` and self-generate the file |

## Documentation

For comprehensive API documentation, run:

```shell
cargo doc --open
````

## Examples

### The Hand

The best way to see the library in action is to run the example file that does a step by step of my favorite hand
of broadcast television poker: "The Hand" between Daniel Negreanu and Gus Hansen on High Stakes Poker. You can
watch it here on [YouTube](https://www.youtube.com/watch?v=vjM60lqRhPg).

`cargo run --example the_hand`

### [cck](examples/cck.rs)

[cck](examples/cck.rs) is a simple command line program that prints out a poker hand's
[Cactus Kev](https://suffe.cool/poker/evaluator.html) value.

```shell
❯ cargo run --example cck -- -c "AS 9C KS QS 2D JS TS"                                               ✔ ▓▒░
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
     Running `target/debug/examples/cck -c 'AS 9C KS QS 2D JS TS'`
CARDS: A♠ 9♣ K♠ Q♠ 2♦ J♠ T♠ - BEST HAND: A♠ K♠ Q♠ J♠ T♠ - 1: RoyalFlush
{TEN: 1, KING: 1, JACK: 1, QUEEN: 1, ACE: 1}
Elapsed: 2.45ms

❯ cargo run --example cck -- -c "7S 9C JS 3S 2D 7S 4C"                                               ✔ ▓▒░
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s
     Running `target/debug/examples/cck -c '7S 9C JS 3S 2D 7S 4C'`
CARDS: 7♠ 9♣ J♠ 3♠ 2♦ 4♣ - BEST HAND: J♠ 9♣ 7♠ 4♣ 3♠ - 7294: JackHigh
{NINE: 1, SEVEN: 1, JACK: 1, TREY: 1, FOUR: 1}
Elapsed: 2.16ms

```


### [calc](examples/calc.rs)

[calc](examples/calc.rs) allows you to do a breakdown of the odds of a specific hand
of poker. Here it is running [the famous hand](https://www.youtube.com/watch?v=vjM60lqRhPg) quads vs full
house between Gus Hansen and Daniel Negreanu on High Stakes Poker:

```shell
❯ cargo run --example calc -- -d "6s 6h 5d 5c" -b "9c 6d 5h 5d 8d"
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s
     Running `target/debug/examples/calc -d '6s 6h 5d 5c' -b '9c 6d 5h 5d 8d'`
DEALT: [6♠ 6♥, 5♦ 5♣] FLOP: 9♣ 6♦ 5♥, TURN: 5♦, RIVER: 8♦

The Flop: 9♣ 6♦ 5♥
  Player #1 [6♠ 6♥] 95.7% (94.04%/1.62%) [931/16]
     6♠ 6♥ 6♦ 9♣ 5♥ (2185-ThreeSixes)
  Player #2 [5♦ 5♣] 6.0% (4.34%/1.62%) [43/16]
     5♥ 5♦ 5♣ 9♣ 6♦ (2251-ThreeFives)

The Turn: 5♦
  Player #1 [6♠ 6♥] 97.8% (97.78%/0.00%) [44/0]
    HAND: 6♠ 6♥ 6♦ 5♥ 5♦ (271-SixesOverFives)
  Player #2 [5♦ 5♣] 2.2% (2.22%/0.00%) [1/0]
    HAND: 5♥ 5♦ 5♣ 9♣ 6♦ (2251-ThreeFives)
    OUTS: 5♠

The River: 8♦
 Winning Hand: 271-SixesOverFives
   Player #1: 6♠ 6♥ 6♦ 5♥ 5♦ - 271-SixesOverFives WINS!
   Player #2: 5♥ 5♦ 5♣ 9♣ 8♦ - 2249-ThreeFives

cargo run --example calc -- -d  "6♠ 6♥ 5♦ 5♣" -b "9♣ 6♦ 5♥ 5♦ 8♦"
Elapsed: 467.50ms
```

Add the -n flag and it will add all possible hands at the flop, sorted by strength:

```shell
❯ cargo run --example calc -- -d "6s 6h 5d 5c" -b "9c 6d 5h 5d 8d" -n
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/examples/calc -d '6s 6h 5d 5c' -b '9c 6d 5h 5d 8d' -n`
DEALT: [6♠ 6♥, 5♦ 5♣] FLOP: 9♣ 6♦ 5♥, TURN: 5♦, RIVER: 8♦

The Flop: 9♣ 6♦ 5♥
  Player #1 [6♠ 6♥] 95.7% (94.04%/1.62%) [931/16]
     6♠ 6♥ 6♦ 9♣ 5♥ (2185-ThreeSixes)
  Player #2 [5♦ 5♣] 6.0% (4.34%/1.62%) [43/16]
     5♥ 5♦ 5♣ 9♣ 6♦ (2251-ThreeFives)

The Nuts @ Flop:
  #1: 9♣ 8♠ 7♠ 6♦ 5♥ - 1605-NineHighStraight
  #2: 9♠ 9♥ 9♣ 6♦ 5♥ - 1996-ThreeNines
  #3: 6♠ 6♥ 6♦ 9♣ 5♥ - 2185-ThreeSixes
  #4: 5♠ 5♥ 5♦ 9♣ 6♦ - 2251-ThreeFives
  #5: 9♠ 9♣ 6♠ 6♦ 5♥ - 3047-NinesAndSixes
  #6: 9♠ 9♣ 5♠ 5♥ 6♦ - 3058-NinesAndFives
  #7: 6♠ 6♦ 5♠ 5♥ 9♣ - 3221-SixesAndFives
  #8: A♠ A♥ 9♣ 6♦ 5♥ - 3501-PairOfAces
  #9: K♠ K♥ 9♣ 6♦ 5♥ - 3721-PairOfKings
  #10: Q♠ Q♥ 9♣ 6♦ 5♥ - 3941-PairOfQueens
  #11: J♠ J♥ 9♣ 6♦ 5♥ - 4161-PairOfJacks
  #12: T♠ T♥ 9♣ 6♦ 5♥ - 4381-PairOfTens
  #13: 9♠ 9♣ A♠ 6♦ 5♥ - 4471-PairOfNines
  #14: 8♠ 8♥ 9♣ 6♦ 5♥ - 4836-PairOfEights
  #15: 7♠ 7♥ 9♣ 6♦ 5♥ - 5056-PairOfSevens
  #16: 6♠ 6♦ A♠ 9♣ 5♥ - 5122-PairOfSixes
  #17: 5♠ 5♥ A♠ 9♣ 6♦ - 5342-PairOfFives
  #18: 4♠ 4♣ 9♣ 6♦ 5♥ - 5720-PairOfFours
  #19: 3♠ 3♥ 9♣ 6♦ 5♥ - 5940-PairOfTreys
  #20: 2♠ 2♥ 9♣ 6♦ 5♥ - 6160-PairOfDeuces
  #21: A♠ K♠ 9♣ 6♦ 5♥ - 6305-AceHigh
  #22: K♠ Q♠ 9♣ 6♦ 5♥ - 6753-KingHigh
  #23: Q♠ J♠ 9♣ 6♦ 5♥ - 7046-QueenHigh
  #24: J♠ T♠ 9♣ 6♦ 5♥ - 7227-JackHigh
  #25: T♠ 9♣ 8♠ 6♦ 5♥ - 7346-TenHigh
  #26: 9♣ 8♠ 6♦ 5♥ 4♠ - 7420-NineHigh

The Turn: 5♦
  Player #1 [6♠ 6♥] 97.8% (97.78%/0.00%) [44/0]
    HAND: 6♠ 6♥ 6♦ 5♥ 5♦ (271-SixesOverFives)
  Player #2 [5♦ 5♣] 2.2% (2.22%/0.00%) [1/0]
    HAND: 5♥ 5♦ 5♣ 9♣ 6♦ (2251-ThreeFives)
    OUTS: 5♠

The River: 8♦
 Winning Hand: 271-SixesOverFives
   Player #1: 6♠ 6♥ 6♦ 5♥ 5♦ - 271-SixesOverFives WINS!
   Player #2: 5♥ 5♦ 5♣ 9♣ 8♦ - 2249-ThreeFives

cargo run --example calc -- -d  "6♠ 6♥ 5♦ 5♣" -b "9♣ 6♦ 5♥ 5♦ 8♦"
Elapsed: 484.90ms
```

### GTO

Here's an example of a preliminary GTO combo string calculator that shows how a player would fare against an opponent
with a common opening range. You can find a pure client side, wasm based version of this demo @
https://imperialbower.github.io/pkgto-web/ .

```rust
use clap::Parser;
use pkcore::prelude::*;
use std::str::FromStr;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
  #[clap(short = 'p', long)]
  player: String,

  #[clap(short = 'v', long)]
  villain: String,

  #[clap(short = 'b', long, required = false)]
  board: Option<Board>,

  #[clap(short = 'n', long)]
  nuts: bool,
}

/// `cargo run -- -p "K♠ K♥" -v "66+,AJs+,KQs,AJo+,KQo"`
fn main() -> Result<(), PKError> {
  let now = std::time::Instant::now();
  env_logger::init();

  let args = Args::parse();

  let solver: Versus;

  if let Some(board) = args.board {
    solver = Versus::new_with_board(Two::from_str(&args.player)?, Combos::from_str(&args.villain)?, board);
  } else {
    solver = Versus::new(Two::from_str(&args.player)?, Combos::from_str(&args.villain)?);
  }

  println!("{solver}");
  println!();
  println!("Villain combos before your blockers:");
  println!("{}", solver.villain.combo_pairs());

  println!();

  println!("Villain combos after your blockers:");
  println!("{}", solver.combo_pairs());
  println!();

  let hups = solver.hups_at_deal();

  println!("Odds per hand matchup:");
  for key in hups.keys() {
    println!("{}", hups.get(key).unwrap());
  }

  let results = Versus::combined_odds_at_deal(&hups.values().collect::<Vec<&HUPResult>>());
  println!();
  println!("Consolidated odds:");
  println!("{results}");

  if solver.has_board() {
    let games = solver.games_at_flop();
    for game in &games {
      let fe = FlopEval::try_from(game.clone()).unwrap();
      println!("{fe}");
      println!("{}", WinLoseDraw::from(fe));
    }
    println!("FLOP: {}", solver.combined_odds_at_flop());
    println!("TURN: {}", solver.combined_odds_at_turn());
  }

  println!();
  println!("Elapsed: {:.2?}", now.elapsed());
  Ok(())
}
```

produces:

```shell
❯ cargo run -- -p "K♠ K♥" -v "66+,AJs+,KQs,AJo+,KQo"                                                                                                      ─╯
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/gto -p 'K♠ K♥' -v 66+,AJs+,KQs,AJo+,KQo`
Solver { hero: K♠ K♥, villain: 66+, KQs, AJs+, AJo+, KQo }

Villain combos before your blockers:
 AA  6 of  6: A♠ A♥, A♠ A♦, A♠ A♣, A♥ A♦, A♥ A♣, A♦ A♣
AKs  4 of  4: A♠ K♠, A♥ K♥, A♦ K♦, A♣ K♣
AKo 12 of 12: A♠ K♥, A♠ K♦, A♠ K♣, A♥ K♠, A♥ K♦, A♥ K♣, A♦ K♠, A♦ K♥, A♦ K♣, A♣ K♠, A♣ K♥, A♣ K♦
AQs  4 of  4: A♠ Q♠, A♥ Q♥, A♦ Q♦, A♣ Q♣
AQo 12 of 12: A♠ Q♥, A♠ Q♦, A♠ Q♣, A♥ Q♠, A♥ Q♦, A♥ Q♣, A♦ Q♠, A♦ Q♥, A♦ Q♣, A♣ Q♠, A♣ Q♥, A♣ Q♦
AJs  4 of  4: A♠ J♠, A♥ J♥, A♦ J♦, A♣ J♣
AJo 12 of 12: A♠ J♥, A♠ J♦, A♠ J♣, A♥ J♠, A♥ J♦, A♥ J♣, A♦ J♠, A♦ J♥, A♦ J♣, A♣ J♠, A♣ J♥, A♣ J♦
 KK  6 of  6: K♠ K♥, K♠ K♦, K♠ K♣, K♥ K♦, K♥ K♣, K♦ K♣
KQs  4 of  4: K♠ Q♠, K♥ Q♥, K♦ Q♦, K♣ Q♣
KQo 12 of 12: K♠ Q♥, K♠ Q♦, K♠ Q♣, K♥ Q♠, K♥ Q♦, K♥ Q♣, K♦ Q♠, K♦ Q♥, K♦ Q♣, K♣ Q♠, K♣ Q♥, K♣ Q♦
 QQ  6 of  6: Q♠ Q♥, Q♠ Q♦, Q♠ Q♣, Q♥ Q♦, Q♥ Q♣, Q♦ Q♣
 JJ  6 of  6: J♠ J♥, J♠ J♦, J♠ J♣, J♥ J♦, J♥ J♣, J♦ J♣
 TT  6 of  6: T♠ T♥, T♠ T♦, T♠ T♣, T♥ T♦, T♥ T♣, T♦ T♣
 99  6 of  6: 9♠ 9♥, 9♠ 9♦, 9♠ 9♣, 9♥ 9♦, 9♥ 9♣, 9♦ 9♣
 88  6 of  6: 8♠ 8♥, 8♠ 8♦, 8♠ 8♣, 8♥ 8♦, 8♥ 8♣, 8♦ 8♣
 77  6 of  6: 7♠ 7♥, 7♠ 7♦, 7♠ 7♣, 7♥ 7♦, 7♥ 7♣, 7♦ 7♣
 66  6 of  6: 6♠ 6♥, 6♠ 6♦, 6♠ 6♣, 6♥ 6♦, 6♥ 6♣, 6♦ 6♣


Villain combos after your blockers:
 AA  6 of  6: A♠ A♥, A♠ A♦, A♠ A♣, A♥ A♦, A♥ A♣, A♦ A♣
AKs  2 of  4: A♦ K♦, A♣ K♣
AKo  6 of 12: A♠ K♦, A♠ K♣, A♥ K♦, A♥ K♣, A♦ K♣, A♣ K♦
AQs  4 of  4: A♠ Q♠, A♥ Q♥, A♦ Q♦, A♣ Q♣
AQo 12 of 12: A♠ Q♥, A♠ Q♦, A♠ Q♣, A♥ Q♠, A♥ Q♦, A♥ Q♣, A♦ Q♠, A♦ Q♥, A♦ Q♣, A♣ Q♠, A♣ Q♥, A♣ Q♦
AJs  4 of  4: A♠ J♠, A♥ J♥, A♦ J♦, A♣ J♣
AJo 12 of 12: A♠ J♥, A♠ J♦, A♠ J♣, A♥ J♠, A♥ J♦, A♥ J♣, A♦ J♠, A♦ J♥, A♦ J♣, A♣ J♠, A♣ J♥, A♣ J♦
 KK  1 of  6: K♦ K♣
KQs  2 of  4: K♦ Q♦, K♣ Q♣
KQo  6 of 12: K♦ Q♠, K♦ Q♥, K♦ Q♣, K♣ Q♠, K♣ Q♥, K♣ Q♦
 QQ  6 of  6: Q♠ Q♥, Q♠ Q♦, Q♠ Q♣, Q♥ Q♦, Q♥ Q♣, Q♦ Q♣
 JJ  6 of  6: J♠ J♥, J♠ J♦, J♠ J♣, J♥ J♦, J♥ J♣, J♦ J♣
 TT  6 of  6: T♠ T♥, T♠ T♦, T♠ T♣, T♥ T♦, T♥ T♣, T♦ T♣
 99  6 of  6: 9♠ 9♥, 9♠ 9♦, 9♠ 9♣, 9♥ 9♦, 9♥ 9♣, 9♦ 9♣
 88  6 of  6: 8♠ 8♥, 8♠ 8♦, 8♠ 8♣, 8♥ 8♦, 8♥ 8♣, 8♦ 8♣
 77  6 of  6: 7♠ 7♥, 7♠ 7♦, 7♠ 7♣, 7♥ 7♦, 7♥ 7♣, 7♦ 7♣
 66  6 of  6: 6♠ 6♥, 6♠ 6♦, 6♠ 6♣, 6♥ 6♦, 6♥ 6♣, 6♦ 6♣


Odds per hand matchup:
K♠ K♥ 71.32% (1221273) A♠ J♥ 28.19% (482674) ties: 0.49% (8357)
K♠ K♥ 90.96% (1557553) K♣ Q♥ 7.78% (133242) ties: 1.26% (21509)
K♠ K♥ 71.21% (1219342) A♥ Q♣ 28.37% (485735) ties: 0.42% (7227)
K♠ K♥ 71.21% (1219342) A♠ Q♦ 28.37% (485735) ties: 0.42% (7227)
K♠ K♥ 90.14% (1543487) K♣ Q♦ 8.70% (148916) ties: 1.16% (19901)
K♠ K♥ 90.14% (1543487) K♦ Q♣ 8.70% (148916) ties: 1.16% (19901)
K♠ K♥ 81.70% (1398993) Q♥ Q♦ 17.83% (305351) ties: 0.46% (7960)
K♠ K♥ 80.93% (1385813) T♥ T♦ 18.66% (319499) ties: 0.41% (6992)
K♠ K♥ 69.38% (1187967) A♥ K♣ 29.74% (509318) ties: 0.88% (15019)
K♠ K♥ 80.62% (1380450) 9♠ 9♣ 18.99% (325103) ties: 0.39% (6751)
K♠ K♥ 71.95% (1231930) A♦ Q♠ 27.63% (473146) ties: 0.42% (7228)
K♠ K♥ 71.32% (1221273) A♥ J♠ 28.19% (482674) ties: 0.49% (8357)
K♠ K♥ 71.72% (1228029) A♥ Q♠ 27.78% (475675) ties: 0.50% (8600)
K♠ K♥ 79.68% (1364328) 7♦ 7♣ 20.05% (343300) ties: 0.27% (4676)
K♠ K♥ 80.31% (1375234) 6♠ 6♦ 19.33% (330994) ties: 0.35% (6076)
K♠ K♥ 80.31% (1375234) 6♠ 6♣ 19.33% (330994) ties: 0.35% (6076)
K♠ K♥ 80.31% (1375234) 6♥ 6♦ 19.33% (330994) ties: 0.35% (6076)
K♠ K♥ 90.96% (1557553) K♦ Q♠ 7.78% (133242) ties: 1.26% (21509)
K♠ K♥ 81.32% (1392403) J♠ J♦ 18.25% (312425) ties: 0.44% (7476)
K♠ K♥ 79.70% (1364648) 6♦ 6♣ 20.03% (342914) ties: 0.28% (4742)
K♠ K♥ 71.95% (1231930) A♦ Q♥ 27.63% (473146) ties: 0.42% (7228)
K♠ K♥ 68.19% (1167667) A♥ Q♥ 31.33% (536525) ties: 0.47% (8112)
K♠ K♥ 85.54% (1464659) K♦ Q♦ 13.27% (227282) ties: 1.19% (20363)
K♠ K♥ 82.35% (1410122) Q♠ Q♥ 17.10% (292846) ties: 0.55% (9336)
K♠ K♥ 18.55% (317694) A♦ A♣ 81.06% (1388072) ties: 0.38% (6538)
K♠ K♥ 80.62% (1380450) 9♠ 9♦ 18.99% (325103) ties: 0.39% (6751)
K♠ K♥ 80.68% (1381504) 8♠ 8♦ 18.94% (324287) ties: 0.38% (6513)
K♠ K♥ 71.44% (1223243) A♣ Q♦ 28.22% (483206) ties: 0.34% (5855)
K♠ K♥ 67.68% (1158934) A♦ Q♦ 31.92% (546637) ties: 0.39% (6733)
K♠ K♥ 81.32% (1392403) J♥ J♣ 18.25% (312425) ties: 0.44% (7476)
K♠ K♥ 81.57% (1396658) T♠ T♥ 17.95% (307282) ties: 0.49% (8364)
K♠ K♥ 80.05% (1370776) 8♦ 8♣ 19.65% (336398) ties: 0.30% (5130)
K♠ K♥ 81.70% (1398993) Q♥ Q♣ 17.83% (305351) ties: 0.46% (7960)
K♠ K♥ 70.82% (1212727) A♠ J♣ 28.77% (492592) ties: 0.41% (6985)
K♠ K♥ 71.56% (1225273) A♣ J♥ 28.04% (480045) ties: 0.41% (6986)
K♠ K♥ 80.30% (1374914) 7♠ 7♣ 19.35% (331347) ties: 0.35% (6043)
K♠ K♥ 80.93% (1385813) T♠ T♣ 18.66% (319499) ties: 0.41% (6992)
K♠ K♥ 2.17% (37210) K♦ K♣ 2.17% (37210) ties: 95.65% (1637884)
K♠ K♥ 80.62% (1380450) 9♥ 9♦ 18.99% (325103) ties: 0.39% (6751)
K♠ K♥ 80.93% (1385820) 6♠ 6♥ 18.63% (319074) ties: 0.43% (7410)
K♠ K♥ 71.06% (1216727) A♦ J♣ 28.61% (489963) ties: 0.33% (5614)
K♠ K♥ 69.61% (1191993) A♣ K♦ 29.60% (506801) ties: 0.79% (13510)
K♠ K♥ 80.68% (1381504) 8♥ 8♦ 18.94% (324287) ties: 0.38% (6513)
K♠ K♥ 67.33% (1152901) A♣ J♣ 32.29% (552858) ties: 0.38% (6545)
K♠ K♥ 81.32% (1392403) J♠ J♣ 18.25% (312425) ties: 0.44% (7476)
K♠ K♥ 67.83% (1161493) A♥ J♥ 31.71% (542888) ties: 0.46% (7923)
K♠ K♥ 71.95% (1231930) A♣ Q♠ 27.63% (473146) ties: 0.42% (7228)
K♠ K♥ 80.62% (1380450) 9♥ 9♣ 18.99% (325103) ties: 0.39% (6751)
K♠ K♥ 65.48% (1121238) A♦ K♦ 33.69% (576944) ties: 0.82% (14122)
K♠ K♥ 90.96% (1557553) K♦ Q♥ 7.78% (133242) ties: 1.26% (21509)
K♠ K♥ 80.93% (1385813) T♥ T♣ 18.66% (319499) ties: 0.41% (6992)
K♠ K♥ 80.30% (1374968) T♦ T♣ 19.37% (331716) ties: 0.33% (5620)
K♠ K♥ 80.93% (1385813) T♠ T♦ 18.66% (319499) ties: 0.41% (6992)
K♠ K♥ 80.68% (1381504) 8♥ 8♣ 18.94% (324287) ties: 0.38% (6513)
K♠ K♥ 80.30% (1374914) 7♠ 7♦ 19.35% (331347) ties: 0.35% (6043)
K♠ K♥ 81.25% (1391172) 9♠ 9♥ 18.28% (313002) ties: 0.47% (8130)
K♠ K♥ 69.38% (1187967) A♠ K♣ 29.74% (509318) ties: 0.88% (15019)
K♠ K♥ 79.99% (1369728) 9♦ 9♣ 19.69% (337204) ties: 0.31% (5372)
K♠ K♥ 80.68% (1381504) 8♠ 8♣ 18.94% (324287) ties: 0.38% (6513)
K♠ K♥ 80.30% (1374914) 7♥ 7♦ 19.35% (331347) ties: 0.35% (6043)
K♠ K♥ 71.21% (1219342) A♥ Q♦ 28.37% (485735) ties: 0.42% (7227)
K♠ K♥ 17.82% (305177) A♥ A♦ 81.71% (1399204) ties: 0.46% (7923)
K♠ K♥ 81.70% (1398993) Q♠ Q♦ 17.83% (305351) ties: 0.46% (7960)
K♠ K♥ 81.70% (1398993) Q♠ Q♣ 17.83% (305351) ties: 0.46% (7960)
K♠ K♥ 67.33% (1152901) A♦ J♦ 32.29% (552858) ties: 0.38% (6545)
K♠ K♥ 81.32% (1392403) J♥ J♦ 18.25% (312425) ties: 0.44% (7476)
K♠ K♥ 71.06% (1216727) A♣ J♦ 28.61% (489963) ties: 0.33% (5614)
K♠ K♥ 69.38% (1187967) A♠ K♦ 29.74% (509318) ties: 0.88% (15019)
K♠ K♥ 65.48% (1121238) A♣ K♣ 33.69% (576944) ties: 0.82% (14122)
K♠ K♥ 80.31% (1375234) 6♥ 6♣ 19.33% (330994) ties: 0.35% (6076)
K♠ K♥ 17.82% (305177) A♠ A♣ 81.71% (1399204) ties: 0.46% (7923)
K♠ K♥ 71.56% (1225273) A♦ J♥ 28.04% (480045) ties: 0.41% (6986)
K♠ K♥ 80.30% (1374914) 7♥ 7♣ 19.35% (331347) ties: 0.35% (6043)
K♠ K♥ 80.68% (1381416) J♦ J♣ 18.97% (324786) ties: 0.36% (6102)
K♠ K♥ 81.96% (1403390) J♠ J♥ 17.52% (300064) ties: 0.52% (8850)
K♠ K♥ 70.82% (1212727) A♥ J♦ 28.77% (492592) ties: 0.41% (6985)
K♠ K♥ 80.91% (1385500) 7♠ 7♥ 18.65% (319394) ties: 0.43% (7410)
K♠ K♥ 90.96% (1557553) K♣ Q♠ 7.78% (133242) ties: 1.26% (21509)
K♠ K♥ 70.82% (1212727) A♥ J♣ 28.77% (492592) ties: 0.41% (6985)
K♠ K♥ 17.09% (292660) A♠ A♥ 82.36% (1410336) ties: 0.54% (9308)
K♠ K♥ 17.82% (305177) A♠ A♦ 81.71% (1399204) ties: 0.46% (7923)
K♠ K♥ 71.21% (1219342) A♠ Q♣ 28.37% (485735) ties: 0.42% (7227)
K♠ K♥ 70.82% (1212727) A♠ J♦ 28.77% (492592) ties: 0.41% (6985)
K♠ K♥ 17.82% (305177) A♥ A♣ 81.71% (1399204) ties: 0.46% (7923)
K♠ K♥ 71.56% (1225273) A♦ J♠ 28.04% (480045) ties: 0.41% (6986)
K♠ K♥ 67.83% (1161493) A♠ J♠ 31.71% (542888) ties: 0.46% (7923)
K♠ K♥ 71.95% (1231930) A♣ Q♥ 27.63% (473146) ties: 0.42% (7228)
K♠ K♥ 71.44% (1223243) A♦ Q♣ 28.22% (483206) ties: 0.34% (5855)
K♠ K♥ 81.05% (1387864) Q♦ Q♣ 18.56% (317856) ties: 0.38% (6584)
K♠ K♥ 81.31% (1392232) 8♠ 8♥ 18.23% (312176) ties: 0.46% (7896)
K♠ K♥ 85.54% (1464659) K♣ Q♣ 13.27% (227282) ties: 1.19% (20363)
K♠ K♥ 69.61% (1191993) A♦ K♣ 29.60% (506801) ties: 0.79% (13510)
K♠ K♥ 71.72% (1228029) A♠ Q♥ 27.78% (475675) ties: 0.50% (8600)
K♠ K♥ 68.19% (1167667) A♠ Q♠ 31.33% (536525) ties: 0.47% (8112)
K♠ K♥ 67.68% (1158934) A♣ Q♣ 31.92% (546637) ties: 0.39% (6733)
K♠ K♥ 71.56% (1225273) A♣ J♠ 28.04% (480045) ties: 0.41% (6986)
K♠ K♥ 69.38% (1187967) A♥ K♦ 29.74% (509318) ties: 0.88% (15019)

Consolidated odds:
72.40% (120246594), 26.11% (43363520), 1.50% (2483374)

Elapsed: 2.72s
```

### Bot Self-Play & Hand History Replay

Three examples cover bot simulation and session replay.  They require the
optional `bot-profiles` and/or `hand-histories` feature flags to be enabled.

**`bot_selfplay`** — all-bot simulation (8 profiles, up to 50 hands):

```shell
cargo run --features bot-profiles --example bot_selfplay
```

**`interactive_play`** — play against bots in a REPL; session saved to
`generated/*.yaml`:

```shell
cargo run --features bot-profiles,hand-histories --example interactive_play
```

**`replay_play`** — load a saved session and replay every hand through the
engine to verify consistency:

```shell
# replay most recent file in generated/
cargo run --features hand-histories --example replay_play

# replay a specific file
cargo run --features hand-histories --example replay_play -- generated/session.yaml
```

See [`docs/epics/EPIC-19_Bot_Self_Play-CLOSED.md`](docs/epics/EPIC-19_Bot_Self_Play-CLOSED.md) for the
full design.

## Performance

Performance is measured by a standalone harness in [`perf/`](perf/)
and publishes results to [`docs/perf/RESULTS.md`](docs/perf/RESULTS.md).

```shell
make perf-native      # measure the pure kernel, write docs/perf/results/
make perf-report      # regenerate docs/perf/RESULTS.md
make perf-bench       # the same nano workloads under Criterion and Divan
make perf-check       # lint + test the harness itself
```

The Criterion and Divan benches exist for comparison of performance techniques,
and the differences can be seen in the
[`docs/perf/HARNESS_COMPARISON.md`](docs/perf/HARNESS_COMPARISON.md). [`docs/perf/PROFILING.md`](docs/perf/PROFILING.md) documents
the allocation hunt that motivated the harness.

---

## TODO:

* Roadmap
* Clear release breakdowns.
* Consolidate card collection structs. We currently have:
  * [Bard](src/bard.rs) - binary representation of one or more [Cards](src/cards.rs)
  * [BoxedCards](src/arrays/sliced.rs) - Boxed slice of [Card](src/cards.rs) values.
  * [Cards](src/cards.rs) - IndexSet collection of unique [Card](src/cards.rs) values.
  * [Board](src/play/board.rs) - The community cards in a game of Hold'em and Omaha.
  * [HoleCards](src/play/hole_cards.rs) - The cards dealt to each player in Hold'em and Omaha.
  * Fixed array [Card](src/cards.rs) structs. Can these all be replaced by [BoxedCards](src/arrays/sliced.rs)?
    * [Deck](src/cards.rs)
    * [Two](src/arrays/two.rs)
    * [Three](src/arrays/three.rs)
    * [Four](src/arrays/four.rs)
    * [Five](src/arrays/five.rs)
    * [Six](src/arrays/six.rs)
    * [Seven](src/arrays/seven.rs)

## Value Stories

* I want a tool that will help me get better at [GTO](https://www.888poker.com/magazine/strategy/beginners-guide-gto-poker) style poker playing.
* I want a library that can be reused for poker applications.

## Resources

* Poker
  * [Mike Caro's Website](https://www.poker1.com/)
  * Solvers
    * [Pro Poker Tools Odds Oracle](http://www.propokertools.com/)
  * GTO
    * [Minimum Defense Frequency vs Pot Odds](https://upswingpoker.com/minimum-defense-frequency-vs-pot-odds/)
    * [A Beginner’s Guide to Poker Combinatorics](https://blog.gtowizard.com/a-beginners-guide-to-poker-combinatorics/)
  * Terms
    * [The Nuts](https://en.wikipedia.org/wiki/Nut_hand)
      * 888poker > [What is Nuts in Poker?](https://www.888poker.com/magazine/poker-terms/nuts)
      * GetMega > [Nuts in Poker](https://www.getmega.com/cards/poker/terms/nuts-in-poker/)
    * 888poker > [What is Texture in Poker?](https://www.888poker.com/magazine/poker-terms/texture)
  * AI
    * [Pluribus](https://en.wikipedia.org/wiki/Pluribus_(poker_bot))
      * [Superhuman AI for multiplayer poker](https://www.science.org/doi/10.1126/science.aay2400)
      * [pluribus-hand-parser](https://github.com/VitamintK/pluribus-hand-parser)
      * [Let's analyze Pluribus's Hands!](http://kevinwang.us/lets-analyze-pluribuss-hands/)
        * [reddit](https://www.reddit.com/r/poker/comments/cdhasb/download_all_10000_hands_that_pluribus_poker_ai/)
      * [fedden / poker_ai](https://github.com/fedden/poker_ai) - An Open Source Texas Hold'em AI
        * FORK: [pluribus-poker-AI(https://github.com/apcode/pluribus-poker-AI)
      * [Remembering Pluribus: The Techniques that Facebook Used to Master World’s Most Difficult Poker Game](https://www.kdnuggets.com/2020/12/remembering-pluribus-facebook-master-difficult-poker-game.html)
      * [PokerHandEvaluator](https://github.com/HenryRLee/PokerHandEvaluator)
      * Naom Brown: [5 years ago we revealed Pluribus](https://x.com/polynoamial/status/1816347598623834365?lang=en)
    * [Neuron Poker: OpenAi gym environment for texas holdem poker](https://github.com/dickreuter/neuron_poker)]
    * [clubs_gym](https://github.com/fschlatt/clubs_gym)
    * [PokerRL](https://github.com/EricSteinberger/PokerRL) Framework for Multi-Agent Deep Reinforcement Learning in Poker games.
    * [Best AI Poker Training Tools](https://thepokeragent.com/best-ai-poker-training-tools/)
  * Probability
    * Wikipedia > [Poker probability](https://en.wikipedia.org/wiki/Poker_probability)
    * [Distinct head-to-head match ups in holdem](https://poker.stackexchange.com/questions/5682/distinct-head-to-head-match-ups-in-holdem)
    * [Texas Hold’em Poker Odds (over 100 Poker Probabilities)](https://www.primedope.com/texas-holdem-poker-probabilities-odds/)
    * Heads up
      * [Mathmatrucker > Preflop High Hand Equity and Tie Percentages](https://www.mathematrucker.com/poker/matchups.php)
  * [Mental Poker](https://en.wikipedia.org/wiki/Mental_poker)
    * Implementations
      * [geometryxyz/mental-poker](https://github.com/geometryxyz/mental-poker)
      * [akonradi/mental-poker](https://github.com/akonradi/mental-poker)
      * [zkShuffle: Mental Poker on SNARK for Ethereum](https://hackmd.io/@ZDZ-B3ktQlOiBE4iqOXVlg/BJA7Zoqns)
      * [LibTMCG](https://www.nongnu.org/libtmcg/) **LOTS OF GOOD RESOURCES HERE!!!**
  * Cheating
    * [FTX’s ‘chief regulatory officer’ Dan Friedberg tied to online poker scandal](https://nypost.com/2022/11/20/ftxs-ex-chief-regulatory-officer-tied-to-online-poker-scandal/)
  * Cool Resources
    * YouTube > [I created an AI to Play Poker](https://www.youtube.com/watch?v=MWRXx2saLw4)
      * The code: [Gongsta / Poker-AI](https://github.com/Gongsta/Poker-AI/)
      * Related Stuff
        * [Counterfactual regret minimization in Rust](https://github.com/erikbrinkman/cfr)
* Rust
  * [The Rust Programming Language](https://doc.rust-lang.org/stable/book/)
  * [Rust Design Patterns](https://rust-unofficial.github.io/patterns/intro.html)
  * [Are we game yet?](https://arewegameyet.rs/)
  * [Are we GUI Yet?](https://www.areweguiyet.com/)
  * [rustlings](https://github.com/rust-lang/rustlings)
  * frameworks
    * [Yew](https://yew.rs/)
    * [Flowistry: Information Flow for Rust](https://github.com/willcrichton/flowistry)
    * Graphic Libraries
      * [tui-rs](https://github.com/fdehau/tui-rs)
        * [Rust and TUI: Building a command-line interface in Rust](https://blog.logrocket.com/rust-and-tui-building-a-command-line-interface-in-rust/)
      * [Crossterm](https://github.com/crossterm-rs/crossterm)
    * [Creating successful open source projects - with @orhunp - RustShip 1](https://www.youtube.com/watch?v=_xABF_H8b3g)
  * OTel
    * [tracing.rs](https://tracing.rs/tracing/) [GitHub](https://github.com/tokio-rs/tracing)
      * [tracing-test](https://docs.rs/tracing-test/latest/tracing_test/)
  * articles
    * [Rust Is Hard, Or: The Misery of Mainstream Programming](https://hirrolot.github.io/posts/rust-is-hard-or-the-misery-of-mainstream-programming.html)
    * [Rust: Your code can be perfect](https://www.youtube.com/watch?v=IA4q0lzmyfM)
      * Probability
        * [How To Work Out Flop Probability In Texas Holdem](https://www.thepokerbank.com/tools/odds-charts/work-out-flop-probability/)
  * videos
    * [Poker Out Loud](https://solveforwhy.io/categories/poker-out-loud)
      * [Poker Our Loud Academy demo](https://www.youtube.com/watch?v=NpSDXJsej-o&t=634s)
      * [Great rant on stack sizes in 2022 WSOP](https://www.youtube.com/watch?v=a8801jTxt4Y&t=820s)
  * mobile
    * android
      * [Building pure Rust apps for Android](https://blog.traverseresearch.nl/building-pure-rust-apps-for-android-d1e388b431b8)
      * [Building and Deploying a Rust library on iOS](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html)
      * [Building and Deploying a Rust library on Android](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html)
      * [Running Rust on Android](https://blog.svgames.pl/article/running-rust-on-android)
  * concurrency
    * [Rayon](https://github.com/rayon-rs/rayon)
      * [How Rust makes Rayon's data parallelism magical](https://developers.redhat.com/blog/2021/04/30/how-rust-makes-rayons-data-parallelism-magical#generic_constraints_in_rayon)
      * [Implementing Rayon’s Parallel Iterators - A Tutorial](https://geo-ant.github.io/blog/2022/implementing-parallel-iterators-rayon/)
  * Macros
    * [The Little Book of Rust Macros](https://github.com/Veykril/tlborm.git)
    * [What Every Rust Developer Should Know About Macro Support in IDEs](https://blog.jetbrains.com/rust/2022/12/05/what-every-rust-developer-should-know-about-macro-support-in-ides/)
  * DBs
    * [SurrealDB](https://surrealdb.com/)
  * Code Coverage
    * [How to do code coverage in Rust](https://blog.rng0.io/how-to-do-code-coverage-in-rust)
* Video
  * [Cloudinary's image overlay feature](https://cloudinary.com/documentation/video_manipulation_and_delivery#adding_image_overlays)
  * [Programmatically add 100s of image overlays on video clip](https://stackoverflow.com/questions/18750525/programatically-add-100s-of-image-overlays-on-video-clip)
  * open source
    * [How to Add Graphics and Overlays to Live Video With Open Broadcaster (OBS)](https://photography.tutsplus.com/tutorials/how-to-add-custom-graphics-obs-studio--cms-35066)
* Programming
  * [The Grug Brained Developer](https://grugbrain.dev/)
  * GUI
    * [Ratatui](https://github.com/tui-rs-revival/ratatui)

## License

Licensed under either of

* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
  <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
  <http://opensource.org/licenses/MIT>)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

## Dependencies

* [assert-struct](https://github.com/carllerche/assert-struct)
* [bitvec](https://github.com/ferrilab/bitvec)
* [Burnt Sushi CSV](https://github.com/BurntSushi/rust-csv) with Serde support
* [itertools](https://github.com/rust-itertools/itertools)
* [Serde](https://serde.rs/)
  * [Serde JSON](https://github.com/serde-rs/json)
  * [Serde YAML](https://github.com/dtolnay/serde-yaml)
* [Termion](https://github.com/redox-os/termion)

## Potential Libraries

* [derive_more](https://github.com/JelteF/derive_more) (Recommended by Rust Power Tools)
* [mycelium-bitfield](https://crates.io/crates/mycelium-bitfield)
* [modular-bitfield](https://crates.io/crates/modular-bitfield)
* [RustyLine](https://github.com/kkawakam/rustyline)
* [sled](https://github.com/spacejam/sled)
* SQLite
  * [rusqlite](https://github.com/rusqlite/rusqlite)
    * [Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html)
    * [In-Memory Databases](https://www.sqlite.org/inmemorydb.html)
* UI
  * [Ratatui](https://github.com/ratatui-org/ratatui)
  * [shadcn/ui](https://ui.shadcn.com/)
    * [This UI Library is NEXT LEVEL](https://www.youtube.com/watch?v=dD1fpoGHuC8&t=29s)

## TMI

This library was intended to be the example code for a book tentatively titled _Rust for Failures_. Here's the intro:

Motto

Failure is a gift.

I am a failure. I've been a failure all my life. I’ve dropped out of college three times. I graduated from high school with a D average. I’ve lost every professional French Horn audition I’ve ever taken.
(I did win first horn at the California State Honor Band, but all the other horn players were so mad that I'd won that didn't talk to me, so I was miserable the whole time.) For a period of time I was homeless. (Ask me about sleeping in the San Francisco Symphony's Davies Symphony Hall musician's lounge.)

In the fifth grade, I got the silver medal for having the second-worst car in the Cub Scouts Pinewood Derby. I would have been last, but the Scout Master from a rival troupe took
the wheel that had fallen off from one of his kids' cars, hammered it back onto the car, and stole first from me by deliberately creating a car that wouldn’t roll. I even failed at failure. I was the worst. I should have gotten the gold, but no, I was the second worst. If you’re not last, you’re…?

There’s only one thing that I’ve ever really felt like a master of. Nobody ran the early morning solo coffee rush at a 7-11 better than me. At the Colma 7-11 I kept all five
pots of coffee humming, making sure to keep them in a steady rotation, so that those smart-asses who tried to get the fresher pot of coffee by grabbing the one in the
back were actually getting the oldest one. Not that it mattered. They were always fresh as fuck. A regular walked in, and I had their three packs of Benson & Hedges Ultra
Lights waiting for them at the register before they even asked. I was the shit, and all of my customers knew it, and were gracious to let me know, if in more family-friendly terms.

Then, one simple event solidified it all for me. A beautiful motor home drove into the parking lot, and out stepped a gentleman to buy a cup of coffee and a pack of gum.
We talked. He wanted to thank me. He was doing really well now. He was working in a restaurant in downtown San Francisco and had really turned his life around, all thanks to me! Great.

A year before, he was pointing a revolver at my face. He ran out with 204 dollars, only to be caught by the K-9 unit a few blocks away
shortly thereafter. This nimrod had fucking robbed me, and he was already doing better than me.
His life was on beautiful, and I was still that dumb schmuck making $9 an hour under the table, farting his life away at the Colma 7-11, where San Francisco buries its dead.

It wasn’t until I became a professional programmer that I realized failure is a gift IF you learn from it. By testing the shit out of the systems I build and practicing techniques
like test-driven development, I turned my ability to fail into a superpower. The faster you fail, the more you profit.

You see… it turns out that we’re all in the feedback business. You will get that feedback. It just depends on if you want to get it when you’re testing your tiles out in a controlled environment,
or if you want to find out when every kid in school is watching your [space shuttle explode with the first teacher in space](https://en.wikipedia.org/wiki/Space_Shuttle_Challenger_disaster). (I was one of those kids.)

More