Skip to content

Add Omaha/PLO foundation: hand type, 2+3 evaluator, equity (Phase 1) - #12

Merged
testtest126 merged 5 commits into
mainfrom
feature/omaha-foundation
Jul 23, 2026
Merged

Add Omaha/PLO foundation: hand type, 2+3 evaluator, equity (Phase 1)#12
testtest126 merged 5 commits into
mainfrom
feature/omaha-foundation

Conversation

@testtest126

Copy link
Copy Markdown
Owner

Summary — Phase 1 of the variants/PLO track

  • OmahaHoleCards: the 4-card hand type, added alongside HoleCards — no existing NLHE model (ChenScore, every range model, Equity, ICM, GameFormat) reads or is affected by this type's existence. Canonical (order-independent) storage; explicit per-card notation ("AsKsAhKh") rather than an invented Hold'em-style shorthand — Omaha has no standardized compact notation the way "AKs"/"AKo" is standardized, and inventing one would be exactly the kind of judgment call this phase avoided.
  • OmahaHandEvaluator: enforces Omaha's defining rule — best 5-card hand uses exactly 2 of 4 hole cards + exactly 3 of 5 board cards. Enumerates all 60 legal combinations via the existing, unmodified HandEvaluator.bestHand(from:). Tests specifically prove the constraint is enforced (not just assumed): a four-aces hole hand that would look like quads/trips if the cap were ignored (must be exactly a pair), a four-spades hole hand that would look like a royal flush with only 1 board spade available (must be High Card), plus a positive counterpart proving a legal straight flush is still found when the split genuinely allows one.
  • OmahaEquity: same exact/Monte Carlo shape as Equity, built on the legal evaluator. No preflop exact (≈2x Hold'em's already-slow preflop exact case — documented, never called).
  • Minimal app screen (OmahaEquityCalculatorView, labeled "(Beta)"): explicit notation in/out, Fast/Precise modes, wired into StudyTool.

Validation — actual vs. published, and the hand-verifiable fallback

No single citable source with a precisely-stated PLO equity figure and fully-specified matchup was found (web search surfaced AAKK-vs-random figures ranging 64–73% depending on source, too wide a spread to validate a decimal against per this project's "a bad number is worse than none" rule). What was consistently repeated across independent sources (e.g. pokerlistings.com's "Pot-Limit Omaha: Top 30 Starting Hands"): AA-KK double-suited is only a 3-2 favorite (~60%) over a strong double-suited rundown like 8-7-6-5.

AAKKds ("AsKsAhKh") vs 8765ds ("8s7s6h5h"), 50,000 Monte Carlo iterations
  → win 61.96%, tie 0.00%, lose 38.04%

61.96% lands within 2 points of the cited ~60%.

Per this project's own fallback rule (pick a hand-verifiable spot when no solid citation exists), two exact tests carry the real correctness weight:

  • Hero holds the board's missing pair (locked quad sevens) vs. a villain with none of that rank → winRate == 1.0 exactly, provable by hand.
  • Mirrored-suit hands (same 4 ranks, different suits) on a suit-neutral board → tieRate == 1.0 exactly, a tie by symmetry, not coincidence.

What went wrong along the way (and got fixed at the root, not worked around)

  • A UI test intermittently failed with "Finish setting the board" even after the board notation was correctly typed. Root-caused via a debug print showing the board field's value had gained a stray trailing character — the Calculate button's tap was actually landing on the still-open keyboard underneath it, not the button. Fixed by properly dismissing the keyboard (typing a newline) before the next interaction, not by padding timeouts.
  • Separately found (and documented, not just tolerated): Omaha's flop-exact mode measured close to, and once over, a 30s timeout on a debug simulator build — meaningfully slower than Hold'em's own flop-exact (120 vs. 42 evaluations per board). The app's Precise mode still allows it (it does complete), but the UI test that proves Precise mode works uses a river-given board specifically so it isn't a flaky benchmark of that timing.

