Skip to content

Add analyse_play to the modern C++ API, C-ABI shim, and Python bindings - #441

Merged
zzcgumn merged 9 commits into
dds-bridge:developfrom
zzcgumn:feature/dds_c_api_analyse_play
Nov 5, 2026
Merged

zzcgumn merged 9 commits into
dds-bridge:developfrom
zzcgumn:feature/dds_c_api_analyse_play

Conversation

@zzcgumn

@zzcgumn zzcgumn commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a context-aware analyse_play(SolverContext&, ...) to the modern C++ API, extracted from legacy AnalysePlayBin's body (which now delegates to it, preserving external behavior).
  • Mirrors it through the pure-C ABI shim as dds_c_analyse_play, plus a new dds_c_convert_play_from_pbn converter (PBN play trace → binary), following the shim's existing no-PBN-twin pattern; regenerates the JNI export lists accordingly.
  • Adds a Python analyse_play (binary) binding and refactors analyse_play_pbn to drop its call into legacy AnalysePlayPBN, converting PBN→binary in C++ and calling the modern analyse_play directly instead.
  • Updates specs/dds-public-api.md to reflect the shim's expanded surface, and fixes two docstrings that overpromised ValueError for illegal plays (they actually raise RuntimeError).
  • Parallelizing analyse_all_plays_pbn was explicitly scoped out as a follow-up (deferred, not implemented here).

Test plan

  • bazel test //library/tests/system:context_equivalence_test — context vs. legacy equivalence
  • bazel test //library/tests/solve_board:analyse_play_consistency_test — regression gate for the AnalysePlayBin extraction
  • bazel test //library/tests:dds_c_api_test — shim null-guards, functional, and converter tests
  • bazel test //jni/tests:export_set_test — export-list regeneration verified
  • bazel test //python:analyse_test — new and refactored Python bindings

🤖 Generated with Claude Code

Martin Nygren and others added 3 commits November 2, 2026 16:34
AnalysePlayBin/AnalysePlayPBN existed only in the legacy C API, with no
SolverContext-taking counterpart and no entry in the pure-C ABI shim
(dds_c_api.h), unlike SolveBoard/CalcDDtable. Add analyse_play(ctx, ...) as
the modern C++ entry point (extracted from AnalysePlayBin's body, which now
delegates to it), mirror it through the shim as dds_c_analyse_play plus a
dds_c_convert_play_from_pbn converter, and give Python a binary analyse_play
binding while refactoring analyse_play_pbn to use the modern API exclusively
instead of calling legacy AnalysePlayPBN.

Parallelizing analyse_all_plays_pbn is intentionally deferred (see the
follow-up note left in the implementation plan) -- out of scope here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Documents the dds_c_analyse_play and dds_c_convert_play_from_pbn
additions in the shim's covered-surface list and explains that the
new converter follows the existing no-PBN-twin rule.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rors

Both docstrings claimed ValueError for an invalid play, but an illegal
play (card not in hand, etc.) returns RETURN_PLAY_FAULT, which
throw_on_dds_error does not map to ValueError -- it falls through to
RuntimeError. Correct both to document RuntimeError for play faults,
keeping ValueError scoped to the Python-level format/validation errors
that actually raise it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Aligning continuation params to the opening paren landed at a 34-space
indent (34 % 4 == 2), tripping the repo's indentation check
(cpp_indentation_test.py), which only runs its full-tree scan outside
the Bazel sandbox -- that's why CI caught it but the local bazel test
run didn't. Reformat both new declarations/definitions to the flat
4-space continuation style dll.h already uses, which sidesteps the
alignment arithmetic entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@zzcgumn zzcgumn self-assigned this Nov 4, 2026
Drop the "modern"/"legacy"/"no longer" framing added for analyse_play
and analyse_play_pbn -- this doc should describe what the functions do
today, not how that differs from history. That contrast belongs in
release notes, not the API reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new source is omitted from both Visual Studio projects, causing link failures, and missing play-trace validation in the binary C entry point permits out-of-bounds memory access.

3 open issues
What changed in this PR

Adds context-aware play analysis across DDS’s modern C++ API, pure-C shim, and Python bindings while preserving the legacy entry points.

Changes:

  • Extracts play analysis into a reusable-context implementation and delegates legacy calls to it.
  • Adds C and Python binary analysis entry points, PBN play conversion, and native exports.
  • Adds equivalence, context-reuse, and input-validation tests, with API documentation updates.
