Skip to content

fix(blueprint_subgame): normalize b/r token equivalence at preflop boundary#182

Merged
amaster97 merged 1 commit into
mainfrom
fix-blueprint-subgame-token-equivalence
May 28, 2026
Merged

fix(blueprint_subgame): normalize b/r token equivalence at preflop boundary#182
amaster97 merged 1 commit into
mainfrom
fix-blueprint-subgame-token-equivalence

Conversation

@amaster97

Copy link
Copy Markdown
Owner

Summary

  • Rust preflop engine emits b<amt> for SB's opening raise; Python HUNLPoker emits r<amt> for the same action. Both refer to the same physical chip-flow when amounts match, but the strict tok == next_tok check in blueprint_subgame rejected the mismatch and returned -1 (infoset-not-found).
  • Adds _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.
  • No engine code touched.

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_blueprint with a real Rust-blueprint action prefix crashes when the loop tries to align tokens at SB's opening step.

Test plan

  • 8 new unit tests in 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).
  • 15 existing tests in tests/test_blueprint_subgame_wiring.py continue to pass.
  • CI green.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

[Generated with Claude Code]

…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>
@amaster97 amaster97 merged commit 2aedb4b into main May 28, 2026
5 of 8 checks passed
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>
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>
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