956 lines
33 KiB
C#
956 lines
33 KiB
C#
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 {
|
|
/// <summary>
|
|
/// Interaction logic for MainWindow.xaml
|
|
/// </summary>
|
|
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);
|
|
|
|
}
|
|
}
|
|
}
|