Test plan

  • swift test (PokerKit): 263/263 green (235 baseline + 28 new Omaha tests).
  • App build succeeds on simulator (iPhone 17, iOS 27.0, Xcode 27 beta).
  • App UI test suite: 26/26 green (excludes BankrollTrackerUITests.testLogSessionUpdatesListAndSummary, previously confirmed pre-existing/flaky on a clean main checkout, unrelated to this branch).
  • New OmahaHoleCardsTests, OmahaHandEvaluatorTests, OmahaEquityTests, OmahaEquityCalculatorUITests.

Proposed Phase 2 scope

PLO preflop hand-strength / starting-hand ranges — deliberately not started here. This is judgment-heavy in a way Phase 1 wasn't: Chen's heuristic doesn't extend to 4 cards, and the Omaha hand-strength heuristics that do exist in strategy literature are themselves competing/disputed, not an agreed standard the way Chen's is for Hold'em. Proposed approach (see ai-docs/OMAHA.md's "What Phase 2 should be" section): use OmahaEquity.monteCarlo equity-vs-random-hand itself as the ranking signal (this project's own exact math, not a second borrowed heuristic), validate it directionally against known strong/weak Omaha hands, then build push/fold-style thresholds on top with every percentage flagged as hand-tuned — same posture RANGES.md already takes for Hold'em.

🤖 Generated with Claude Code

Added alongside HoleCards, never replacing it — no existing NLHE model
reads or is affected by this type. Canonical (order-independent) storage,
explicit per-card notation (no invented Hold'em-style shorthand — Omaha has
no standardized one), and a descriptive (not parseable) suitPattern label.

Card(notation:) is a small new Card.swift-adjacent parsing helper, added in
this file rather than Card.swift itself, matching HoleCards.swift's own
precedent of hosting Rank.from(symbol:) alongside the type that needs it.
Enumerates the 60 legal (2-of-4-hole, 3-of-5-board) combinations via the
existing, unmodified HandEvaluator.bestHand(from:) and keeps the best.

Tests specifically prove the constraint is enforced, not assumed: a
four-aces hole hand that could look like quads/trips if the 2-card cap were
ignored (must be exactly a pair), and a four-spades hole hand that could
look like a royal flush (must be High Card, since only 1 board spade is
available) — plus a positive counterpart proving a legal straight flush is
still found when the split genuinely allows one, and a brute-force
cross-check of the 60-combination enumeration itself.
Same two-mode shape as Equity (headsUp exact, monteCarlo fixed-seed),
built on OmahaHandEvaluator instead of HandEvaluator directly. No preflop
exact — roughly 2x Hold'em's own already-slow preflop exact case.

Validated against the closest thing to a solid citation web search turned
up (AAKK double-suited "only a 3-2 favorite" over a strong double-suited
rundown, repeated across multiple PLO strategy sources): measured 61.96%,
within 2 points of the cited ~60%. Two additional tests are exact and
hand-verifiable with no citation needed at all (a locked-quads win, and a
symmetric-suits tie by construction) — the primary correctness gate per
this project's own "pick a hand-verifiable spot over an unsourced number"
rule.
Full write-up of why Omaha needs a new foundation (not a reuse of any NLHE
model), the citation search for a validate-able PLO equity figure and why
it landed on a directional/wide-tolerance check plus exact hand-verifiable
tests, the flop-exact performance finding from building the app screen, and
a concrete proposal for what Phase 2 (preflop hand-strength/ranges) should
be and why it's judgment-heavy in a way Phase 1 deliberately wasn't.
Explicit 4-card notation in/out (no invented hand-class shorthand), Fast
(Monte Carlo) / Precise (exact, postflop only) modes mirroring
EquityCalculatorView's own async-safety pattern. Labeled "(Beta)" — Phase 1
foundation, no preflop hand-strength/ranges yet.

UI tests found and fixed a real bug along the way: a still-open keyboard
intercepted the Calculate button's tap, landing on a keyboard key instead
and corrupting the board notation field — fixed by dismissing the keyboard
(typing a newline) before the next interaction, not by working around the
symptom.
@testtest126
testtest126 merged commit 406e9a1 into main Jul 23, 2026
2 checks passed
@testtest126
testtest126 deleted the feature/omaha-foundation branch July 23, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant