Skip to content

Add par output to dd_table_for_deal - #291

Open
tameware wants to merge 17 commits into
dds-bridge:developfrom
tameware:add-par
Open

Add par output to dd_table_for_deal#291
tameware wants to merge 17 commits into
dds-bridge:developfrom
tameware:add-par

Conversation

@tameware

@tameware tameware commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Extend C++ and Python dd_table_for_deal to print compact par after the DD table (--vul none|both|ns|ew)
  • Process every unique deal in multi-board PBN files (higher read limit, clear missing-file errors, dedupe exact deal strings)
  • Match Python multi-level Par encodings (45S) to C++ (4S +1); extract C++ helpers into a tested library; add an anonymized multi-board example PBN

Test plan

  • bazelisk test //examples:dd_table_for_deal_test //python:dd_table_for_deal_par_test
  • bazelisk run //examples:dd_table_for_deal -- hands/example.pbn and compare par with Python
  • bazelisk run //python/examples:dd_table_for_deal -- --vul ns hands/multi_board.pbn (unique deals only, blank lines between boards)
  • Confirm making contracts show = / +N and sacrifices show undertricks with declaring-side score

tameware and others added 6 commits August 6, 2026 12:02
Show par alongside the DD table (optional --vul), using a one-line summary when there is a single par contract and falling back to verbose output otherwise.

Co-authored-by: Cursor <cursoragent@cursor.com>
Raise the PBN read limit so large exports are not truncated, report missing paths clearly, and separate multi-deal output with blank lines.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep sacrifices and equals makes compact (e.g. EW 3Dx,EW 3Cx -1 -100; EW 2S = 110) instead of the verbose four-line breakdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move parse/vuln/path/par formatting into a cc_library with gtests under examples/tests/.

Co-authored-by: Cursor <cursoragent@cursor.com>
Deduplicate exact deal strings in first-seen order so multi-table exports are not re-solved.

Co-authored-by: Cursor <cursoragent@cursor.com>
DDS Par strings encode overtricks as concatenated levels (e.g. 45S);
normalize them to level + overtricks like the binary formatter, and
keep a single blank line after the hand diagram.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

Extends the dd_table_for_deal example (C++ and Python) to compute and print a compact one-line par summary immediately after the double-dummy table, with an optional --vul none|both|ns|ew flag and improved multi-board PBN handling (read more input, extract all deals, and de-duplicate exact deals).

Changes:

  • Add par computation + compact formatting to both C++ (SidesParBin/Par) and Python (calc_par_from_table) dd_table_for_deal.
  • Improve PBN ingestion for multi-board files: raise size cap, extract all [Deal "..."] tags, and process unique deals in order.
  • Factor C++ helpers into a small library with new unit tests; add a RealBridge multi-board example PBN and update docs/specs.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
specs/examples-cli.md Documents that dd_table_for_deal now prints par output and supports --vul.
python/tests/test_dd_table_for_deal_par.py Adds Python unit tests covering --vul, multi-deal PBN loading/deduping, and compact par formatting.
python/examples/README.md Updates Bazel run examples (including -- separator and --vul).
python/examples/dd_table_for_deal.py Implements --vul, multi-board PBN parsing/deduping, and compact par formatting/printing.
python/BUILD.bazel Registers the new Python par-output test target.
hands/RB_250718227059AKBCOpenFri18Jul1pm_full.pbn Adds a large RealBridge multi-board PBN example for exercising multi-deal processing.
examples/tests/dd_table_for_deal_test.cpp Adds C++ unit tests for parsing, deal extraction/deduping, and compact par line formatting.
examples/README Updates CLI usage examples for the Python dd_table_for_deal including --vul.
examples/hands.cpp Tweaks example hand printing to emit a single trailing newline instead of two.
examples/dd_table_for_deal.hpp Introduces shared C++ helper API/constants for dd_table_for_deal.
examples/dd_table_for_deal.cpp Updates the C++ example to load/process multiple unique deals and print compact par output with --vul.
examples/dd_table_for_deal_lib.cpp Implements shared parsing/extraction/deduping + compact par formatting helpers.
examples/BUILD.bazel Adds the new C++ helper library + its cc_test, and wires the library into the dd_table_for_deal binary.

