From 65d99fe6852bb9bf90c5d51c31d6d89f63d851d8 Mon Sep 17 00:00:00 2001 From: Soren Hein Date: Sun, 28 Jun 2015 10:53:01 +0200 Subject: [PATCH] Retroactive fix to old par contract calculation in rare cases Around v2.8.1 it wad discovered that the old par contract calculation could incomplete results in certain very rare cases (empirically, a few hands per million). As v2.5.2 was an important release, we decided to fix this. --- DLL-dds_252_t.rtf => DLL-dds_253_t.rtf | 0 DLL-dds_252_t.txt => DLL-dds_253_t.txt | 0 dds.cpp | 1237 +++++++++++++----------- dll.h | 51 +- release_notes.txt | 4 + 5 files changed, 733 insertions(+), 559 deletions(-) rename DLL-dds_252_t.rtf => DLL-dds_253_t.rtf (100%) rename DLL-dds_252_t.txt => DLL-dds_253_t.txt (100%) diff --git a/DLL-dds_252_t.rtf b/DLL-dds_253_t.rtf similarity index 100% rename from DLL-dds_252_t.rtf rename to DLL-dds_253_t.rtf diff --git a/DLL-dds_252_t.txt b/DLL-dds_253_t.txt similarity index 100% rename from DLL-dds_252_t.txt rename to DLL-dds_253_t.txt diff --git a/dds.cpp b/dds.cpp index 19b3df94..caf0139d 100755 --- a/dds.cpp +++ b/dds.cpp @@ -1,6 +1,7 @@ -/* DDS 2.5.2 A bridge double dummy solver. */ -/* Copyright (C) 2006-2014 by Bo Haglund */ +/* DDS 2.5.3 A bridge double dummy solver. */ +/* Copyright (C) 2006-2014 by Bo Haglund / */ +/* 2014-2015 by Bo Haglund & Soren Hein. */ /* Cleanups and porting to Linux and MacOSX (C) 2006 by Alex Martelli. */ /* The code for calculation of par score / contracts is based upon the */ /* perl code written by Matthew Kidd for ACBLmerge. He has kindly given me */ @@ -17,9 +18,21 @@ /* limitations under the License. */ -/*#include "stdafx.h"*/ /* Needed by Visual C++ */ - #include "dll.h" +#include + +struct par_suits_type +{ + int suit; + int tricks; + int score; +}; + +struct best_par_type +{ + int par_denom; + int par_tricks; +}; struct localVarType localVar[MAXNOOFTHREADS]; @@ -6447,6 +6460,7 @@ int STDCALL SolveAllChunks(struct boardsPBN *bop, struct solvedBoards *solvedp, #endif #ifdef PBN_PLUS + int STDCALL CalcParPBN(struct ddTableDealPBN tableDealPBN, struct ddTableResults * tablep, int vulnerable, struct parResults *presp) { int res; @@ -6482,517 +6496,6 @@ int STDCALL CalcPar(struct ddTableDeal tableDeal, int vulnerable, } #endif -int Par(struct ddTableResults * tablep, struct parResults *presp, 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. */ - -/* 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; - int current_side, both_sides_once_flag, denom_max, max_lower; - int new_score_flag, sc1, sc2; - int prev_par_denom=0, prev_par_tricks=0; - - int ut, t1, t2, tt, score, dr, ke, tu, tu_max, t3, t4, n; - struct 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 */ - int par_score[2] = {0, 0}; - int par_sacut[2] = {0, 0}; /* Undertricks for sacrifice (0 if not sac) */ - - 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); - - /* 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. */ - - for (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.*/ - - current_side=0; both_sides_once_flag=0; - while (1) { - - /* Find best contract for current side that beats current contract. - Choose highest contract if results are equal. */ - - k=(i+current_side) % 2; - - isvul=((vulnerable==1)||(k ? (vulnerable==3) : (vulnerable==2))); - - new_score_flag=0; - prev_par_denom=par_denom[i]; - prev_par_tricks=par_tricks[i]; - - /* Calculate tricks and score values and - store them for each denomination in structure par_suits[5]. */ - - for (j=0; j<=4; j++) { - 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.*/ - par_suits[j].suit=j; - par_suits[j].tricks=tt; - if ((tt > par_tricks[i]) || ((tt == par_tricks[i]) && - (j < par_denom[i]))) - par_suits[j].score=rawscore(j, tt, isvul); - else - par_suits[j].score=rawscore(-1, prev_par_tricks - tt, isvul); - } - - /* Sort the items in the par_suits structure with decreasing order of the - values on the scores. */ - - for (int s = 1; s < 5; s++) { - struct par_suits_type tmp = par_suits[s]; - int r = s; - for (; r && tmp.score > par_suits[r - 1].score ; --r) - par_suits[r] = par_suits[r - 1]; - par_suits[r] = tmp; - } - - /* Do the iteration as before but now in the order of the sorted denominations. */ - - for (m=0; m<=4; m++) { - j=par_suits[m].suit; - tt=par_suits[m].tricks; - - if ((tt > par_tricks[i]) || ((tt == par_tricks[i]) && - (j < par_denom[i]))) { - /* Can bid higher and make contract.*/ - score=rawscore(j, tt, isvul); - } - else { - /* 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!) */ - if (prev_par_tricks == 13) - continue; - /* It will be necessary to bid one level higher, resulting in - one more undertrick. */ - ut++; - } - /* Not a sacrifice (due to par_tricks > prev_par_tricks) */ - if (ut <= 0) - continue; - /* Compute sacrifice.*/ - score=rawscore(-1, ut, isvul); - } - if (current_side == 1) - score=-score; - - if (((current_side == 0)&&(score > par_score[i])) || - ((current_side == 1)&&(score < par_score[i]))) { - new_score_flag = 1; - par_score[i] = score; - par_denom[i] = j; - - if (((current_side == 0)&&(score > 0)) || - ((current_side == 1)&&(score < 0))) { - /* New par score from a making contract. - Can immediately update since score at same level in higher - ranking suit is always >= score in lower ranking suit and - better than any sacrifice. */ - par_tricks[i] = tt; - par_sacut[i] = 0; - } - else { - par_tricks[i] = tt + ut; - par_sacut[i] = ut; - } - } - } - - if (!new_score_flag && both_sides_once_flag) - break; - both_sides_once_flag = 1; - current_side = 1 - current_side; - } - } - - 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'; - - /*itoa(par_score[0], temp, 10);*/ - sprintf(temp, "%d", par_score[0]); - strcat(presp->parScore[0], temp); - /*itoa(par_score[1], temp, 10);*/ - sprintf(temp, "%d", par_score[1]); - strcat(presp->parScore[1], temp); - - for (i=0; i<=1; i++) { - 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'; - } - - if (par_score[0] == 0) { - /* Neither side can make anything.*/ - return 1; - } - - - for (i=0; i<=1; i++) { - - if ( par_sacut[i] > 0 ) { - - dr = (par_score[i] > 0) ? 0 : 1; - - for (j=0/*par_denom[i]*/; j<=4; j++) { - - 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; - - tu_max=0; - - for (m=0; m<=4; m++) { - t3 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : tablep->resTable[denom_conv[m]][1]; - t4 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : tablep->resTable[denom_conv[m]][3]; - tu = (t3 > t4) ? t3 : t4; - if (tu > tu_max) { - tu_max=tu; - denom_max=m; - } - } - - if (j >= par_denom[i]) { - if (((par_tricks[i] - par_sacut[i]) != tt) || ((par_denom[i] < denom_max) && (j > denom_max))) - continue; - } - else if ((denom_max < par_denom[i])&&(j < denom_max)) { - if ((par_tricks[i] - 1 - par_sacut[i]) != tt) - continue; - } - else { - if ((par_tricks[i] - par_sacut[i]) != tt) - continue; - } - - - IniSidesString(dr, i, t1, t2, buff); - - if (presp->parContractsString[i][3]!='\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); - - /*itoa(par_tricks[i]-6, temp, 10);*/ - if ((denom_max < par_denom[i]) && (j < denom_max)) - sprintf(temp, "%d", par_tricks[i]-7); - else - sprintf(temp, "%d", par_tricks[i]-6); - buff[0]=cardSuit[denom_conv[j]]; - buff[1]='x'; - buff[2]='\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); - - stat_contr[0]++; - } - } - else { - /* Par contract is a makeable contract.*/ - dr = (par_score[i] < 0) ? 0 : 1; - - /* If spades or diamonds, lower major / minor may also be a par contract.*/ - ke = (par_denom[i] == 1 || par_denom[i] == 3) ? 1 : 0; - - for (j=par_denom[i]; j<=par_denom[i]+ke; j++) { - 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; - - if (tt < par_tricks[i]) { continue; } - - IniSidesString(dr, i, t1, t2, buff); - - tu_max=0; - for (m=0; m<=4; m++) { - t3 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : tablep->resTable[denom_conv[m]][1]; - t4 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : tablep->resTable[denom_conv[m]][3]; - tu = (t3 > t4) ? t3 : t4; - if (tu > tu_max) { - tu_max=tu; - denom_max=m; /* Lowest denomination if several denominations have max tricks. */ - } - } - - if (presp->parContractsString[i][3]!='\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); - - if (denom_max < par_denom[i]) - max_lower = par_tricks[i] - tu_max - 1; - else - max_lower = par_tricks[i] - 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.*/ - - sc2 = abs(par_score[i]); - /* Score for making the tentative lower par contract. */ - while (max_lower > 0) { - if (denom_max < par_denom[i]) - sc1 = -rawscore(-1, par_tricks[i] - max_lower - tu_max, - VulnerDefSide(par_score[0]>0, vulnerable)); - else - sc1 = -rawscore(-1, par_tricks[i] - max_lower - tu_max + 1, - VulnerDefSide(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. */ - } - - switch (par_denom[i]) { - case 0: k = 0; break; - case 1: case 2: k = 1; break; - case 3: case 4: k = 2; - } - - max_lower = Min(max_low[k][par_tricks[i]-6], max_lower); - - n = CalcMultiContracts(max_lower, par_tricks[i]); - - /*itoa(n, temp, 10);*/ - sprintf(temp, "%d", n); - buff[0]=cardSuit[denom_conv[j]]; - buff[1]='\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); - - stat_contr[1]++; - } - - - /* Deal with special case of 3N/5m (+400/600) */ - if ((par_denom[i] == 0) && (par_tricks[i] == 9)) { - - for (j=3; j<=4; j++) { - 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; - - if (tt != 11) { continue; } - - IniSidesString(dr, i, t1, t2, buff); - - if (presp->parContractsString[i][3]!='\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); - - /*itoa(5, temp, 10);*/ - sprintf(temp, "%d", 5); - buff[0]=cardSuit[denom_conv[j]]; - buff[1]='\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); - - stat_contr[2]++; - } - - } - /* Deal with special case of 2S/2H (+110) which may have 3C and 3D - as additional par contract(s).*/ - if ((par_denom[i] <=2) && (par_denom[i] != 0) && (par_tricks[i] == 8)) { - /* Check if 3C and 3D make.*/ - for (j=3; j<=4; j++) { - 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; - - if (tt != 9) { continue; } - - IniSidesString(dr, i, t1, t2, buff); - - tu_max=0; - - for (m=0; m<=4; m++) { - t3 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : tablep->resTable[denom_conv[m]][1]; - t4 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : tablep->resTable[denom_conv[m]][3]; - tu = (t3 > t4) ? t3 : t4; - if (tu > tu_max) { - tu_max=tu; - denom_max=m; - } - } - - if (presp->parContractsString[i][3]!='\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); - - if (denom_max < j) - max_lower = 9 - tu_max - 1; - else - max_lower = 9 - 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.*/ - - sc2 = abs(par_score[i]); - /* Score for making the tentative lower par contract. */ - while (max_lower > 0) { - if (denom_max < j) - sc1 = -rawscore(-1, 9 - max_lower - tu_max, - VulnerDefSide(par_score[0]>0, vulnerable)); - else - sc1 = -rawscore(-1, 9 - max_lower - tu_max + 1, - VulnerDefSide(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. */ - } - - switch (par_denom[i]) { - case 0: k = 0; break; - case 1: case 2: k = 1; break; - case 3: case 4: k = 2; - } - - max_lower = Min(max_low[k][3], max_lower); - - n = CalcMultiContracts(max_lower, 9); - - /*itoa(n, temp, 10);*/ - sprintf(temp, "%d", n); - buff[0]=cardSuit[denom_conv[j]]; - buff[1]='\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); - - stat_contr[3]++; - } - } - /* Deal with special case 1NT (+90) which may have 2C or 2D as additonal par - contracts(s). */ - if ((par_denom[i] == 0) && (par_tricks[i] == 7)) { - for (j=3; j<=4; j++) { - 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; - - if (tt != 8) { continue; } - - IniSidesString(dr, i, t1, t2, buff); - - tu_max=0; - for (m=0; m<=4; m++) { - t3 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][0] : tablep->resTable[denom_conv[m]][1]; - t4 = ((dr+i) % 2 == 0) ? tablep->resTable[denom_conv[m]][2] : tablep->resTable[denom_conv[m]][3]; - tu = (t3 > t4) ? t3 : t4; - if (tu > tu_max) { - tu_max=tu; - denom_max=m; - } - } - - if (presp->parContractsString[i][3]!='\0') - strcat(presp->parContractsString[i], contr_sep); - - strcat(presp->parContractsString[i], buff); - - if (denom_max < j) - max_lower = 8 - tu_max - 1; - else - max_lower = 8 - 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.*/ - - sc2 = abs(par_score[i]); - /* Score for making the tentative lower par contract. */ - while (max_lower > 0) { - if (denom_max < j) - sc1 = -rawscore(-1, 8 - max_lower - tu_max, - VulnerDefSide(par_score[0]>0, vulnerable)); - else - sc1 = -rawscore(-1, 8 - max_lower - tu_max + 1, - VulnerDefSide(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. */ - } - - switch (par_denom[i]) { - case 0: k = 0; break; - case 1: case 2: k = 1; break; - case 3: case 4: k = 2; - } - - max_lower = Min(max_low[k][3], max_lower); - - n = CalcMultiContracts(max_lower, 8); - - /*itoa(n, temp, 10);*/ - sprintf(temp, "%d", n); - buff[0]=cardSuit[denom_conv[j]]; - buff[1]='\0'; - strcat(temp, buff); - strcat(presp->parContractsString[i], temp); - - stat_contr[4]++; - } - } - } - } - - return 1; -} - int rawscore(int denom, int tricks, int isvul) { int game_bonus, level, score; @@ -7049,48 +6552,6 @@ int rawscore(int denom, int tricks, int isvul) { } -void IniSidesString(int dr, int i, int t1, int t2, char stri[]) { - - if ((dr+i) % 2 ) { - if (t1==t2) { - stri[0]='N'; - stri[1]='S'; - stri[2]=' '; - stri[3]='\0'; - } - else if (t1 > t2) { - stri[0]='N'; - stri[1]=' '; - stri[2]='\0'; - } - else { - stri[0]='S'; - stri[1]=' '; - stri[2]='\0'; - } - } - else { - if (t1==t2) { - stri[0]='E'; - stri[1]='W'; - stri[2]=' '; - stri[3]='\0'; - } - else if (t1 > t2) { - stri[0]='E'; - stri[1]=' '; - stri[2]='\0'; - } - else { - stri[0]='W'; - stri[1]=' '; - stri[2]='\0'; - } - } - return; -} - - int CalcMultiContracts(int max_lower, int tricks) { int n; @@ -7138,6 +6599,668 @@ int VulnerDefSide(int side, int vulnerable) { } } +int Par(ddTableResults * tablep, parResults * presp, + 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. */ + + /* The Par function computes the par result and contracts. */ + + int SidesParBin( + ddTableResults * tablep, + parResultsMaster sidesRes[2], + int vulnerable); + + 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 " } }; + + res = SidesParBin(tablep, sidesRes, vulnerable); + + if (res != RETURN_NO_FAULT) + return res; + + 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", sidesRes[0].score); + strcat(presp->parScore[0], temp); + sprintf(temp, "%d", sidesRes[1].score); + 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'; + + if (sidesRes[0].score == 0) + { + /* Neither side can make anything.*/ + + return RETURN_NO_FAULT; + } + + for (int i = 0; i <= 1; i++) + { + if (sidesRes[i].contracts[0].underTricks > 0) + { + /* Sacrifice*/ + + for (k = 0; k < sidesRes[i].number; k++) { + + 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 { + /* Make */ + + for (k = 0; k < sidesRes[i].number; k++) { + + strcat(presp->parContractsString[i], seats[sidesRes[i].contracts[k].seats]); + + int n = CalcMultiContracts(sidesRes[i].contracts[k].overTricks, sidesRes[i].contracts[k].overTricks + + sidesRes[i].contracts[k].level + 6); + + + 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 RETURN_NO_FAULT; +} + + +int 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. */ + + /* 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; + 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; + int denom_filter[5] = { 0, 0, 0, 0, 0 }; + int no_filtered[2] = { 0, 0 }; + int no_of_denom[2]; + int best_par_score[2]; + int best_par_sacut[2]; + best_par_type best_par[5][2]; /* 1st index order number. */ + + int ut = 0, t1, t2, tt, score, dr, tu, tu_max, t3[5], t4[5], n; + par_suits_type par_suits[5]; + + int par_denom[2] = { -1, -1 }; /* 0-4 = NT,S,H,D,C */ + int par_tricks[2] = { 6, 6 }; /* Initial "contract" beats 0 NT */ + int par_score[2] = { 0, 0 }; + int par_sacut[2] = { 0, 0 }; /* Undertricks for sacrifice (0 if not sac) */ + + + /* 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. */ + + for (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.*/ + + no_filtered[i] = 0; + for (m = 0; m <= 4; m++) + denom_filter[m] = 0; + + current_side = 0; + both_sides_once_flag = 0; + while (1) + { + + /* Find best contract for current side that beats current contract. + Choose highest contract if results are equal. */ + + k = (i + current_side) % 2; + + isvul = ((vulnerable == 1) || (k ? (vulnerable == 3) : (vulnerable == 2))); + + new_score_flag = 0; + prev_par_denom = par_denom[i]; + prev_par_tricks = par_tricks[i]; + + /* Calculate tricks and score values and + store them for each denomination in structure par_suits[5]. */ + + n = 0; + for (j = 0; j <= 4; j++) + { + 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]; + tt = Max(t1, t2); + /* tt is the maximum number of tricks current side can take in + denomination.*/ + + par_suits[n].suit = j; + par_suits[n].tricks = tt; + + if ((tt > par_tricks[i]) || ((tt == par_tricks[i]) && + (j < par_denom[i]))) + par_suits[n].score = rawscore(j, tt, isvul); + else + par_suits[n].score = rawscore(-1, prev_par_tricks - tt, isvul); + n++; + } + } + + /* Sort the items in the par_suits structure with decreasing order of the + values on the scores. */ + + for (int s = 1; s < n; s++) + { + par_suits_type tmp = par_suits[s]; + int r = s; + for (; r && tmp.score > par_suits[r - 1].score; --r) + par_suits[r] = par_suits[r - 1]; + par_suits[r] = tmp; + } + + /* Do the iteration as before but now in the order of the sorted denominations. */ + + for (m = 0; m < n; m++) + { + j = par_suits[m].suit; + tt = par_suits[m].tricks; + + if ((tt > par_tricks[i]) || ((tt == par_tricks[i]) && + (j < par_denom[i]))) + { + /* Can bid higher and make contract.*/ + score = rawscore(j, tt, isvul); + } + else + { + /* 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!) */ + if (prev_par_tricks == 13) + continue; + /* It will be necessary to bid one level higher, resulting in + one more undertrick. */ + ut++; + } + /* Not a sacrifice (due to par_tricks > prev_par_tricks) */ + if (ut <= 0) + continue; + /* Compute sacrifice.*/ + score = rawscore(-1, ut, isvul); + } + + if (current_side == 1) + score = -score; + + if (((current_side == 0) && (score > par_score[i])) || + ((current_side == 1) && (score < par_score[i]))) + { + new_score_flag = 1; + par_score[i] = score; + par_denom[i] = j; + + if (((current_side == 0) && (score > 0)) || + ((current_side == 1) && (score < 0))) + { + /* New par score from a making contract. + Can immediately update since score at same level in higher + ranking suit is always >= score in lower ranking suit and + better than any sacrifice. */ + + par_tricks[i] = tt; + par_sacut[i] = 0; + } + else + { + par_tricks[i] = tt + ut; + par_sacut[i] = ut; + } + } + } + + + if (!new_score_flag && both_sides_once_flag) + { + 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; + } + else if (best_par_score[i] != par_score[i]) + break; + if (no_filtered[i] >= 5) + break; + denom_filter[par_denom[i]] = 1; + no_filtered[i]++; + best_par[no_of_denom[i]][i].par_denom = par_denom[i]; + best_par[no_of_denom[i]][i].par_tricks = par_tricks[i]; + no_of_denom[i]++; + both_sides_once_flag = 0; + current_side = 0; + par_denom[i] = -1; + par_tricks[i] = 6; + par_score[i] = 0; + par_sacut[i] = 0; + } + else + { + both_sides_once_flag = 1; + current_side = 1 - current_side; + } + } + } + + /* 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++) + { + 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. */ + + for (int s = 1; s < no_of_denom[i]; s++) + { + best_par_type tmp = best_par[s][i]; + int r = s; + for (; r && tmp.par_denom < best_par[r - 1][i].par_denom; --r) + best_par[r][i] = best_par[r - 1][i]; + best_par[r][i] = tmp; + } + + for (m = 0; m < no_of_denom[i]; m++) + { + + 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]; + tt = (t1 > t2) ? t1 : t2; + + 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]; + + } + } + else + { + /* Par contract is a makeable contract.*/ + + dr = (best_par_score[i] < 0) ? 0 : 1; + + 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]; + 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. */ + } + } + + for (m = 0; m < no_of_denom[i]; m++) + { + 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]; + tt = (t1 > t2) ? t1 : t2; + + 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.*/ + + 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)); + 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.*/ + + 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. */ + } + + 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)); + + /* 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. */ + max_lower--; + else + break; + } + + switch (j) + { + case 0: + k = 0; + break; + case 1: + case 2: + k = 1; + break; + case 3: + case 4: + k = 2; + break; + default: + 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); + + sidesRes[i].contracts[m].denom = j; + sidesRes[i].contracts[m].underTricks = 0; + + 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] = { -1, -1 }; /* 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; + + if (((sidesRes[opp_side[i]].contracts[k].level + sidesRes[opp_side[i]].contracts[k].overTricks) > dom_level[opp_side[i]]) || + (((sidesRes[opp_side[i]].contracts[k].level + sidesRes[opp_side[i]].contracts[k].overTricks) == 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 m = 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[m] = sidesRes[i].contracts[k]; + m++; + } + } + sidesRes[i].number = m; + } + } + + return RETURN_NO_FAULT; +} + + +void SideSeats(int dr, int i, int t1, int t2, int order, parResultsMaster sidesRes[2]) +{ + + if ((dr + i) % 2) + { + if (t1 == t2) + { + sidesRes[i].contracts[order].seats = 4; + } + else if (t1 > t2) + { + sidesRes[i].contracts[order].seats = 0; + } + else + { + sidesRes[i].contracts[order].seats = 2; + } + } + else + { + if (t1 == t2) + { + sidesRes[i].contracts[order].seats = 5; + } + else if (t1 > t2) + { + sidesRes[i].contracts[order].seats = 1; + } + else + { + 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; +} + + + + + + + diff --git a/dll.h b/dll.h index e5bfcbab..422e4846 100755 --- a/dll.h +++ b/dll.h @@ -46,6 +46,8 @@ #define PBN_PLUS +#define RETURN_NO_FAULT 1 + /*#define BENCH*/ #include @@ -333,11 +335,29 @@ struct allParResults { struct parResults presults[MAXNOOFBOARDS / 20]; }; -struct par_suits_type { +struct contractType +{ + int underTricks; /* 0 = make 1-13 = sacrifice */ + int overTricks; /* 0-3, e.g. 1 for 4S + 1. */ + int level; /* 1-7 */ + int denom; /* 0 = No Trumps, 1 = trump Spades, 2 = trump Hearts, + 3 = trump Diamonds, 4 = trump Clubs */ + int seats; /* One of the cases N, E, W, S, NS, EW; + 0 = N 1 = E, 2 = S, 3 = W, 4 = NS, 5 = EW */ +}; + +struct parResultsMaster +{ + int score; /* Sign according to the NS view */ + int number; /* Number of contracts giving the par score */ + struct contractType contracts[10]; /* Par contracts */ +}; + +/*struct par_suits_type { int suit; int tricks; int score; -}; +};*/ struct localVarType { @@ -536,6 +556,33 @@ void Wipe(int thrId); void AddNodeSet(int thrId); void AddLenSet(int thrId); void AddWinSet(int thrId); +int rawscore( +int denom, +int tricks, +int isvul); + +void SideSeats( +int dr, +int i, +int t1, +int t2, +int order, +parResultsMaster sidesRes[2]); + +void CalcOverTricks( +int i, +int max_lower, +int tricks, +int order, +parResultsMaster sidesRes[2]); + +int CalcMultiContracts( +int max_lower, +int tricks); + +int VulnerDefSide( +int side, +int vulnerable); void PrintDeal(FILE *fp, unsigned short ranks[4][4]); diff --git a/release_notes.txt b/release_notes.txt index 7b0d7869..a9d611c4 100755 --- a/release_notes.txt +++ b/release_notes.txt @@ -456,4 +456,8 @@ A fault in the par calculation has been fixed. The fault could cause presentation of wrongly extra par contracts in cases were the sides had different vulnerability. +Release Notes DDS 2.5.3 +----------------------- +Par calculation faults have been fixed. +