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];
}
}
}