Files
holdem_poker/CLAUDE.md
T
thejayman77 479be1f6b9 Initial commit: Hold'em engine, bots, and simulation harness
Kotlin Multiplatform engine (JVM target only for now; androidTarget and
iosArm64 slot in without touching commonMain).

Core:
- HandEvaluator: single-pass 5-7 card evaluation, ~24M evals/sec. Verified
  exhaustively against published frequencies for all 2,598,960 five-card hands.
- Equity: Monte Carlo with ties split. PreflopChart ranks the 169 starting
  hands using all-in equity plus an explicit playability adjustment, so
  looseness means "plays the top N%".
- Table: no-limit betting rounds, side pots, odd-chip splits, uncalled-bet
  refunds, and incomplete (short all-in) raises that correctly do not reopen
  betting.

Bots:
- SkillLevel and PlayStyle are orthogonal axes. Skill drives decision quality
  (rollout accuracy, pot-odds discipline, position awareness, error rate);
  style drives bluffing, sandbagging, aggression, tightness.
- BotMood gives tilt that persists between hands and decays.
- OpponentModel lets Advanced/Expert exploit habitual bettors.
- MathBot emits a DecisionTrace of the numbers behind each decision, which the
  coach will later hand to an LLM to narrate. The LLM never does poker maths.

Simulator:
- 2,200-3,400 hands/sec. Deck RNG is separate from bot RNGs so rollout counts
  cannot shift the deal.
- Controlled skill-ladder test asserts the difficulty gradient is monotonic:
  73.9 / 53.9 / 27.6 / -155.4 bb/100 over 50k hands.

Assets: 52 CC0 English-pattern card faces plus generated backs.

Tests: 30 passing (evaluator, table rules, pre-flop chart).

Known open: win-rate magnitudes ~10x realistic and several profiles looser
than their labels. Tuning, not correctness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 04:36:03 -04:00

3.1 KiB
Raw Blame History

Poker — Texas Hold'em with teachable AI opponents

Kotlin Multiplatform. Ships iOS + Android; Android first (only Android hardware for physical testing).

Build

No java/gradle on PATH — use Android Studio's bundled JDK:

export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
./gradlew :engine:jvmTest          # evaluator + engine tests
./gradlew :sim:run --args="50000"  # simulate 50k hands, print bot stats

Layout

Path What
engine/src/commonMain/.../core/ Cards, evaluator, equity, pre-flop chart
engine/src/commonMain/.../bot/ Skill/style profiles, MathBot
engine/src/commonMain/.../game/ Table — betting rounds, side pots, showdown
sim/ JVM-only headless simulator used to tune bot profiles
assets/cards/ 52 CC0 card faces + generated backs

engine is pure Kotlin with no platform APIs, so androidTarget() / iosArm64() slot in without touching commonMain.

Design rules

  1. Poker maths never goes near the LLM. Difficulty and style are engine-side EV/frequency calculations — instant, deterministic, testable, offline. The LLM only narrates numbers the engine already computed (DecisionTrace), and adds persona/table talk.
  2. Skill and style are orthogonal. SkillLevel = how correct decisions are; PlayStyle = bluffing, sandbagging, aggression, tightness. Build the strongest bot, then inject controlled error for lower tiers.
  3. Pre-flop is range-based, not equity-based. All-in equity overvalues trash (7-2o has ~35% vs one random hand but is unplayable). PreflopChart ranks the 169 starting hands so looseness means "plays the top N%".
  4. The simulator is how bots get tuned. Run it after any bot change; it prints a controlled skill-ladder test that must stay monotonic.

Testing notes

  • Table takes a CardSource, so StackedDeck.of(holes, board) gives fully deterministic hands. Use it for any rule test.
  • The simulator gives the deck its own RNG, separate from each bot's. Never share one: bots consume RNG proportional to their equityIterations, so a shared stream means changing a profile silently changes the cards dealt.
  • ./gradlew :sim:run --args="chart" dumps the starting-hand ranking.
  • Small samples lie. 1,000 hands is not enough to rank profiles — use 50,000+ before believing a gradient.

Status

  • Evaluator: verified exhaustively against published frequencies for all 2,598,960 five-card hands. ~24M evals/sec.
  • Engine: chip-conserving; side pots, odd-chip splits, uncalled-bet refunds, and incomplete (short all-in) raises all covered by tests.
  • Bots: skill gradient passes monotonically (73.9 / 53.9 / 27.6 / 155.4 bb/100 at 50k hands).
  • Known-imperfect: win-rate magnitudes are still ~10x realistic, and several profiles are looser than their labels (the Rock plays ~38% VPIP, should be ~12%). Tuning is the open work.
  • Gradle emits an archives deprecation from the Kotlin Multiplatform plugin's own jvm() target registration — upstream in Kotlin 2.2.10, not our build.
  • Not built yet: LLM persona layer, opt-in coach, Compose UI.