Repository navigation
Add analyse_play to the modern C++ API, C-ABI shim, and Python bindings #441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
zzcgumn
merged 9 commits into
dds-bridge:develop
from
zzcgumn:feature/dds_c_api_analyse_play
Nov 5, 2026
Merged
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
bf4c0f8
Add analyse_play to the modern C++ API, C-ABI shim, and Python bindings
aaa5015
docs: update dds-public-api spec for analyse_play shim additions
97202b3
docs: fix analyse_play/analyse_play_pbn docstrings on illegal-play er…
5791228
fix: use flat continuation indent in dds_c_analyse_play signatures
3b8fe6d
docs: keep python_interface.md to current behavior only
3aae589
fix: validate play trace in analyse_play before indexing bit_map_rank…
fa22286
fix: add analyse_play_adapter.cpp to the Visual Studio project files
2660af5
docs: clarify analyse_play/analyse_play_pbn tricks[] is a fixed partn…
21a05b3
fix: move PlayTraceBin/PlayTracePBN/SolvedPlay into the C-ABI data-ty…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| #include <api/analyse_play.hpp> | ||
| #include <solver_if.hpp> | ||
|
|
||
| /** | ||
| * @brief Analyze a sequence of played cards (binary format) and determine the tricks taken. | ||
| * | ||
| * This function simulates play of a bridge Deal according to the provided play trace, | ||
| * using double dummy analysis to determine the number of tricks won at each step. | ||
| * | ||
| * @param ctx Solver context containing state and resources, reused across the | ||
| * initial solve and every subsequent per-card analyse_later_board | ||
| * call so the hint-bounded incremental searches see a warm TT. | ||
| * @param dl_in The Deal to analyze | ||
| * @param play The sequence of played cards (binary format) | ||
| * @param solvedp Pointer to result structure for solved play | ||
| * @return RETURN_NO_FAULT on success, error code otherwise | ||
| */ | ||
| auto analyse_play( | ||
| SolverContext& ctx, | ||
| const Deal& dl_in, | ||
| const PlayTraceBin& play, | ||
| SolvedPlay* solvedp) -> int | ||
| { | ||
| Deal dl = dl_in; | ||
|
|
||
| MoveType move; | ||
| FutureTricks fut; | ||
|
|
||
| int ret = solve_board_internal(ctx, dl, -1, 1, 1, &fut); | ||
| if (ret != RETURN_NO_FAULT) | ||
| return ret; | ||
| const int ini_depth = ctx.search().ini_depth(); | ||
| const int numTricks = ((ini_depth + 3) >> 2) + 1; | ||
| const int numCardsPlayed = ((48 - ini_depth) % 4) + 1; | ||
|
|
||
| int last_trick = (play.number + 3) / 4; | ||
| int last_card = ((play.number + 3) % 4) + 1; | ||
| if (last_trick >= numTricks) | ||
| { | ||
| last_trick = numTricks-1; | ||
| last_card = 4; | ||
| } | ||
| solvedp->number = 0; | ||
|
|
||
| solvedp->tricks[0] = (numCardsPlayed % 2 == 1 ? | ||
| numTricks - fut.score[0] : fut.score[0]); | ||
| int hint = solvedp->tricks[0]; | ||
| int hintDir; | ||
|
|
||
| int running_remainder = numTricks; | ||
| int running_declarer = 0; | ||
| int running_player = dl.first; | ||
| int running_side = 1; /* defenders */ | ||
| int start_side = running_player % 2; | ||
|
|
||
| for (int trick = 1; trick <= last_trick; trick++) | ||
| { | ||
| int best_card = 0, best_suit = 0, best_player = 0, trump_played = 0; | ||
| int lc = (trick == last_trick ? last_card : 4); | ||
|
|
||
| bool haveCurrent = (numCardsPlayed > 1 && trick == 1); | ||
| int offset = 4 * (trick - 1) - (numCardsPlayed - 1); | ||
|
|
||
| for (int card = 1; card <= lc; card++) | ||
| { | ||
| int suit, rr; | ||
| bool usingCurrent = (haveCurrent && card < numCardsPlayed); | ||
| if (usingCurrent) | ||
| { | ||
| suit = dl.currentTrickSuit[card - 1]; | ||
| rr = dl.currentTrickRank[card - 1]; | ||
| } | ||
| else | ||
| { | ||
| suit = play.suit[offset + card - 1]; | ||
| rr = play.rank[offset + card - 1]; | ||
| } | ||
| unsigned hold = static_cast<unsigned>(bit_map_rank[rr] << 2); | ||
|
|
||
| move.suit = suit; | ||
| move.rank = rr; | ||
| move.sequence = rr; | ||
|
|
||
| /* Keep track of the winner of the trick so far */ | ||
| if (card == 1) | ||
| { | ||
| best_card = rr; | ||
| best_suit = suit; | ||
| best_player = dl.first; | ||
| trump_played = (suit == dl.trump ? 1 : 0); | ||
| } | ||
| else if (suit == dl.trump) | ||
| { | ||
| if (! trump_played || rr > best_card) | ||
| { | ||
| best_card = rr; | ||
| best_suit = suit; | ||
| best_player = running_player; | ||
| trump_played = 1; | ||
| } | ||
| } | ||
| else if (! trump_played && suit == best_suit && rr > best_card) | ||
| { | ||
| best_card = rr; | ||
| best_player = running_player; | ||
| } | ||
|
|
||
| if ((dl.remainCards[running_player][suit] & hold) == 0) | ||
| { | ||
| if (! usingCurrent) | ||
| return RETURN_PLAY_FAULT; | ||
| } | ||
| else | ||
| dl.remainCards[running_player][suit] ^= hold; | ||
|
|
||
| if (card == 4) | ||
| { | ||
| running_declarer += (best_player % 2 == start_side ? 0 : 1); | ||
| running_remainder--; | ||
|
|
||
| if ((dl.first + best_player) % 2 == 0) | ||
| { | ||
| hintDir = 0; // Same side leads again; lower bound | ||
| hint = running_remainder - fut.score[0]; | ||
| } | ||
| else | ||
| { | ||
| hintDir = 1; // Other ("our") side wins trick; upper bound | ||
| hint = fut.score[0] - 1; | ||
| } | ||
|
|
||
| dl.first = best_player; | ||
| running_side = (dl.first % 2 == start_side ? 1 : 0); | ||
| running_player = dl.first; | ||
| } | ||
| else | ||
| { | ||
| running_player = (running_player + 1) % 4; | ||
| running_side = 1 - running_side; | ||
| hint = running_remainder - fut.score[0]; | ||
| hintDir = 0; | ||
| } | ||
|
|
||
| if (usingCurrent) | ||
| continue; | ||
|
|
||
| if ((ret = analyse_later_board(ctx, dl.first, &move, hint, | ||
| hintDir, &fut)) | ||
| != RETURN_NO_FAULT) | ||
| { | ||
| return ret; | ||
| } | ||
|
|
||
| int new_solved_decl = running_declarer + (running_side ? | ||
| running_remainder - fut.score[0] : fut.score[0]); | ||
|
|
||
| solvedp->tricks[offset + card] = new_solved_decl; | ||
| } | ||
| } | ||
| solvedp->number = 4 * last_trick + last_card - 3 - (numCardsPlayed - 1); | ||
|
|
||
| return RETURN_NO_FAULT; | ||
| } |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /** | ||
| * @file analyse_play.hpp | ||
| * @brief C++ interface for play analysis with explicit solver context. | ||
| * | ||
| * This header provides the C++-only, context-aware entry point for play | ||
| * analysis, allowing clients to manage solver state across multiple | ||
| * analyse_play calls. | ||
| * | ||
| * Naming note: this mirrors the legacy AnalysePlayBin/AnalysePlayPBN API | ||
| * family, so "analyse" (British spelling) is kept deliberately here, | ||
| * matching that existing name rather than switching to "analyze". | ||
| * | ||
| * @copyright (C) 2006-2014 by Bo Haglund / 2014-2018 by Bo Haglund & Soren Hein. | ||
| * @see LICENSE and README. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <api/dds_data_types.hpp> | ||
| #include <solver_context/solver_context.hpp> | ||
|
|
||
| /** | ||
| * @brief Analyze a sequence of played cards (binary format) with explicit solver context. | ||
| * | ||
| * C++-only overload that accepts an explicit SolverContext, allowing clients | ||
| * to manage solver state and resources across multiple analyse_play calls. | ||
| * This is the preferred C++ API following DDS 3 snake_case naming conventions. | ||
| * | ||
| * @param ctx Solver context containing state and resources | ||
| * @param dl Deal to analyze | ||
| * @param play Sequence of played cards (binary format) | ||
| * @param solvedp Output structure for the solved play | ||
| * @return Error code (RETURN_NO_FAULT on success) | ||
| * @see AnalysePlayBin() for the backward-compatible PascalCase legacy version | ||
| */ | ||
| auto analyse_play( | ||
| SolverContext& ctx, | ||
| const Deal& dl, | ||
| const PlayTraceBin& play, | ||
| SolvedPlay* solvedp) -> int; |
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
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.