fix(blueprint_subgame): normalize b/r token equivalence at preflop boundary#182
Merged
Merged
Conversation
…undary The Rust preflop engine (crates/cfr_core/src/preflop_rvr.rs) emits ``b<amt>`` for SB's opening raise (treating the open as a "bet"), while the Python HUNLPoker engine emits ``r<amt>`` for the same action (treating the BB blind as a pending bet). Both refer to the same physical action when chip amounts match — but the prior strict ``tok == next_tok`` check in blueprint_subgame's preflop walk helpers rejected this mismatch and returned -1 (no infoset found), crashing the J7o postflop walkthrough script before it could land its first flop solve. Adds ``_tokens_equivalent(emitted, requested)`` and routes the three preflop walk loops (_player_to_act_after_tokens, _pot_chips_after_tokens, _is_terminal_fold_or_allin) through it. Treats ``b<amt>`` and ``r<amt>`` as equivalent iff the numeric tails are identical; otherwise falls back to strict equality. No engine code touched. Tests: 8 unit tests for _tokens_equivalent (identity, b/r-with-same- amount, b/r-with-different-amount, fold/call/check non-equivalence, non-numeric body fallback, leading-zero strictness). 15 existing blueprint_subgame_wiring tests continue to pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
amaster97
added a commit
that referenced
this pull request
May 28, 2026
#183) Companion to docs/j7o_walkthrough_tests_1_4_2026-05-28.md (PR #179), which reported equity only. This doc reports **actual GTO action distributions** from postflop subgame solves at each decision point — true player-POV format. Architecture per street: - Preflop: read SB's 169-class blueprint at the root infoset (||p|); J7o gets its action distribution directly. - Turn / River: call ``solve_postflop_from_blueprint`` with the preflop action sequence and the board, then extract J7o's per-class strategy and J♠7♦'s specific per-history strategy. Limitation surfaced honestly: flop subgame solves are DEFERRED. Empirical measurement (2026-05-28): the vector-form Rust solver runs > 5 minutes of CPU per flop solve at the smallest viable parameters (top-K = 4 hand classes + J7o pin, 5 DCFR iterations). The chance tree from flop to river blows up per-iter cost. Turn solves succeed in ~15s and river solves in <1s (TerminalCache amortizes the dominant evaluator cost on a constant board). Flop directional reads are from the equity-only walkthrough in PR #179. Headline result for Test 1 (J7o, 40 BB, A♦8♥9♦ 2♣ 3♠): - Preflop: open_to_200 = 92.1%, open_to_300 = 6.3%, call = 1.6%, fold ≈ 0%. The 169-class engine confirms J7o is an opening hand at this stack. - Turn (2♣): raise_33 = 30%, raise_75 = 19%, fold = 17%, raise_100 = 14%. J7o still raises a lot of the time as a polarized bluff after following BB's modal turn action. - River (3♠): all_in = 49%, bet_100 = 15%, bet_200 = 11%, check = 7%. SB bombs the river half the time (BB's modal action is check at 84.5%, so this is SB facing a checked river with J-high). Total wall time: 21.6s (preflop blueprint 4.5s, turn solve 14.7s, river solve 0.5s, overhead 1.9s). Script: scripts/run_j7o_walkthrough_full_pov.py — designed so the SKIP_FLOP_SOLVES gate can be flipped to False in a future burst once the flop-subgame perf path is improved. Depends on: PR #182 (fix(blueprint_subgame): normalize b/r token equivalence at preflop boundary), which unblocks the postflop wiring for SB's opening raise. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 28, 2026
amaster97
added a commit
that referenced
this pull request
May 28, 2026
#196) Audits #69 (v1.9.0) and #71 (v1.10.0) both flagged that the persona- table claimed Wendy (Premium-A consumer) coverage existed but the W5.x workflows had never been authored. This PR ships the five W5.x persona tests via tests/test_w5_premium_a_personas.py, restores the persona acceptance spec at docs/pr13_prep/persona_acceptance_spec.md (lost when the docs/pr13_prep/ directory was historically purged), and bumps the snapshot count 17 → 22 in docs/persona_status_2026-05-28-w5x.md. The five workflows: W5.1 — Blueprint Casual Lookup (100 BB / no-ante / AKs, <100 ms warm-cache, 10 cells populated) — PR #174 (BlueprintLoader) W5.2 — Interpolated Depth Lookup (67 BB = 0.65 * v60 + 0.35 * v80 convex blend) — PR #173 (interp) + PR #181 (router) W5.3 — Blueprint → Postflop Chained on Qs7h2d, synthetic 2-class smoke fixture (production-scale OOM-blocked per the v1.10 perf roadmap; documented as a deferred follow-up) — PR #177 + PR #182 + PR #181 W5.4 — Custom Range Fallback (B10 override forces custom_live_solve, not blueprint_lookup) — PR #181 + B10 PRs #149/#154/#158 W5.5 — Ante Selection (40 BB / 72o shifts L1>0.05 across no/half/ full ante) — 27-shard bundle (chore `1783bef`) + PR #174 13/13 tests PASS in 1.13 s on M-series arm64. No engine code touched; docs + tests only. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
b<amt>for SB's opening raise; PythonHUNLPokeremitsr<amt>for the same action. Both refer to the same physical chip-flow when amounts match, but the stricttok == next_tokcheck inblueprint_subgamerejected the mismatch and returned -1 (infoset-not-found)._tokens_equivalent(emitted, requested)and routes the 3 preflop walk helpers (_player_to_act_after_tokens,_pot_chips_after_tokens,_is_terminal_fold_or_allin) through it. Falls back to strict equality if amounts differ or bodies aren't numeric.Why this matters
This unblocks the J7o full-POV postflop walkthrough (PR #179 follow-up). Without it, the very first call into
solve_postflop_from_blueprintwith a real Rust-blueprint action prefix crashes when the loop tries to align tokens at SB's opening step.Test plan
tests/test_blueprint_subgame_tokens.py(identity, b/r-with-same-amount, b/r-with-different-amount, fold/call/check non-equivalence, non-numeric body fallback, leading-zero strictness).tests/test_blueprint_subgame_wiring.pycontinue to pass.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
[Generated with Claude Code]