Add par output to dd_table_for_deal - #291
Conversation
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>
There was a problem hiding this comment.
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. |
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>
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>
There was a problem hiding this comment.
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>
|
Addressed Copilot’s suppressed review notes in 19a7895:
|
There was a problem hiding this comment.
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;
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>
Show the side once (e.g. EW 4Hx, 5Cx) instead of repeating seats on each alternate contract. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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 > 0indicates a sacrifice per the DDS API (ContractType::under_tricks), not a doubled contract. Naming this flagdoubledis 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
doubledis actually the presence of thexsuffix, which in DDS par strings corresponds to a sacrifice (seeContractType.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>
|
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. |
Also drop an unused PBN_FILE_MAX using-declaration that can break builds under -Werror. Co-authored-by: Cursor <cursoragent@cursor.com>
|
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 |
There was a problem hiding this comment.
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 returnsvoid, soprocess_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>
There was a problem hiding this comment.
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/--helpwhen it is the first argument. Invocations likedd_table_for_deal --vul ns --helpcurrently raiseValueError("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>
There was a problem hiding this comment.
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_tricksis documented as a sacrifice indicator (0=make, 1-13=sacrifice), so storing it as acharand using it as a boolean is misleading and unnecessary. Use aboolfor the condition to make intent clearer and avoid char-as-bool confusion.
const char doubled = contract.under_tricks > 0 ? 'x' : '\0';
| namespace dd_table_for_deal { | ||
| namespace { | ||
|
|
||
| const std::regex DEAL_TAG_RE{ |
There was a problem hiding this comment.
This makes me wonder if we should add a pbn parser to the dds API.
|
I’ve wondered the same!
…On Fri, Aug 7, 2026 at 5:51 PM Martin Nygren ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In examples/dd_table_for_deal_lib.cpp
<#291 (comment)>:
> +#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <istream>
+#include <limits>
+#include <regex>
+#include <string>
+#include <unordered_set>
+
+namespace dd_table_for_deal {
+namespace {
+
+const std::regex DEAL_TAG_RE{
This makes me wonder if we should add a pbn parser to the dds API.
—
Reply to this email directly, view it on GitHub
<#291?email_source=notifications&email_token=ABC4PYGXYZB7QPUH5NQLBR35IX3HJA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTIOBYGQZTSNJVGEYKM4TFMFZW63VGMFZXG2LHN2SWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#pullrequestreview-4884395510>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABC4PYB3D2RE4OALV3TT2YD5IX3HJAVCNFSNUABEKJSXA33TNF2G64TZHMZDMOJYHAZDANR3JFZXG5LFHM2TAOBQGU3TKMZUHGQXMAQ>
.
You are receiving this because you were assigned.Message ID:
***@***.***>
|
|
Ready to approve as is, @zzcgumn? Shall I add an "add a pbn parser to the dds API" issue? |
Summary
dd_table_for_dealto print compact par after the DD table (--vul none|both|ns|ew)45S) to C++ (4S +1); extract C++ helpers into a tested library; add an anonymized multi-board example PBNTest plan
bazelisk test //examples:dd_table_for_deal_test //python:dd_table_for_deal_par_testbazelisk run //examples:dd_table_for_deal -- hands/example.pbnand compare par with Pythonbazelisk run //python/examples:dd_table_for_deal -- --vul ns hands/multi_board.pbn(unique deals only, blank lines between boards)=/+Nand sacrifices show undertricks with declaring-side score