rename references

This commit is contained in:
2023-09-28 20:24:18 +08:00
parent 50b5f8c2c1
commit 0be2781d70
274 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/WindowController"/>
<classpathentry combineaccessrules="false" kind="src" path="/MineSweeperSolver"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -0,0 +1,2 @@
/bin/
/build/

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Minesweeper</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="ASCII"?>
<anttasks:AntTask xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:anttasks="http://org.eclipse.fx.ide.jdt/1.0" buildDirectory="${project}/build">
<deploy>
<application name="Minesweeper"/>
<info/>
</deploy>
<signjar/>
</anttasks:AntTask>

View File

@ -0,0 +1,88 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package minesweeper;
import javafx.application.Platform;
/**
*
* @author David
*/
public class Animator implements Runnable{
private Thread animator;
private ScreenController scon;
private boolean stopped = false;
private long gameTime = 0;
public Animator(ScreenController scon) {
this.scon = scon;
// start animating the display pane - see the run method
animator = new Thread(this, "Animator");
}
public void start() {
animator.start();
}
public void stop() {
stopped = true;
}
@Override
public void run() {
long timeDiff, sleep, timeDelay;
timeDelay = 50;
gameTime = System.currentTimeMillis();
while (!stopped) {
Platform.runLater(new Runnable() {
@Override public void run() {
scon.updateTime();
scon.moveCheck();
scon.highlightMove();
}
});
// calculate how long the work took
timeDiff = System.currentTimeMillis() - gameTime;
// pause for the ramainder of timeDelay
sleep = timeDelay - timeDiff;
if (sleep < 0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
gameTime += timeDelay;
}
System.out.println("Animator thread stopped");
}
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="window" prefHeight="169.0" prefWidth="240.0" stylesheets="@Screen.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="minesweeper.CustomController">
<children>
<TextField fx:id="heightText" alignment="CENTER_RIGHT" layoutX="68.0" layoutY="29.0" prefHeight="25.0" prefWidth="49.0">
<effect>
<DropShadow />
</effect>
</TextField>
<Label layoutX="15.0" layoutY="65.0" text="Width" />
<Label layoutX="13.0" layoutY="33.0" text="Height" />
<Label layoutX="15.0" layoutY="98.0" text="Mines" />
<TextField fx:id="widthText" alignment="CENTER_RIGHT" layoutX="68.0" layoutY="61.0" prefHeight="25.0" prefWidth="49.0">
<effect>
<DropShadow />
</effect>
</TextField>
<TextField fx:id="minesText" alignment="CENTER_RIGHT" layoutX="68.0" layoutY="94.0" prefHeight="25.0" prefWidth="49.0">
<effect>
<DropShadow />
</effect>
</TextField>
<Label layoutX="13.0" layoutY="6.0" prefHeight="17.0" prefWidth="137.0" text="Choose custom settings" />
<Button layoutX="135.0" layoutY="29.0" mnemonicParsing="false" onAction="#handleOkayButton" prefHeight="25.0" prefWidth="78.0" text="Okay" textAlignment="CENTER">
<effect>
<InnerShadow />
</effect>
</Button>
<Button layoutX="135.0" layoutY="61.0" mnemonicParsing="false" onAction="#handleCancelButton" prefHeight="25.0" prefWidth="78.0" text="Cancel">
<effect>
<InnerShadow />
</effect>
</Button>
<Label layoutX="11.0" layoutY="132.0" text="Game #" />
<TextField fx:id="gameCodeText" layoutX="68.0" layoutY="128.0">
<effect>
<DropShadow />
</effect>
</TextField>
</children>
</AnchorPane>

View File

@ -0,0 +1,230 @@
package minesweeper;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import minesweeper.coach.HelperController;
import minesweeper.gamestate.GameStateModel;
import minesweeper.settings.GameSettings;
public class CustomController {
@FXML
private AnchorPane window;
@FXML private TextField heightText;
@FXML private TextField widthText;
@FXML private TextField minesText;
@FXML private TextField gameCodeText;
private Stage stage;
private Scene scene;
private int height;
private int width;
private int mines;
private GameSettings gameSettings;
private long gameCode;
private static CustomController custom;
private boolean wasCancelled = false;
/**
* Initializes the controller class.
*/
/*
@Override
public void initialize(URL url, ResourceBundle rb) {
System.out.println("Entered Custom Control initialize method");
}
*/
@FXML
void initialize() {
System.out.println("Entered Custom Control initialize method");
if (heightText == null) {
System.out.println("heightText is null");
}
}
@FXML
private void handleOkayButton(ActionEvent event) {
this.mines = StringToInteger(minesText.getText(), 1, 20000, this.mines);
this.width = StringToInteger(widthText.getText(), 2, 200, this.width);
this.height = StringToInteger(heightText.getText(), 2, 200, this.height);
try {
this.gameCode = Long.parseLong(gameCodeText.getText());
} catch (NumberFormatException e) {
this.gameCode = 0;
}
if (mines > width * height - 1) {
mines = width * height - 1;
}
gameSettings = GameSettings.create(width, height, mines);
stage.close();
}
@FXML
private void handleCancelButton(ActionEvent event) {
wasCancelled = true;
stage.close();
}
public static CustomController launch(Window owner, GameStateModel game) {
// if we have already created it then show it and return
if (custom != null) {
//custom.stage.show();
custom.wasCancelled = false;
return custom;
}
if (CustomController.class.getResource("Custom.fxml") == null) {
System.out.println("Custom.fxml not found");
}
// create the helper screen
FXMLLoader loader = new FXMLLoader(CustomController.class.getResource("Custom.fxml"));
Parent root = null;
try {
root = (Parent) loader.load();
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
custom = loader.getController();
//helperController = loader.getController();
if (custom == null) {
System.out.println("Custom is null");
}
if (root == null) {
System.out.println("Root is null");
}
custom.scene = new Scene(root);
custom.stage = new Stage();
custom.stage.setScene(custom.scene);
custom.stage.setTitle("Custom board");
custom.stage.getIcons().add(Graphics.getMine());
custom.stage.setResizable(false);
custom.stage.initOwner(owner);
custom.stage.initModality(Modality.WINDOW_MODAL);
custom.width = game.getWidth();
custom.height = game.getHeight();
custom.mines = game.getMines();
custom.widthText.setText(String.valueOf(custom.width));
custom.heightText.setText(String.valueOf(custom.height));
custom.minesText.setText(String.valueOf(custom.mines));
//Stage st = Minesweeper.getStage();
//custom.stage.setX(st.getX()+ st.getWidth());
//custom.stage.setY(st.getY());
custom.stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.out.println("Entered OnCloseRequest handler");
custom.wasCancelled = true;
}
});
return custom;
}
private int StringToInteger(String text, int min, int max, int dflt) {
int val = dflt;
try {
val = Integer.parseInt(text);
} catch (NumberFormatException e) {
}
val = Math.max(val, min);
val = Math.min(val, max);
return val;
}
public static CustomController getCustomController() {
return custom;
}
public Stage getStage() {
return this.stage;
}
public GameSettings getGameSettings() {
return this.gameSettings;
}
public int getMines() {
return this.mines;
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
public long getGameCode() {
return this.gameCode;
}
public boolean wasCancelled() {
return wasCancelled;
}
}

View File

@ -0,0 +1,83 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package minesweeper;
import javafx.scene.image.Image;
/**
*
* @author David
*/
public class Graphics {
public static final double SIZE = 50;
private static final Image button;
private static final Image mineBang;
private static final Image flag;
private static final Image[] number = new Image[9];
private static final Image mine;
static {
button = clean(new Image(Graphics.class.getResource("resources/ms_button.png").toExternalForm(), SIZE, SIZE, true, true));
mineBang = clean(new Image(Graphics.class.getResource("resources/ms_mine_bang.png").toExternalForm(), SIZE, SIZE, true, true));
flag = clean(new Image(Graphics.class.getResource("resources/ms_flag.png").toExternalForm(), SIZE, SIZE, true, true));
mine = clean(new Image(Graphics.class.getResource("resources/ms_mine.png").toExternalForm(), SIZE, SIZE, true, true));
number[0] = clean(new Image(Graphics.class.getResource("resources/ms_zero.png").toExternalForm(), SIZE, SIZE, true, true));
number[1] = clean(new Image(Graphics.class.getResource("resources/ms_one.png").toExternalForm(), SIZE, SIZE, true, true));
number[2] = clean(new Image(Graphics.class.getResource("resources/ms_two.png").toExternalForm(), SIZE, SIZE, true, true));
number[3] = clean(new Image(Graphics.class.getResource("resources/ms_three.png").toExternalForm(), SIZE, SIZE, true, true));
number[4] = clean(new Image(Graphics.class.getResource("resources/ms_four.png").toExternalForm(), SIZE, SIZE, true, true));
number[5] = clean(new Image(Graphics.class.getResource("resources/ms_five.png").toExternalForm(), SIZE, SIZE, true, true));
number[6] = clean(new Image(Graphics.class.getResource("resources/ms_six.png").toExternalForm(), SIZE, SIZE, true, true));
number[7] = clean(new Image(Graphics.class.getResource("resources/ms_seven.png").toExternalForm(), SIZE, SIZE, true, true));
number[8] = clean(new Image(Graphics.class.getResource("resources/ms_eight.png").toExternalForm(), SIZE, SIZE, true, true));
}
static public Image getNumber(int c) {
return number[c];
}
static public Image getMineBang() {
return mineBang;
}
static public Image getMine() {
return mine;
}
static public Image getFlag() {
return flag;
}
static public Image getButton() {
return button;
}
// in case we want to do some image manipulation
static private Image clean(Image image) {
return image;
}
}

View File

@ -0,0 +1,247 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package minesweeper;
import java.io.File;
import java.util.Optional;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import minesweeper.gamestate.GameFactory;
import minesweeper.gamestate.GameStateModel;
import minesweeper.gamestate.GameStateModelViewer;
import minesweeper.gamestate.GameStateReader;
import minesweeper.gamestate.GameStateStandardWith8;
import minesweeper.gamestate.msx.GameStateX;
import minesweeper.gamestate.msx.ScreenScanner;
import minesweeper.settings.GameSettings;
import minesweeper.settings.GameType;
import minesweeper.solver.Solver;
/**
*
* @author David
*/
public class Minesweeper extends Application {
public final static String VERSION = "1.04b";
public static final String TITLE = "Minesweeper coach (" + VERSION + ") Solver version " + Solver.VERSION;
private static GameStateModelViewer myGame;
private static GameSettings gameSettings;
private static Stage myStage = null;
private static ScreenController myController;
@Override
public void start(Stage stage) throws Exception {
myStage = stage;
// this creates a hard game on start-up
createNewGame(ScreenController.DIFFICULTY_EXPERT, GameType.STANDARD, null);
System.out.println("creating root");
FXMLLoader loader = new FXMLLoader(getClass().getResource("Screen.fxml"));
Parent root = (Parent) loader.load();
myController = loader.getController();
System.out.println("root created");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle(TITLE);
stage.getIcons().add(Graphics.getMine());
stage.setX(50);
stage.setY(50);
stage.setWidth(1000);
stage.setHeight(650);
stage.show();
//stage.setResizable(false);
myController.newGame(ScreenController.DIFFICULTY_EXPERT);
stage.setOnHidden(null);
// actions to perform when a close request is received
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.out.println("Minesweeper window has received a close request");
//event.consume();
myController.kill();
Platform.exit();
}
});
}
// use the difficulty set in the menu system
static public GameStateModel newGame() {
return createNewGame(myController.getDifficulty(), myController.getGameType(), null);
}
// force a difficulty setting
static public GameStateModel createNewGame(int difficulty, GameType gameType, File fileSelected) {
long gameCode = 0;
gameSettings = GameSettings.EXPERT;
switch (difficulty) {
case ScreenController.DIFFICULTY_BEGINNER:
gameSettings = GameSettings.BEGINNER;
break;
case ScreenController.DIFFICULTY_ADVANCED:
gameSettings = GameSettings.ADVANCED;
break;
case ScreenController.DIFFICULTY_EXPERT:
gameSettings = GameSettings.EXPERT;
break;
case ScreenController.DEFER_TO_MINESWEEPERX:
ScreenScanner scanner = new ScreenScanner("Minesweeper X");
if (!scanner.isValid()) {
System.out.println("MinsweeperX not found");
Alert alert = new Alert(AlertType.ERROR, "MinesweeperX can't be found: ensure it is maximised and unobstructed");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
return myGame; // old game
}
}
myGame = new GameStateX(scanner);
System.out.println("X = " + myGame.getWidth() + " Y =" + myGame.getHeight());
break;
case ScreenController.DIFFICULTY_FILE:
GameStateModelViewer game;
try {
game = GameStateReader.load(fileSelected);
myGame = game;
} catch (Exception e) {
Alert alert = new Alert(AlertType.ERROR, e.getLocalizedMessage());
Optional<ButtonType> result = alert.showAndWait();
return null;
}
break;
case ScreenController.DIFFICULTY_CUSTOM:
CustomController custom = CustomController.getCustomController();
gameSettings = custom.getGameSettings();
gameCode = custom.getGameCode();
break;
default:
gameSettings = GameSettings.EXPERT;
}
// if we are shadowing minesweeperX then we don't need to do any more
if (difficulty == ScreenController.DEFER_TO_MINESWEEPERX || difficulty == ScreenController.DIFFICULTY_FILE) {
return myGame;
}
myGame = GameFactory.create(gameType, gameSettings, gameCode);
//myGame = new GameStateStandardWith8(gameSettings);
/*
switch (gameType) {
case GameType.:
if (gameCode == 0) {
myGame = new GameStateEasy(gameSettings);
} else {
myGame = new GameStateEasy(gameSettings, gameCode);
}
break;
case ScreenController.GAMETYPE_NORMAL:
if (gameCode == 0) {
myGame = new GameStateStandard(gameSettings);
} else {
myGame = new GameStateStandard(gameSettings, gameCode);
}
break;
case ScreenController.GAMETYPE_HARD:
if (gameCode == 0) {
myGame = new GameStateHard(gameSettings);
} else {
myGame = new GameStateHard(gameSettings, gameCode);
}
break;
default:
if (gameCode == 0) {
myGame = new GameStateStandard(gameSettings);
} else {
myGame = new GameStateStandard(gameSettings, gameCode);
}
}
*/
return myGame;
}
static public GameStateModelViewer getGame() {
return myGame;
}
static public void playGame(GameStateModelViewer gs) {
myGame = gs;
myController.newGame(gs);
}
static public GameSettings getGameSettings() {
return gameSettings;
}
static public Stage getStage() {
return myStage;
}
@Override
public void stop() {
myController.stop();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,56 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package minesweeper;
import javafx.scene.Node;
/**
*
* @author David
*/
public class Rotator implements Runnable {
private Thread rotator;
Node object;
public Rotator(Node object) {
this.object = object;
rotator = new Thread(this, "Rotator");
}
public void start() {
rotator.start();
}
@Override
public void run() {
for (int i=0; i < 360; i=i+20) {
this.object.setRotate(i);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
}
this.object.setRotate(0);
}
}

View File

@ -0,0 +1,47 @@
/*
Document : Screen
Created on : 29-Nov-2013, 15:48:32
Author : David
Description:
Purpose of the stylesheet follows.
*/
.root {
-fx-background-color: linear-gradient(GREEN, DARKGREEN);
-fx-background-color: linear-gradient(#61a2b1, #2A5058);
}
.menu-bar {
-fx-background-color: linear-gradient(#61a2b1, #2A5058);
}
#myPane {
-fx-border-color: black;
-fx-border-width: 1;
-fx-border-style: solid outside;
}
#scoreLabel {
-fx-border-color: black;
-fx-border-width: 1;
-fx-border-style: solid outside;
-fx-border-radius: 15;
}
#timeLabel {
-fx-border-color: black;
-fx-border-width: 1;
-fx-border-style: solid outside;
-fx-border-radius: 15;
}

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?scenebuilder-stylesheet Screen.css?>
<AnchorPane id="AnchorPane" fx:id="window" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="424.0" prefWidth="591.0" style="-fx-background-color: green;" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="minesweeper.ScreenController">
<children>
<Pane fx:id="myPane" minWidth="-1.0" onMousePressed="#mouseDown" onMouseReleased="#mouseUp" pickOnBounds="true" prefHeight="261.0" prefWidth="455.0" style="&#10;" AnchorPane.bottomAnchor="35.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="70.0" />
<MenuBar prefWidth="455.0" style="-fx-background-color: white;" stylesheets="@Screen.css" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu mnemonicParsing="false" text="Game">
<items>
<RadioMenuItem id="easyMode" fx:id="easyMode" mnemonicParsing="false" onAction="#handleDifficulty" text="Easy (9x9x10)">
<toggleGroup>
<ToggleGroup fx:id="Difficulty" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="mediumMode" mnemonicParsing="false" onAction="#handleDifficulty" text="Medium (16x16x40)" toggleGroup="$Difficulty" />
<RadioMenuItem fx:id="hardMode" mnemonicParsing="false" onAction="#handleDifficulty" selected="true" text="Hard (30x16x99)" toggleGroup="$Difficulty" />
<RadioMenuItem fx:id="msxMode" mnemonicParsing="false" onAction="#handleDifficulty" text="MinesweeperX" toggleGroup="$Difficulty" />
<RadioMenuItem fx:id="fromFile" mnemonicParsing="false" onAction="#handleDifficulty" text="From file..." toggleGroup="$Difficulty" />
<RadioMenuItem fx:id="customMode" mnemonicParsing="false" onAction="#handleDifficulty" text="Custom..." toggleGroup="$Difficulty" />
<SeparatorMenuItem mnemonicParsing="false" text="Game type" />
<Menu mnemonicParsing="false" text="Game type">
<items>
<RadioMenuItem fx:id="gameTypeEasy" mnemonicParsing="false" onAction="#handleGameType" text="Easy (zero on start)">
<toggleGroup>
<ToggleGroup fx:id="gameType" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="gameTypeNormal" mnemonicParsing="false" onAction="#handleGameType" selected="true" text="Standard (safe start)" toggleGroup="$gameType" />
<RadioMenuItem fx:id="gameTypeHard" mnemonicParsing="false" onAction="#handleGameType" text="Hard (unsafe start)" toggleGroup="$gameType" />
</items>
</Menu>
<SeparatorMenuItem mnemonicParsing="false" />
<MenuItem fx:id="saveBoard" mnemonicParsing="false" onAction="#saveBoardHandle" text="Save board..." />
<SeparatorMenuItem mnemonicParsing="false" />
<MenuItem mnemonicParsing="false" onAction="#exitGameHandle" text="Exit game" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Coach">
<items>
<CheckMenuItem fx:id="showMove" mnemonicParsing="false" selected="true" text="Show best move" />
<CheckMenuItem fx:id="showTooltips" mnemonicParsing="false" selected="true" text="Show tooltips" />
<CheckMenuItem fx:id="acceptGuess" mnemonicParsing="false" selected="false" text="Accept guesses" />
<CheckMenuItem fx:id="showMines" mnemonicParsing="false" onAction="#showMinesToggled" text="Show mines" />
<CheckMenuItem fx:id="dumpTree" mnemonicParsing="false" onAction="#dumpTreeToggled" text="Dump probability tree" />
<CheckMenuItem fx:id="probHeatMap" disable="true" mnemonicParsing="false" onAction="#probHeatMapToggled" text="Probability heat map" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Options">
<items>
<MenuItem mnemonicParsing="false" onAction="#handleCopyToClipboard" text="Copy seed to clipboard" />
<Menu mnemonicParsing="false" text="Play style">
<items>
<RadioMenuItem fx:id="psFlagging" mnemonicParsing="false" onAction="#setPlayStyle" selected="true" text="Flagging">
<toggleGroup>
<ToggleGroup fx:id="playStyle" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="psNoFlagging" mnemonicParsing="false" onAction="#setPlayStyle" text="No flagging" toggleGroup="$playStyle" />
<RadioMenuItem fx:id="psEfficiency" mnemonicParsing="false" onAction="#setPlayStyle" text="Efficiency" toggleGroup="$playStyle" />
<RadioMenuItem fx:id="psNfEfficiency" mnemonicParsing="false" onAction="#setPlayStyle" text="No flag efficiency" toggleGroup="$playStyle" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Guessing method">
<items>
<RadioMenuItem fx:id="standardGuess" mnemonicParsing="false" text="Safety with progress">
<toggleGroup>
<ToggleGroup fx:id="GuessType" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="secondarySafetyGuess" mnemonicParsing="false" selected="true" text="Secondary safety with progress" toggleGroup="$GuessType" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Random numbers">
<items>
<RadioMenuItem fx:id="rngJava" mnemonicParsing="false" selected="true" text="Java random numbers">
<toggleGroup>
<ToggleGroup fx:id="RNG" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="rngKiss64" mnemonicParsing="false" text="KISS64 random numbers" toggleGroup="$RNG" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Brute force analysis">
<items>
<RadioMenuItem fx:id="sol400" mnemonicParsing="false" text="400 solutions">
<toggleGroup>
<ToggleGroup fx:id="BFDA" />
</toggleGroup>
</RadioMenuItem>
<RadioMenuItem fx:id="sol4000" mnemonicParsing="false" selected="true" text="4000 solutions" toggleGroup="$BFDA" />
</items>
</Menu>
</items>
</Menu>
<Menu mnemonicParsing="false" text="Bulk run">
<items>
<MenuItem mnemonicParsing="false" onAction="#handleNewBulkRun" text="New bulk run..." />
</items>
</Menu>
</menus>
</MenuBar>
<Label fx:id="scoreLabel" alignment="TOP_CENTER" layoutY="28.0" minHeight="24.0" prefHeight="37.0" prefWidth="56.0" style="-fx-border-color: Black; -fx-border-radius: 15; -fx-border-style: solid outside;" text="000" textAlignment="LEFT" textFill="BLACK" AnchorPane.leftAnchor="10.0">
<font>
<Font name="System Bold" size="24.0" fx:id="x1" />
</font>
</Label>
<Label fx:id="timeLabel" alignment="TOP_CENTER" contentDisplay="LEFT" font="$x1" layoutY="28.0" prefHeight="37.0" prefWidth="56.0" style="-fx-border-color: Black; -fx-border-style: solid outside; -fx-border-radius: 15;" text="000" textAlignment="LEFT" textFill="BLACK" AnchorPane.rightAnchor="10.0" />
<Button id="button" fx:id="automateButton" alignment="TOP_CENTER" minWidth="-Infinity" onAction="#handleAutomateButton" prefWidth="100.0" text="Automate" textAlignment="CENTER" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="10.0" />
<Circle fx:id="highlight" centerX="50.0" centerY="50.0" fill="#fff500" layoutX="281.0" layoutY="-11.0" mouseTransparent="true" opacity="0.49" radius="26.25" stroke="BLACK" strokeType="INSIDE" strokeWidth="4.0" />
<HBox alignment="CENTER" layoutX="10.0" layoutY="28.0" minHeight="-Infinity" prefHeight="37.0" prefWidth="571.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="28.0">
<children>
<Button fx:id="newGameButton" alignment="TOP_CENTER" minWidth="-Infinity" onAction="#handleNewGameButton" prefHeight="25.0" prefWidth="100.0" text="New Game" textAlignment="CENTER" />
</children>
</HBox>
</children>
<stylesheets>
<URL value="@Screen.css" />
</stylesheets>
</AnchorPane>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,253 @@
package minesweeper.bulk;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Random;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import minesweeper.Graphics;
import minesweeper.gamestate.GameStateModel;
import minesweeper.random.DefaultRNG;
import minesweeper.settings.GameSettings;
import minesweeper.settings.GameType;
import minesweeper.solver.settings.SolverSettings;
import minesweeper.structure.Location;
public class BulkController {
private final static DecimalFormat PERCENT = new DecimalFormat("#0.000%");
@FXML private AnchorPane window;
@FXML private TextField gameCount;
@FXML private TextField gameSeed;
@FXML private Label winPercentage;
@FXML private ProgressBar progressRun;
@FXML private Label progressRunLabel;
@FXML private TextField startLocX;
@FXML private TextField startLocY;
@FXML private CheckBox showGames;
@FXML private CheckBox winsOnly;
private Stage stage;
private Scene scene;
private int gamesMax;
private long gameGenerator;
private GameSettings gameSettings;
private GameType gameType;
private Location startLocation;
private SolverSettings preferences;
//private ResultsController resultsController;
private BulkRunner bulkRunner;
private boolean wasCancelled = false;
@FXML
void initialize() {
System.out.println("Entered Bulk Screen initialize method");
}
@FXML
private void handleNewSeedButton(ActionEvent event) {
gameSeed.setText(String.valueOf(new Random().nextLong()));
}
@FXML
private void handleOkayButton(ActionEvent event) {
System.out.println("handleOkayButton method entered");
if (bulkRunner != null && !bulkRunner.isFinished()) {
System.out.println("Previous bulk run still running");
return;
}
if (gameCount.getText().trim().isEmpty()) {
gamesMax = 1000;
} else {
try {
gamesMax = Integer.parseInt(gameCount.getText().trim());
if (gamesMax < 1) {
gamesMax = 1000;
}
} catch (NumberFormatException e) {
gamesMax = 1000;
}
}
gameCount.setText(String.valueOf(gamesMax));
if (gameSeed.getText().trim().isEmpty()) {
gameGenerator = new Random().nextLong();
} else {
try {
gameGenerator = Long.parseLong(gameSeed.getText().trim());
} catch (NumberFormatException e) {
gameGenerator = new Random().nextLong();
//gameSeed.setText("");
}
}
startLocation = null;
if (!startLocX.getText().trim().isEmpty() && !startLocY.getText().trim().isEmpty()) {
try {
int startX = Integer.parseInt(startLocX.getText().trim());
int startY = Integer.parseInt(startLocY.getText().trim());
if (startX >= 0 && startX < gameSettings.width && startY >= 0 && startY < gameSettings.height) {
startLocation = new Location(startX, startY);
System.out.println("Start location set to " + startLocation.toString());
}
} catch (NumberFormatException e) {
}
}
gameSeed.setText(String.valueOf(gameGenerator));
bulkRunner = new BulkRunner(this, gamesMax, gameSettings, gameType, gameGenerator, startLocation, showGames.isSelected(), winsOnly.isSelected(), preferences);
new Thread(bulkRunner, "Bulk Run").start();
}
@FXML
private void handleCancelButton(ActionEvent event) {
System.out.println("handleCancelButton method entered");
stage.close();
}
public static BulkController launch(Window owner, GameSettings gameSettings, GameType gameType, SolverSettings preferences ) {
if (BulkController.class.getResource("BulkScreen.fxml") == null) {
System.out.println("BulkScreen.fxml not found");
}
// create the bulk runner screen
FXMLLoader loader = new FXMLLoader(BulkController.class.getResource("BulkScreen.fxml"));
Parent root = null;
try {
root = (Parent) loader.load();
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
BulkController custom = loader.getController();
if (custom == null) {
System.out.println("Custom is null");
}
if (root == null) {
System.out.println("Root is null");
}
custom.gameSettings = gameSettings;
custom.gameType = gameType;
custom.preferences = preferences;
custom.scene = new Scene(root);
custom.stage = new Stage();
custom.stage.setScene(custom.scene);
custom.stage.setTitle("Bulk run - " + gameSettings.description() + ", " + gameType.name + ", " + DefaultRNG.getRNG(1).shortname());
custom.stage.getIcons().add(Graphics.getMine());
custom.stage.setResizable(false);
custom.stage.initOwner(owner);
custom.stage.initModality(Modality.WINDOW_MODAL);
custom.stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.out.println("Entered OnCloseRequest handler");
if (custom.bulkRunner != null) {
custom.bulkRunner.forceStop();
}
System.gc();
}
});
custom.gameCount.setText("1000");
custom.progressRun.setProgress(0d);
custom.progressRunLabel.setText("");
//custom.resultsController = ResultsController.launch(null, gameSettings, gameType);
custom.getStage().show();
return custom;
}
public Stage getStage() {
return this.stage;
}
public boolean wasCancelled() {
return wasCancelled;
}
public void update(int steps, int maxSteps, int wins) {
Platform.runLater(new Runnable() {
@Override public void run() {
double prog = (double) steps / (double) maxSteps;
progressRun.setProgress(prog);
progressRunLabel.setText(steps + "(" + wins + ") /" + maxSteps);
double winPerc = (double) wins / (double) steps;
double err = Math.sqrt(winPerc * ( 1- winPerc) / (double) steps) * 1.9599d;
winPercentage.setText(PERCENT.format(winPerc) + " +/- " + PERCENT.format(err));
}
});
}
/*
public void storeResult(GameStateModel gs) {
resultsController.update(gs);
}
*/
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="554.0" prefWidth="446.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="minesweeper.bulk.ResultsController">
<children>
<Button alignment="CENTER" layoutX="379.0" layoutY="401.0" mnemonicParsing="false" onAction="#handlePlayButton" prefHeight="25.0" prefWidth="84.0" text="Play game" AnchorPane.bottomAnchor="5.0" AnchorPane.rightAnchor="20.0" />
<TableView fx:id="resultsTable" prefHeight="414.0" prefWidth="550.0" AnchorPane.bottomAnchor="40.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="columnCount" prefWidth="79.0" style="-fx-alignment: CENTER_RIGHT;" text="Number" />
<TableColumn fx:id="columnSeed" prefWidth="165.0" style="-fx-alignment: CENTER;" text="Seed" />
<TableColumn fx:id="columnComplete" prefWidth="99.0" style="-fx-alignment: CENTER_RIGHT;" text="Tiles revealed" />
<TableColumn fx:id="columnResult" prefWidth="88.0" style="-fx-alignment: CENTER;" text="Result" />
</columns>
</TableView>
</children>
</AnchorPane>

