From 8214d0e58d201d08598b82dfab77e67560dad85b Mon Sep 17 00:00:00 2001 From: Adam Wildavsky Date: Sun, 28 Jun 2026 07:45:52 -0500 Subject: [PATCH 1/3] Fix signed->unsigned cast bugs that corrupted move ordering and pruning The heuristic/quick-tricks refactor introduced static_cast wrappers on values that v2.9 used as signed, changing search behavior: - make_3 / make_3_ctx: winner[]/second_best[] .hand and .rank were cast to unsigned char, turning the -1 "no card" sentinel into 255. This broke winner[trump].hand == -1 style checks in QuickTricks, losing cutoffs. - weight_alloc_trump_void2 / _void3: rel_rank[aggr[suit]][...] indexed through static_cast(aggr[suit]), truncating the 13-bit aggregate holding to 8 bits and reading the wrong rel_rank row. - QuickTricksPartnerHand{Trump,NT}: bit_map_rank index cast the signed rank through unsigned char. With these reverted to v2.9's signed handling, the per-move-generation ordering trace now matches v2.9 exactly (0 divergences on list1), closing the residual calc gap to parity. Ordering/pruning-only change; double-dummy results are unchanged and all library tests pass. Co-authored-by: Cursor --- library/src/ab_search.cpp | 16 ++++++++-------- .../src/heuristic_sorting/heuristic_sorting.cpp | 15 +++------------ library/src/quick_tricks.cpp | 4 ++-- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/library/src/ab_search.cpp b/library/src/ab_search.cpp index 762d75f0..c5b8ee96 100644 --- a/library/src/ab_search.cpp +++ b/library/src/ab_search.cpp @@ -878,10 +878,10 @@ void make_3( int aggr = posPoint->aggr[st]; - posPoint->winner[st].rank = static_cast(thrp->rel[aggr].abs_rank[1][st].rank); - posPoint->winner[st].hand = static_cast(thrp->rel[aggr].abs_rank[1][st].hand); - posPoint->second_best[st].rank = static_cast(thrp->rel[aggr].abs_rank[2][st].rank); - posPoint->second_best[st].hand = static_cast(thrp->rel[aggr].abs_rank[2][st].hand); + posPoint->winner[st].rank = thrp->rel[aggr].abs_rank[1][st].rank; + posPoint->winner[st].hand = thrp->rel[aggr].abs_rank[1][st].hand; + posPoint->second_best[st].rank = thrp->rel[aggr].abs_rank[2][st].rank; + posPoint->second_best[st].hand = thrp->rel[aggr].abs_rank[2][st].hand; } } @@ -944,10 +944,10 @@ static void make_3_ctx( int aggr = posPoint->aggr[st]; - posPoint->winner[st].rank = static_cast(thrp->rel[aggr].abs_rank[1][st].rank); - posPoint->winner[st].hand = static_cast(thrp->rel[aggr].abs_rank[1][st].hand); - posPoint->second_best[st].rank = static_cast(thrp->rel[aggr].abs_rank[2][st].rank); - posPoint->second_best[st].hand = static_cast(thrp->rel[aggr].abs_rank[2][st].hand); + posPoint->winner[st].rank = thrp->rel[aggr].abs_rank[1][st].rank; + posPoint->winner[st].hand = thrp->rel[aggr].abs_rank[1][st].hand; + posPoint->second_best[st].rank = thrp->rel[aggr].abs_rank[2][st].rank; + posPoint->second_best[st].hand = thrp->rel[aggr].abs_rank[2][st].hand; } } diff --git a/library/src/heuristic_sorting/heuristic_sorting.cpp b/library/src/heuristic_sorting/heuristic_sorting.cpp index 88478881..8ddd0a2d 100644 --- a/library/src/heuristic_sorting/heuristic_sorting.cpp +++ b/library/src/heuristic_sorting/heuristic_sorting.cpp @@ -1213,10 +1213,7 @@ void weight_alloc_trump_void2(HeuristicContext& ctx) mply[k].rank < ctx.move1_rank) { // Don't underruff. - unsigned char aggrSuit = static_cast(tpos.aggr[suit]); - unsigned char moveRank = static_cast(mply[k].rank); - unsigned char relRankValue = static_cast(rel_rank[aggrSuit][moveRank]); - int r_rank = static_cast(relRankValue); + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; suitAdd = (suitCount << 6) / 40; mply[k].weight = -32 + r_rank + suitAdd; } @@ -1386,10 +1383,7 @@ void weight_alloc_trump_void3(HeuristicContext& ctx) { for (int k = last_num_moves; k < num_moves; k++) { - int r_rank = static_cast( - static_cast( - rel_rank[static_cast(tpos.aggr[suit])] - [static_cast(mply[k].rank)])); + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; if (mply[k].rank > ctx.move2_rank) mply[k].weight = 33 + r_rank; // Overruff else @@ -1404,10 +1398,7 @@ void weight_alloc_trump_void3(HeuristicContext& ctx) { for (int k = last_num_moves; k < num_moves; k++) { - int r_rank = static_cast( - static_cast( - rel_rank[static_cast(tpos.aggr[suit])] - [static_cast(mply[k].rank)])); + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; mply[k].weight = 33 + r_rank; } } diff --git a/library/src/quick_tricks.cpp b/library/src/quick_tricks.cpp index 0c161406..48f37fe5 100644 --- a/library/src/quick_tricks.cpp +++ b/library/src/quick_tricks.cpp @@ -1000,7 +1000,7 @@ int QuickTricksPartnerHandTrump( if (ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].hand == partner[hand]) { tpos.win_ranks[depth][suit] |= bit_map_rank[ - static_cast(static_cast(ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].rank)) ]; + static_cast(ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].rank) ]; tpos.win_ranks[depth][commSuit] |= bit_map_rank[commRank]; @@ -1110,7 +1110,7 @@ int QuickTricksPartnerHandNT( if (ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].hand == partner[hand]) { tpos.win_ranks[depth][suit] |= bit_map_rank[ - static_cast(static_cast(ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].rank)) ]; + static_cast(ctx.thread_ptr()->rel[ranks].abs_rank[3][suit].rank) ]; qt++; if (qt >= cutoff) return qt; From 038b36077b3c9f3462edb7f784e23366f4abb9b6 Mon Sep 17 00:00:00 2001 From: Adam Wildavsky Date: Thu, 2 Jul 2026 23:55:35 -0400 Subject: [PATCH 2/3] Fix indentation in weight_alloc_trump_void1 and trump_void3 Whitespace-only cleanup of misindented if/else-if chains and wrapped conditions left over from the v2.9 port; no logic change. Co-authored-by: Cursor --- .../heuristic_sorting/heuristic_sorting.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library/src/heuristic_sorting/heuristic_sorting.cpp b/library/src/heuristic_sorting/heuristic_sorting.cpp index 8ddd0a2d..a9ab8980 100644 --- a/library/src/heuristic_sorting/heuristic_sorting.cpp +++ b/library/src/heuristic_sorting/heuristic_sorting.cpp @@ -672,7 +672,7 @@ void weight_alloc_trump_void1(HeuristicContext& ctx) const int partner_lh = partner[lead_hand]; const int rho_lh = rho[lead_hand]; - + unsigned short suitCount = tpos.length[curr_hand][suit]; int suitAdd; @@ -702,9 +702,9 @@ void weight_alloc_trump_void1(HeuristicContext& ctx) if (tpos.length[partner_lh][lead_suit] != 0) { // 3rd hand will follow. - if (tpos.rank_in_suit[rho_lh][lead_suit] > - (tpos.rank_in_suit[partner_lh][lead_suit] | - bit_map_rank[ctx.lead0_rank])) + if (tpos.rank_in_suit[rho_lh][lead_suit] > + (tpos.rank_in_suit[partner_lh][lead_suit] | + bit_map_rank[ctx.lead0_rank])) // Partner has winning card. suitAdd = 60 + (suitCount << 6) / 44; else if ((tpos.length[rho_lh][lead_suit] == 0) @@ -726,9 +726,9 @@ void weight_alloc_trump_void1(HeuristicContext& ctx) tpos.rank_in_suit[partner_lh][trump])) // Partner can overruff 3rd hand. suitAdd = 60 + (suitCount << 6) / 44; - else if ((tpos.length[partner_lh][trump] == 0) - && (tpos.rank_in_suit[rho_lh][lead_suit] > - bit_map_rank[ctx.lead0_rank])) + else if ((tpos.length[partner_lh][trump] == 0) + && (tpos.rank_in_suit[rho_lh][lead_suit] > + bit_map_rank[ctx.lead0_rank])) // 3rd hand has no trumps, and partner has suit winner. suitAdd = 60 + (suitCount << 6) / 44; else @@ -1181,7 +1181,7 @@ void weight_alloc_trump_void2(HeuristicContext& ctx) MoveType* mply = ctx.mply; const int rho_lh = rho[lead_hand]; - + int suitAdd; const unsigned short suitCount = tpos.length[curr_hand][suit]; const int max4th = highest_rank[tpos.rank_in_suit[rho_lh][lead_suit]]; @@ -1209,16 +1209,16 @@ void weight_alloc_trump_void2(HeuristicContext& ctx) for (int k = last_num_moves; k < num_moves; k++) { - if (ctx.move1_suit == trump && - mply[k].rank < ctx.move1_rank) + if (ctx.move1_suit == trump && + mply[k].rank < ctx.move1_rank) { // Don't underruff. - int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; suitAdd = (suitCount << 6) / 40; mply[k].weight = -32 + r_rank + suitAdd; } - else if (ctx.high1 == 0) + else if (ctx.high1 == 0) { // We ruff partner's winner over 2nd hand. if (max4th != 0) @@ -1383,7 +1383,7 @@ void weight_alloc_trump_void3(HeuristicContext& ctx) { for (int k = last_num_moves; k < num_moves; k++) { - int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; if (mply[k].rank > ctx.move2_rank) mply[k].weight = 33 + r_rank; // Overruff else From 577e76e08eaa726ceb6b8450913e1665af81d213 Mon Sep 17 00:00:00 2001 From: Adam Wildavsky Date: Fri, 3 Jul 2026 13:47:00 +0100 Subject: [PATCH 3/3] Apply comment and format suggestions from Copilot code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- library/src/ab_search.cpp | 8 ++++---- library/src/heuristic_sorting/heuristic_sorting.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/src/ab_search.cpp b/library/src/ab_search.cpp index c5b8ee96..dd6fdd61 100644 --- a/library/src/ab_search.cpp +++ b/library/src/ab_search.cpp @@ -878,10 +878,10 @@ void make_3( int aggr = posPoint->aggr[st]; - posPoint->winner[st].rank = thrp->rel[aggr].abs_rank[1][st].rank; - posPoint->winner[st].hand = thrp->rel[aggr].abs_rank[1][st].hand; - posPoint->second_best[st].rank = thrp->rel[aggr].abs_rank[2][st].rank; - posPoint->second_best[st].hand = thrp->rel[aggr].abs_rank[2][st].hand; + posPoint->winner[st].rank = thrp->rel[aggr].abs_rank[1][st].rank; + posPoint->winner[st].hand = thrp->rel[aggr].abs_rank[1][st].hand; + posPoint->second_best[st].rank = thrp->rel[aggr].abs_rank[2][st].rank; + posPoint->second_best[st].hand = thrp->rel[aggr].abs_rank[2][st].hand } } diff --git a/library/src/heuristic_sorting/heuristic_sorting.cpp b/library/src/heuristic_sorting/heuristic_sorting.cpp index a9ab8980..d175e451 100644 --- a/library/src/heuristic_sorting/heuristic_sorting.cpp +++ b/library/src/heuristic_sorting/heuristic_sorting.cpp @@ -705,7 +705,7 @@ void weight_alloc_trump_void1(HeuristicContext& ctx) if (tpos.rank_in_suit[rho_lh][lead_suit] > (tpos.rank_in_suit[partner_lh][lead_suit] | bit_map_rank[ctx.lead0_rank])) - // Partner has winning card. + // RHO can win. suitAdd = 60 + (suitCount << 6) / 44; else if ((tpos.length[rho_lh][lead_suit] == 0) && (tpos.length[rho_lh][trump] != 0)) @@ -1398,7 +1398,7 @@ void weight_alloc_trump_void3(HeuristicContext& ctx) { for (int k = last_num_moves; k < num_moves; k++) { - int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; + int r_rank = rel_rank[tpos.aggr[suit]][mply[k].rank]; mply[k].weight = 33 + r_rank; } }