using System; using System.Collections.Generic; using System.Linq; using System.IO; namespace Analizator9000 { /// /// Analysis accumulator for scoring contracts /// class ScoreAccumulator : Accumulator { private readonly string[] vulnerabilities = { Form1.GetResourceManager().GetString("ScoreAccumulator_vulNone", Form1.GetCulture()), Form1.GetResourceManager().GetString("ScoreAccumulator_vulBoth", Form1.GetCulture()), Form1.GetResourceManager().GetString("ScoreAccumulator_vulNS", Form1.GetCulture()), Form1.GetResourceManager().GetString("ScoreAccumulator_vulEW", Form1.GetCulture()) }; /// /// Vulnerability setting of the analysis /// private int vulnerability; /// /// List of contracts coming from GUI table /// private List contractsToScore; /// /// Scores for the contracts /// private Dictionary> scores; /// /// Counter of deals already fully scored /// private long dealsScored = 0; /// /// Number of tricks accumulator /// private Dictionary trickSums; /// /// Sum of scores accumulator /// private Dictionary scoreSums; /// /// Sum of success rate accumulator /// private Dictionary successSums; /// /// Matchpoint score accumulator /// private Dictionary maxScoreSums; /// /// IMP score accumulator /// private Dictionary impScoreSums; /// /// Scorer instance for matchpoints /// private IScorer maxScorer; /// /// Scorer instance for IMPs /// private IScorer impScorer; /// /// Constructor for score accumulator /// /// Base class parameter /// Base class parameter (contracts for BCalc engine) /// List of contracts in board traveller /// Vulnerability setting for analysis /// Base class parameter public ScoreAccumulator(String[] deals, List contracts, List contractsToScore, int vulnerability, Form1 form) : base(deals, contracts, form) { this.contractsToScore = contractsToScore; this.vulnerability = vulnerability; this.scores = new Dictionary>(); this.trickSums = new Dictionary(); this.scoreSums = new Dictionary(); this.successSums = new Dictionary(); this.maxScoreSums = new Dictionary(); this.maxScorer = new MaxScorer(); this.impScoreSums = new Dictionary(); this.impScorer = new ImpScorer(); foreach (Contract sC in this.contractsToScore) { if (sC.Frequency > 0) { this.trickSums[sC] = 0; this.scoreSums[sC] = 0; this.successSums[sC] = 0; this.maxScoreSums[sC] = 0; this.impScoreSums[sC] = 0; } } } /// /// Returns user-readable summary of analysis /// /// Append matchpoint/IMP scores summary /// Formatted log string protected override String getString(bool full = false) { String output = base.getString(full); if (full) { StringWriter sw = new StringWriter(); sw.WriteLine(); sw.WriteLine(Form1.GetResourceManager().GetString("ScoreAccumulator_vulnerability", Form1.GetCulture()) + ": {0}", this.vulnerabilities[this.vulnerability]); sw.WriteLine(Form1.GetResourceManager().GetString("ScoreAccumulator_txtHeader", Form1.GetCulture())); foreach (KeyValuePair tricks in this.trickSums) { sw.WriteLine(" {0,6} (x{1,3}) {2,5:0.00} {3,9:0.00} {6,5:0.00} {4,5:0.00} {5,7:0.00} ", this.getContractLogLine(tricks.Key), tricks.Key.Frequency, (double)tricks.Value / this.dealsScored, (double)this.scoreSums[tricks.Key] / this.dealsScored, this.maxScoreSums[tricks.Key] / this.dealsScored, this.impScoreSums[tricks.Key] / this.dealsScored, (double)this.successSums[tricks.Key] / this.dealsScored); } sw.Close(); output += sw.ToString(); } return output; } /// /// Handling single DD analysis result /// /// BCalc analysis result 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(); 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); } } /// /// Check whether the entire board for specific deal can be scored /// /// Deal number private void checkScoring(long dealNo) { // Don't score as long as there are some unscored contracts foreach (KeyValuePair scoreTable in this.scores[dealNo]) { if (scoreTable.Value == long.MinValue) { return; } } this.dealsScored++; // IMP scores for every contract in the traveller Dictionary impScores = this.impScorer.scoreBoard(this.scores[dealNo]); foreach (KeyValuePair result in impScores) { this.impScoreSums[result.Key] += result.Key.Frequency > 0 ? result.Value : 0; } // Matchpoint scores for every contract in the traveller Dictionary maxScores = this.maxScorer.scoreBoard(this.scores[dealNo]); foreach (KeyValuePair result in maxScores) { this.maxScoreSums[result.Key] += result.Key.Frequency > 0 ? result.Value : 0; } this.logScores(impScores, dealNo, Form1.GetResourceManager().GetString("ScoreAccumulator_IMP", Form1.GetCulture())); this.logScores(maxScores, dealNo, Form1.GetResourceManager().GetString("ScoreAccumulator_MP", Form1.GetCulture())); this.form.updateContractTable(this.trickSums, this.scoreSums, this.successSums, this.maxScoreSums, this.impScoreSums, this.dealsScored); } /// /// Helper method for logging entire board scores /// /// Scored board /// Deal number /// Score suffix private void logScores(Dictionary scores, long dealNo, string type) { foreach (KeyValuePair score in scores) { string logLine = "#" + dealNo.ToString() + ", " + this.getContractLogLine(score.Key) + ": " + score.Value.ToString("0.##") + " " + type; this.form.addStatusLine(logLine); this.outputFile.WriteLine(logLine); } } /// /// Generates human-readable representation of a contract (level, denomination, modifiers, declarer) /// /// Contract to transform /// Representation of the contract, e.g. 3Hx N, PASS 1Nxx S, 7S E private string getContractLogLine(Contract contract) { if (contract.Level == 0) { return "PASS"; } return contract.Level.ToString() + BCalcWrapper.denominations[contract.Denomination] + new String('x', contract.Modifiers) + " " + BCalcWrapper.table[contract.Declarer]; } } }