View File

@ -0,0 +1,154 @@
package minesweeper.bulk;
import minesweeper.gamestate.GameFactory;
import minesweeper.gamestate.GameStateModel;
import minesweeper.random.DefaultRNG;
import minesweeper.random.RNG;
import minesweeper.settings.GameSettings;
import minesweeper.settings.GameType;
import minesweeper.solver.Solver;
import minesweeper.solver.settings.SolverSettings;
import minesweeper.structure.Action;
import minesweeper.structure.Location;
public class BulkRunner implements Runnable {
private boolean stop = false;
private final int maxSteps;
private final long seed;
private final BulkController controller;
private final GameSettings gameSettings;
private final GameType gameType;
private final Location startLocation;
private final SolverSettings preferences;
//private final Random seeder;
private int steps = 0;
private int wins = 0;
private final RNG seeder;
private ResultsController resultsController;
private boolean showGames;
private boolean winsOnly;
public BulkRunner(BulkController controller, int iterations, GameSettings gameSettings, GameType gameType,
long seed, Location startLocation, boolean showGames, boolean winsOnly, SolverSettings preferences) {
maxSteps = iterations;
this.seed = seed;
this.controller = controller;
this.gameSettings = gameSettings;
this.gameType = gameType;
this.startLocation = startLocation;
this.seeder = DefaultRNG.getRNG(seed);
this.showGames = showGames;
this.winsOnly = winsOnly;
this.preferences = preferences;
if (startLocation != null) {
this.preferences.setStartLocation(startLocation);
}
if (showGames) {
resultsController = ResultsController.launch(null, gameSettings, gameType);
}
}
@Override
public void run() {
System.out.println("At BulkRunner run method");
while (!stop && steps < maxSteps) {
steps++;
GameStateModel gs = GameFactory.create(gameType, gameSettings, seeder.random(0));
Solver solver = new Solver(gs, preferences, false);
boolean win = playGame(gs, solver);
if (win) {
wins++;
}
if (showGames && (win || !win && !winsOnly)) {
if (!resultsController.update(gs)) { // this returns false if the window has been closed
showGames = false;
resultsController = null;
System.out.println("Results window has been closed... will no longer send data to it");
}
}
controller.update(steps, maxSteps, wins);
}
stop = true;
System.out.println("BulkRunner run method ending");
}
private boolean playGame(GameStateModel gs, Solver solver) {
int state;
play: while (true) {
Action[] moves;
try {
solver.start();
moves = solver.getResult();
} catch (Exception e) {
System.out.println("Game " + gs.showGameKey() + " has thrown an exception!");
stop = true;
return false;
}
if (moves.length == 0) {
System.err.println("No moves returned by the solver for game " + gs.showGameKey());
stop = true;
return false;
}
// play all the moves until all done, or the game is won or lost
for (int i=0; i < moves.length; i++) {
boolean result = gs.doAction(moves[i]);
state = gs.getGameState();
if (state == GameStateModel.LOST || state == GameStateModel.WON) {
break play;
}
}
}
if (state == GameStateModel.LOST) {
return false;
} else {
return true;
}
}
public void forceStop() {
System.out.println("Bulk run being requested to stop");
stop = true;
}
public boolean isFinished() {
return stop;
}
}

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="278.0" prefWidth="403.0" style="-fx-background-color: green;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="minesweeper.bulk.BulkController">
<children>
<Button layoutX="317.0" layoutY="242.0" mnemonicParsing="false" onAction="#handleOkayButton" prefHeight="25.0" prefWidth="72.0" text="Start" />
<ProgressBar fx:id="progressRun" layoutX="96.0" layoutY="171.0" prefHeight="25.0" prefWidth="293.0" progress="0.21" style="-fx-control-inner-background: palegreen; -fx-accent: darkgreen;" />
<TextField fx:id="gameCount" layoutX="95.0" layoutY="18.0" style="-fx-background-color: silver;" />
<Label layoutX="15.0" layoutY="132.0" prefHeight="25.0" prefWidth="72.0" text="Win rate">
<font>
<Font size="14.0" />
</font></Label>
<Label layoutX="14.0" layoutY="18.0" prefHeight="25.0" prefWidth="72.0" text="Games" textAlignment="RIGHT">
<font>
<Font size="14.0" />
</font>
</Label>
<Label layoutX="15.0" layoutY="171.0" prefHeight="25.0" prefWidth="72.0" text="Progress" textAlignment="RIGHT">
<font>
<Font size="14.0" />
</font>
</Label>
<Label fx:id="progressRunLabel" alignment="CENTER" layoutX="112.0" layoutY="171.0" prefHeight="25.0" prefWidth="267.0" text="590/1000">
<font>
<Font name="System Bold" size="16.0" />
</font>
</Label>
<Label fx:id="winPercentage" layoutX="96.0" layoutY="131.0" prefHeight="27.0" prefWidth="293.0">
<font>
<Font name="System Bold" size="18.0" />
</font>
</Label>
<Label layoutX="14.0" layoutY="57.0" prefHeight="25.0" prefWidth="72.0" text="Seed" textAlignment="RIGHT">
<font>
<Font size="14.0" />
</font>
</Label>
<TextField fx:id="gameSeed" layoutX="95.0" layoutY="57.0" style="-fx-background-color: silver;" />
<Button layoutX="253.0" layoutY="57.0" mnemonicParsing="false" onAction="#handleNewSeedButton" prefHeight="25.0" prefWidth="72.0" text="New seed" />
<Label layoutX="14.0" layoutY="100.0" prefHeight="25.0" prefWidth="92.0" text="Start Loc: X=" textAlignment="RIGHT">
<font>
<Font size="14.0" />
</font>
</Label>
<TextField fx:id="startLocX" layoutX="95.0" layoutY="100.0" prefHeight="25.0" prefWidth="43.0" style="-fx-background-color: silver;" />
<Label layoutX="145.0" layoutY="100.0" prefHeight="25.0" prefWidth="29.0" text="Y=" textAlignment="RIGHT">
<font>
<Font size="14.0" />
</font>
</Label>
<TextField fx:id="startLocY" layoutX="165.0" layoutY="100.0" prefHeight="25.0" prefWidth="43.0" style="-fx-background-color: silver;" />
<CheckBox fx:id="showGames" layoutX="99.0" layoutY="211.0" mnemonicParsing="false" text="Show game results">
<font>
<Font size="14.0" />
</font>
</CheckBox>
<CheckBox fx:id="winsOnly" layoutX="99.0" layoutY="244.0" mnemonicParsing="false" text="Wins only">
<font>
<Font size="14.0" />
</font>
</CheckBox>
</children>
</AnchorPane>

