/// <summary> /// Handling single DD analysis result /// </summary> /// <param name="result">BCalc analysis result</param> protected override void update(BCalcResult result) { base.update(result); lock (this.contractsToScore) { if (!this.scores.Keys.Contains(result.dealNo)) // first time we see such deal, so we should initialize some stuff { this.scores[result.dealNo] = new Dictionary <Contract, long>(); foreach (Contract sC in this.contractsToScore) { this.scores[result.dealNo][sC] = sC.Level > 0 ? long.MinValue : 0; // All Pass contracts are already scored, other are set as "empty" (MinValue) } } foreach (Contract sC in this.contractsToScore) { // if the analysis result matches the contract, to score, score it if (sC.Level > 0 && BCalcWrapper.table[sC.Declarer] == result.declarer && BCalcWrapper.denominations[sC.Denomination] == result.trumpSuit) { int score = sC.getScore(result, this.vulnerability); string logLine = "#" + result.dealNo.ToString() + ", " + this.getContractLogLine(sC) + ": " + result.tricks.ToString() + " " + Form1.GetResourceManager().GetString("Accumulator_tricks", Form1.GetCulture()) + ", " + score.ToString(); this.form.addStatusLine(logLine); this.outputFile.WriteLine(logLine); this.scores[result.dealNo][sC] = score; this.trickSums[sC] += result.tricks; this.scoreSums[sC] += score; if ((sC.Declarer == Contract.DECLARER_NORTH || sC.Declarer == Contract.DECLARER_SOUTH) != (score < 0)) // NS plays XOR negative score (NS plays and positive score or EW plays and negative score) { this.successSums[sC]++; } } } // check if the entire board can already be scored this.checkScoring(result.dealNo); } }
/// <summary> /// Feeds the overall results with chunks of data from single contract analysis. /// </summary> /// <param name="result">Result of BCalc analysis.</param> protected virtual void update(BCalcResult result) { int tricks = result.tricks; int suit = BCalcWrapper.denominations.IndexOf(result.trumpSuit); int player = BCalcWrapper.table.IndexOf(result.declarer); this.sums[suit][player][0] += tricks; this.sums[suit][player][1] += tricks * tricks; this.sums[suit][player][2]++; }
/// <summary> /// Worker method for a single deal. /// </summary> /// <param name="deal">Deal in BCalc's "NESW" format, with deal number prepended.</param> private void analyzeDeal(String deal) { try { this.form.addStatusLine(deal); BCalcWrapper solver = new BCalcWrapper(deal); foreach (KeyValuePair <int, List <int> > row in this.contracts) { try { solver.setTrump(row.Key); } catch (Exception ex) { this.form.addStatusLine(Form1.GetResourceManager().GetString("Form1_error", Form1.GetCulture()) + ": " + ex.Message); } foreach (int entry in row.Value) { try { BCalcResult result = solver.run(entry); if (!this.abort) { String line = "#" + result.dealNo + ", " + result.declarer + " " + Form1.GetResourceManager().GetString("Accumulator_playsIn", Form1.GetCulture()) + " " + result.trumpSuit + ", " + Form1.GetResourceManager().GetString("Accumulator_tricks", Form1.GetCulture()) + ": " + result.tricks; this.form.addStatusLine(line); this.outputFile.WriteLine(line); this.update(result); this.form.setResult(this.getString()); } } catch (Exception ex) { this.form.addStatusLine(Form1.GetResourceManager().GetString("Form1_error", Form1.GetCulture()) + ": " + ex.Message); } } } solver.destroy(); } catch (Exception ex) { this.outputFile.WriteLine(ex.Message); this.form.addStatusLine(Form1.GetResourceManager().GetString("Form1_error", Form1.GetCulture()) + ": " + ex.Message); } }
/// <summary> /// Calculates score for contract result /// </summary> /// <param name="result">BCalc output result</param> /// <param name="vulnerability">Vulnerability for the deal</param> /// <returns>Score for NS pair</returns> public int getScore(BCalcResult result, int vulnerability) { // All PASS. if (this.Level == 0) { return(0); } if (BCalcWrapper.table[this.Declarer] != result.declarer) { throw new Exception("Declarer mismatch!"); } // Determining vulnerability of the contract bool vulnerable = (vulnerability == Contract.VULNERABLE_BOTH) || (vulnerability == Contract.VULNERABLE_EW && this.Declarer == Contract.DECLARER_EAST) || (vulnerability == Contract.VULNERABLE_EW && this.Declarer == Contract.DECLARER_WEST) || (vulnerability == Contract.VULNERABLE_NS && this.Declarer == Contract.DECLARER_NORTH) || (vulnerability == Contract.VULNERABLE_NS && this.Declarer == Contract.DECLARER_SOUTH); int score = 0; // Contract not made if (this.Level + 6 > result.tricks) { int undertricks = this.Level + 6 - result.tricks; if (this.Modifiers < 1) // undoubled undertricks { score = vulnerable ? -100 : -50; score *= undertricks; } else // (re-)doubled undertricks { do { if (undertricks == 1) // first undertrick: 100 non-vul, 200 vul { score -= vulnerable ? 200 : 100; } else { if (undertricks <= 3 && !vulnerable) // second non-vul undertrick: 200 { score -= 200; } else // further undertricks: 300 { score -= 300; } } undertricks--; }while (undertricks > 0); score *= this.Modifiers; // redouble doubles the score } } else // Contract made, yay! { int parTricks = this.Level; do { if (this.Denomination == Contract.DENOMINATION_NONTRUMP && parTricks == 1) // first non-trump trick: 40 { score += 40; } else // other tricks { switch (this.Denomination) { case Contract.DENOMINATION_NONTRUMP: case Contract.DENOMINATION_SPADES: case Contract.DENOMINATION_HEARTS: score += 30; break; case Contract.DENOMINATION_DIAMONDS: case Contract.DENOMINATION_CLUBS: score += 20; break; } } parTricks--; }while (parTricks > 0); if (this.Modifiers > 0) // (re-)doubled tricks { score *= this.Modifiers * 2; } score += (score >= 100) ? (vulnerable ? 500 : 300) : 50; // game premium if (this.Level == 7) // grand slam premium { score += vulnerable ? 1500 : 1000; } else if (this.Level == 6) // small slam premium { score += vulnerable ? 750 : 500; } score += this.Modifiers * 50; // failed (re-)double premium int overtricks = result.tricks - this.Level - 6; score += this.Modifiers > 0 ? (vulnerable ? 200 : 100) * this.Modifiers * overtricks // (re-)double overtricks: 100/200/200/400 : overtricks * ((this.Denomination == Contract.DENOMINATION_CLUBS || this.Denomination == Contract.DENOMINATION_DIAMONDS) ? 20 : 30); // undoubled overtricks } // If EW played the board, the score is shifted to the other side if (this.Declarer == Contract.DECLARER_WEST || this.Declarer == Contract.DECLARER_EAST) { score = -score; } return(score); }