diff --git a/.gitignore b/.gitignore index e7db440f..00fd5e87 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ *.def src/[Mm]akefile test/[Mm]akefile +examples/[Mm]akefile test/dtest test/itest diff --git a/ChangeLog b/ChangeLog index bb8fe601..36bf6ed4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Release Notes DDS 2.8.2 +----------------------- +Fixed bug in the calculation of par score contracts (par score +values were OK) in certain rare cases. + +Fixed a typo in in the CalcAllTables.cpp example in the examples +directory. + + Release Notes DDS 2.8.1 ----------------------- Added Makefile for shared library (.d) under Linux. Contributed diff --git a/README.md b/README.md index d98f1410..6d61bdd9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DDS offers a wide range of functions, including par-score calculations. Please refer to the [home page](http://privat.bahnhof.se/wb758135) for details. -The current version is DDS 2.8.1 released in January 2015 and licensed under the Apache 2.0 license in the LICENSE FILE. +The current version is DDS 2.8.2 released in June 2015 and licensed under the Apache 2.0 license in the LICENSE FILE. Release notes are in the ChangeLog file. @@ -83,7 +83,7 @@ The DDS library interface is very well documented. You find the docs, including Bugs ==== -Version 2.8.1 has no known bugs. +Version 2.8.2 has no known bugs. Please report bugs to bo.haglund@bahnhof.se and soren.hein@gmail.com. diff --git a/examples/CalcAllTablesPBN.cpp b/examples/CalcAllTablesPBN.cpp index 95ed0216..719928b1 100644 --- a/examples/CalcAllTablesPBN.cpp +++ b/examples/CalcAllTablesPBN.cpp @@ -24,7 +24,7 @@ int main() ddTablesRes tableRes; allParResults pres; - int mode = 0: // No par calculation + int mode = 0; // No par calculation int trumpFilter[DDS_STRAINS] = {0, 0, 0, 0, 0}; // All int res; char line[80]; diff --git a/include/dll.h b/include/dll.h index d0ac3a44..275105a6 100644 --- a/include/dll.h +++ b/include/dll.h @@ -26,8 +26,8 @@ #define EXTERN_C #endif -/* Version 2.8.1. Allowing for 2 digit minor versions */ -#define DDS_VERSION 20801 +/* Version 2.8.2. Allowing for 2 digit minor versions */ +#define DDS_VERSION 20802 #define DDS_HANDS 4 @@ -259,6 +259,13 @@ struct allParResults struct parResultsDealer { + /* number: Number of contracts yielding the par score. + score: Par score for the specified dealer hand. + contracts: Par contract text strings. The first contract + is in contracts[0], the last one in contracts[number-1]. + The detailed text format is is given in the DLL interface + document. + */ int number; int score; char contracts[10][10]; diff --git a/src/Par.cpp b/src/Par.cpp index 5e078566..9ec8d4e9 100644 --- a/src/Par.cpp +++ b/src/Par.cpp @@ -32,11 +32,20 @@ struct parContr2Type }; int stat_contr[5] = {0, 0, 0, 0, 0}; -const int max_low[3][8] = {{0, 0, 1, 0, 1, 2, 0, 0}, {0, 0, 1, 2, 0, 1, 0, 0}, {0, 0, 1, 2, 3, 0, 0, 0}}; /* index 1: 0=NT, 1=Major, 2=Minor index 2: contract level 1-7 */ +/* index 1: 0=NT, 1=Major, 2=Minor index 2: contract level 1-7 */ +const int max_low[3][8] = { + {0, 0, 1, 0, 1, 2, 0, 0}, + {0, 0, 1, 2, 0, 1, 0, 0}, + {0, 0, 1, 2, 3, 0, 0, 0} +}; -int STDCALL CalcParPBN(ddTableDealPBN tableDealPBN, - ddTableResults * tablep, int vulnerable, parResults * presp) + +int STDCALL CalcParPBN( + ddTableDealPBN tableDealPBN, + ddTableResults * tablep, + int vulnerable, + parResults * presp) { int res; ddTableDeal tableDeal; @@ -52,254 +61,165 @@ int STDCALL CalcParPBN(ddTableDealPBN tableDealPBN, return res; } -#ifdef DEALER_PAR_ENGINE_ONLY -int STDCALL Par(ddTableResults * tablep, parResults * presp, - int vulnerable) -{ - /* vulnerable 0: None 1: Both 2: NS 3: EW */ +int rawscore( + int denom, + int tricks, + int isvul); - int res, i, k, m; - parResultsDealer sidesRes[2]; - parContr2Type parContr2[10]; +void SideSeats( + int dr, + int i, + int t1, + int t2, + int order, + parResultsMaster sidesRes[2]); - int CalcMultiContracts(int max_lower, int tricks); +void CalcOverTricks( + int i, + int max_lower, + int tricks, + int order, + parResultsMaster sidesRes[2]); - res = SidesPar(tablep, sidesRes, vulnerable); - if (res != 1) - return res; +int CalcMultiContracts( + int max_lower, + int tricks); - for (k = 0; k < 16; k++) - { - presp->parScore[0][k] = '\0'; - presp->parScore[1][k] = '\0'; - } +int VulnerDefSide( + int side, + int vulnerable); - sprintf(presp->parScore[0], "NS %d", sidesRes[0].score); - sprintf(presp->parScore[1], "EW %d", sidesRes[1].score); - for (k = 0; k < 128; k++) - { - presp->parContractsString[0][k] = '\0'; - presp->parContractsString[1][k] = '\0'; - } +int STDCALL Par( + ddTableResults * tablep, + parResults * presp, + int vulnerable) +{ + /* vulnerable 0: None 1: Both 2: NS 3: EW */ - strcat(presp->parContractsString[0], "NS:"); - strcat(presp->parContractsString[1], "EW:"); + /* The code for calculation of par score / contracts is based upon the + perl code written by Matthew Kidd ACBLmerge. He has kindly given me + permission to include a C++ adaptation in DDS. */ - char one_contr[4]; - for ( m = 0; m < 3; m++) - one_contr[m] = '\0'; + /* The Par function computes the par result and contracts. */ - char strain_contr[2] = {'0', '\0'}; + parResultsMaster sidesRes[2]; + int res, k; + char temp[8], buff[3]; + int denom_conv[5] = { 4, 0, 1, 2, 3 }; + char contr_sep[2] = { ',', '\0' }; + char seats[6][4] = { + { "N " }, { "E " }, { "S " }, { "W " }, { "NS " }, { "EW " } }; - for (i = 0; i < 2; i++) - { + res = SidesParBin(tablep, sidesRes, vulnerable); - if (sidesRes[i].score == 0) - continue; + if (res != RETURN_NO_FAULT) + return res; - if (sidesRes[i].contracts[0][2] == '*') - { - /* Sacrifice */ + presp->parScore[0][0] = 'N'; + presp->parScore[0][1] = 'S'; + presp->parScore[0][2] = ' '; + presp->parScore[0][3] = '\0'; + presp->parScore[1][0] = 'E'; + presp->parScore[1][1] = 'W'; + presp->parScore[1][2] = ' '; + presp->parScore[1][3] = '\0'; - for (k = 0; k < sidesRes[i].number; k++) - { + sprintf(temp, "%d", sidesRes[0].score); + strcat(presp->parScore[0], temp); + sprintf(temp, "%d", sidesRes[1].score); + strcat(presp->parScore[1], temp); - for (int u = 0; u < 10; u++) - parContr2[k].contracts[u] = sidesRes[i].contracts[k][u]; - - if (sidesRes[i].contracts[k][1] == 'N') - parContr2[k].denom = 0; - else if (sidesRes[i].contracts[k][1] == 'S') - parContr2[k].denom = 1; - else if (sidesRes[i].contracts[k][1] == 'H') - parContr2[k].denom = 2; - else if (sidesRes[i].contracts[k][1] == 'D') - parContr2[k].denom = 3; - else if (sidesRes[i].contracts[k][1] == 'C') - parContr2[k].denom = 4; - } + presp->parContractsString[0][0] = 'N'; + presp->parContractsString[0][1] = 'S'; + presp->parContractsString[0][2] = ':'; + presp->parContractsString[0][3] = '\0'; + presp->parContractsString[1][0] = 'E'; + presp->parContractsString[1][1] = 'W'; + presp->parContractsString[1][2] = ':'; + presp->parContractsString[1][3] = '\0'; - for (int s = 1; s < sidesRes[i].number; s++) - { - parContr2Type tmp = parContr2[s]; - int r = s; - for (; r && tmp.denom < parContr2[r - 1].denom ; --r) - parContr2[r] = parContr2[r - 1]; - parContr2[r] = tmp; - } + if (sidesRes[0].score == 0) + { + /* Neither side can make anything.*/ + return RETURN_NO_FAULT; + } - for (int t = 0; t < sidesRes[i].number; t++) - { + for (int i = 0; i <= 1; i++) + { + if (sidesRes[i].contracts[0].underTricks > 0) + { + /* Sacrifice*/ - if (t != 0) - strcat(presp->parContractsString[i], ","); + for (k = 0; k < sidesRes[i].number; k++) + { - if (parContr2[t].contracts[5] == 'W') - strcat(presp->parContractsString[i], "EW "); - else if (parContr2[t].contracts[5] == 'S') - strcat(presp->parContractsString[i], "NS "); - else - { - switch (parContr2[t].contracts[4]) - { - case 'N': - strcat(presp->parContractsString[i], "N "); - break; - case 'S': - strcat(presp->parContractsString[i], "S "); - break; - case 'E': - strcat(presp->parContractsString[i], "E "); - break; - case 'W': - strcat(presp->parContractsString[i], "W "); - break; - } - } - for (m = 0; m < 2; m++) - one_contr[m] = parContr2[t].contracts[m]; - one_contr[2] = 'x'; - one_contr[3] = '\0'; - strcat(presp->parContractsString[i], one_contr); + strcat(presp->parContractsString[i], + seats[sidesRes[i].contracts[k].seats]); + sprintf(temp, "%d", sidesRes[i].contracts[k].level); + buff[0] = static_cast( + cardSuit[denom_conv[sidesRes[i].contracts[k].denom]]); + buff[1] = 'x'; + buff[2] = '\0'; + strcat(temp, buff); + strcat(presp->parContractsString[i], temp); + if (k != (sidesRes[i].number - 1)) + strcat(presp->parContractsString[i], contr_sep); } - } else { - /* Contract(s) make */ - - char levels_coll[12]; - for (m = 0; m < 12; m++) - levels_coll[m] = '\0'; + /* Make */ for (k = 0; k < sidesRes[i].number; k++) { - for (int u = 0; u < 10; u++) - parContr2[k].contracts[u] = sidesRes[i].contracts[k][u]; - - if (sidesRes[i].contracts[k][1] == 'N') - parContr2[k].denom = 0; - else if (sidesRes[i].contracts[k][1] == 'S') - parContr2[k].denom = 1; - else if (sidesRes[i].contracts[k][1] == 'H') - parContr2[k].denom = 2; - else if (sidesRes[i].contracts[k][1] == 'D') - parContr2[k].denom = 3; - else if (sidesRes[i].contracts[k][1] == 'C') - parContr2[k].denom = 4; - } - for (int s = 1; s < sidesRes[i].number; s++) - { - parContr2Type tmp = parContr2[s]; - int r = s; - for (; r && tmp.denom < parContr2[r - 1].denom; --r) - parContr2[r] = parContr2[r - 1]; - parContr2[r] = tmp; - } + strcat(presp->parContractsString[i], + seats[sidesRes[i].contracts[k].seats]); - for (int t = 0; t < sidesRes[i].number; t++) - { - if (t != 0) - strcat(presp->parContractsString[i], ","); + int n = CalcMultiContracts(sidesRes[i].contracts[k].overTricks, + sidesRes[i].contracts[k].overTricks + + sidesRes[i].contracts[k].level + 6); - if (parContr2[t].contracts[4] == 'W') - strcat(presp->parContractsString[i], "EW "); - else if (parContr2[t].contracts[4] == 'S') - strcat(presp->parContractsString[i], "NS "); - else - { - switch (parContr2[t].contracts[3]) - { - case 'N': - strcat(presp->parContractsString[i], "N "); - break; - case 'S': - strcat(presp->parContractsString[i], "S "); - break; - case 'E': - strcat(presp->parContractsString[i], "E "); - break; - case 'W': - strcat(presp->parContractsString[i], "W "); - break; - } - } - - for (m = 0; m < 2; m++) - one_contr[m] = parContr2[t].contracts[m]; - one_contr[2] = '\0'; - - strain_contr[0] = one_contr[1]; - - char * ptr_c = strchr(parContr2[t].contracts, '+'); - if (ptr_c != nullptr) - { - ptr_c++; - int add_contr = (*ptr_c) - 48; - - - sprintf(levels_coll, "%d", - CalcMultiContracts(add_contr, - (parContr2[t].contracts[0] - 48) + 6 + add_contr)); - - strcat(presp->parContractsString[i], levels_coll); - strcat(presp->parContractsString[i], strain_contr); - } - else - { - strcat(presp->parContractsString[i], one_contr); - - } + sprintf(temp, "%d", n); + buff[0] = static_cast( + cardSuit[denom_conv[sidesRes[i].contracts[k].denom]]); + buff[1] = '\0'; + strcat(temp, buff); + strcat(presp->parContractsString[i], temp); + if (k != (sidesRes[i].number - 1)) + strcat(presp->parContractsString[i], contr_sep); } } } - return res; + return RETURN_NO_FAULT; } -#else - -int rawscore( - int denom, - int tricks, - int isvul); - -void IniSidesString( - int dr, - int i, - int t1, - int t2, - char stri[]); - -int CalcMultiContracts( - int max_lower, - int tricks); - -int VulnerDefSide( - int side, - int vulnerable); +#ifndef DEALER_PAR_ENGINE_ONLY -int STDCALL Par(ddTableResults * tablep, parResults * presp, - int vulnerable) +int STDCALL SidesParBin( + ddTableResults * tablep, + parResultsMaster sidesRes[2], + int vulnerable) { + /* vulnerable 0: None 1: Both 2: NS 3: EW */ /* The code for calculation of par score / contracts is based upon the - perl code written by Matthew Kidd ACBLmerge. He has kindly given me permission - to include a C++ adaptation in DDS. */ + perl code written by Matthew Kidd ACBLmerge. He has kindly given me + permission to include a C++ adaptation in DDS. */ /* The Par function computes the par result and contracts. */ int denom_conv[5] = { 4, 0, 1, 2, 3 }; /* Preallocate for efficiency. These hold result from last direction - (N-S or E-W) examined. */ - int i, j, k, m, isvul; + (N-S or E-W) examined. */ + int j, k, m, isvul; int current_side, both_sides_once_flag, denom_max = 0, max_lower; int new_score_flag, sc1, sc2, sc3; int prev_par_denom = 0, prev_par_tricks = 0; @@ -312,8 +232,6 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, int ut = 0, t1, t2, tt, score, dr, tu, tu_max, t3[5], t4[5], n; par_suits_type par_suits[5]; - char contr_sep[2] = { ',', '\0' }; - char temp[8], buff[4]; int par_denom[2] = { -1, -1 }; /* 0-4 = NT,S,H,D,C */ int par_tricks[2] = { 6, 6 }; /* Initial "contract" beats 0 NT */ @@ -322,13 +240,14 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, /* Find best par result for N-S (i==0) or E-W (i==1). These will - nearly always be the same, but when we have a "hot" situation - they will not be. */ + nearly always be the same, but when we have a "hot" situation + they will not be. */ - for (i = 0; i <= 1; i++) + for (int i = 0; i <= 1; i++) { - /* Start with the with the offensive side (current_side = 0) and alternate - between sides seeking the to improve the result for the current side.*/ + /* Start with the with the offensive side (current_side = 0) and + alternate between sides seeking the to improve the result for the + current side.*/ no_filtered[i] = 0; for (m = 0; m <= 4; m++) @@ -344,7 +263,8 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, k = (i + current_side) % 2; - isvul = ((vulnerable == 1) || (k ? (vulnerable == 3) : (vulnerable == 2))); + isvul = ((vulnerable == 1) || + (k ? (vulnerable == 3) : (vulnerable == 2))); new_score_flag = 0; prev_par_denom = par_denom[i]; @@ -359,8 +279,10 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, if (denom_filter[j] == 0) { /* Current denomination is not filtered out. */ - t1 = k ? tablep->resTable[denom_conv[j]][1] : tablep->resTable[denom_conv[j]][0]; - t2 = k ? tablep->resTable[denom_conv[j]][3] : tablep->resTable[denom_conv[j]][2]; + t1 = k ? tablep->resTable[denom_conv[j]][1] : + tablep->resTable[denom_conv[j]][0]; + t2 = k ? tablep->resTable[denom_conv[j]][3] : + tablep->resTable[denom_conv[j]][2]; tt = Max(t1, t2); /* tt is the maximum number of tricks current side can take in denomination.*/ @@ -377,8 +299,8 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, } } - /* Sort the items in the par_suits structure with decreasing order of the - values on the scores. */ + /* Sort the items in the par_suits structure with decreasing order + of the values on the scores. */ for (int s = 1; s < n; s++) { @@ -389,7 +311,8 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, par_suits[r] = tmp; } - /* Do the iteration as before but now in the order of the sorted denominations. */ + /* Do the iteration as before but now in the order of the sorted + denominations. */ for (m = 0; m < n; m++) { @@ -404,17 +327,17 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, } else { - /* Bidding higher in this denomination will not beat previous denomination - and may be a sacrifice. */ + /* Bidding higher in this denomination will not beat previous + denomination and may be a sacrifice. */ ut = prev_par_tricks - tt; if (j >= prev_par_denom) { /* Sacrifices higher than 7N are not permitted (but long ago - the official rules did not prohibit bidding higher than 7N!) */ + the official rules did not prohibit bidding higher than 7N!) */ if (prev_par_tricks == 13) continue; /* It will be necessary to bid one level higher, resulting in - one more undertrick. */ + one more undertrick. */ ut++; } /* Not a sacrifice (due to par_tricks > prev_par_tricks) */ @@ -459,6 +382,8 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, if (no_filtered[i] == 0) { best_par_score[i] = par_score[i]; + if (best_par_score[i] == 0) + break; best_par_sacut[i] = par_sacut[i]; no_of_denom[i] = 0; } @@ -486,44 +411,39 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, } } - presp->parScore[0][0] = 'N'; - presp->parScore[0][1] = 'S'; - presp->parScore[0][2] = ' '; - presp->parScore[0][3] = '\0'; - presp->parScore[1][0] = 'E'; - presp->parScore[1][1] = 'W'; - presp->parScore[1][2] = ' '; - presp->parScore[1][3] = '\0'; - - sprintf(temp, "%d", best_par_score[0]); - strcat(presp->parScore[0], temp); - sprintf(temp, "%d", best_par_score[1]); - strcat(presp->parScore[1], temp); - - presp->parContractsString[0][0] = 'N'; - presp->parContractsString[0][1] = 'S'; - presp->parContractsString[0][2] = ':'; - presp->parContractsString[0][3] = '\0'; - presp->parContractsString[1][0] = 'E'; - presp->parContractsString[1][1] = 'W'; - presp->parContractsString[1][2] = ':'; - presp->parContractsString[1][3] = '\0'; + /* Output: "best par score" */ + sidesRes[0].score = best_par_score[0]; + sidesRes[1].score = best_par_score[1]; if (best_par_score[0] == 0) { /* Neither side can make anything.*/ + sidesRes[0].contracts[0].denom = 0; + sidesRes[0].contracts[0].level = 0; + sidesRes[0].contracts[0].overTricks = 0; + sidesRes[0].contracts[0].underTricks = 0; + sidesRes[0].contracts[0].seats = 0; + sidesRes[0].number = 1; + sidesRes[1].contracts[0].denom = 0; + sidesRes[1].contracts[0].level = 0; + sidesRes[1].contracts[0].overTricks = 0; + sidesRes[1].contracts[0].underTricks = 0; + sidesRes[1].contracts[0].seats = 0; + sidesRes[1].number = 1; return RETURN_NO_FAULT; } - for (i = 0; i <= 1; i++) + for (int i = 0; i <= 1; i++) { + sidesRes[i].number = no_of_denom[i]; + sidesRes[i].score = best_par_score[i]; if (best_par_sacut[i] > 0) { /* Sacrifice */ dr = (best_par_score[i] > 0) ? 0 : 1; - /* Sort the items in the best_par structure with increasing order of the - values on denom. */ + /* Sort the items in the best_par structure with increasing order + of the values on denom. */ for (int s = 1; s < no_of_denom[i]; s++) { @@ -539,22 +459,18 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, j = best_par[m][i].par_denom; - t1 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][0] : tablep->resTable[denom_conv[j]][1]; - t2 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][2] : tablep->resTable[denom_conv[j]][3]; + t1 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][0] : + tablep->resTable[denom_conv[j]][1]; + t2 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][2] : + tablep->resTable[denom_conv[j]][3]; tt = (t1 > t2) ? t1 : t2; - IniSidesString(dr, i, t1, t2, buff); - - if (presp->parContractsString[i][3] != '\0') - strcat(presp->parContractsString[i], contr_sep); + SideSeats(dr, i, t1, t2, m, sidesRes); + sidesRes[i].contracts[m].denom = j; + sidesRes[i].contracts[m].level = best_par[m][i].par_tricks - 6; + sidesRes[i].contracts[m].overTricks = 0; + sidesRes[i].contracts[m].underTricks = best_par_sacut[i]; - strcat(presp->parContractsString[i], buff); - sprintf(temp, "%d", best_par[m][i].par_tricks - 6); - buff[0] = static_cast(cardSuit[denom_conv[j]]); - buff[1] = 'x'; - buff[2] = '\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); } } else @@ -566,13 +482,16 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, tu_max = 0; for (m = 0; m <= 4; m++) { - t3[m] = ((dr + i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : tablep->resTable[denom_conv[m]][1]; - t4[m] = ((dr + i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : tablep->resTable[denom_conv[m]][3]; + t3[m] = ((dr + i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : + tablep->resTable[denom_conv[m]][1]; + t4[m] = ((dr + i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : + tablep->resTable[denom_conv[m]][3]; tu = (t3[m] > t4[m]) ? t3[m] : t4[m]; if (tu > tu_max) { tu_max = tu; - denom_max = m; /* Lowest denomination if several denominations have max tricks. */ + denom_max = m; + /* Lowest if several denominations have max tricks. */ } } @@ -580,59 +499,62 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, { j = best_par[m][i].par_denom; - t1 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][0] : tablep->resTable[denom_conv[j]][1]; - t2 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][2] : tablep->resTable[denom_conv[j]][3]; + t1 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][0] : + tablep->resTable[denom_conv[j]][1]; + t2 = ((dr + i) % 2) ? tablep->resTable[denom_conv[j]][2] : + tablep->resTable[denom_conv[j]][3]; tt = (t1 > t2) ? t1 : t2; - IniSidesString(dr, i, t1, t2, buff); - - if (presp->parContractsString[i][3] != '\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); + SideSeats(dr, i, t1, t2, m, sidesRes); if (denom_max < j) max_lower = best_par[m][i].par_tricks - tu_max - 1; else max_lower = best_par[m][i].par_tricks - tu_max; - /* max_lower is the maximal contract lowering, otherwise opponent contract is - higher. It is already known that par_score is high enough to make - opponent sacrifices futile. - To find the actual contract lowering allowed, it must be checked that the - lowered contract still gets the score bonus points that is present in par score.*/ + /* max_lower is the maximal contract lowering, otherwise + opponent contract is higher. It is already known that par_score + is high enough to make opponent sacrifices futile. + To find the actual contract lowering allowed, it must be + checked that the lowered contract still gets the score bonus + points that is present in par score.*/ sc2 = abs(best_par_score[i]); /* Score for making the tentative lower par contract. */ while (max_lower > 0) { if (denom_max < j) - sc1 = -rawscore(-1, best_par[m][i].par_tricks - max_lower - tu_max, - VulnerDefSide(best_par_score[0] > 0, vulnerable)); + sc1 = -rawscore(-1, + best_par[m][i].par_tricks - max_lower - tu_max, + VulnerDefSide(best_par_score[0] > 0, vulnerable)); else - sc1 = -rawscore(-1, best_par[m][i].par_tricks - max_lower - tu_max + 1, - VulnerDefSide(best_par_score[0] > 0, vulnerable)); - /* Score for undertricks needed to beat the tentative lower par contract.*/ + sc1 = -rawscore(-1, + best_par[m][i].par_tricks - max_lower - tu_max + 1, + VulnerDefSide(best_par_score[0] > 0, vulnerable)); + /* Score for undertricks needed to beat the tentative + lower par contract.*/ if (sc2 < sc1) break; else max_lower--; - /* Tentative lower par contract must be 1 trick higher, since the cost - for the sacrifice is too small. */ + /* Tentative lower par contract must be 1 trick higher, + since the cost for the sacrifice is too small. */ } int opp_tricks = Max(t3[j], t4[j]); while (max_lower > 0) { - sc3 = -rawscore(-1, best_par[m][i].par_tricks - max_lower - opp_tricks, - VulnerDefSide(best_par_score[0] > 0, vulnerable)); + sc3 = -rawscore(-1, + best_par[m][i].par_tricks - max_lower - opp_tricks, + VulnerDefSide(best_par_score[0] > 0, vulnerable)); - /* If opponents to side with par score start the bidding and has a sacrifice - in the par denom on the same trick level as implied by current max_lower, - then max_lower must be decremented. */ + /* If opponents to side with par score start the bidding + and has a sacrifice in the par denom on the same trick level + as implied by current max_lower, then max_lower must be + decremented. */ if ((sc2 > sc3) && (best_par_score[i] < 0)) /* Opposite side with best par score starts the bidding. */ @@ -658,22 +580,251 @@ int STDCALL Par(ddTableResults * tablep, parResults * presp, throw std::runtime_error("j not in [0..3] in Par"); } - max_lower = Min(max_low[k][best_par[m][i].par_tricks - 6], max_lower); + max_lower = Min(max_low[k][best_par[m][i].par_tricks - 6], + max_lower); - n = CalcMultiContracts(max_lower, best_par[m][i].par_tricks); + sidesRes[i].contracts[m].denom = j; + sidesRes[i].contracts[m].underTricks = 0; - sprintf(temp, "%d", n); - buff[0] = static_cast(cardSuit[denom_conv[j]]); - buff[1] = '\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); + CalcOverTricks(i, max_lower, best_par[m][i].par_tricks, + m, sidesRes); + + sidesRes[i].contracts[m].level = best_par[m][i].par_tricks - 6 - + sidesRes[i].contracts[m].overTricks; + + } + } + } + + /* Filter out par contracts where the other side has a higher par + contract. This can happen when par scores differ for the two sides. */ + + int opp_side[2]; + + int denom_to_remove[2][5] = { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; + + int dom_denom[2] = { -1, -1 }; /* Dominating denom */ + + int dom_level[2]; /* Dominating level */ + + for (int i = 0; i < 2; i++) + { + k = 0; + opp_side[i] = (i == 0) ? 1 : 0; + + while (k < sidesRes[opp_side[i]].number) + { + j = sidesRes[opp_side[i]].contracts[k].denom; + int ss = sidesRes[opp_side[i]].contracts[k].level + + sidesRes[opp_side[i]].contracts[k].overTricks; + + if (ss > dom_level[opp_side[i]] || + (ss == dom_level[opp_side[i]] && j < dom_denom[opp_side[i]])) + { + if (((i == 0) && + ((sidesRes[opp_side[i]].contracts[k].seats % 2) != 0)) || + ((i == 1) && + ((sidesRes[opp_side[i]].contracts[k].seats % 2) == 0))) + { + dom_denom[opp_side[i]] = j; + dom_level[opp_side[i]] = + sidesRes[opp_side[i]].contracts[k].level + + sidesRes[opp_side[i]].contracts[k].overTricks; + } + } + k++; + } + } + + if ((dom_denom[0] != -1) && (dom_denom[1] != -1)) + { + + /* Remove par contracts that can be dominated by the other side. */ + + for (int i = 0; i < 2; i++) + { + opp_side[i] = (i == 0) ? 1 : 0; + + for (k = 0; k < sidesRes[i].number; k++) + { + j = sidesRes[i].contracts[k].denom; + + if (((sidesRes[i].contracts[k].level + + sidesRes[i].contracts[k].overTricks) < + dom_level[opp_side[i]]) || + (((sidesRes[i].contracts[k].level + + sidesRes[i].contracts[k].overTricks) == dom_level[opp_side[i]]) + && (dom_denom[opp_side[i]] < sidesRes[i].contracts[k].denom))) + denom_to_remove[i][j] = 1; + } + + int mm = 0; + + for (k = 0; k < sidesRes[i].number; k++) + { + j = sidesRes[i].contracts[k].denom; + if (denom_to_remove[i][j] != 1) + { + sidesRes[i].contracts[mm] = sidesRes[i].contracts[k]; + mm++; + } + } + sidesRes[i].number = mm; + } + + } + + return RETURN_NO_FAULT; +} +#else + +int STDCALL SidesParBin( + ddTableResults * tablep, + parResultsMaster sidesRes[2], + int vulnerable) +{ + + int res, h, hbest[2], i, k, m, index; + parResultsMaster parRes2[4]; + int cross_index[4][5] = { + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 } }; + + for (h = 0; h <= 3; h++) + { + + res = DealerParBin(tablep, &parRes2[h], h, vulnerable); + + if (res != RETURN_NO_FAULT) + return res; + + if (parRes2[h].score == 0) + { + sidesRes[0].number = 1; + sidesRes[0].score = 0; + sidesRes[1].number = 1; + sidesRes[1].score = 0; + + return RETURN_NO_FAULT; + } + } + + + for (h = 0; h <= 3; h++) + { + for (k = 0; k < parRes2[h].number; k++) + /* Corresponding index and denom */ + cross_index[h][parRes2[h].contracts[k].denom] = k; + } + + for (i = 0; i <= 1; i++) + /* Sides 0 and 1 */ + { + if (parRes2[2 + i].score != parRes2[i].score) + { + if (i == 0) + { + if (parRes2[2 + i].score > parRes2[i].score) + hbest[i] = 2 + i; + else + hbest[i] = i; + } + else + { + if (parRes2[2 + i].score < parRes2[i].score) + hbest[i] = 2 + i; + else + hbest[i] = i; } + + sidesRes[i].number = parRes2[hbest[i]].number; + if (i == 0) + sidesRes[i].score = parRes2[hbest[i]].score; + else + sidesRes[i].score = -parRes2[hbest[i]].score; + + + for (k = 0; k < sidesRes[i].number; k++) + sidesRes[i].contracts[k] = parRes2[hbest[i]].contracts[k]; + } + else + { + if (i == 0) + sidesRes[i].score = parRes2[0].score; + else + sidesRes[i].score = -parRes2[1].score; + + index = 0; + + for (m = 0; m < 5; m++) + { + /* Iterate through denoms */ + + if ((cross_index[2 + i][m] == -1) && (cross_index[i][m] == -1)) + { + continue; + } + else if (cross_index[2 + i][m] == -1) + { + if (((i == 0) && ((parRes2[i].contracts[index].seats % 2) != 0)) || + ((i == 1) && ((parRes2[i].contracts[index].seats % 2) == 0))) + continue; + sidesRes[i].contracts[index] = + parRes2[i].contracts[cross_index[i][m]]; + index++; + } + else if (cross_index[i][m] == -1) + { + if (((i == 0) && ((parRes2[i].contracts[index].seats % 2) != 0)) || + ((i == 1) && ((parRes2[i].contracts[index].seats % 2) == 0))) + continue; + sidesRes[i].contracts[index] = + parRes2[2 + i].contracts[cross_index[2 + i][m]]; + index++; + } + else if (parRes2[2 + i].contracts[cross_index[2 + i][m]].level == + parRes2[i].contracts[cross_index[i][m]].level) + { + sidesRes[i].contracts[index] = + parRes2[i].contracts[cross_index[i][m]]; + index++; + } + else if (((i == 0) && (parRes2[i].score > 0) || + (i == 1) && (parRes2[i].score < 0)) && + (parRes2[2 + i].contracts[cross_index[2 + i][m]].level > + parRes2[i].contracts[cross_index[i][m]].level)) + { + sidesRes[i].contracts[index] = + parRes2[i].contracts[cross_index[i][m]]; + index++; + } + else if (((i == 0) && (parRes2[i].score < 0) || (i == 1) && + (parRes2[i].score > 0)) && + (parRes2[2 + i].contracts[cross_index[2 + i][m]].level < + parRes2[i].contracts[cross_index[i][m]].level)) + { + sidesRes[i].contracts[index] = + parRes2[i].contracts[cross_index[i][m]]; + index++; + } + else + { + sidesRes[i].contracts[index] = + parRes2[2 + i].contracts[cross_index[2 + i][m]]; + index++; + } + } + sidesRes[i].number = index; } } + return RETURN_NO_FAULT; } +#endif int rawscore(int denom, int tricks, int isvul) { @@ -684,8 +835,9 @@ int rawscore(int denom, int tricks, int isvul) for a par contract (aside from a passed out hand). denom - 0 = NT, 1 = Spades, 2 = Hearts, 3 = Diamonds, 4 = Clubs - (same order as results from double dummy solver); -1 undertricks - tricks - For making contracts (7-13); otherwise, number of undertricks. + (same order as results from double dummy solver); -1 undertricks + tricks - For making contracts (7-13); otherwise, number of + undertricks. isvul - True if vulnerable */ if (denom == -1) @@ -739,56 +891,123 @@ int rawscore(int denom, int tricks, int isvul) } -void IniSidesString(int dr, int i, int t1, int t2, char stri[]) +void SideSeats( + int dr, + int i, + int t1, + int t2, + int order, + parResultsMaster sidesRes[2]) { if ((dr + i) % 2 ) { if (t1 == t2) { - stri[0] = 'N'; - stri[1] = 'S'; - stri[2] = ' '; - stri[3] = '\0'; + sidesRes[i].contracts[order].seats = 4; } else if (t1 > t2) { - stri[0] = 'N'; - stri[1] = ' '; - stri[2] = '\0'; + sidesRes[i].contracts[order].seats = 0; } else { - stri[0] = 'S'; - stri[1] = ' '; - stri[2] = '\0'; + sidesRes[i].contracts[order].seats = 2; } } else { if (t1 == t2) { - stri[0] = 'E'; - stri[1] = 'W'; - stri[2] = ' '; - stri[3] = '\0'; + sidesRes[i].contracts[order].seats = 5; } else if (t1 > t2) { - stri[0] = 'E'; - stri[1] = ' '; - stri[2] = '\0'; + sidesRes[i].contracts[order].seats = 1; } else { - stri[0] = 'W'; - stri[1] = ' '; - stri[2] = '\0'; + sidesRes[i].contracts[order].seats = 3; } } return; } +void CalcOverTricks( + int i, + int max_lower, + int tricks, + int order, + parResultsMaster sidesRes[2]) +{ + switch (tricks - 6) + { + case 5: + if (max_lower == 3) + { + sidesRes[i].contracts[order].overTricks = 3; + } + else if (max_lower == 2) + { + sidesRes[i].contracts[order].overTricks = 2; + } + else if (max_lower == 1) + { + sidesRes[i].contracts[order].overTricks = 1; + } + else + { + sidesRes[i].contracts[order].overTricks = 0; + } + break; + case 4: + if (max_lower == 3) + { + sidesRes[i].contracts[order].overTricks = 3; + } + else if (max_lower == 2) + { + sidesRes[i].contracts[order].overTricks = 2; + } + else if (max_lower == 1) + { + sidesRes[i].contracts[order].overTricks = 1; + } + else + { + sidesRes[i].contracts[order].overTricks = 0; + } + break; + case 3: + if (max_lower == 2) + { + sidesRes[i].contracts[order].overTricks = 2; + } + else if (max_lower == 1) + { + sidesRes[i].contracts[order].overTricks = 1; + } + else + { + sidesRes[i].contracts[order].overTricks = 0; + } + break; + case 2: + if (max_lower == 1) + { + sidesRes[i].contracts[order].overTricks = 1; + } + else + { + sidesRes[i].contracts[order].overTricks = 0; + } + break; + default: + sidesRes[i].contracts[order].overTricks = 0; + } + return; +} + int VulnerDefSide(int side, int vulnerable) { @@ -813,7 +1032,6 @@ int VulnerDefSide(int side, int vulnerable) } } -#endif int CalcMultiContracts(int max_lower, int tricks) @@ -888,8 +1106,11 @@ int CalcMultiContracts(int max_lower, int tricks) return n; } -int STDCALL CalcPar(ddTableDeal tableDeal, int vulnerable, - ddTableResults * tablep, parResults * presp) +int STDCALL CalcPar( + ddTableDeal tableDeal, + int vulnerable, + ddTableResults * tablep, + parResults * presp) { int res; @@ -904,9 +1125,7 @@ int STDCALL CalcPar(ddTableDeal tableDeal, int vulnerable, return res; } -#if 0 -/* Unsorted*/ int STDCALL DealerParBin( ddTableResults * tablep, parResultsMaster * presp, @@ -917,6 +1136,7 @@ int STDCALL DealerParBin( /* vulnerable 0: None 1: Both 2: NS 3: EW */ parResultsDealer parResDealer; + parContr2Type parContr2[10]; int k, delta; int res = DealerPar(tablep, &parResDealer, dealer, vulnerable); @@ -926,99 +1146,14 @@ int STDCALL DealerParBin( return res; } - presp->score = parResDealer.score; - presp->number = parResDealer.number; - - for (k = 0; k < parResDealer.number; k++) + if (parResDealer.contracts[0][0] == 'p') { - delta = 1; - - presp->contracts[k].level = int(parResDealer.contracts[k][0] - '0'); - - switch (parResDealer.contracts[k][1]) - { - case 'N': - presp->contracts[k].denom = 0; - break; - case 'S': - presp->contracts[k].denom = 1; - break; - case 'H': - presp->contracts[k].denom = 2; - break; - case 'D': - presp->contracts[k].denom = 3; - break; - case 'C': - presp->contracts[k].denom = 4; - break; - } - - if (strstr(parResDealer.contracts[k], "NS")) - presp->contracts[k].seats = 4; - else if (strstr(parResDealer.contracts[k], "EW")) - presp->contracts[k].seats = 5; - else if (strstr(parResDealer.contracts[k], "-N")) - { - presp->contracts[k].seats = 0; - delta = 0; - } - else if (strstr(parResDealer.contracts[k], "-E")) - { - presp->contracts[k].seats = 1; - delta = 0; - } - else if (strstr(parResDealer.contracts[k], "-S")) - { - presp->contracts[k].seats = 2; - delta = 0; - } - else if (strstr(parResDealer.contracts[k], "-W")) - { - presp->contracts[k].seats = 3; - delta = 0; - } - - if (parResDealer.contracts[0][2] == '*') - { - /* Sacrifice */ - presp->contracts[k].underTricks = (int) (parResDealer.contracts[k][6 + delta] - '0'); - presp->contracts[k].overTricks = 0; - } - else - /* Make */ - { - if (strchr(parResDealer.contracts[k], '+')) - presp->contracts[k].overTricks = (int)(parResDealer.contracts[k][5 + delta] - '0'); - else - presp->contracts[k].overTricks = 0; - presp->contracts[k].underTricks = 0; - } + /*Passed out, i.e. no par contract can be found.*/ + presp->number = 1; + presp->score = 0; + return RETURN_NO_FAULT; } -} -return RETURN_NO_FAULT; -} -#endif -int STDCALL DealerParBin( - ddTableResults * tablep, - parResultsMaster * presp, - int dealer, - int vulnerable) -{ - /* dealer 0: North 1: East 2: South 3: West */ - /* vulnerable 0: None 1: Both 2: NS 3: EW */ - - parResultsDealer parResDealer; - parContr2Type parContr2[10]; - int k, delta; - - int res = DealerPar(tablep, &parResDealer, dealer, vulnerable); - - if (res != RETURN_NO_FAULT) - { - return res; - } for (k = 0; k < parResDealer.number; k++) { @@ -1126,108 +1261,100 @@ int STDCALL DealerParBin( } -int STDCALL SidesPar(ddTableResults * tablep, parResultsDealer sidesRes[2], int vulnerable) -{ - - int res = 0, h, hbest[2], k; - parResultsDealer parRes2[4]; - - for (h = 0; h <= 3; h++) - { - - res = DealerPar(tablep, &parRes2[h], h, vulnerable); - - char * p = strstr(parRes2[h].contracts[0], "pass"); - if (p != nullptr) - { - parRes2[h].number = 1; - parRes2[h].score = 0; - } - } - - if (parRes2[2].score > parRes2[0].score) - hbest[0] = 2; - else - hbest[0] = 0; - - if (parRes2[3].score > parRes2[1].score) - hbest[1] = 3; - else - hbest[1] = 1; - - sidesRes[0].number = parRes2[hbest[0]].number; - sidesRes[0].score = parRes2[hbest[0]].score; - sidesRes[1].number = parRes2[hbest[1]].number; - sidesRes[1].score = -parRes2[hbest[1]].score; - - for (k = 0; k < sidesRes[0].number; k++) - strcpy(sidesRes[0].contracts[k], parRes2[hbest[0]].contracts[k]); - - for (k = 0; k < sidesRes[1].number; k++) - strcpy(sidesRes[1].contracts[k], parRes2[hbest[1]].contracts[k]); - - return res; -} - - -int STDCALL SidesParBin( - ddTableResults * tablep, - parResultsMaster sidesRes[2], +int STDCALL SidesPar( + ddTableResults * tablep, + parResultsDealer sidesRes[2], int vulnerable) { + parResultsMaster parm[2]; + int res, i, k; + char buff[4]; - int res, h, hbest[2], k; - parResultsMaster parRes2[4]; - - for (h = 0; h <= 3; h++) - { + res = SidesParBin(tablep, parm, vulnerable); - res = DealerParBin(tablep, &parRes2[h], h, vulnerable); + if (res != RETURN_NO_FAULT) + return res; - if (res != RETURN_NO_FAULT) - return res; + /*Convert the bin results to DealerPar format. */ + for (i = 0; i <= 1; i++) + { + sidesRes[i].score = parm[i].score; + sidesRes[i].number = parm[i].number; - if (parRes2[h].score == 0) + for (k = 0; k < sidesRes[i].number; k++) { - if ((h == 0) || (h == 2)) + sprintf(sidesRes[i].contracts[k], "%d", parm[i].contracts[k].level); + switch (parm[i].contracts[k].denom) { - sidesRes[0].number = 1; - sidesRes[0].score = 0; + case 0: + strcat(sidesRes[i].contracts[k], "N"); + break; + case 1: + strcat(sidesRes[i].contracts[k], "S"); + break; + case 2: + strcat(sidesRes[i].contracts[k], "H"); + break; + case 3: + strcat(sidesRes[i].contracts[k], "D"); + break; + case 4: + strcat(sidesRes[i].contracts[k], "C"); + break; + default: + // Cannot happen, but keeps gcc happy. + break; } - else + if (parm[i].contracts[k].underTricks > 0) + /* Sacrifice */ + strcat(sidesRes[i].contracts[k], "*"); + switch (parm[i].contracts[k].seats) { - sidesRes[1].number = 1; - sidesRes[1].score = 0; + case 0: + strcat(sidesRes[i].contracts[k], "-N"); + break; + case 1: + strcat(sidesRes[i].contracts[k], "-E"); + break; + case 2: + strcat(sidesRes[i].contracts[k], "-S"); + break; + case 3: + strcat(sidesRes[i].contracts[k], "-W"); + break; + case 4: + strcat(sidesRes[i].contracts[k], "-NS"); + break; + case 5: + strcat(sidesRes[i].contracts[k], "-EW"); + break; + default: + // Cannot happen, but keeps gcc happy. + break; + } + if (parm[i].contracts[k].underTricks > 0) + { + /* Sacrifice */ + sprintf(buff, "-%d", parm[i].contracts[k].underTricks); + strcat(sidesRes[i].contracts[k], buff); + } + else if (parm[i].contracts[k].overTricks > 0) + { + /* Make */ + sprintf(buff, "+%d", parm[i].contracts[k].overTricks); + strcat(sidesRes[i].contracts[k], buff); } } } - if (parRes2[2].score > parRes2[0].score) - hbest[0] = 2; - else - hbest[0] = 0; - - if (parRes2[3].score > parRes2[1].score) - hbest[1] = 3; - else - hbest[1] = 1; - - sidesRes[0].number = parRes2[hbest[0]].number; - sidesRes[0].score = parRes2[hbest[0]].score; - sidesRes[1].number = parRes2[hbest[1]].number; - sidesRes[1].score = -parRes2[hbest[1]].score; - - for (k = 0; k < sidesRes[0].number; k++) - sidesRes[0].contracts[k] = parRes2[hbest[0]].contracts[k]; - - for (k = 0; k < sidesRes[1].number; k++) - sidesRes[1].contracts[k] = parRes2[hbest[1]].contracts[k]; - return RETURN_NO_FAULT; } -int STDCALL ConvertToDealerTextFormat(parResultsMaster * pres, char * resp) + +int STDCALL ConvertToDealerTextFormat( + parResultsMaster * pres, + char * resp) { int k, i; char buff[20]; @@ -1311,9 +1438,9 @@ int STDCALL ConvertToDealerTextFormat(parResultsMaster * pres, char * resp) } - - -int STDCALL ConvertToSidesTextFormat(parResultsMaster * pres, parTextResults * resp) +int STDCALL ConvertToSidesTextFormat( + parResultsMaster * pres, + parTextResults * resp) { int k, i, j; char buff[20];