Comment thread examples/dd_table_for_deal.cpp
Comment thread python/examples/dd_table_for_deal.py
Allow stdin input exactly at PBN_FILE_MAX like file reads and C++,
and use snprintf for deal titles.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.

Comment thread hands/RB_250718227059AKBCOpenFri18Jul1pm_full.pbn Outdated
Keep only Board/Dealer/Vulnerable/Deal tags so the example has no
player names, RealBridge IDs, or session identifiers.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

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

Suppressed comments (4)

python/examples/dd_table_for_deal.py:176

  • The --vul help text omits that numeric codes 0|1|2|3 are accepted (see _parse_vulnerable / tests). This makes the CLI help inconsistent with the actual accepted inputs.
        "  <pbn_deal_or_file>  DDS PBN deal string, or path to a .pbn file\n"
        "  --vul              Vulnerability: none, both, ns, ew (default: none)\n"

examples/dd_table_for_deal.cpp:243

  • The help/usage text lists only none|both|ns|ew, but parse_vulnerable() also accepts numeric codes 0|1|2|3. This mismatch can confuse users (especially since the error message mentions numeric codes).
  fprintf(stderr,
          "Usage: %s [--vul none|both|ns|ew] <pbn_deal_or_file>\n"
          "       %s -h | --help\n"

python/examples/dd_table_for_deal.py:170

  • The usage line documents only textual values for --vul, but _parse_vulnerable() also accepts numeric codes (0|1|2|3). Consider documenting the accepted numeric codes here as well to match actual behavior.

This issue also appears on line 175 of the same file.

        f"Usage: {prog} [--vul none|both|ns|ew] <pbn_deal_or_file>\n"
        f"       {prog} -h | --help\n"

examples/dd_table_for_deal.cpp:119

  • read_pbn_stream() can return nullopt either for empty input or for oversized input (it already prints a "PBN input too large" message). Emitting "No PBN input on stdin" in both cases can be misleading; consider a more generic message that covers both cases.
    if (!text)
    {
      std::cerr << "No PBN input on stdin\n";
      return std::nullopt;
    }

Align CLI help with accepted 0|1|2|3 values, and use a generic
stdin failure message when input is empty or oversize.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tameware

tameware commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed Copilot’s suppressed review notes in 19a7895:

  • Documented numeric --vul codes (0|1|2|3) in C++ and Python usage/help
  • Changed the stdin failure message to the generic “Cannot read PBN from stdin”

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.

Pull request overview

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

Suppressed comments (1)

examples/dd_table_for_deal.cpp:118

  • When stdin exceeds the size cap, read_pbn_stream() already prints "PBN input too large ..." and returns nullopt, but load_deals() unconditionally prints "Cannot read PBN from stdin" as well. That yields redundant/misleading output for the size-limit case. Consider only emitting the stdin message when stdin is actually empty (EOF) or in a hard I/O error state.
    const auto text = read_pbn_stream(std::cin);
    if (!text)
    {
      std::cerr << "Cannot read PBN from stdin\n";
      return std::nullopt;

@tameware
tameware requested a review from zzcgumn August 6, 2026 14:50
Only report a stdin read failure when the stream is at EOF or bad;
oversized input is already explained by read_pbn_stream.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tameware
tameware requested a lite review from Copilot August 6, 2026 15:36
Show the side once (e.g. EW 4Hx, 5Cx) instead of repeating seats on
each alternate contract.

Co-authored-by: Cursor <cursoragent@cursor.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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@tameware
tameware requested a lite review from Copilot August 6, 2026 16:52

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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.

Pull request overview

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

Suppressed comments (2)

examples/dd_table_for_deal.cpp:335

  • When multiple positional arguments are provided, the program prints usage but does not explain what was wrong. This makes the failure harder to diagnose compared to the Python CLI, which reports "Only one deal argument is allowed".
    if (input != nullptr)
    {
      print_usage(argv[0]);
      return 1;
    }

examples/dd_table_for_deal.cpp:152

  • When reading a .pbn/.txt path fails, this branch always prints "Cannot read file", even if the file exists but is empty (currently treated as unreadable by read_pbn_stream) or if read_pbn_stream already printed the oversize error. This can produce misleading/double error output for large PBN exports and hides the more actionable "No [Deal "..."] tag found" message for empty files.
  if (looks_like_path(arg))
  {
    std::cerr << "Cannot read file: " << arg << "\n";
    return std::nullopt;
  }

Report when multiple deal arguments are given, treat empty PBN as a
missing Deal tag, and avoid a redundant Cannot-read message after an
oversize rejection.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

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

Suppressed comments (3)

examples/dd_table_for_deal_lib.cpp:79

  • contract.under_tricks > 0 indicates a sacrifice per the DDS API (ContractType::under_tricks), not a doubled contract. Naming this flag doubled is misleading and makes the formatting logic harder to understand/maintain.
  const char doubled = contract.under_tricks > 0 ? 'x' : '\0';
  if (include_seats)
  {
    if (doubled)
    {

python/examples/dd_table_for_deal.py:263

  • The regex group named doubled is actually the presence of the x suffix, which in DDS par strings corresponds to a sacrifice (see ContractType.under_tricks), not doubling. Renaming avoids confusion when reading/parsing par encodings.
    seats, levels, denom, doubled = match.groups()
    seats = seats.upper()
    denom = denom.upper()
    digits = [int(ch) for ch in levels]
    level = digits[0]

python/examples/dd_table_for_deal.py:230

  • Docstring says the input score is in the “NS-view”, but _sacrifice_undertricks() is called with _declaring_score(...) (which can be the EW score). The implementation already handles either sign; updating the docstring will prevent misunderstandings.
def _sacrifice_undertricks(score: int, vulnerable: int, seats: str) -> int:
    """Infer sacrifice undertricks from the NS-view par score."""
    is_vul = _side_vulnerable(seats, vulnerable)

Co-authored-by: Cursor <cursoragent@cursor.com>
@tameware

tameware commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

I addressed Copilot's third suppressed comment – NS-view should in fact be declaring-side view. The other two are incorrect or pedantic – Copilot has no bridge knowledge.

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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread examples/dd_table_for_deal.cpp
Comment thread examples/dd_table_for_deal_lib.cpp
Also drop an unused PBN_FILE_MAX using-declaration that can break
builds under -Werror.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tameware

tameware commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

These were not intended to be examples, I just couldn't think of a better place for them. They're growing features. Maybe move them to new utilities/ directories? If so, I'd do in a separate PR.

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.

Pull request overview

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

Suppressed comments (1)

examples/dd_table_for_deal.cpp:166

  • print_par_or_verbose() reports DDS errors to stderr but returns void, so process_deal()/main() cannot fail the run when par computation fails. That can yield exit code 0 with incomplete output, which is surprising for a CLI intended to print par alongside the DD table.
  const int res = SidesParBin(table, sidesRes, vulnerable);
  if (res != RETURN_NO_FAULT)
  {
    char line[80];
    ErrorMessage(res, line);

Propagate SidesParBin/Par failures from print_par_or_verbose so the
process exits non-zero instead of printing a partial DD-only result.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

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

Suppressed comments (1)

python/examples/dd_table_for_deal.py:156

  • _parse_cli() only recognizes -h/--help when it is the first argument. Invocations like dd_table_for_deal --vul ns --help currently raise ValueError("Unknown option: --help") instead of showing usage (the C++ CLI accepts help anywhere).
    while i < len(argv):
        arg = argv[i]
        if arg == "--vul":
            if i + 1 >= len(argv):

Match the C++ parser so help after other flags shows usage instead of
Unknown option.

Co-authored-by: Cursor <cursoragent@cursor.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.

Pull request overview

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

Suppressed comments (1)

examples/dd_table_for_deal_lib.cpp:55

  • contract.under_tricks is documented as a sacrifice indicator (0=make, 1-13=sacrifice), so storing it as a char and using it as a boolean is misleading and unnecessary. Use a bool for the condition to make intent clearer and avoid char-as-bool confusion.
  const char doubled = contract.under_tricks > 0 ? 'x' : '\0';

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.

Pull request overview

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

@tameware tameware self-assigned this Aug 7, 2026
namespace dd_table_for_deal {
namespace {

const std::regex DEAL_TAG_RE{

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.

This makes me wonder if we should add a pbn parser to the dds API.

@tameware

tameware commented Aug 7, 2026 via email

Copy link
Copy Markdown
Collaborator Author

@tameware

tameware commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Ready to approve as is, @zzcgumn? Shall I add an "add a pbn parser to the dds API" issue?

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.

3 participants