using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; using System.IO; using System.Resources; using System.Threading; using System.Globalization; using System.Collections.Specialized; using System.Configuration; namespace Analizator9000 { /// /// Main application window control. /// public partial class Form1 : Form { /// /// Dealer input scripts parser instance. /// private DealerParser parser; private static CultureInfo _culture; /// /// Returns application culture info. /// /// Culture info to be applied to localization mechanisms public static CultureInfo GetCulture() { if (_culture == null) { _culture = GetConfigCulture(); } return _culture; } /// /// Read language setting from configuration file /// /// CultureInfo object for specified language public static CultureInfo GetConfigCulture() { string config = System.Configuration.ConfigurationManager.AppSettings["language"]; string fallbackLanguage = "pl-PL"; if (config == null) { config = fallbackLanguage; } try { return new CultureInfo(config); } catch (ArgumentException) { return new CultureInfo(fallbackLanguage); } } /// /// Set UI culture /// /// CultureInfo object for specified language public void SetCulture(CultureInfo culture) { Thread.CurrentThread.CurrentUICulture = culture; } /// /// Display icons for language selection menu correctly and fall back to Polish if configuration file has unsupported locale /// /// public void SetCultureMenu(CultureInfo culture) { bool languageSupported = false; foreach (ToolStripDropDownItem item in langSelectSplitButton.DropDownItems) { if (culture.TwoLetterISOLanguageName == item.Tag.ToString()) { langSelectSplitButton.Image = item.Image; languageSupported = true; } } if (!languageSupported) { SetCultureMenu(new CultureInfo("pl")); } } /// /// Write user preference info on language to configuration file /// /// Language code public void SetConfigCulture(string newLang) { Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration("Analizator9000.exe"); try { config.AppSettings.Settings["language"].Value = newLang; } catch (NullReferenceException) { config.AppSettings.Settings.Add("language", newLang); } config.AppSettings.SectionInformation.ForceSave = true; config.Save(); Application.Restart(); } /// /// Constructs the main window. /// public Form1() { CultureInfo ci = GetCulture(); SetCulture(ci); InitializeComponent(); SetCultureMenu(ci); this.parser = new DealerParser(); } private static ResourceManager resManager; /// /// Public getter for localization resource manager /// /// Component resource manager for Form1 public static ResourceManager GetResourceManager() { if (resManager == null) { resManager = Strings.ResourceManager; } return resManager; } /// /// "Select file" button click, opens file selection dialog for input script. /// /// /// private void button1_Click(object sender, EventArgs e) { generateFileDialog.ShowDialog(); } /// /// Input script file selection event. Initiates input script parsing. /// /// /// private void openFileDialog1_FileOk(object sender, CancelEventArgs e) { try { this.parser = new DealerParser(); parser.loadFile(generateFileDialog.FileName); if (parser.produce > 0) produceBox.Text = parser.produce.ToString(); if (parser.generate > 0) generateBox.Text = parser.generate.ToString(); conditionBox.Text = parser.condition; if (parser.predeal.ContainsKey("east")) { predealEastSpadesBox.Text = parser.predeal["east"][0]; predealEastHeartsBox.Text = parser.predeal["east"][1]; predealEastDiamondsBox.Text = parser.predeal["east"][2]; predealEastClubsBox.Text = parser.predeal["east"][3]; } if (parser.predeal.ContainsKey("west")) { predealWestSpadesBox.Text = parser.predeal["west"][0]; predealWestHeartsBox.Text = parser.predeal["west"][1]; predealWestDiamondsBox.Text = parser.predeal["west"][2]; predealWestClubsBox.Text = parser.predeal["west"][3]; } if (parser.predeal.ContainsKey("north")) { predealNorthSpadesBox.Text = parser.predeal["north"][0]; predealNorthHeartsBox.Text = parser.predeal["north"][1]; predealNorthDiamondsBox.Text = parser.predeal["north"][2]; predealNorthClubsBox.Text = parser.predeal["north"][3]; } if (parser.predeal.ContainsKey("south")) { predealSouthSpadesBox.Text = parser.predeal["south"][0]; predealSouthHeartsBox.Text = parser.predeal["south"][1]; predealSouthDiamondsBox.Text = parser.predeal["south"][2]; predealSouthClubsBox.Text = parser.predeal["south"][3]; } foreach (String action in parser.actions) { actionsBox.Text += action.Substring("average".Length) + "\n"; } generateFileNameTextBox.Text = generateFileDialog.FileName; } catch (Exception ex) { MessageBox.Show( GetResourceManager().GetString("Form1_fileOpenError", GetCulture()) + ": " + ex.Message, GetResourceManager().GetString("Form1_fileOpenError", GetCulture()), MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// "Generate" button click. Saves the input script file and runs deals generating. /// /// /// private void generateButton_Click(object sender, EventArgs e) { generateGroup.Enabled = false; analyzeGroup.Enabled = false; statusListBox.Items.Clear(); progressBar.Value = 0; resultTextBox.Text = ""; try { this.parser.generate = Convert.ToInt64(generateBox.Text); this.parser.produce = Convert.ToInt64(produceBox.Text); parser.condition = conditionBox.Text; parser.predeal["north"] = new String[] { predealNorthSpadesBox.Text, predealNorthHeartsBox.Text, predealNorthDiamondsBox.Text, predealNorthClubsBox.Text }; parser.predeal["south"] = new String[] { predealSouthSpadesBox.Text, predealSouthHeartsBox.Text, predealSouthDiamondsBox.Text, predealSouthClubsBox.Text }; parser.predeal["west"] = new String[] { predealWestSpadesBox.Text, predealWestHeartsBox.Text, predealWestDiamondsBox.Text, predealWestClubsBox.Text }; parser.predeal["east"] = new String[] { predealEastSpadesBox.Text, predealEastHeartsBox.Text, predealEastDiamondsBox.Text, predealEastClubsBox.Text }; foreach (KeyValuePair predeal in parser.predeal) { for (int i = 0; i < predeal.Value.Length; i++) { predeal.Value[i] = new String(predeal.Value[i].ToUpper().Replace('D', 'Q').Replace('W', 'J').Replace("10", "T").OrderBy(c => "AKQJT98765432".IndexOf(c)).ToArray()); } } parser.actions = actionsBox.Text.Split('\n').ToList(); } catch (Exception ex) { MessageBox.Show(GetResourceManager().GetString("Form1_generateInputError", GetCulture()) + ": " + ex.Message, GetResourceManager().GetString("Form1_generateInputError"), MessageBoxButtons.OK, MessageBoxIcon.Error); generateGroup.Enabled = true; analyzeGroup.Enabled = true; } try { String outputFileName = parser.saveFile(); DealerWrapper dealer = new DealerWrapper("files/" + outputFileName, this, this.parser.produce); dealer.run(this.generateEnd); } catch (FileNotFoundException) { MessageBox.Show(GetResourceManager().GetString("Form1_generateFileNotFoundError", GetCulture()), GetResourceManager().GetString("Form1_error"), MessageBoxButtons.OK, MessageBoxIcon.Error); generateGroup.Enabled = true; analyzeGroup.Enabled = true; } catch (Exception ex) { MessageBox.Show(GetResourceManager().GetString("Form1_generateGeneratorError", GetCulture()) + ": " + ex.Message, GetResourceManager().GetString("Form1_generateGeneratorError"), MessageBoxButtons.OK, MessageBoxIcon.Error); generateGroup.Enabled = true; analyzeGroup.Enabled = true; } } /// /// Delegate for generating end method callback. /// /// private delegate void endDelegate(String filename); /// /// Generating end callback method. Prints out deal generating summary. /// /// Output (generated deals) file name. private void generateEnd(String filename) { if (this.InvokeRequired) { this.Invoke(new endDelegate(generateEnd), new object[] { filename }); } else { progressBar.Value = 100; if (filename != null) { this.addStatusLine(GetResourceManager().GetString("Form1_generateFileSaved", GetCulture()) + ": " + filename); } analyzeFileNameTextBox.Text = Path.GetFullPath(@"files\" + filename); exportBtn.Enabled = true; generateGroup.Enabled = true; analyzeGroup.Enabled = true; } } /// /// Delegate for debug line addition method. /// /// private delegate void AddStatusDelegate(String line); /// /// Debug line addition method (thread-safe). /// /// String to be appended to the debug output. public void addStatusLine(String line) { if (line != null) { if (statusListBox.InvokeRequired) { this.Invoke(new AddStatusDelegate(addStatusLine), new object[] { line }); } else { statusListBox.Items.Add(line); statusListBox.SelectedIndex = statusListBox.Items.Count - 1; } } } /// /// Delegate for progress bar update method. /// /// Percentage of progress to set (0-100) private delegate void SetProgressDelegate(int progress); /// /// Progress bar update method (thread-safe). /// /// Percentage of progress to set (0-100) public void setProgress(int progress) { if (progressBar.InvokeRequired) { this.Invoke(new SetProgressDelegate(setProgress), new object[]{ progress }); } else { progressBar.Value = progress; } } /// /// Deals file selection event. Sets the deals file path in the text field. /// /// /// private void analyzeFileDialog_FileOk(object sender, CancelEventArgs e) { analyzeFileNameTextBox.Text = analyzeFileDialog.FileName; exportBtn.Enabled = true; } /// /// "Select file" button click for deals file selection. /// /// /// private void button2_Click(object sender, EventArgs e) { analyzeFileDialog.ShowDialog(); } /// /// Instance of analysis summary class. /// private Accumulator ac; /// /// "Analyze" button click. Runs the analysis. /// /// /// private void analyzeButton_Click(object sender, EventArgs e) { this.contractCancelButton.Enabled = true; this.contractAnalyzeButton.Enabled = false; this.analyzeButton.Enabled = false; this.abortButton.Enabled = true; this.fullContractTable.Enabled = false; statusListBox.Items.Clear(); this.addStatusLine(GetResourceManager().GetString("Form1_analyzeOpenFile", GetCulture()) + ": " + analyzeFileNameTextBox.Text); try { String[] deals = File.ReadAllLines(analyzeFileNameTextBox.Text); if (deals.Length == 0) { throw new Exception(GetResourceManager().GetString("Form1_analyzeNoDealsError", GetCulture())); } List cons = new List(); foreach (int i in Enumerable.Range(1, 5)) { foreach (int j in Enumerable.Range(1, 4)) { if (((CheckBox)contractTable.GetControlFromPosition(i, j)).Checked) { cons.Add(new Contract(5 - i, j - 1)); } } } if (cons.Count == 0) { throw new Exception(GetResourceManager().GetString("Form1_analyzeNoContractsError", GetCulture())); } // we run either "old" Accumulator or contract analysis with ScoreAccumulator, // depending on the button of event origin this.ac = sender.GetHashCode().Equals(this.analyzeButton.GetHashCode()) ? new Accumulator(deals, cons, this) : new ScoreAccumulator(deals, cons, this.contractsToScore, vulnerabilityBox.SelectedIndex, this); this.ac.run(10); } catch (Exception ex) { MessageBox.Show(GetResourceManager().GetString("Form1_analyzeError", GetCulture()) + ": " + ex.Message, GetResourceManager().GetString("Form1_analyzeError"), MessageBoxButtons.OK, MessageBoxIcon.Error); this.setProgress(0); analyzeButton.Enabled = true; abortButton.Enabled = false; analyzeGroup.Enabled = true; generateGroup.Enabled = true; contractAnalyzeButton.Enabled = true; contractCancelButton.Enabled = false; fullContractTable.Enabled = true; } } /// /// Delegate for analysis end callback method. /// private delegate void EndAnalysisDelegate(); /// /// Analysis end callback method. Cleans up visually after the analysis. /// public void endAnalysis() { if (this.InvokeRequired) { this.Invoke(new EndAnalysisDelegate(endAnalysis)); } else { this.setProgress(100); abortButton.Enabled = false; analyzeGroup.Enabled = true; generateGroup.Enabled = true; analyzeButton.Enabled = true; contractAnalyzeButton.Enabled = true; contractCancelButton.Enabled = false; fullContractTable.Enabled = true; statusListBox.Focus(); this.ac = null; } } /// /// Delegate for result summary display method. /// /// Result summary string. private delegate void SetResultDelegate(String res); /// /// Displays analysis results summary. /// /// Result summary string. public void setResult(String res) { if (this.InvokeRequired) { this.Invoke(new SetResultDelegate(setResult), new object[] { res }); } else { resultTextBox.Text = res; } } /// /// Delegate for contract analysis output /// /// Dictionary of trick sums for analyzed contracts /// Dictionary of score sums for analyzed contracts /// Dictionary of success rate for analyzed contracts /// Dictionary of matchpoint sums for analyzed contracts /// Dictionary of IMP sums for analyzed contracts /// Number of deals already scored private delegate void UpdateContractTableDelegate(Dictionary trickSum, Dictionary scoreSum, Dictionary successSum, Dictionary maxSum, Dictionary impSum, long dealCount); /// /// Updates GUI output for contract analysis /// /// Dictionary of trick sums for analyzed contracts /// Dictionary of score sums for analyzed contracts /// Dictionary of success rate for analyzed contracts /// Dictionary of matchpoint sums for analyzed contracts /// Dictionary of IMP sums for analyzed contracts /// Number of deals already scored public void updateContractTable(Dictionary trickSum, Dictionary scoreSum, Dictionary successSum, Dictionary maxSum, Dictionary impSum, long dealCount) { if (this.InvokeRequired) { this.Invoke(new UpdateContractTableDelegate(updateContractTable), new object[] { trickSum, scoreSum, successSum, maxSum, impSum, dealCount }); } else { foreach (int row in Enumerable.Range(1, fullContractTable.RowCount - 1)) { Contract rowContract = this.getContractFromTableRow(row); if (rowContract != null && rowContract.Frequency > 0) { ((TextBox)fullContractTable.GetControlFromPosition(5, row)).Text = ((double)trickSum[rowContract] / dealCount).ToString("0.##"); ((TextBox)fullContractTable.GetControlFromPosition(6, row)).Text = ((double)scoreSum[rowContract] / dealCount).ToString("0.##"); ((TextBox)fullContractTable.GetControlFromPosition(7, row)).Text = ((double)successSum[rowContract] / dealCount).ToString("0.##"); double max = maxSum[rowContract] / dealCount; double imp = impSum[rowContract] / dealCount; if (rowContract.Declarer == Contract.DECLARER_EAST || rowContract.Declarer == Contract.DECLARER_WEST) { max = 1.0 - max; imp = -imp; } ((TextBox)fullContractTable.GetControlFromPosition(8, row)).Text = max.ToString("0.##"); ((TextBox)fullContractTable.GetControlFromPosition(9, row)).Text = imp.ToString("0.##"); } } } } /// /// "Abort" button click event. /// /// /// private void abortButton_Click(object sender, EventArgs e) { if (this.ac != null) { this.ac.abortAnalysis(); } } /// /// Mass checkboxes within contract table layout toggle method. /// /// Set of x-coordinates for checkboxes to toggle. /// Set of y-coordinates for checkboxes to toggle. /// Indicates whether box range should be toggled (true) or just checked (false) private void toggleBoxes(IEnumerable xRange, IEnumerable yRange, bool toggle = true) { foreach (int x in xRange) { foreach (int y in yRange) { CheckBox cb = ((CheckBox)contractTable.GetControlFromPosition(x, y)); cb.Checked = !(toggle && cb.Checked); } } } /// /// Toggles all contract chackboxes. /// /// /// private void button3_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(1, 4)); } /// /// Toggles the first column of checkboxes ("NT"). /// /// /// private void label18_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 1), Enumerable.Range(1, 4), false); } /// /// Toggles the second column of checkboxes (spades). /// /// /// private void label14_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(2, 1), Enumerable.Range(1, 4), false); } /// /// Toggles the third column of checkboxes (hearts). /// /// /// private void label15_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(3, 1), Enumerable.Range(1, 4), false); } /// /// Toggles the fourth column of checkboxes (diamonds). /// /// /// private void label16_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(4, 1), Enumerable.Range(1, 4), false); } /// /// Toggles the fifth column of checkboxes (clubs). /// /// /// private void label17_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(5, 1), Enumerable.Range(1, 4), false); } /// /// Toggles the first row of checkboxes (North). /// /// /// private void label19_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(1, 1), false); } /// /// Toggles the second row of checkboxes (East). /// /// /// private void label20_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(2, 1), false); } /// /// Toggles the third row of checkboxes (South). /// /// /// private void label21_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(3, 1), false); } /// /// Toggles the fourth row of checkboxes (West). /// /// /// private void label22_Click(object sender, EventArgs e) { this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(4, 1), false); } /// /// List of contracts to score by ScoreAccumulator /// private List contractsToScore = new List(); /// /// Starts contract analysis /// /// /// private void button4_Click(object sender, EventArgs e) { this.contractsToScore.Clear(); this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(1, 4), false); this.toggleBoxes(Enumerable.Range(1, 5), Enumerable.Range(1, 4)); foreach (int row in Enumerable.Range(1, fullContractTable.RowCount - 1)) { Contract rowContract = this.getContractFromTableRow(row); if (rowContract != null) { if (rowContract.Level > 0) { this.toggleBoxes(Enumerable.Range(5 - rowContract.Denomination, 1), Enumerable.Range(rowContract.Declarer + 1, 1), false); // forcing analysis of certain denomination-declarer combinations } if (rowContract.Frequency > 0) { this.contractsToScore.Add(rowContract); } } foreach (int column in Enumerable.Range(5, 4)) { ((TextBox)this.fullContractTable.GetControlFromPosition(column, row)).Text = ""; } } this.analyzeButton_Click(sender, e); } /// /// Enables contract table fields accordingly to selected combo boxes. /// /// /// private void tableCombo_Change(object sender, EventArgs e) { TableLayoutPanelCellPosition position = fullContractTable.GetPositionFromControl((Control)sender); NumericUpDown frequencyBox = (NumericUpDown)fullContractTable.GetControlFromPosition(4, position.Row); Contract rowContract = this.getContractFromTableRow(position.Row); if (rowContract != null) { frequencyBox.Enabled = true; if (frequencyBox.Value < 1) { frequencyBox.Value = 1; } if (rowContract.Level == 0) { ((ComboBox)fullContractTable.GetControlFromPosition(1, position.Row)).SelectedIndex = 0; ((ComboBox)fullContractTable.GetControlFromPosition(2, position.Row)).SelectedIndex = 0; ((ComboBox)fullContractTable.GetControlFromPosition(3, position.Row)).SelectedIndex = 0; } } else { frequencyBox.Enabled = false; frequencyBox.Value = 0; } } /// /// Retrieves contract based on a row of GUI table /// /// Index of the row for the contract /// Contract object representing user's choice or NULL when the choice is invalid/incomplete private Contract getContractFromTableRow(int rowIndex) { ComboBox levelBox = (ComboBox)fullContractTable.GetControlFromPosition(0, rowIndex); ComboBox denominationBox = (ComboBox)fullContractTable.GetControlFromPosition(1, rowIndex); ComboBox modifiersBox = (ComboBox)fullContractTable.GetControlFromPosition(2, rowIndex); ComboBox declarerBox = (ComboBox)fullContractTable.GetControlFromPosition(3, rowIndex); NumericUpDown frequencyBox = (NumericUpDown)fullContractTable.GetControlFromPosition(4, rowIndex); if (levelBox.SelectedIndex < 1 || (denominationBox.SelectedIndex < 1 && levelBox.SelectedIndex != 1) || (declarerBox.SelectedIndex < 1 && levelBox.SelectedIndex != 1)) { return null; } return new Contract(levelBox.SelectedIndex - 1, denominationBox.SelectedIndex - 1, (declarerBox.SelectedIndex + 2) % 4, Math.Max(0, modifiersBox.SelectedIndex), (int)frequencyBox.Value, rowIndex); } private void Form1_Load(object sender, EventArgs e) { vulnerabilityBox.SelectedIndex = 0; // set default vulnerability for contracts analysis } /// /// Cancels contract analysis /// /// /// private void button5_Click(object sender, EventArgs e) { this.contractCancelButton.Enabled = false; this.contractAnalyzeButton.Enabled = true; this.abortButton_Click(sender, e); } /// /// Selects language from toolbar/menu /// /// /// private void langSelectSplitButton_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) { string newLang = e.ClickedItem.Tag.ToString(); SetConfigCulture(newLang); } /// /// Displays project website in default browser /// /// /// private void toolStripWebsiteButton_Click(object sender, EventArgs e) { System.Diagnostics.Process.Start("http://an9k.emkael.info/"); } /// /// Exits from the application /// /// /// private void toolStripExitButton_Click(object sender, EventArgs e) { Application.Exit(); } private void button4_Click_1(object sender, EventArgs e) { exportFileDialog.ShowDialog(); } private void exportFileDialog_FileOk(object sender, CancelEventArgs e) { try { List output = new List(); String[] deals = File.ReadAllLines(analyzeFileNameTextBox.Text); if (deals.Length == 0) { throw new Exception(GetResourceManager().GetString("Form1_analyzeNoDealsError", GetCulture())); } String[] vulnerabilities = { "None", "All", "NS", "EW" }; output.Add("% PBN 1.0"); output.Add("[Generator \"Analizator9000\"]"); foreach (string deal in deals) { String[] dealParts = deal.Split(':'); output.Add("[Board \"" + dealParts[0] + "\"]"); output.Add("[Dealer \"N\"]"); output.Add("[Vulnerable \"" + vulnerabilities[vulnerabilityBox.SelectedIndex] + "\"]"); output.Add("[Deal \"N:" + dealParts[1].Trim() + "\"]"); output.Add(""); } File.WriteAllLines(exportFileDialog.FileName, output.ToArray()); MessageBox.Show(GetResourceManager().GetString("Form1_exportSuccess", GetCulture()) + ": " + exportFileDialog.FileName); } catch (Exception ex) { MessageBox.Show(GetResourceManager().GetString("Form1_error", GetCulture()) + ": " + ex.Message, GetResourceManager().GetString("Form1_analyzeError"), MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }