/// <inheritdoc/> protected override void OnClosing(CancelEventArgs e) { // Ask when worth. if (Settings.AskWhileQuitting && Messagings.AskWhileQuitting() == MessageBoxResult.No) { e.Cancel = true; return; } // Save configuration. SaveConfig(); #if SUDOKU_RECOGNIZING // Dispose the instance. // If the service provider is not initialized, this value will be null. _recognition?.Dispose(); #endif GC.Collect(); base.OnClosing(e); #if SUDOKU_RECOGNIZING if (_recognition?.ToolIsInitialized ?? false) { // If you don't use this feature, the program will not need to use // this method to KILL itself... KILL... sounds terrible and dangerous, isn't it? // To be honest, I don't know why the program fails to exit... The background // threads still running after base close method executed completely. If you // know the detail of Emgu.CV, please tell me, thx! Process.GetCurrentProcess().Kill(); } #endif }
private void MenuItemFileOpenDatabase_Click(object sender, RoutedEventArgs e) { var dialog = new OpenFileDialog { DefaultExt = ".sudokus", Filter = "Text file|*.txt|Sudoku database file|*.sudokus|All files|*.*", Multiselect = false, Title = "Open sudoku file from..." }; if (dialog.ShowDialog() is true) { using var sr = new StreamReader(Settings.CurrentPuzzleDatabase = _database = dialog.FileName); _puzzlesText = sr.ReadToEnd().Split(Splitter, StringSplitOptions.RemoveEmptyEntries); Messagings.LoadDatabase(_puzzlesText.Length); if (_puzzlesText.Length != 0) { LoadPuzzle(_puzzlesText[Settings.CurrentPuzzleNumber = 0].TrimEnd(Splitter)); UpdateDatabaseControls(false, false, true, true); _textBoxJumpTo.IsEnabled = true; _labelPuzzleNumber.Content = $"1/{_puzzlesText.Length}"; } } }
private void MenuItemGenerateWithTechniqueFiltering_Click(object sender, RoutedEventArgs e) #endif { #if DEBUG await internalOperation(); async Task internalOperation() { DisableGeneratingControls(); Puzzle = new UndoableGrid( await Task.Run(() => new TechniqueFilteringPuzzleGenerator().Generate( new TechniqueCodeFilter { TechniqueCode.AlmostLockedPair }))); EnableGeneratingControls(); SwitchOnGeneratingComboBoxesDisplaying(); ClearItemSourcesWhenGeneratedOrSolving(); UpdateImageGrid(); } #else Messagings.NotSupportedWhileGeneratingWithFilter(); #endif }
private void MenuItemEditRecomputeCandidates_Click(object sender, RoutedEventArgs e) { int[] z = new int[81]; for (int cell = 0; cell < 81; cell++) { z[cell] = _puzzle[cell] + 1; } var grid = SudokuGrid.CreateInstance(z); if (new BitwiseSolver().Solve(grid.ToString(), null, 2) == 0) { Messagings.SukakuCannotUseThisFunction(); e.Handled = true; return; } _puzzle = new UndoableGrid(grid); _puzzle.Unfix(); _puzzle.ClearStack(); UpdateImageGrid(); UpdateUndoRedoControls(); }
private async void MenuItemGenerateHardPattern_Click(object sender, RoutedEventArgs e) { await internalOperation(); async Task internalOperation() { if (_database is null || Messagings.AskWhileGeneratingWithDatabase() == MessageBoxResult.Yes) { // Disable relative database controls. Settings.CurrentPuzzleDatabase = _database = null; Settings.CurrentPuzzleNumber = -1; UpdateDatabaseControls(false, false, false, false); _labelPuzzleNumber.ClearValue(ContentProperty); DisableGeneratingControls(); int depth = _comboBoxBackdoorFilteringDepth.SelectedIndex; Puzzle = new UndoableGrid(await Task.Run(() => new HardPatternPuzzleGenerator().Generate(depth - 1))); EnableGeneratingControls(); SwitchOnGeneratingComboBoxesDisplaying(); ClearItemSourcesWhenGeneratedOrSolving(); UpdateImageGrid(); } } }
private void MenuItemViewsGspView_Click(object sender, RoutedEventArgs e) { if (Enumerable.Range(0, 81).All(i => _puzzle.GetStatus(i) != CellStatus.Given)) { Messagings.SukakuCannotUseGspChecking(); e.Handled = true; return; } if (!(new GspTechniqueSearcher().GetOne(_puzzle) is GspTechniqueInfo info)) { Messagings.DoesNotContainGsp(); e.Handled = true; return; } bool[] series = new bool[9]; int?[] mapping = info.MappingTable; var cellOffsets = new List <(int, int)>(); for (int i = 0, p = 0; i < 9; i++) { if (series[i]) { continue; } int?value = mapping[i]; if (value is null) { continue; } int j = value.Value; (series[i], series[j]) = (true, true); for (int cell = 0; cell < 81; cell++) { int cellValue = _puzzle[cell]; if (cellValue == i || cellValue == j) { cellOffsets.Add((p, cell)); } } p++; } _textBoxInfo.Text = info.ToString(); _layerCollection.Add( new ViewLayer( _pointConverter, new View(cellOffsets, null, null, null), info.Conclusions, Settings.PaletteColors, Settings.EliminationColor, Settings.CannibalismColor, Settings.ChainColor)); UpdateImageGrid(); }
private void MenuItemClearStack_Click(object sender, RoutedEventArgs e) { if ((_puzzle.HasUndoSteps || _puzzle.HasRedoSteps) && Messagings.AskWhileClearingStack() == MessageBoxResult.Yes) { _puzzle.ClearStack(); UpdateUndoRedoControls(); } }
private void MenuItemExport_Click(object sender, RoutedEventArgs e) { if (_analyisResult is null) { Messagings.YouShouldSolveFirst(); e.Handled = true; return; } new ExportAnalysisResultWindow(_analyisResult).Show(); }
/// <inheritdoc/> protected override void OnInitialized(EventArgs e) { // Call the base method. base.OnInitialized(e); var mutex = new Mutex(true, SolutionName, out bool mutexIsNew); if (mutexIsNew) { mutex.ReleaseMutex(); } else { Messagings.OnlyOpenOneProgram(); Environment.Exit(0); } #if SUDOKU_RECOGNIZING // Then initialize for recognizer. try { _recognition = new RecognitionServiceProvider(); } catch (Exception ex) { Messagings.FailedToLoadRecognitionTool(ex); } #endif // Define shortcuts. AddShortCut(Key.C, ModifierKeys.Control, null, MenuItemEditCopy_Click); AddShortCut(Key.H, ModifierKeys.Control, _menuItemGenerateHardPattern, MenuItemGenerateHardPattern_Click); AddShortCut(Key.O, ModifierKeys.Control, _menuItemFileOpen, MenuItemFileOpen_Click); AddShortCut(Key.P, ModifierKeys.Control, null, MenuItemFileGetSnapshot_Click); AddShortCut(Key.S, ModifierKeys.Control, null, MenuItemFileSave_Click); AddShortCut(Key.V, ModifierKeys.Control, _menuItemEditPaste, MenuItemEditPaste_Click); AddShortCut(Key.Y, ModifierKeys.Control, _menuItemEditRedo, MenuItemEditRedo_Click); AddShortCut(Key.Z, ModifierKeys.Control, _menuItemEditUndo, MenuItemEditUndo_Click); AddShortCut(Key.F5, ModifierKeys.Control, _menuItemEditRecomputeCandidates, MenuItemEditRecomputeCandidates_Click); AddShortCut(Key.OemTilde, ModifierKeys.Control, _menuItemEditFix, MenuItemEditFix_Click); AddShortCut(Key.F9, ModifierKeys.None, _menuItemAnalyzeAnalyze, MenuItemAnalyzeAnalyze_Click); AddShortCut(Key.F10, ModifierKeys.None, _menuItemAnalyzeSolve, MenuItemAnalyzeSolve_Click); AddShortCut(Key.F4, ModifierKeys.Alt, null, MenuItemFileQuit_Click); AddShortCut(Key.N, ModifierKeys.Control | ModifierKeys.Shift, _menuItemEditClear, MenuItemEditClear_Click); AddShortCut(Key.C, ModifierKeys.Control | ModifierKeys.Shift, null, MenuItemEditCopyCurrentGrid_Click); AddShortCut(Key.OemTilde, ModifierKeys.Control | ModifierKeys.Shift, _menuItemEditUnfix, MenuItemEditUnfix_Click); Title = $"{SolutionName} Ver {SolutionVersion}"; LoadConfigIfWorth(); InitializePointConverterAndLayers(); LoadDatabaseIfWorth(); UpdateControls(); }
private void MenuItemFileGetSnapshot_Click(object sender, RoutedEventArgs e) { try { Clipboard.SetImage((BitmapSource)_imageGrid.Source); } catch (Exception ex) { Messagings.ShowExceptionMessage(ex); } }
private void MenuItemAnalyzeBugN_Click(object sender, RoutedEventArgs e) { if (!_puzzle.IsValid(out _)) { Messagings.FailedToCheckDueToInvaildPuzzle(); e.Handled = true; return; } new BugNSearchWindow(_puzzle).ShowDialog(); }
private void MenuItemEditCopyHodokuLibrary_Click(object sender, RoutedEventArgs e) { try { string z = _puzzle.ToString(Settings.TextFormatPlaceholdersAreZero ? "#0" : "#."); Clipboard.SetText($":0000:x:{z}{(z.Contains(':') ? "::" : ":::")}"); } catch (Exception ex) { Messagings.ShowExceptionMessage(ex); } }
private async void MenuItemGenerateWithSymmetry_Click(object sender, RoutedEventArgs e) { await internalOperation(); async Task internalOperation() { #region Obsolete code //if (_comboBoxDifficulty.SelectedIndex == 0) //{ // MessageBox.Show( // "We may not allow you to generate the puzzle whose difficulty is unknown.", "Warning"); // // e.Handled = true; // return; //} // //if (_comboBoxDifficulty.SelectedIndex >= 5 // && MessageBox.Show( // "You selected a difficulty that generate a puzzle will be too slow " + // "and you may not cancel the operation when generating. " + // "Would you like to generate anyway?", "Info", MessageBoxButton.YesNo // ) != MessageBoxResult.Yes) //{ // e.Handled = true; // return; //} #endregion if (_database is null || Messagings.AskWhileGeneratingWithDatabase() == MessageBoxResult.Yes) { // Disable relative database controls. Settings.CurrentPuzzleDatabase = _database = null; Settings.CurrentPuzzleNumber = -1; UpdateDatabaseControls(false, false, false, false); DisableGeneratingControls(); // These two value should be assigned first, rather than // inlining in the asynchronized environment. var symmetry = ((PrimaryElementTuple <string, SymmetryType>)_comboBoxSymmetry.SelectedItem).Value2; //var diff = (DifficultyLevel)_comboBoxDifficulty.SelectedItem; Puzzle = new UndoableGrid(await Task.Run(() => new BasicPuzzleGenerator().Generate(33, symmetry))); EnableGeneratingControls(); SwitchOnGeneratingComboBoxesDisplaying(); ClearItemSourcesWhenGeneratedOrSolving(); UpdateImageGrid(); } } }
private void ContextListBoxPathsCopyCurrentStep_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem) { try { Clipboard.SetText(_listBoxPaths.SelectedItem.ToString()); } catch { Messagings.CannotCopyStep(); } } }
private void MenuItemAnalyzeSolve_Click(object sender, RoutedEventArgs e) { if (!applyNormal() && !applySukaku()) { Messagings.FailedToApplyPuzzle(); e.Handled = true; return; } bool applySukaku() { var sb = new StringBuilder(SudokuGrid.EmptyString); if (new SukakuBitwiseSolver().Solve( _puzzle.ToString($"~{(Settings.TextFormatPlaceholdersAreZero ? "0" : ".")}"), sb, 2) != 1) { return(!(e.Handled = true)); } Puzzle = new UndoableGrid(SudokuGrid.Parse(sb.ToString())); UpdateImageGrid(); return(true); } bool applyNormal() { var sb = new StringBuilder(SudokuGrid.EmptyString); for (int cell = 0; cell < 81; cell++) { sb[cell] += (char)(_puzzle[cell] + 1); } if (new BitwiseSolver().Solve(sb.ToString(), sb, 2) != 1) { return(!(e.Handled = true)); } Puzzle = new UndoableGrid(SudokuGrid.Parse(sb.ToString())); UpdateImageGrid(); return(true); } }
private async void MenuItemViewsShowBugN_Click(object sender, RoutedEventArgs e) { await internalOperation(); async ValueTask internalOperation() { if (!_puzzle.IsValid(out _)) { Messagings.FailedToCheckDueToInvaildPuzzle(); e.Handled = true; return; } _textBoxInfo.Text = "The program is running as quickly as possible to search " + "all non-BUG candidates (true candidates). Please wait."; var trueCandidates = await Task.Run(() => new BugChecker(_puzzle).GetAllTrueCandidates(64)); _textBoxInfo.ClearValue(TextBox.TextProperty); if (trueCandidates.Count == 0) { Messagings.DoesNotContainBugMultiple(); e.Handled = true; return; } _layerCollection.Add( new ViewLayer( _pointConverter, new View( null, new List <(int, int)>(from candidate in trueCandidates select(0, candidate)), null, null), null, Settings.PaletteColors, DColor.Empty, DColor.Empty, DColor.Empty)); UpdateImageGrid(); _textBoxInfo.Text = $"All true candidate(s): {new CandidateCollection(trueCandidates).ToString()}"; } }
private async void ButtonFindAllSteps_Click(object sender, RoutedEventArgs e) { await internalOperation(); async Task internalOperation() { if (!_puzzle.IsValid(out _)) { Messagings.FunctionIsUnavailable(); e.Handled = true; return; } _listBoxTechniques.ClearValue(ItemsControl.ItemsSourceProperty); _textBoxInfo.Text = "The solver is running slowly, please wait..."; _buttonFindAllSteps.IsEnabled = false; DisableSolvingControls(); var techniqueGroups = await new StepFinder(Settings).SearchAsync(_puzzle); EnableSolvingControls(); SwitchOnGeneratingComboBoxesDisplaying(); _buttonFindAllSteps.IsEnabled = true; _textBoxInfo.ClearValue(TextBox.TextProperty); // The boolean value stands for whether the technique is enabled. var list = new List <ListBoxItem>(); foreach (var techniqueGroup in techniqueGroups) { string name = techniqueGroup.Key; foreach (var info in techniqueGroup) { var(fore, back) = Settings.DiffColors[info.DifficultyLevel]; list.Add( new ListBoxItem { Content = new PrimaryElementTuple <string, TechniqueInfo, bool>( info.ToSimpleString(), info, true), BorderThickness = default,
private void ContextListBoxPathsCopyAllSteps_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem) { var sb = new StringBuilder(); foreach (string step in from object item in _listBoxPaths.Items select item.ToString()) { sb.AppendLine(step); } try { Clipboard.SetText(sb.ToString()); } catch { Messagings.CannotCopyStep(); } } }
private void MenuItemBackupConfig_Click(object sender, RoutedEventArgs e) { var dialog = new SaveFileDialog { AddExtension = true, CheckPathExists = true, DefaultExt = "scfg", Filter = "Configuration file|*.scfg", Title = "Save configuration file to..." }; if (dialog.ShowDialog() is true) { try { SaveConfig(dialog.FileName); } catch { Messagings.FailedToBackupConfig(); } } }
private void MenuItemEditPasteAsSukaku_Click(object sender, RoutedEventArgs e) { string puzzleStr = Clipboard.GetText(); if (!(puzzleStr is null)) { try { Puzzle = new UndoableGrid(SudokuGrid.Parse(puzzleStr, GridParsingOption.Sukaku)); _menuItemEditUndo.IsEnabled = _menuItemEditRedo.IsEnabled = false; UpdateImageGrid(); } catch (ArgumentException) { Messagings.FailedToPasteText(); } _listBoxPaths.ClearValue(ItemsControl.ItemsSourceProperty); _listViewSummary.ClearValue(ItemsControl.ItemsSourceProperty); _listBoxTechniques.ClearValue(ItemsControl.ItemsSourceProperty); } }
private void MenuItemFileLoadPicture_Click(object sender, RoutedEventArgs e) #endif { #if SUDOKU_RECOGNIZING await internalOperation(); async Task internalOperation() { if (_recognition is null) { Messagings.FailedToLoadPicture(); e.Handled = true; return; } var dialog = new OpenFileDialog { AddExtension = true, DefaultExt = "png", Filter = "PNG file|*.png|JPEG file|*.jpg;*.jpeg|BMP file|*.bmp|GIF file|*.gif", Multiselect = false, Title = "Load picture from..." }; if (dialog.ShowDialog() is true) { try { if (_recognition.ToolIsInitialized) { if (Messagings.AskWhileLoadingPicture() == MessageBoxResult.Yes) { _textBoxInfo.Text = "Load picture successfully, now grab all digits in the picture, please wait..."; using (var bitmap = new Bitmap(dialog.FileName)) { var grid = (await Task.Run(() => _recognition.Recorgnize(bitmap))).ToMutable(); grid.Fix(); Puzzle = new UndoableGrid(grid); } UpdateUndoRedoControls(); UpdateImageGrid(); } } else { Messagings.FailedToLoadPictureDueToNotHavingInitialized(); } } catch (Exception ex) { Messagings.ShowExceptionMessage(ex); } _textBoxInfo.ClearValue(TextBox.TextProperty); } } #else Messagings.NotSupportedForLoadingPicture(); #endif }
private async void MenuItemViewsBackdoorView_Click(object sender, RoutedEventArgs e) { await internalOperation(); async ValueTask internalOperation() { if (!_puzzle.IsValid(out _)) { Messagings.FailedToCheckDueToInvaildPuzzle(); e.Handled = true; return; } _textBoxInfo.Text = "The program is running as quickly as possible to search " + "all backdoors (at level 0 or 1). Please wait."; var backdoors = new List <Conclusion>(); for (int level = 0; level <= 1; level++) { foreach (var backdoor in await Task.Run(() => new BackdoorSearcher().SearchForBackdoorsExact(_puzzle, level))) { backdoors.AddRange(backdoor); } } _textBoxInfo.ClearValue(TextBox.TextProperty); if (backdoors.None()) { Messagings.DoesNotContainBackdoor(); e.Handled = true; return; } var highlightCandidates = new List <(int, int)>(); int currentLevel = 0; foreach (var(_, candidate) in backdoors) { highlightCandidates.Add((currentLevel, candidate)); currentLevel++; } _layerCollection.Add( new ViewLayer( _pointConverter, new View( null, new List <(int, int)>( from conclusion in backdoors where conclusion.ConclusionType == ConclusionType.Assignment select(0, conclusion.CellOffset * 9 + conclusion.Digit)), null, null), backdoors, Settings.PaletteColors, Settings.EliminationColor, DColor.Empty, DColor.Empty)); UpdateImageGrid(); _textBoxInfo.Text = $"All backdoor(s) at level 0 or 1: {new ConclusionCollection(backdoors).ToString()}"; } }
private async void MenuItemAnalyzeAnalyze_Click(object sender, RoutedEventArgs e) { if (!await internalOperation(false) && !await internalOperation(true)) { Messagings.FailedToApplyPuzzle(); e.Handled = true; return; } async Task <bool> internalOperation(bool sukakuMode) { if (_puzzle.HasSolved) { Messagings.PuzzleAlreadySolved(); return(!(e.Handled = true)); } var sb = new StringBuilder(SudokuGrid.EmptyString); if (sukakuMode) { string puzzleString = _puzzle.ToString("~"); if (new SukakuBitwiseSolver().Solve(puzzleString, sb, 2) != 1) { return(!(e.Handled = true)); } } else { for (int cell = 0; cell < 81; cell++) { sb[cell] += (char)(_puzzle[cell] + 1); } if (new BitwiseSolver().Solve(sb.ToString(), null, 2) != 1) { return(!(e.Handled = true)); } } // Update status. ClearItemSourcesWhenGeneratedOrSolving(); _textBoxInfo.Text = "Solving, please wait. During solving you can do some other work..."; DisableSolvingControls(); // Run the solver asynchronizedly, during solving you can do other work. _analyisResult = await Task.Run(() => { if (!Settings.SolveFromCurrent) { _puzzle.Reset(); _puzzle.ClearStack(); } return(_manualSolver.Solve(_puzzle)); }); // Solved. Now update the technique summary. EnableSolvingControls(); SwitchOnGeneratingComboBoxesDisplaying(); if (_analyisResult.HasSolved) { _textBoxInfo.Text = $"{_analyisResult.SolvingStepsCount} " + $"{(_analyisResult.SolvingStepsCount == 1 ? "step" : "steps")}, " + $"time elapsed: {_analyisResult.ElapsedTime:hh':'mm'.'ss'.'fff}."; int i = 0; var pathList = new List <ListBoxItem>(); foreach (var step in _analyisResult.SolvingSteps !) { var(fore, back) = Settings.DiffColors[step.DifficultyLevel]; var item = new ListBoxItem { Foreground = new SolidColorBrush(fore.ToWColor()), Background = new SolidColorBrush(back.ToWColor()), Content = new PrimaryElementTuple <int, TechniqueInfo>(i++, step, 2), BorderThickness = new Thickness() }; pathList.Add(item); } _listBoxPaths.ItemsSource = pathList; // Gather the information. // GridView should list the instance with each property, not fields, // even if fields are public. // Therefore, here may use anonymous type is okay, but using value tuples // is bad. var collection = new List <AnonymousType>(); decimal summary = 0, summaryMax = 0; int summaryCount = 0; foreach (var techniqueGroup in from solvingStep in _analyisResult.SolvingSteps ! orderby solvingStep.Difficulty group solvingStep by solvingStep.Name) { string name = techniqueGroup.Key; int count = techniqueGroup.Count(); decimal total = 0, maximum = 0; foreach (var step in techniqueGroup) { summary += step.Difficulty; summaryCount++; total += step.Difficulty; maximum = Math.Max(step.Difficulty, maximum); summaryMax = Math.Max(step.Difficulty, maximum); } collection.Add(new { Technique = name, Count = count, Total = total, Max = maximum }); } collection.Add(new { Technique = default(string?), Count = summaryCount, Total = summary, Max = summaryMax }); GridView view; _listViewSummary.ItemsSource = collection; _listViewSummary.View = view = new GridView(); view.Columns.AddRange(new[] { createGridViewColumn("Technique", .6), createGridViewColumn("Count", .1), createGridViewColumn("Total", .2), createGridViewColumn("Max", .1) }); view.AllowsColumnReorder = false; } else { Messagings.FailedToSolveWithMessage(_analyisResult); } return(true); } GridViewColumn createGridViewColumn(string name, double widthScale) => new GridViewColumn { Header = name, DisplayMemberBinding = new Binding(name), Width = _tabControlInfo.ActualWidth * widthScale - 4, }; }