View File

@ -0,0 +1,178 @@
package minesweeper.bulk;
import java.io.IOException;
import java.text.DecimalFormat;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableView.TableViewSelectionModel;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import minesweeper.Graphics;
import minesweeper.Minesweeper;
import minesweeper.gamestate.GameFactory;
import minesweeper.gamestate.GameStateModel;
import minesweeper.gamestate.GameStateModelViewer;
import minesweeper.settings.GameSettings;
import minesweeper.settings.GameType;
public class ResultsController {
private Stage stage;
private Scene scene;
@FXML private TableView<TableData> resultsTable;
@FXML private TableColumn<TableData, Integer> columnCount;
@FXML private TableColumn<TableData, Long> columnSeed;
@FXML private TableColumn<TableData, Integer> columnComplete;
@FXML private TableColumn<TableData, String> columnResult;
private GameSettings gameSettings;
private GameType gameType;
private int count = 0;
private boolean closed = false;
private ObservableList<TableData> items = FXCollections.observableArrayList();
@FXML
void initialize() {
System.out.println("Entered Bulk Result Screen initialize method");
}
@FXML
private void handlePlayButton(ActionEvent event) {
System.out.println("handlePlayButton method entered");
TableData selected = resultsTable.getSelectionModel().getSelectedItem();
long seed = selected.seedProperty().longValue();
System.out.println("Selected seed " + seed);
GameStateModelViewer gs = GameFactory.create(gameType, gameSettings, seed);
Minesweeper.playGame(gs);
}
public static ResultsController launch(Window owner, GameSettings gameSettings, GameType gameType) {
if (ResultsController.class.getResource("BulkScreen.fxml") == null) {
System.out.println("BulkScreen.fxml not found");
}
// create the bulk runner screen
FXMLLoader loader = new FXMLLoader(ResultsController.class.getResource("BulkResults.fxml"));
Parent root = null;
try {
root = (Parent) loader.load();
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
ResultsController custom = loader.getController();
if (custom == null) {
System.out.println("Custom is null");
}
if (root == null) {
System.out.println("Root is null");
}
custom.scene = new Scene(root);
custom.stage = new Stage();
custom.stage.setScene(custom.scene);
custom.stage.setTitle("Bulk run results");
custom.stage.getIcons().add(Graphics.getMine());
custom.stage.setResizable(false);
custom.stage.initOwner(owner);
custom.stage.initModality(Modality.WINDOW_MODAL);
custom.stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.out.println("Entered OnCloseRequest handler for Bulk Results Screen");
custom.closed = true;
custom.items.clear(); // clear down the table to encourage the items to be garbage collected
}
});
custom.resultsTable.setItems(custom.items);
custom.resultsTable.getSelectionModel();
custom.columnCount.setCellValueFactory(new PropertyValueFactory<TableData, Integer>("count"));
custom.columnSeed.setCellValueFactory(new PropertyValueFactory<TableData, Long>("seed"));
custom.columnComplete.setCellValueFactory(new PropertyValueFactory<TableData, Integer>("complete"));
custom.columnResult.setCellValueFactory(new PropertyValueFactory<TableData, String>("result"));
custom.getStage().show();
custom.gameType = gameType;
custom.gameSettings = gameSettings;
//System.out.println("Columns = " + custom.resultsTable.getColumns().size());
return custom;
}
public Stage getStage() {
return this.stage;
}
public boolean update(GameStateModel gs) {
if (closed) { // if the window has been closed then let anyone who calls know
return false;
}
count++;
final TableData td = new TableData(count, gs);
Platform.runLater(new Runnable() {
@Override public void run() {
items.add(td);
}
});
return true;
}
}