File Description
specs/​dds-public-api.md Documents expanded shim coverage and PBN conversion.
python/​tests/​test_analyse.py Tests binary analysis, representation equivalence, and contexts.
python/​src/​converters.hpp Declares binary play-trace conversion.
python/​src/​converters.cpp Converts and validates Python play sequences.
python/​src/​bindings.cpp Adds binary analysis and modernizes PBN analysis.
python/​dds3/​__init__.py Exports analyse_play.
library/​tests/​system/​context_equivalence_test.cpp Tests legacy equivalence and context reuse.
library/​tests/​dds_c_api_test.cpp Tests shim analysis, null guards, and conversion.
library/​src/​play_analyser.cpp Delegates legacy analysis to the modern implementation.
library/​src/​dds.hpp Includes the new public analysis header.
library/​src/​dds_api.cpp Implements the context-handle forwarding entry point.
library/​src/​api/​dds_c_api.h Declares C analysis and play conversion functions.
library/​src/​api/​dds_c_api.cpp Implements guarded C shim forwarding.
library/​src/​api/​dds_api.hpp Declares context-handle play analysis.
library/​src/​api/​BUILD.bazel Registers the new public header.
library/​src/​api/​analyse_play.hpp Defines the context-aware C++ interface.
library/​src/​analyse_play_adapter.cpp Holds the extracted play-analysis implementation.
jni/​version_script.lds Exports the new C functions on ELF platforms.
jni/​exported_symbols.lds Exports the new C functions on macOS.
docs/​python_interface.md Documents binary analysis and reusable contexts.

Review effort: Balanced

Comment thread library/src/api/dds_c_api.cpp Resolved
Comment thread library/src/play_analyser.cpp Resolved
Comment thread python/src/bindings.cpp Resolved
Martin Nygren and others added 3 commits November 4, 2026 10:59
…/remainCards

dds_c_analyse_play (and every other caller: legacy AnalysePlayBin, the
dds_api.hpp forwarder, the Python binding) forwarded play.suit/play.rank
straight into bit_map_rank[play.rank] and dl.remainCards[player][play.suit]
with no range check. A garbage or malicious suit/rank is an out-of-bounds
array access, not a C++ exception, so dds_c_api.cpp's catch-all could not
have contained it.

Push the fix into the one shared analyse_play() implementation so every
caller gets it: reject play.number outside [0, 52] and any play.suit/
play.rank entry outside [0, DDS_SUITS) / [2, 14] up front, returning
RETURN_PLAY_FAULT instead of touching the arrays. Add coverage both at
the shared-implementation level (context_equivalence_test.cpp) and at
the dds_c_api.cpp shim boundary where this was flagged
(dds_c_api_test.cpp), since that's the entry point an external FFI
caller would actually hit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bazel's source glob picked it up automatically, but DDS.vcxproj and
dds_native.vcxproj both compile play_analyser.cpp (now a two-line
delegate) and never gained the extracted analyse_play_adapter.cpp,
leaving analyse_play with no linked definition on the Windows VS build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ership

tricks[i] was documented as "the trick count for the side to play",
which reads as the side on lead -- but it's actually a fixed
partnership (the one opposite the hand that led to the first trick)
tracked throughout the whole trace, not the alternating side to move.
Clarify this in both Python docstrings, the dds_c_analyse_play header
comment, and docs/python_interface.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The C API header does not expose the new play input and result layouts, preventing C consumers from allocating these types and header-based binding generators from reading their definitions.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Expose play structs in the shared C data-types header

library/​src/​api/​dds_c_api.h:124

A consumer including only dds_c_api.h cannot allocate the new play inputs or results: PlayTraceBin, PlayTracePBN, and SolvedPlay are defined only in dds_data_types.hpp, leaving header-based binding generators without their layouts. Move these three POD definitions into dds_c_data_types.h and remove the original definitions; the C++ header already includes that shared header. This also fixes dds_c_convert_play_from_pbn below.

3 resolved since last review

Review effort: Balanced

…pes header

dds_c_api.h now takes these three types by pointer (dds_c_analyse_play,
dds_c_convert_play_from_pbn), but they were defined only in
dds_data_types.hpp, unreachable from dds_c_api.h alone -- leaving C
consumers and header-based binding generators (jextract) unable to see
their layout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Human judgment is needed to weigh the remaining mismatch between documented exceptions and actual behavior for malformed play tokens.

No open issues 🎉

Review effort: Balanced

@zzcgumn

zzcgumn commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator Author

I have opened #442 to review documented and actual behaviour for malformed inputs. I think this might be wider than the current pull request.

@zzcgumn
zzcgumn requested a review from tameware November 4, 2026 13:51
@zzcgumn zzcgumn added the Clean Copilot review Copilot reviewed and had neither new comments nor new suppressed comments. label Nov 4, 2026
@zzcgumn

zzcgumn commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator Author

@tameware, here is another one exposing more functions through the C API. Note that I deferred the decision about synchronising documentation and actual error types as I think this might be a wider problem.

@tameware tameware left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM.

I think I've mentioned before that in the long term, I'm not sure we should support the binary interface. There can't be any measurable efficiency gain, and sticking to text-oriented standards such as PBN for externals will make our clients' implementation and debugging easier.

@zzcgumn
zzcgumn merged commit 7d0b716 into dds-bridge:develop Nov 5, 2026
14 checks passed
@zzcgumn
zzcgumn deleted the feature/dds_c_api_analyse_play branch November 5, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Clean Copilot review Copilot reviewed and had neither new comments nor new suppressed comments.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants