Skip to content

Fix AnalysePlay under-counting (issue #156): reuse solver context across cards#157

Merged
zzcgumn merged 3 commits into
dds-bridge:developfrom
ThorvaldAagaard:fix/analyse-play-cold-tt
May 30, 2026
Merged

Fix AnalysePlay under-counting (issue #156): reuse solver context across cards#157
zzcgumn merged 3 commits into
dds-bridge:developfrom
ThorvaldAagaard:fix/analyse-play-cold-tt

Conversation

@ThorvaldAagaard
Copy link
Copy Markdown
Contributor

Summary

AnalysePlay under-counts tricks for many deals (issue #156). The root cause is in the incremental analyser, not the alpha-beta core — a large SolveBoard/CalcDDtable differential against 2.9 shows zero differences; only the analyse-play path regressed, which is why dtest/list10000.txt does not catch it.

AnalysePlayBin solves the opening position once, then for each later card calls analyse_later_board, which runs a hint-bounded null-window search. That search is only correct against the transposition table built by the initial solve and the preceding cards — the per-card hint/hintDir bounds assume a warm TT.

After the context refactor, analyse_later_board built a fresh SolverContext per call:

SolverContext ctxLater{thrp};

Each SolverContext owns its own TT (SearchContext::tt_, created lazily), so every card got a cold, empty TT. The null-window search then settles on the wrong bound and under-counts. The error is intermittent and "self-heals" (e.g. in #156: wrong at plies 2, 4, 7–15, correct again from 16), because some positions resolve correctly within the bounds regardless of TT state.

This is the same defect already fixed for calc_dd_table in 27030ba ("Fix calc DD table consistency by reusing SolverContext across declarers"). analyse_later_board is the play-analyser analogue and was missed by that fix.

Fix

Make analyse_later_board take SolverContext& and reuse the caller's context (and its warm TT), exactly as solve_same_board already does. AnalysePlayBin passes its persistent outer_ctx to every call. ctx.search().ini_depth() still resolves to thr_->iniDepth (shared ThreadData), so the per-card --ini_depth is unchanged; the only behavioral difference is that the TT now persists across the play, as it did before the refactor.

Test

Adds library/tests/solve_board/analyse_play_consistency.cpp: an independent self-consistency check that needs no external reference solver — AnalysePlayPBN's trick count after each card must equal a fresh SolveBoardPBN of that same position. It covers the exact issue #156 deal plus deterministic random play-outs.

  • Fails on the current code (both cases).
  • Passes with this fix.
  • All existing unit tests still pass.

Verification

  • The incorrect results with v3 #156 deal now matches 2.9 on all 49 plies.
  • Random full-play-out differential vs 2.9, 500 deals: current develop diverges on 336/500; with this fix, 0/500.

Fixes #156

analyse_later_board constructed a fresh SolverContext for every card,
so each incremental hint-bounded search ran against a cold, empty
transposition table. The per-card hint/hintDir bounds assume the warm
TT built by the initial solve and the preceding cards; with a cold TT
the null-window search settles on the wrong bound and under-counts
tricks. This is the same defect that was fixed for calc_dd_table in
27030ba ("reuse SolverContext across declarers") -- the analyse-play
path was simply missed.

Make analyse_later_board take SolverContext& and reuse the caller's
context (and its TT), exactly as solve_same_board already does.
AnalysePlayBin passes its persistent outer_ctx to every call.
ini_depth still resolves to the shared ThreadData, so the per-card
decrement is unchanged; only the TT now persists across the play, as
it did before the context refactor.

Add a self-consistency regression test: AnalysePlayPBN's trick count
after each card must equal an independent SolveBoardPBN of that same
position (no external reference solver needed). Covers the issue dds-bridge#156
deal plus deterministic random play-outs. The test fails on the old
code and passes with the fix.

Fixes dds-bridge#156

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes issue #156 where AnalysePlay under-counts tricks. After the recent solver context refactor, analyse_later_board was constructing a fresh SolverContext per card (ctxLater), giving each call a cold transposition table. Since the per-card hint-bounded null-window search depends on a warm TT built up by the initial solve and preceding cards, the searches settled on incorrect bounds. The fix takes SolverContext& as a parameter and reuses the caller's persistent context, mirroring the earlier calc_dd_table fix (commit 27030ba). A new self-consistency test compares AnalysePlayPBN results against fresh SolveBoardPBN calls at every ply.

Changes:

  • Change analyse_later_board signature to accept SolverContext& instead of std::shared_ptr<ThreadData>&, replacing all internal ctxLater uses with the passed-in ctx.
  • Update AnalysePlayBin to pass its persistent outer_ctx (renamed-purpose) into analyse_later_board rather than creating per-call contexts.
  • Add analyse_play_consistency.cpp test (and Bazel target) verifying AnalysePlay matches SolveBoard for the issue #156 deal and deterministic random play-outs.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
library/src/solver_if.hpp Changes analyse_later_board declaration to take SolverContext&.
library/src/solver_if.cpp Replaces per-call ctxLater with caller-supplied ctx, preserving warm TT state across cards.
library/src/play_analyser.cpp Removes local thrp, passes the persistent ctx to analyse_later_board.
library/tests/solve_board/analyse_play_consistency.cpp New self-consistency regression test covering the issue #156 deal and random play-outs.
library/tests/solve_board/BUILD.bazel Adds Bazel target for the new test.

Copy link
Copy Markdown
Collaborator

@zzcgumn zzcgumn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. Changes look good to me.

@zzcgumn zzcgumn merged commit c92ef14 into dds-bridge:develop May 30, 2026
3 checks passed
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.

incorrect results with v3

3 participants