using System; using System.Collections.Generic; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using MinesweeperControl; using MinesweeperSolver; using static MinesweeperControl.GameDescription; using static MinesweeperControl.MinesweeperGame; namespace MinesweeperGui { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { private readonly bool verbose = true; // determines whether diagnostic info gets written to the console private readonly ImageSource Hidden; private readonly ImageSource[] MineValue = new ImageSource[9]; private readonly ImageSource Flagged; private readonly ImageSource FlaggedWrong; private readonly ImageSource Mine; private readonly ImageSource MineWrong; private readonly ImageSource[] LedValue = new ImageSource[10]; private int tileSize = 32; private GameDescription gameDescription = GameDescription.EXPERT_SAFE; // details about the solver private SolverInfo solverInfo; private SolverActionHeader solverActions = new SolverActionHeader(); // graphical components private Image hintImage; // overlay of hints from the solver private Image totalImage; // composite image of imageDrawings private DrawingGroup imageDrawings; // contains all the tiles private ImageDrawing[,] images; // all the tiles private readonly Image[] digits = new Image[8]; // tool tip elements private Popup gameToolTip = new Popup(); private readonly TextBox popupText = new TextBox(); // details about the visible board private int topX = 0; private int topY = 0; private int boardWidth = 0; private int boardHeight = 0; private ActionResult[,] tiles; private MinesweeperGame game; private int minesLeftSize = 6; private bool useSolver = false; private bool autoRunning = false; private bool useGuesses = false; private bool logicalLock = false; private int gameNumber = 1; private int autoPlayMinDelay = 250; private SolverAction jumpTo; public object MinesweeperMain { get; private set; } public MainWindow() { Utility.Write("At MainWindow constructor"); InitializeComponent(); // load the images we need Hidden = Utility.BuildImageSource("facingDown.png", tileSize); Flagged = Utility.BuildImageSource("flagged.png", tileSize); FlaggedWrong = Utility.BuildImageSource("flaggedWrong.png", tileSize); Mine = Utility.BuildImageSource("mine.png", tileSize); MineWrong = Utility.BuildImageSource("exploded.png", tileSize); for (int i = 0; i < 9; i++) { MineValue[i] = Utility.BuildImageSource(i + ".png", tileSize); } for (int i = 0; i < 10; i++) { LedValue[i] = Utility.BuildImageSource("led" + i + ".png", 24, 40); } CustomWidth.Text = "100"; CustomHeight.Text = "100"; CustomMines.Text = "2000"; for (int i = 0; i < digits.Length; i++) { digits[i] = new Image() { Source = LedValue[0] }; Canvas.SetTop(digits[i], 0); Canvas.SetLeft(digits[i], i * LedValue[0].Width); MinesLeft.Children.Add(digits[i]); } // complete creating the popup popupText.Text = ""; popupText.Background = new SolidColorBrush( Color.FromArgb(128, 227, 227, 227)); popupText.Foreground = Brushes.Black; popupText.FontSize = 20; popupText.FontWeight = FontWeights.Bold; gameToolTip.Child = popupText; gameToolTip.AllowsTransparency = true; gameToolTip.Placement = PlacementMode.Relative; gameToolTip.PlacementTarget = gameCanvas; gameToolTip.IsOpen = true; gameCanvas.Children.Add(gameToolTip); // start up a game InitializeTiles1(); Utility.Write("Graphics rendering tier = " + (RenderCapability.Tier >> 16)); SolverMain.Initialise(); } private void AutoPlay() { Thread autoplay = new Thread(AutoPlayRunner); autoplay.Start(); } // this method runs on a different thread. gameNumber is changed by clicking chosing a new game from the UI thread. private void AutoPlayRunner() { Dispatcher.Invoke(() => gameCanvas.Cursor = Cursors.Wait); logicalLock = true; int runningGameNumber = gameNumber; // remember the game number we are solving //Utility.Write("AutoRunning thread starting"); solverActions = SolverMain.FindActions(solverInfo); // pass the solver info into the solver and see what it comes up with Dispatcher.Invoke(() => RenderHints()); while (IsAutoPlayValid() && gameNumber == runningGameNumber) { long start = DateTime.Now.Ticks; GameResult result = game.ProcessActions(solverActions.solverActions); //Utility.Write("Game controller returned " + result.actionResults.Count + " results from this action"); long end1 = DateTime.Now.Ticks; // do the rendering while we are stil playing the same game if (gameNumber == runningGameNumber) { gameCanvas.Dispatcher.Invoke(() => RenderResults(result)); } else { break; } //Utility.Write("After RenderResults took " + (DateTime.Now.Ticks - start) + " ticks"); solverInfo.AddInformation(result); // let the solver know what has happened // nothing more to do if we have won or lost if (result.status == GameStatus.Lost || result.status == GameStatus.Won) { Dispatcher.Invoke(() => DisplayFinalOutcome(result)); break; } solverActions = SolverMain.FindActions(solverInfo); // pass the solver info into the solver and see what it comes up with //foreach (SolverAction action in solverActions) { // Utility.Write("Playing " + action.AsText() + " probability " + action.safeProbability); //} //long end2 = DateTime.Now.Ticks; // do the rendering while we are stil playing the same game if (gameNumber == runningGameNumber) { Dispatcher.Invoke(() => RenderHints()); } else { break; } //Utility.Write("After RenderHints took " + (DateTime.Now.Ticks - start) + " ticks"); Utility.Write("Tiles remaining " + solverInfo.GetTilesLeft()); long end = DateTime.Now.Ticks; int milliseconds = (int) ((end - start) / 10000); int wait = Math.Max(0, autoPlayMinDelay - milliseconds); //Utility.Write("Autoplay processing took " + milliseconds + " milliseconds (" + (end - start) + " ticks)"); //Console.WriteLine("Autoplay processing took " + milliseconds + " milliseconds"); if (!IsAutoPlayValid()) { break; } Thread.Sleep(wait); // wait until all the time is used up } logicalLock = false; Dispatcher.Invoke(() => gameCanvas.Cursor = Cursors.Arrow); //Utility.Write("AutoRunning thread ending"); } // create the non-graphical parts of the game private void InitializeTiles1() { gameNumber++; logicalLock = false; minesLeftSize = Math.Max(3, (int) Math.Log10(gameDescription.mines) + 1); MinesLeftHolder.Width = 24 * minesLeftSize + 8; for (int i= 0; i < digits.Length; i++) { if (i < minesLeftSize) { digits[i].Visibility = Visibility.Visible; } else { digits[i].Visibility = Visibility.Hidden; } } int seed = 0; if (useSeed.IsChecked == true) { try { seed = int.Parse(SeedTextBox.Text); } catch (Exception) { } } //game = new MinesweeperGame(gameDescription, seed); game = new MinesweeperGame(gameDescription, seed, (hardcore.IsChecked == true)); solverInfo = new SolverInfo(gameDescription, verbose); RenderMinesLeft(); long start = DateTime.Now.Ticks; tiles = new ActionResult[gameDescription.width, gameDescription.height]; solverActions = new SolverActionHeader(); // remove any hints from the previous game Utility.Write("Ticks to build screen " + (DateTime.Now.Ticks - start)); } // build the graphical part of the board private void BuildBoard() { //long start = DateTime.Now.Ticks; images = new ImageDrawing[boardWidth, boardHeight]; // remove existing tiles from the canvas gameCanvas.Children.Clear(); // clear game board imageDrawings = new DrawingGroup(); for (int y = 0; y < boardHeight; y++) { for (int x = 0; x < boardWidth; x++) { ActionResult tile = tiles[topX + x, topY + y]; ImageSource imageSource; if (tile == null) { imageSource = Hidden; } else if (tile.resultType == ResultType.Cleared) { imageSource = MineValue[tile.value]; } else if (tile.resultType == ResultType.Flagged) { imageSource = Flagged; } else if (tile.resultType == ResultType.Hidden) { imageSource = Hidden; } else if (tile.resultType == ResultType.Exploded) { imageSource = MineWrong; } else if (tile.resultType == ResultType.FlaggedWrong) { imageSource = FlaggedWrong; } else if (tile.resultType == ResultType.Mine) { imageSource = Mine; } else { imageSource = Hidden; } ImageDrawing image = new ImageDrawing() { ImageSource = imageSource, Rect = new Rect(x * tileSize, y * tileSize, tileSize, tileSize) }; imageDrawings.Children.Add(image); images[x, y] = image; } } totalImage = new Image() { Source = new DrawingImage(imageDrawings) }; Canvas.SetLeft(totalImage, 0); Canvas.SetTop(totalImage, 0); gameCanvas.Children.Add(totalImage); // create an empty hints image which we'll use later hintImage = new Image(); Canvas.SetLeft(hintImage, 0); Canvas.SetTop(hintImage, 0); gameCanvas.Children.Add(hintImage); //Utility.Write("Ticks to build screen " + (DateTime.Now.Ticks - start)); } private void DoAction(int x, int y, ActionType action) { //int index = gameDescription.width * y + x; GameResult result = game.ProcessActions(new GameAction[] { new GameAction(x, y, action) }); Utility.Write("Game controller returned " + result.actionResults.Count + " results from this action"); RenderResults(result); // draw the new board solverInfo.AddInformation(result); // let the solver know what has happened if (result.status == GameStatus.Lost) { MessageLine.Content = "The game is lost!"; } else if (result.status == GameStatus.Won) { if (game.GetDeaths() != 0) { MessageLine.Content = "The game is won with " + game.GetDeaths() + " deaths"; } else { MessageLine.Content = "The game is won"; } } else { // if we are showing hints or auto playing then get the hints if (useSolver || autoRunning) { AutoPlay(); // let the solver playout the moves } } } private void DisplayFinalOutcome(GameResult result) { if (result.status == GameStatus.Lost) { MessageLine.Content = "The game is lost!"; } else if (result.status == GameStatus.Won) { if (this.game.GetDeaths() != 0) { MessageLine.Content = "The game is won with " + game.GetDeaths() + " deaths"; } else { MessageLine.Content = "The game is won"; } } } private bool IsAutoPlayValid() { if (!autoRunning) { //Utility.Write("Autoplay not valid - AutoPlay is not checked"); return false; } // if we have no actions then we can't autoplay if (solverActions.solverActions.Count == 0) { //Utility.Write("Autoplay not valid - no actions found by the solver"); return false; } // if we are accepting guesses then everything goes if (useGuesses) { //Utility.Write("Autoplay valid - accepting guesses"); return true; } if ((solverActions.solverActions[0].safeProbability > 0 && solverActions.solverActions[0].safeProbability < 1)) { //Utility.Write("Autoplay not valid - no certain actions found by the solver and we aren't accepting guesses"); return false; } //Utility.Write("Autoplay valid - certain actions found by the solver"); return true; } // draw the outputs from the Game onto the visible board private void RenderResults(GameResult result) { //long start = DateTime.Now.Ticks; SeedTextBox.Text = game.seed.ToString(); hintImage.Visibility = Visibility.Hidden; // hide the previous hints foreach (ActionResult ar in result.actionResults) { // remember what happen here last tiles[ar.x, ar.y] = ar; int boardX = ar.x - topX; int boardY = ar.y - topY; // if the action is taking place in the part of the board we are looking at if (boardX >= 0 && boardX < boardWidth && boardY >= 0 && boardY < boardHeight) { ImageDrawing image = images[boardX, boardY]; if (ar.resultType == ResultType.Cleared) { image.ImageSource = MineValue[ar.value]; } else if (ar.resultType == ResultType.Flagged) { image.ImageSource = Flagged; } else if (ar.resultType == ResultType.Hidden) { image.ImageSource = Hidden; } else if (ar.resultType == ResultType.Exploded) { image.ImageSource = MineWrong; } else if (ar.resultType == ResultType.FlaggedWrong) { image.ImageSource = FlaggedWrong; } else if (ar.resultType == ResultType.Mine) { image.ImageSource = Mine; } } } RenderMinesLeft(); //Utility.Write("Ticks to render results " + (DateTime.Now.Ticks - start)); } // draw the outputs from the Solver onto the visible board private void RenderHints() { //long start = DateTime.Now.Ticks; DrawingGroup hintDrawings = new DrawingGroup(); int offSetLeft = tileSize * (boardWidth + 1); int offSetTop = tileSize * (boardHeight + 1); jumpTo = null; if (this.game.GetGameStatus() == GameStatus.InPlay) { if (solverActions.solverActions.Count == 0) { MessageLine.Content = "The solver has no suggestions!"; } else if (solverActions.solverActions.Count > 1) { MessageLine.Content = "The solver has found " + solverActions.solverActions.Count + " moves."; } else if (solverActions.solverActions[0].safeProbability > 0 && solverActions.solverActions[0].safeProbability < 1) { MessageLine.Content = "The solver suggests guessing " + solverActions.solverActions[0].AsText(); jumpTo = solverActions.solverActions[0]; } else { MessageLine.Content = "The solver has found 1 move."; } } foreach (SolverAction ar in solverActions.solverActions) { int boardX = ar.x - topX; int boardY = ar.y - topY; // if the action is taking place in the part of the board we are looking at if (boardX >= 0 && boardX < boardWidth && boardY >= 0 && boardY < boardHeight) { int x = boardX * tileSize; int y = boardY * tileSize; if (x < offSetLeft) { offSetLeft = x; } if (y < offSetTop) { offSetTop = y; } Color fill; if (ar.action == ActionType.Clear) { if (ar.safeProbability == 1) { fill = Color.FromArgb(128, 0, 255, 0); // green if safe } else if (ar.isDead) { fill = Color.FromArgb(128, 0, 0, 0); // black if excluded } else { fill = Color.FromArgb(128, 255, 165, 0); // orange if not safe } } else if (ar.action == ActionType.Flag) { fill = Color.FromArgb(128, 255, 0, 0); // red if a mine } GeometryDrawing square = new GeometryDrawing( new SolidColorBrush(fill), new Pen(Brushes.Black, 0), new RectangleGeometry(new Rect(x, y, tileSize, tileSize)) ); hintDrawings.Children.Add(square); } } // render dead tiles foreach (SolverAction ar in solverActions.deadActions) { int boardX = ar.x - topX; int boardY = ar.y - topY; // if the action is taking place in the part of the board we are looking at if (boardX >= 0 && boardX < boardWidth && boardY >= 0 && boardY < boardHeight) { int x = boardX * tileSize; int y = boardY * tileSize; if (x < offSetLeft) { offSetLeft = x; } if (y < offSetTop) { offSetTop = y; } Color fill = Color.FromArgb(128, 0, 0, 0); // black if dead GeometryDrawing square = new GeometryDrawing( new SolidColorBrush(fill), new Pen(Brushes.Black, 0), new RectangleGeometry(new Rect(x, y, tileSize, tileSize)) ); hintDrawings.Children.Add(square); } } // replace the old composite hints image with the new hintImage.Source = new DrawingImage(hintDrawings); // and place it in the write position Canvas.SetLeft(hintImage, offSetLeft); Canvas.SetTop(hintImage, offSetTop); hintImage.Visibility = Visibility.Visible; //Utility.Write("Ticks to render hints " + (DateTime.Now.Ticks - start)); } private void RebuildBoard(int topX, int topY, int boardWidth, int boardHeight) { this.topX = topX; this.topY = topY; this.boardWidth = boardWidth; this.boardHeight = boardHeight; // make sure the top position allows enough space to fill the board if (this.topX + this.boardWidth > gameDescription.width) { this.topX = gameDescription.width - this.boardWidth; } if (this.topY + this.boardHeight > gameDescription.height) { this.topY = gameDescription.height - this.boardHeight; } if (boardWidth > 0 && boardHeight > 0) { BuildBoard(); RenderHints(); } } private void RenderMinesLeft() { int i = minesLeftSize - 1; int mines = game.GetMinesLeft(); while (i >= 0) { int d0 = mines % 10; mines = (mines - d0) / 10; digits[i].Source = LedValue[d0]; i--; } } private void CanvasMouseDown(object sender, MouseButtonEventArgs e) { if (logicalLock) { // this means the solver is playing the game and the user input is ignored return; } int x = (int) e.GetPosition(gameCanvas).X; int y = (int) e.GetPosition(gameCanvas).Y; //Utility.Write("Mouse clicked at X=" + x + ", Y=" + y); int col = x / tileSize; int row = y / tileSize; if (col < 0 || col > boardWidth || row < 0 || row > boardHeight) { Utility.Write("Clicked outside of game boundary col=" + col + ", row=" + row); return; } //Utility.Write("col=" + col + ", row=" + row); ActionType actionType; ActionResult tile = tiles[col + topX, row + topY]; if (e.ChangedButton == MouseButton.Left) { if (tile == null || tile.resultType == ResultType.Hidden) { actionType = ActionType.Clear; } else if (tile.resultType == ResultType.Cleared) { actionType = ActionType.Chord; } else { return; } } else if (e.ChangedButton == MouseButton.Right) { actionType = ActionType.Flag; } else { return; } DoAction(col + topX, row + topY, actionType); } private void NewGame() { InitializeTiles1(); // reposition the display to the top left //topX = 0; //topY = 0; // move the scroll bars back to the start horizontalScrollbar.Value = 0; verticalScrollbar.Value = 0; // work out if scroll bars are need and how big the thumb is //boardWidth = DoWidth(mainWindow.ActualWidth); //boardHeight = DoHeight(mainWindow.ActualHeight); RebuildBoard(0, 0, DoWidth(mainWindow.ActualWidth), DoHeight(mainWindow.ActualHeight)); } private void BeginnerClick(object sender, RoutedEventArgs e) { if (zeroStart.IsChecked == true) { gameDescription = GameDescription.BEGINNER_ZERO; } else { gameDescription = GameDescription.BEGINNER_SAFE; } NewGame(); } private void IntermediateClick(object sender, RoutedEventArgs e) { if (zeroStart.IsChecked == true) { gameDescription = GameDescription.INTERMEDIATE_ZERO; } else { gameDescription = GameDescription.INTERMEDIATE_SAFE; } NewGame(); } private void ExpertClick(object sender, RoutedEventArgs e) { if (zeroStart.IsChecked == true) { gameDescription = GameDescription.EXPERT_ZERO; } else { gameDescription = GameDescription.EXPERT_SAFE; } NewGame(); } private void CustomClick(object sender, RoutedEventArgs e) { int gameWidth; int gameHeight; int gameMines; try { gameWidth = int.Parse(CustomWidth.Text); } catch (System.Exception) { gameWidth = 30; CustomWidth.Text = gameWidth.ToString(); } try { gameHeight = int.Parse(CustomHeight.Text); } catch (System.Exception) { gameHeight = 16; CustomHeight.Text = gameHeight.ToString(); } try { gameMines = int.Parse(CustomMines.Text); } catch (System.Exception) { gameMines = gameWidth * gameHeight / 5; CustomMines.Text = gameMines.ToString(); } if (zeroStart.IsChecked == true) { gameDescription = new GameDescription(gameWidth, gameHeight, gameMines, GameType.Zero); } else { gameDescription = new GameDescription(gameWidth, gameHeight, gameMines, GameType.Safe); } NewGame(); } // redraws the controls based on the new width and returns the number of columns we are now able to show private int DoWidth(double width) { MessageHolder.Width = Math.Max(0, width - 260); double boardMax = gameDescription.width * tileSize + 8; double actualWidth = Math.Max(8, Math.Min(boardMax, width - 260)); int showTilesWidth = (int)Math.Floor((actualWidth - 8) / tileSize); double boardWidthPixels = showTilesWidth * tileSize + 8; // board is in Whole tiles BoardHolder.Width = boardWidthPixels; if (showTilesWidth == gameDescription.width) { horizontalScrollbar.Visibility = Visibility.Hidden; } else { horizontalScrollbar.Visibility = Visibility.Visible; } horizontalScrollbar.Width = boardWidthPixels; horizontalScrollbar.Maximum = (gameDescription.width - showTilesWidth); horizontalScrollbar.ViewportSize = showTilesWidth; horizontalScrollbar.LargeChange = showTilesWidth; // scroll bar large scroll is a whole screen return showTilesWidth; } // redraws the controls based on the new height and returns the number of columns we are now able to show private int DoHeight(double height) { double boardMax = gameDescription.height * tileSize + 8; double actualHeight = Math.Max(8, Math.Min(boardMax, height - 140)); int showTilesHeight = (int) Math.Floor((actualHeight - 8) / tileSize); double boardHeightPixels = showTilesHeight * tileSize + 8; // board is in Whole tiles BoardHolder.Height = boardHeightPixels; if (showTilesHeight == gameDescription.height) { verticalScrollbar.Visibility = Visibility.Hidden; } else { verticalScrollbar.Visibility = Visibility.Visible; } verticalScrollbar.Height = boardHeightPixels; verticalScrollbar.Maximum = (gameDescription.height - showTilesHeight); verticalScrollbar.ViewportSize = showTilesHeight; verticalScrollbar.LargeChange = showTilesHeight; // scroll bar large scroll is a whole screen return showTilesHeight; } private void RedrawWindow(object sender, SizeChangedEventArgs e) { //Utility.Write("New width:" + e.NewSize.Width + " height:" + e.NewSize.Height); int newBoardWidth = boardWidth; int newBoardHeight = boardHeight; if (e.WidthChanged == true) { newBoardWidth = DoWidth(e.NewSize.Width); } if (e.HeightChanged == true) { newBoardHeight = DoHeight(e.NewSize.Height); } if (newBoardWidth != this.boardWidth || newBoardHeight != this.boardHeight) { RebuildBoard(topX, topY, newBoardWidth, newBoardHeight); } } private void VerticalScroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e) { //Utility.Write("Vertical scroll : " + e.NewValue); int newTopY = (int) Math.Floor(e.NewValue + 0.5d); if (newTopY != topY) { RebuildBoard(topX, newTopY, boardWidth, boardHeight); } } private void HorizontalScroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e) { //Utility.Write("Horizontal scroll : " + e.NewValue); int newTopX = (int)Math.Floor(e.NewValue + 0.5d); if (newTopX != topX) { RebuildBoard(newTopX, topY, boardWidth, boardHeight); } } private void MouseMoveOnBoard(object sender, MouseEventArgs e) { int x = (int)e.GetPosition(gameCanvas).X; int y = (int)e.GetPosition(gameCanvas).Y; int col = x / tileSize; int row = y / tileSize; if (col < 0 || col > boardWidth || row < 0 || row > boardHeight) { Utility.Write("Mouse outside of game boundary col=" + col + ", row=" + row); return; } int realCol = col + topX; int realRow = row + topY; double prob = solverInfo.GetProbability(realCol, realRow); string probText; if (prob == -1) { probText = ""; } else if (prob == 0) { probText = "Mine"; } else if (prob == 1) { probText = "Clear"; } else { //probText = String.Format("0.00", prob) + " Safe"; probText = (prob * 100).ToString("N2") + "% Safe"; } popupText.Text = "(" + realCol + "," + realRow + ") " + probText; gameToolTip.HorizontalOffset = x; gameToolTip.VerticalOffset = y + 10; } private void MouseLeftBoard(object sender, MouseEventArgs e) { gameToolTip.IsOpen = false; } private void MouseEnteredBoard(object sender, MouseEventArgs e) { gameToolTip.IsOpen = true; } private void ClickMessageLine(object sender, MouseButtonEventArgs e) { if (jumpTo == null) { return; } // set the scroll bars verticalScrollbar.Value = jumpTo.y - this.boardHeight / 2; horizontalScrollbar.Value = jumpTo.x - this.boardWidth / 2; // now see where the scrollbars are int newTopY = (int)Math.Floor(verticalScrollbar.Value + 0.5d); int newTopX = (int)Math.Floor(horizontalScrollbar.Value + 0.5d); // redraw board if we need to if (newTopY != topY || newTopX != topX) { RebuildBoard(newTopX, newTopY, boardWidth, boardHeight); } } private void MouseWheelOnBoard(object sender, MouseWheelEventArgs e) { // if shift is pressed scroll left and right if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) { horizontalScrollbar.Value -= e.Delta / 40; int newTopX = (int)Math.Floor(horizontalScrollbar.Value + 0.5d); if (newTopX != topX) { RebuildBoard(newTopX, topY, boardWidth, boardHeight); } return; } if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { return; } verticalScrollbar.Value -= e.Delta / 40; int newTopY = (int)Math.Floor(verticalScrollbar.Value + 0.5d); if (newTopY != topY) { RebuildBoard(topX, newTopY, boardWidth, boardHeight); } } private void SetSolverDetails(object sender, RoutedEventArgs e) { useSolver = (showHInts.IsChecked == true); autoRunning = (autoPlay.IsChecked == true); useGuesses = (acceptGuesses.IsChecked == true); //Console.WriteLine(useSolver + " " + autoRunning + " " + useGuesses); } } }