View File

@ -0,0 +1,74 @@
package minesweeper.bulk;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.FloatProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleFloatProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import minesweeper.gamestate.GameStateModel;
public class TableData {
private final static BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);
private final static DecimalFormat PERCENT = new DecimalFormat("#0.000%");
private final IntegerProperty count;
private final LongProperty seed;
private final IntegerProperty complete;
private final StringProperty result;
public TableData(int count, GameStateModel gs) {
this.count = new SimpleIntegerProperty(count);
seed = new SimpleLongProperty(gs.getSeed());
/*
BigDecimal areaToReveal = BigDecimal.valueOf(gs.getx() * gs.gety() - gs.getMines());
BigDecimal areaLeftToReveal = BigDecimal.valueOf(gs.getHidden() - gs.getMines());
BigDecimal percentageToDo = ONE_HUNDRED.multiply(BigDecimal.ONE.subtract(areaLeftToReveal.divide(areaToReveal, 6, RoundingMode.HALF_UP))).setScale(2, RoundingMode.HALF_UP);
System.out.println(percentageToDo + " " + (gs.getGameState() == GameStateModel.WON) + " " + areaLeftToReveal);
*/
complete = new SimpleIntegerProperty(gs.getWidth() * gs.getHeight() - gs.getHidden());
if (gs.getGameState() == GameStateModel.WON) {
result = new SimpleStringProperty("Won");
} else {
result = new SimpleStringProperty("Lost");
}
}
public LongProperty seedProperty() {
return seed;
}
public IntegerProperty completeProperty() {
return complete;
}
public StringProperty resultProperty() {
return result;
}
public IntegerProperty countProperty() {
return count;
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="300.0" prefWidth="477.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="minesweeper.coach.HelperController">
<children>
<TextArea fx:id="text" editable="false" focusTraversable="false" prefHeight="241.0" prefWidth="420.0" text="" wrapText="true" AnchorPane.bottomAnchor="25.0" AnchorPane.leftAnchor="25.0" AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="25.0" />
</children>
<stylesheets>
<URL value="@helper.css" />
</stylesheets>
</AnchorPane>

View File

@ -0,0 +1,177 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package minesweeper.coach;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import minesweeper.Graphics;
import minesweeper.Minesweeper;
import minesweeper.solver.Solver;
import minesweeper.solver.coach.CoachModel;
/**
* FXML Controller class
*
* @author David
*/
public class HelperController implements CoachModel, Initializable {
private static final Background BG_Red = new Background(new BackgroundFill(Color.RED, null, null));
private static final Background BG_Green = new Background(new BackgroundFill(Color.GREEN, null, null));
private static final Background BG_Orange = new Background(new BackgroundFill(Color.ORANGE, null, null));
@FXML
private TextArea text;
//private HelperController helperController;
public Stage stage;
public Scene scene;
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
System.out.println("Entered Helper Control initialize method");
// TODO
}
/*
public TextArea getTextArea() {
return text;
}
*/
public void kill() {
stage.close();
System.out.println("Killing the Helper Control Object");
}
public static HelperController launch() {
// create the helper screen
FXMLLoader loader = new FXMLLoader(HelperController.class.getResource("Helper.fxml"));
Parent root = null;
try {
root = (Parent) loader.load();
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
HelperController coach = loader.getController();
//helperController = loader.getController();
coach.scene = new Scene(root);
coach.stage = new Stage();
coach.stage.setScene(coach.scene);
coach.stage.setTitle(Minesweeper.TITLE + " solver " + Solver.VERSION);
coach.stage.getIcons().add(Graphics.getFlag());
coach.writeLine("Minesweeper coach dedicated to Annie");
//((AnchorPane) scene.getRoot()).setBackground(BG_Green);
coach.setOkay();
Stage st = Minesweeper.getStage();
coach.stage.setX(st.getX()+ st.getWidth());
coach.stage.setY(st.getY());
coach.align();
coach.stage.show();
return coach;
}
final public void align() {
Stage st = Minesweeper.getStage();
if (st != null) {
stage.setX(st.getX()+ st.getWidth());
stage.setY(st.getY());
}
stage.setHeight(500);
}
@Override
final public void setOkay() {
setColor(BG_Green);
}
@Override
final public void setWarn() {
setColor(BG_Orange);
}
@Override
final public void setError() {
setColor(BG_Red);
}
private void setColor(Background c) {
((AnchorPane) scene.getRoot()).setBackground(c);
}
@Override
public void clearScreen() {
Platform.runLater(new Runnable() {
@Override public void run() {
text.clear();
}
});
}
@Override
public void writeLine(String line) {
Platform.runLater(new Runnable() {
@Override public void run() {
String textLine = text.getText();
if (textLine.equals("")) {
textLine = line;
} else {
textLine = textLine + "\n" + line;
}
text.setText(textLine);
}
});
}
@Override
public boolean analyseFlags() {
return true;
}
}

View File

@ -0,0 +1,7 @@
/*
* Empty Stylesheet file.
*/
.mainFxmlClass {
}

View File

@ -0,0 +1,160 @@
package minesweeper.gamestate.msx;
import minesweeper.gamestate.GameStateModel;
import minesweeper.gamestate.GameStateModelViewer;
import minesweeper.settings.GameSettings;
import minesweeper.structure.Location;
public class GameStateX extends GameStateModelViewer {
private ScreenScanner scanner;
public GameStateX(ScreenScanner scanner) {
super(GameSettings.create(scanner.getColumns(), scanner.getRows(), scanner.getMines()));
this.scanner = scanner;
doAutoComplete = false; // minesweeperX will do this itself
}
@Override
protected void placeFlagHandle(Location m) {
scanner.flag(m.x, m.y);
// give it time to set the flag
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
scanner.updateField();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected boolean clearSquareHitMine(Location m) {
scanner.clear(m.x, m.y);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
scanner.updateField();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// since the click could have expanded more areas we need to allow for that
for (int x=0; x < scanner.getColumns(); x++) {
for (int y=0; y < scanner.getRows(); y++) {
if (scanner.getValue(x, y) != ScreenScanner.HIDDEN && scanner.getValue(x, y) != ScreenScanner.FLAG) {
setRevealed(x,y);
}
// if a flag has been revealed by minesweeperX then place that in the model (this happens at the end of a game)
if (scanner.getValue(x, y) == ScreenScanner.FLAG && this.query(new Location(x,y)) == HIDDEN) {
setFlag(x,y);
}
}
}
return scanner.isGameLost();
}
@Override
protected void startHandle(Location m) {
}
@Override
protected int queryHandle(int x, int y) {
int value = scanner.getValue(x, y);
if (value == ScreenScanner.HIDDEN) {
System.out.println("value at (" + x + "," + y + ") is hidden and yet queried!");
}
if (value == ScreenScanner.FLAG) {
System.out.println("value at (" + x + "," + y + ") is a flag and yet queried!");
}
if (value == ScreenScanner.HIDDEN) {
return GameStateModel.HIDDEN;
} else if (value == ScreenScanner.EMPTY) {
return 0;
} else if (value == ScreenScanner.FLAG) {
return GameStateModel.HIDDEN;
} else if (value == ScreenScanner.BOMB) {
return GameStateModel.MINE;
} else if (value == ScreenScanner.EXPLODED_BOMB) {
return GameStateModel.EXPLODED_MINE;
} else {
return value;
}
}
@Override
/**
* No privileged access since MinesweeperX can't show it
*/
public int privilegedQuery(Location m, boolean showMines) {
return query(m);
}
@Override
protected boolean clearSurroundHandle(Location m) {
scanner.clearAll(m.x, m.y);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
scanner.updateField();
} catch (Exception e) {
e.printStackTrace();
}
// since the click could have expanded more areas we need to allow for that
for (int x=0; x < scanner.getColumns(); x++) {
for (int y=0; y < scanner.getRows(); y++) {
if (scanner.getValue(x, y) != ScreenScanner.HIDDEN && scanner.getValue(x, y) != ScreenScanner.FLAG) {
setRevealed(x,y);
}
// if a flag has been revealed by minesweeperX then place that in the model (this happens at the end of a game)
if (scanner.getValue(x, y) == ScreenScanner.FLAG && this.query(new Location(x,y)) == HIDDEN) {
setFlag(x,y);
}
}
}
return true;
}
@Override
public String showGameKey() {
return "Minesweeper X";
}
}

View File

@ -0,0 +1,17 @@
package minesweeper.gamestate.msx;
public class ScreenLocation {
public int topX, topY, botX, botY;
public boolean found = false;
final int botOffsetX=15;
final int botOffsetY=15;
final int offsetX=15;
final int offsetY=-101;
int width;
int height;
}

View File

@ -0,0 +1,477 @@
package minesweeper.gamestate.msx;
import java.awt.AWTException;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
import window.controller.GetWindowRect;
public class ScreenScanner {
public static final int EXPLODED_BOMB = -4;
public static final int BOMB = -2;
public static final int HIDDEN = 0;
public static final int EMPTY = -1;
public static final int FLAG = -3;
private final static int[] digitCheckX = new int[] {6, 2, 10, 6, 2, 10, 6};
private final static int[] digitCheckY = new int[] {3, 6, 6, 10, 16, 16, 20};
private class DigitCheck {
private boolean checks[];
private int value;
private DigitCheck(int value, boolean... checks) {
this.value = value;
this.checks = checks;
}
private boolean matches(boolean[] test) {
for (int i = 0; i < checks.length; i++ ) {
if (checks[i] != test[i]) {
return false;
}
}
return true;
}
}
private final DigitCheck DIGIT_ZERO = new DigitCheck(0, true, true, true, false, true, true, true);
private final DigitCheck DIGIT_ONE = new DigitCheck(1, false, false, true, false, false, true, false);
private final DigitCheck DIGIT_TWO = new DigitCheck(2, true, false, true, true, true, false, true);
private final DigitCheck DIGIT_THREE = new DigitCheck(3, true, false, true, true, false, true, true);
private final DigitCheck DIGIT_FOUR = new DigitCheck(4, false, true, true, true, false, true, false);
private final DigitCheck DIGIT_FIVE = new DigitCheck(5, true, true, false, true, false, true, true);
private final DigitCheck DIGIT_SIX = new DigitCheck(6, true, true, false, true, true, true, true);
private final DigitCheck DIGIT_SEVEN = new DigitCheck(7, true, false, true, false, false, true, false);
private final DigitCheck DIGIT_EIGHT = new DigitCheck(8, true, true, true, true, true, true, true);
private final DigitCheck DIGIT_NINE = new DigitCheck(9, true, true, true, true, false, true, true);
private final DigitCheck[] DIGIT_LIST = new DigitCheck[] {DIGIT_ZERO, DIGIT_ONE, DIGIT_TWO, DIGIT_THREE, DIGIT_FOUR, DIGIT_FIVE, DIGIT_SIX, DIGIT_SEVEN, DIGIT_EIGHT, DIGIT_NINE};
private String windowName;
private ScreenLocation location;
final int offsetX=15;
final int offsetY=-101;
final int botOffsetX=15;
final int botOffsetY=15;
private Robot clicker;
private int[][] field;
private int rows;
private int columns;
private int mines;
private int numHidden;
private int numEmpty;
private boolean dead = false;
public ScreenScanner(String windowName) {
this.windowName = windowName;
try {
clicker = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
//System.out.println("Locate");
locate();
// if we can't find the minesweeperX game then return
if (!location.found) {
return;
}
//System.out.println("Start MSX " + columns + " by " + rows);
startMinesweeperX(); // get focus
startMinesweeperX(); // start
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println("Get mines");
try {
mines = getMineCount();
} catch (AWTException e) {
System.out.println("Failed to parse the mine counter");
location.found = false;
return;
}
//System.out.println("update field");
try {
updateField();
} catch (Exception e) {
e.printStackTrace();
}
}
private void locate() {
location = new ScreenLocation();
//System.out.println("Locating minesweeper window... \nPlease do not move the window.");
int[] rect;
try {
rect = GetWindowRect.getRect(windowName);
//System.out.println("corner locations for minesweeper: \n"+ windowName+ Arrays.toString(rect));
location.topX = rect[0];
location.topY = rect[1];
location.botX = rect[2];
location.botY = rect[3];
int height = (location.botY - botOffsetY) - (location.topY - offsetY);
int width = (location.botX - botOffsetX) - (location.topX + offsetX);
rows = height/16;
columns = width/16;
if (rows < 1 || columns < 1) {
location.found = false;
return;
}
} catch (GetWindowRect.WindowNotFoundException e){
System.out.println("Please open minesweeper and try again");
location.found = false;
return;
} catch (GetWindowRect.GetWindowRectException e){
System.out.println(e.getMessage());
location.found = false;
return;
}
location.found = true;
}
private int getMineCount() throws AWTException
{
int height = 22;
int width = 38;
//System.out.println("window of size: " +width +"x" + height + "found");
Rectangle window = new Rectangle(location.topX + 20, location.topY + 62, width, height);
Robot robot = new Robot();
BufferedImage screenShot = robot.createScreenCapture(window);
int d1 = getDigit(screenShot, 0);
int d2 = getDigit(screenShot, 12);
int d3 = getDigit(screenShot, 26);
int mines = d1 * 100 + d2 * 10 + d3;
System.out.println("mines = " + mines);
return mines;
}
private int getDigit(BufferedImage screenShot, int position) {
boolean[] results = new boolean[7];
for (int i=0; i < digitCheckX.length; i++) {
int red = getRed(screenShot, position + digitCheckX[i], digitCheckY[i]);
if (red == 255) {
results[i] = true;
} else {
results[i] = false;
}
//System.out.println(" at " + digitCheckX[i] + ", " + digitCheckY[i] + " red is " + getRed(screenShot, digitCheckX[i], digitCheckY[i]));
}
int integer = -1;
for (DigitCheck dc: DIGIT_LIST) {
if (dc.matches(results)) {
integer = dc.value;
}
}
//System.out.println(integer);
return integer;
}
private int getRed(BufferedImage screenShot, int x, int y) {
int rgb = screenShot.getRGB(x, y);
final int red = (rgb & 0x00ff0000) >> 16;
//final int green = (rgb & 0x0000ff00) >> 8;
//final int blue = (rgb & 0x000000ff);
return red;
}
private BufferedImage getFieldImage() throws AWTException
{
int height = (location.botY - botOffsetY) - (location.topY - offsetY);
int width = (location.botX - botOffsetX) - (location.topX + offsetX);
//System.out.println("window of size: " +width +"x" + height + "found");
Rectangle window = new Rectangle(location.topX + offsetX, location.topY - offsetY, width, height);
Robot robot = new Robot();
BufferedImage screenShot = robot.createScreenCapture(window);
//rows = screenShot.getHeight()/16;
//columns = screenShot.getWidth()/16;
field = new int[columns][rows];
return screenShot;
}
public void updateField() throws Exception
{
numHidden=0;
//System.out.println("-UPDATING FIELD-");
BufferedImage screenShot = getFieldImage();
for(int x=0; x < columns; x++)
for(int y=0; y < rows; y++)
{
//System.out.println("row: "+y+" of " + rows + ", column: "+x + " of " +columns);
if(field[x][y]==HIDDEN || field[x][y] == FLAG) {
field[x][y]=readTile(screenShot, x , y);
if(field[x][y]==HIDDEN) {
numHidden++;
}
} else if(field[x][y]==EMPTY)
numEmpty++;
}
}
public void printField()
{
for(int y=rows+1;y>=0;y--)
{
System.out.print("[");
for(int x=0;x<columns+2;x++)
{
//System.out.println("column: "+x+" row: "+y);
System.out.print(field[x][y]+", ");
}
System.out.println("]");
}
}
private int readTile(BufferedImage image, int x, int y)
{
int tileType=0;//0=unknown,-1=empty -2=bomb, 1-8 are numbers
//System.out.println("tile to read: " + x + ", " + y);
int pixelX=(x)*16;
//int pixelY=(rows-(y+1))*16;
int pixelY=(y)*16;
//System.out.println("checking pixel color at: " + pixelX + ", "+pixelY);
String color="";
int n=1;
do
{
color=getPixelColor(image, pixelX+8,pixelY+n);
switch(color)
{
case("white"): {tileType=HIDDEN; n=16;} break;
case("flag"): {tileType=FLAG; n=16;} break;
case("blue"): {tileType=1; n=16;} break;
case("green"): {tileType=2; n=16;} break;
case("red"): {tileType=3; n=16;} break;
case("purple"): {tileType=4; n=16;} break;
case("brown"): {tileType=5; n=16;} break;
case("teal"): {tileType=6; n=16;} break;
case("black"): {tileType=7; n=16;} break;
case("gray"): {tileType=8; n=16;} break;
case("bomb"): {tileType=BOMB; n=16;} break;
case("exp_bomb"): {tileType=EXPLODED_BOMB; n=16;} break;
default: {tileType=EMPTY;} break;
}
n++;
}while(n<15);
//System.out.println("checking pixel color at: " + pixelX + ", "+pixelY + " x = " + x + " y = " + y + " colour " + color + " tiletype = " + tileType);
return tileType;
}
private String getPixelColor(BufferedImage image, int x, int y)//get pixel color
{
String color = "empty";
final int clr = image.getRGB(x, y);
final int red = (clr & 0x00ff0000) >> 16;
final int green = (clr & 0x0000ff00) >> 8;
final int blue = (clr & 0x000000ff);
if(red==255 && green==255 && blue==255) {
//int flagCheckColor = (image.getRGB(x-2, y+12) & 0x00ffffff);
//System.out.println("flag check = " + getRed(image, x, y+8));
if (getRed(image, x, y+8) == 0) { // flag
color = "flag";
} else {
color = "white";
}
}
else if(blue==255 && red==0 && green==0)
color = "blue";
else if(green==128 && red==0 && blue==0)
color = "green";
else if(red==255 && green==0 && blue==0)
{
color = "red";
int deadCheckColor = (image.getRGB(x-4, y+8) & 0x00ffffff);
if(deadCheckColor==0)
{
dead=true;
color="exp_bomb";
}
}
else if(blue==128 && red==0 && green==0)
color = "purple";
else if(red==128 && green==0 && blue==0)
color = "brown";
else if(red==0 && green==128 && blue==128)
color = "teal";
else if(red==0 && green==0 && blue==0)
{
color = "black";
int deadCheckColor = (image.getRGB(x-4, y+8) & 0x00ffffff);
//System.out.println(deadCheckColor);
if(deadCheckColor==0)
{
dead=true;
color="bomb";
}
}
else if(red==128 && green==128 && blue==128)
color = "gray";
return color;
}
private void click(int x, int y, int mask)
{
//int mask;
Point p = MouseInfo.getPointerInfo().getLocation();
//System.out.println("CLICKING - "+ x +", "+y);
/*
if(flag)
mask = InputEvent.BUTTON1_DOWN_MASK;
else
mask = InputEvent.BUTTON3_DOWN_MASK;
*/
// release the mouse (to remove the human click if there was one)
clicker.mouseRelease(InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK);
int pixelX=(x*16)+8;
//int pixelY=Math.abs((rows-y - 1 )*16)+8;
int pixelY=(y*16)+8;
pixelX+= location.topX + offsetX;
pixelY+= location.topY - offsetY;
clicker.mouseMove(pixelX, pixelY);
clicker.mousePress(mask);
clicker.mouseRelease(mask);
// move mouse back again
clicker.mouseMove(p.x, p.y);
}
public boolean isValid() {
return this.location.found;
}
public int getColumns() {
return columns;
}
public int getRows() {
return rows;
}
public int getMines() {
return mines;
}
public int getValue(int x, int y) {
return field[x][y];
}
public void clear(int x, int y) {
click(x, y, InputEvent.BUTTON1_DOWN_MASK);
}
public void flag(int x, int y) {
click(x, y, InputEvent.BUTTON3_DOWN_MASK);
}
public void clearAll(int x, int y) {
click(x, y, InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK);
}
public int getHiddenCount() {
return numHidden;
}
public boolean isGameLost() {
return dead;
}
public void startMinesweeperX() {
int x = columns / 2;
int y = - 2;
click(x, y, InputEvent.BUTTON1_DOWN_MASK);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B