├── Asynchronous ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs └── src │ ├── Asynchronous │ └── Asynchronous.java │ └── Monitor │ └── AsynchMonitor.java ├── Images ├── .project └── ReadMe │ ├── BruteForce_deep_analysis1.JPG │ ├── GUI.JPG │ ├── Local_analysis1.JPG │ ├── No_information_on_edge.JPG │ ├── Probability_engine1.JPG │ ├── Probability_engine2.JPG │ ├── Trivial_analysis1.JPG │ └── Trivial_analysis2.jpg ├── MineSweeperSolver ├── .classpath ├── .gitignore ├── .project └── src │ └── minesweeper │ └── solver │ ├── BoardState.java │ ├── BoardStateCache.java │ ├── BruteForce.java │ ├── BruteForceAnalysis.java │ ├── BruteForceAnalysisModel.java │ ├── CrunchResult.java │ ├── Cruncher.java │ ├── EfficiencyHelper.java │ ├── FiftyFiftyHelper.java │ ├── LocationEvaluator.java │ ├── LongTermRiskHelper.java │ ├── LongTermRiskHelperOld.java │ ├── ProbabilityEngineFast.java │ ├── ProbabilityEngineModel.java │ ├── ProgressEvaluator.java │ ├── PseudoHelper.java │ ├── RecursiveSafetyEvaluator.java │ ├── RolloutGenerator.java │ ├── SecondarySafetyEvaluator.java │ ├── SolutionCounter.java │ ├── Solver.java │ ├── SpaceCounter.java │ ├── WitnessWeb.java │ ├── bulk │ ├── BulkController.java │ ├── BulkEventController.java │ ├── BulkEventGame.java │ ├── BulkEventMain.java │ ├── BulkListener.java │ ├── BulkPlayer.java │ ├── BulkRequest.java │ ├── BulkRequestGame.java │ ├── BulkRollout.java │ ├── BulkWorker.java │ ├── GamePostListener.java │ ├── GamePreListener.java │ └── StaticCounter.java │ ├── coach │ ├── CoachModel.java │ └── CoachSilent.java │ ├── constructs │ ├── Box.java │ ├── CandidateLocation.java │ ├── ChordLocation.java │ ├── EvaluatedLocation.java │ ├── InformationLocation.java │ ├── LinkedLocation.java │ ├── LocationValue.java │ ├── Square.java │ ├── Witness.java │ └── WitnessData.java │ ├── iterator │ ├── Iterator.java │ ├── RandomIterator.java │ ├── SequentialIterator.java │ └── WitnessWebIterator.java │ ├── settings │ ├── PlayStyle.java │ ├── SettingsFactory.java │ └── SolverSettings.java │ └── utility │ ├── BigDecimalCache.java │ ├── Binomial.java │ ├── BinomialCache.java │ ├── Logger.java │ ├── PrimeSieve.java │ ├── ProgressMonitor.java │ └── Timer.java ├── Minesweeper ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs ├── build.fxbuild └── src │ └── minesweeper │ ├── Animator.java │ ├── Custom.fxml │ ├── CustomController.java │ ├── Graphics.java │ ├── Minesweeper.java │ ├── Rotator.java │ ├── Screen.css │ ├── Screen.fxml │ ├── ScreenController.java │ ├── bulk │ ├── BulkController.java │ ├── BulkResults.fxml │ ├── BulkRunner.java │ ├── BulkScreen.fxml │ ├── ResultsController.java │ └── TableData.java │ ├── coach │ ├── Helper.fxml │ ├── HelperController.java │ └── helper.css │ ├── gamestate │ └── msx │ │ ├── GameStateX.java │ │ ├── ScreenLocation.java │ │ └── ScreenScanner.java │ └── resources │ ├── ms_button.png │ ├── ms_eight.png │ ├── ms_five.png │ ├── ms_flag.png │ ├── ms_four.png │ ├── ms_mine.png │ ├── ms_mine_bang.png │ ├── ms_one.png │ ├── ms_question.png │ ├── ms_seven.png │ ├── ms_six.png │ ├── ms_three.png │ ├── ms_two.png │ └── ms_zero.png ├── MinesweeperBulk ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs └── src │ └── minesweeperbulk │ ├── BbbvMonitor.java │ ├── EfficiencyMonitor.java │ ├── GuessMonitor.java │ ├── MinesweeperBulk.java │ ├── PreActions.java │ ├── RandomGuesser.java │ ├── RemainingMonitor.java │ ├── StartStrategy.java │ └── StartStrategyResign.java ├── MinesweeperBulkCompare ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs └── src │ └── bulkCompare │ ├── BulkCompare.java │ ├── CompareMonitor.java │ ├── GuessMonitor.java │ ├── PreActions.java │ ├── StartStrategy.java │ ├── StartStrategyResign.java │ └── TwoWayCompare.java ├── MinesweeperExplorer ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs ├── build.fxbuild └── src │ ├── minesweeper │ └── explorer │ │ ├── busy │ │ ├── BusyController.java │ │ ├── BusyScreen.fxml │ │ ├── MonitorTask.java │ │ └── ParallelTask.java │ │ ├── gamestate │ │ └── GameStateExplorer.java │ │ ├── main │ │ ├── BoardMonitor.java │ │ ├── Explorer.java │ │ ├── Graphics.java │ │ ├── MainScreen.fxml │ │ ├── MainScreenController.java │ │ ├── TileValueData.java │ │ ├── TileValuesController.java │ │ ├── TileValuesScreen.fxml │ │ └── application.css │ │ ├── rollout │ │ ├── BulkRunner.java │ │ ├── RolloutController.java │ │ └── RolloutScreen.fxml │ │ └── structure │ │ ├── Board.java │ │ ├── Expander.java │ │ ├── LedDigit.java │ │ ├── LedDigits.java │ │ └── Tile.java │ └── resources │ └── images │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── exploded.png │ ├── flagged.png │ ├── flaggedWrong.png │ ├── flaggedWrong_thin.png │ ├── hidden.png │ ├── led0.png │ ├── led1.png │ ├── led2.png │ ├── led3.png │ ├── led4.png │ ├── led5.png │ ├── led6.png │ ├── led7.png │ ├── led8.png │ ├── led9.png │ ├── led_blank.png │ ├── mine.png │ └── mine_transparent.png ├── MinesweeperGameState ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs └── src │ └── minesweeper │ ├── gamestate │ ├── GameFactory.java │ ├── GameStateEasy.java │ ├── GameStateHard.java │ ├── GameStateModel.java │ ├── GameStateModelViewer.java │ ├── GameStateReader.java │ ├── GameStateStandard.java │ ├── GameStateStandardWith8.java │ └── MoveMethod.java │ ├── random │ ├── DefaultRNG.java │ ├── RNG.java │ ├── RNGJSF.java │ ├── RNGJava.java │ └── RNGKiss64.java │ ├── settings │ ├── GameSettings.java │ └── GameType.java │ └── structure │ ├── Action.java │ ├── Area.java │ └── Location.java ├── README.md └── WindowController ├── .classpath ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── bin └── window │ └── controller │ ├── GetWindowRect$GetWindowRectException.class │ ├── GetWindowRect$MyUser32.class │ ├── GetWindowRect$WindowNotFoundException.class │ └── GetWindowRect.class ├── jna-4.2.1.jar ├── jna-platform-4.2.1.jar └── src └── window └── controller └── GetWindowRect.java /Asynchronous/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Asynchronous/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /Asynchronous/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Asynchronous 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /Asynchronous/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /Asynchronous/src/Asynchronous/Asynchronous.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package Asynchronous; 6 | 7 | /** 8 | * 9 | * This interface defines an object which can be run asynchronously 10 | */ 11 | public interface Asynchronous { 12 | 13 | public void start(); 14 | 15 | public void requestStop(); 16 | 17 | public V getResult(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Images/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Images 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Images/ReadMe/BruteForce_deep_analysis1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/BruteForce_deep_analysis1.JPG -------------------------------------------------------------------------------- /Images/ReadMe/GUI.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/GUI.JPG -------------------------------------------------------------------------------- /Images/ReadMe/Local_analysis1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/Local_analysis1.JPG -------------------------------------------------------------------------------- /Images/ReadMe/No_information_on_edge.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/No_information_on_edge.JPG -------------------------------------------------------------------------------- /Images/ReadMe/Probability_engine1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/Probability_engine1.JPG -------------------------------------------------------------------------------- /Images/ReadMe/Probability_engine2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/Probability_engine2.JPG -------------------------------------------------------------------------------- /Images/ReadMe/Trivial_analysis1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/Trivial_analysis1.JPG -------------------------------------------------------------------------------- /Images/ReadMe/Trivial_analysis2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidNHill/Minesweeper/0806bb80042184e4f2b1413f4092c4e09526c237/Images/ReadMe/Trivial_analysis2.jpg -------------------------------------------------------------------------------- /MineSweeperSolver/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MineSweeperSolver/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /MineSweeperSolver/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | MineSweeperSolver 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/BoardStateCache.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | import minesweeper.structure.Location; 8 | 9 | public class BoardStateCache { 10 | 11 | private final static int[] DX = {0, 1, 1, 1, 0, -1, -1, -1}; 12 | private final static int[] DY = {-1, -1, 0, 1, 1, 1, 0, -1}; 13 | 14 | protected class Cache { 15 | 16 | private int width; 17 | private int height; 18 | 19 | protected Location[][] locations; 20 | 21 | protected AdjacentSquares[][] adjacentLocations1; 22 | protected AdjacentSquares[][] adjacentLocations2; 23 | 24 | protected Location getLocation(int x, int y) { 25 | return locations[x][y]; 26 | } 27 | 28 | } 29 | 30 | // iterator for adjacent squares 31 | protected class AdjacentSquares implements Iterable { 32 | 33 | private Location loc; 34 | private final int size; 35 | private List locations; 36 | 37 | AdjacentSquares(Cache cache, Location l, int width, int height, int size) { 38 | this.loc = l; 39 | this.size = size; 40 | 41 | if (size == 1) { 42 | locations = new ArrayList<>(8); 43 | for (int i=0; i < DX.length; i++) { 44 | if (loc.x + DX[i] >= 0 && loc.x + DX[i] < width && loc.y + DY[i] >= 0 && loc.y + DY[i] < height) { 45 | locations.add(cache.getLocation(loc.x + DX[i], loc.y + DY[i])); 46 | } 47 | 48 | } 49 | } else { 50 | int startX = Math.max(0, loc.x - this.size); 51 | int endX = Math.min(width - 1, loc.x + this.size); 52 | 53 | int startY = Math.max(0, loc.y - this.size); 54 | int endY = Math.min(height - 1, loc.y + this.size); 55 | 56 | locations = new ArrayList<>((this.size * 2 - 1) * (this.size * 2 - 1)); 57 | for (int i=startX; i <= endX; i++) { 58 | for (int j=startY; j <= endY; j++) { 59 | if (i == loc.x && j == loc.y) { 60 | // don't send back the central location 61 | } else { 62 | locations.add(cache.getLocation(i,j)); 63 | } 64 | 65 | } 66 | } 67 | } 68 | 69 | 70 | } 71 | 72 | @Override 73 | public Iterator iterator() { 74 | return locations.iterator(); 75 | } 76 | 77 | 78 | } 79 | 80 | private static List cacheAdjSqu = new ArrayList<>(); 81 | private static BoardStateCache me; 82 | 83 | 84 | public synchronized Cache getAdjacentSquares1(int width, int height) { 85 | 86 | for (Cache cache: cacheAdjSqu) { 87 | if (cache.height == height && cache.width == width) { 88 | return cache; 89 | } 90 | } 91 | 92 | Cache cache = new Cache(); 93 | 94 | cache.height = height; 95 | cache.width = width; 96 | 97 | cache.locations = new Location[width][height]; 98 | 99 | // Create a Location for each entry yon the board 100 | for (int x=0; x < width; x++) { 101 | for (int y=0; y < height; y++) { 102 | cache.locations[x][y] = new Location(x,y); 103 | } 104 | } 105 | 106 | cache.adjacentLocations1 = new AdjacentSquares[width][height]; 107 | cache.adjacentLocations2 = new AdjacentSquares[width][height]; 108 | 109 | // set up how many adjacent locations there are to each square - they are all unrevealed to start with 110 | for (int x=0; x < width; x++) { 111 | for (int y=0; y < height; y++) { 112 | 113 | cache.adjacentLocations1[x][y] = new AdjacentSquares(cache, cache.getLocation(x,y), width, height, 1); 114 | cache.adjacentLocations2[x][y] = new AdjacentSquares(cache, cache.getLocation(x,y), width, height, 2); 115 | 116 | } 117 | } 118 | 119 | cacheAdjSqu.add(cache); 120 | 121 | return cache; 122 | } 123 | 124 | public static synchronized BoardStateCache getInstance() { 125 | 126 | if (me == null) { 127 | me = new BoardStateCache(); 128 | } 129 | 130 | return me; 131 | } 132 | 133 | 134 | } 135 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/BruteForceAnalysisModel.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.List; 5 | 6 | import minesweeper.structure.Action; 7 | import minesweeper.structure.Area; 8 | import minesweeper.structure.Location; 9 | 10 | abstract public class BruteForceAnalysisModel { 11 | 12 | protected boolean completed = false; 13 | protected boolean tooMany = false; 14 | 15 | abstract protected void addSolution(byte[] solution); 16 | 17 | abstract protected void process(); 18 | 19 | 20 | protected boolean isComplete() { 21 | return this.completed; 22 | } 23 | 24 | protected boolean tooMany() { 25 | return this.tooMany; 26 | } 27 | 28 | protected boolean isShallow() { 29 | return false; 30 | } 31 | 32 | abstract protected int getSolutionCount(); 33 | 34 | abstract protected int getMovesProcessed(); 35 | 36 | abstract protected int getMovesToProcess(); 37 | 38 | abstract protected Location checkForBetterMove(Location location); 39 | 40 | abstract protected long getNodeCount(); 41 | 42 | abstract protected Action getNextMove(BoardState boardState); 43 | 44 | abstract protected Location getExpectedMove(); 45 | 46 | abstract protected boolean allDead(); 47 | 48 | abstract Area getDeadLocations(); 49 | 50 | abstract BigDecimal getSolveChance(); 51 | 52 | abstract List getLocations(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/LocationEvaluator.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | 6 | import minesweeper.solver.constructs.CandidateLocation; 7 | import minesweeper.solver.constructs.EvaluatedLocation; 8 | import minesweeper.structure.Action; 9 | import minesweeper.structure.Location; 10 | 11 | public interface LocationEvaluator { 12 | 13 | abstract public Action[] bestMove(); 14 | abstract public List getEvaluatedLocations(); 15 | abstract public void evaluateLocations(); 16 | abstract public void showResults(); 17 | 18 | abstract public void evaluateOffEdgeCandidates(List allUnrevealedSquares); 19 | abstract public void addLocations(Collection tiles); 20 | } 21 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/ProbabilityEngineModel.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.util.List; 6 | 7 | import minesweeper.solver.constructs.Box; 8 | import minesweeper.solver.constructs.CandidateLocation; 9 | import minesweeper.solver.constructs.LinkedLocation; 10 | import minesweeper.solver.utility.BinomialCache; 11 | import minesweeper.structure.Area; 12 | import minesweeper.structure.Location; 13 | 14 | abstract public class ProbabilityEngineModel { 15 | 16 | abstract public void process(); 17 | 18 | abstract protected long getDuration(); 19 | abstract protected long getIndependentGroups(); 20 | abstract Box getBox(Location l); 21 | abstract public BigDecimal getSafety(Location l); 22 | abstract protected List getBestCandidates(BigDecimal freshhold, boolean excludeDead); 23 | abstract protected List getProbableMines(BigDecimal freshhold); 24 | abstract protected List getFiftyPercenters(); 25 | abstract protected BigInteger getSolutionCount(); 26 | //abstract protected BigDecimal getBestOnEdgeProb(); 27 | abstract protected BigDecimal getOffEdgeSafety(); 28 | abstract protected BigInteger getOffEdgeTally(); 29 | abstract protected boolean foundCertainty(); 30 | abstract protected Area getDeadLocations(); 31 | abstract boolean allDead(); 32 | abstract protected int getDeadValueDelta(Location l); 33 | abstract protected List getMines(); 34 | abstract protected List getLinkedLocations(); 35 | abstract protected LinkedLocation getLinkedLocation(Location tile); 36 | abstract protected List getIsolatedEdges(); 37 | abstract protected boolean isBestGuessOffEdge(); 38 | abstract protected int getLivingClearCount(); 39 | abstract protected int getSafeTileCount(); 40 | abstract protected int getTilesOffEdgeCount(); 41 | abstract protected List getEmptyBoxes(); 42 | 43 | abstract protected BigDecimal getBlendedSafety(); 44 | abstract protected BigDecimal getBestSafety(); 45 | abstract protected BigDecimal getBestLivingSafety(); 46 | 47 | abstract protected Location getSingleSafestTile(); 48 | 49 | abstract public void setBinomialCache(BinomialCache cache); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/SpaceCounter.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import minesweeper.structure.Location; 7 | 8 | public class SpaceCounter { 9 | 10 | protected final static int[] DX = {0, 1, 1, 1, 0, -1, -1, -1}; 11 | protected final static int[] DY = {-1, -1, 0, 1, 1, 1, 0, -1}; 12 | 13 | private final BoardState board; 14 | private final int threshold; 15 | 16 | private final byte[][] data; 17 | private final int width; 18 | private final int height; 19 | 20 | 21 | public SpaceCounter(BoardState board, int threshold) { 22 | 23 | this.board = board; 24 | this.threshold = threshold; // an area is considered large if greater than or equal to the threshold 25 | this.height = this.board.getGameHeight(); 26 | this.width = this.board.getGameWidth(); 27 | 28 | this.data = new byte[this.width][this.height]; 29 | 30 | } 31 | 32 | 33 | public boolean meetsThreshold(Location loc) { 34 | 35 | boolean large = false; 36 | 37 | 38 | if (data[loc.x][loc.y] != 0) { 39 | large = (data[loc.x][loc.y] == 1); 40 | 41 | } else { 42 | 43 | int index = 0; 44 | 45 | List tiles = new ArrayList<>(); 46 | 47 | tiles.add(loc); 48 | 49 | data[loc.x][loc.y] = -1; 50 | 51 | top: while (tiles.size() != index) { 52 | 53 | Location m = tiles.get(index); 54 | 55 | for (int j=0; j < DX.length; j++) { 56 | final int x1 = m.x + DX[j]; 57 | final int y1 = m.y + DY[j]; 58 | 59 | if (x1 >= 0 && x1 < this.width && y1 >= 0 && y1 < this.height) { 60 | if (this.board.isUnrevealed(x1, y1)) { 61 | if (data[x1][y1] == 0) { // unprocessed tile 62 | data[x1][y1] = -1; 63 | tiles.add(this.board.getLocation(x1, y1)); 64 | 65 | } else if (data[x1][y1] == 1) { // if we meet a tile which is already in a large area, we are in a large area 66 | large = true; 67 | break top; 68 | 69 | } else { // something has gone wrong since we can't encounter a small area 70 | //this.board.getLogger().log(Level.ERROR, "Space counter encountered an area below threshold"); 71 | } 72 | 73 | } else if (this.board.isRevealed(x1, y1)) { // if he board is revealed then see if we can hop over the tile into more open space 74 | 75 | for (int k=0; k < DX.length; k++) { 76 | final int x2 = x1 + DX[k]; 77 | final int y2 = y1 + DY[k]; 78 | if (x2 >= 0 && x2 < this.width && y2 >= 0 && y2 < this.height) { 79 | if (this.board.isUnrevealed(x2, y2)) { 80 | if (data[x2][y2] == 0) { // unprocessed tile 81 | data[x2][y2] = -1; 82 | tiles.add(this.board.getLocation(x2, y2)); 83 | 84 | } else if (data[x2][y2] == 1) { // if we meet a tile which is already in a large area, we are in a large area 85 | large = true; 86 | break top; 87 | 88 | } 89 | } 90 | } 91 | 92 | 93 | } 94 | } 95 | 96 | } 97 | } 98 | 99 | if (tiles.size() >= this.threshold) { 100 | large = true; 101 | break; 102 | } 103 | 104 | index++; 105 | 106 | } 107 | 108 | // record the area if it exceeds the threshold 109 | if (large) { 110 | for (Location l: tiles) { 111 | data[l.x][l.y] = 1; 112 | } 113 | } 114 | 115 | } 116 | 117 | if (large) { 118 | return true; 119 | } else { 120 | return false; 121 | } 122 | 123 | } 124 | 125 | 126 | } 127 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkEventController.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import minesweeper.gamestate.GameStateModel; 6 | 7 | public class BulkEventController { 8 | 9 | private final int gamesToPlay; 10 | 11 | private volatile int gamesPlayed = 0; 12 | 13 | private volatile int failedToStart = 0; 14 | private volatile int wins = 0; 15 | private volatile int guesses = 0; 16 | private volatile int noGuessWins = 0; 17 | 18 | private volatile int totalActions = 0; 19 | private volatile long total3BV = 0; 20 | private volatile long total3BVSolved = 0; 21 | private volatile BigDecimal fairness = BigDecimal.ZERO; 22 | private volatile int currentWinStreak = 0; 23 | private volatile int bestWinStreak = 0; 24 | private volatile int currentMastery = 0; 25 | private volatile int bestMastery = 0; 26 | 27 | private volatile boolean[] mastery = new boolean[100]; 28 | 29 | //private volatile BulkEventGame event; 30 | private volatile BulkEventGame finalEvent; 31 | 32 | BulkEventController(int gamesToPlay) { 33 | this.gamesToPlay = gamesToPlay; 34 | } 35 | 36 | 37 | /** 38 | * When the process is finished you can get the final results from here 39 | */ 40 | public BulkEventGame getResults() { 41 | return this.finalEvent; 42 | } 43 | 44 | 45 | 46 | void processGame(BulkRequest request, int index) { 47 | 48 | int masteryIndex = request.sequence % 100; 49 | 50 | BulkRequestGame game = request.getRequestGames()[index]; 51 | 52 | gamesPlayed++; 53 | 54 | if (game.gs.getGameState() == GameStateModel.WON) { 55 | wins++; 56 | 57 | if (game.guesses == 0) { 58 | noGuessWins++; 59 | } 60 | 61 | currentWinStreak++; 62 | if (currentWinStreak > bestWinStreak) { 63 | bestWinStreak = currentWinStreak; 64 | } 65 | 66 | // if we lost 100 games ago then mastery is 1 more 67 | if (!mastery[masteryIndex]) { 68 | mastery[masteryIndex] = true; 69 | currentMastery++; 70 | if (currentMastery > bestMastery) { 71 | bestMastery = currentMastery; 72 | } 73 | } 74 | 75 | //double efficiency = 100 * ((double) game.gs.getTotal3BV() / (double) game.gs.getActionCount()); 76 | 77 | 78 | } else { 79 | 80 | if (!game.startedOkay) { 81 | failedToStart++; 82 | } 83 | 84 | currentWinStreak = 0; 85 | 86 | // if we won 100 games ago, then mastery is now 1 less 87 | if (mastery[masteryIndex]) { 88 | mastery[masteryIndex] = false; 89 | currentMastery--; 90 | } 91 | } 92 | 93 | // accumulate the total actions taken 94 | totalActions = totalActions + game.gs.getActionCount(); 95 | 96 | // accumulate 3BV in the game and how much was solved 97 | total3BV = total3BV + game.gs.getTotal3BV(); 98 | total3BVSolved = total3BVSolved + game.gs.getCleared3BV(); 99 | 100 | // accumulate total guesses made 101 | guesses = guesses + game.guesses; 102 | 103 | fairness = fairness.add(game.fairness); 104 | 105 | } 106 | 107 | BulkEventGame createEvent() { 108 | 109 | BulkEventGame event = new BulkEventGame(); 110 | 111 | event.setGamesToPlay(gamesToPlay); 112 | event.setGamesPlayed(gamesPlayed); 113 | event.setGamesWon(wins); 114 | 115 | event.setTotalGuesses(guesses); 116 | event.setNoGuessWins(noGuessWins); 117 | if (guesses != 0) { 118 | event.setFairness(fairness.doubleValue() / guesses); 119 | } else { 120 | event.setFairness(0); 121 | } 122 | 123 | event.setMastery(bestMastery); 124 | event.setWinStreak(bestWinStreak); 125 | event.setTotalActions(totalActions); 126 | event.setFailedToStart(failedToStart); 127 | event.setTotal3BV(total3BV); 128 | event.setTotal3BVSolved(total3BVSolved); 129 | 130 | return event; 131 | 132 | } 133 | 134 | 135 | 136 | } 137 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkEventGame.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | public class BulkEventGame { 4 | 5 | private int gamesToPlay; 6 | private int gamesPlayed; 7 | private int gamesWon; 8 | private int noGuessWins; 9 | private int totalActions; 10 | 11 | private long total3BV; 12 | private long total3BVSolved; 13 | 14 | private int totalGuesses; 15 | private double fairness; 16 | 17 | private int winStreak; 18 | private int mastery; 19 | 20 | private int failedToStart; // this is when the game didn't survive the start sequence 21 | 22 | 23 | public int getFailedToStart() { 24 | return failedToStart; 25 | } 26 | public int getGamesToPlay() { 27 | return gamesToPlay; 28 | } 29 | public int getGamesPlayed() { 30 | return gamesPlayed; 31 | } 32 | public int getGamesWon() { 33 | return gamesWon; 34 | } 35 | public int getNoGuessWins() { 36 | return noGuessWins; 37 | } 38 | public int getTotalGuesses() { 39 | return totalGuesses; 40 | } 41 | public double getFairness() { 42 | return fairness; 43 | } 44 | public int getWinStreak() { 45 | return winStreak; 46 | } 47 | public int getMastery() { 48 | return mastery; 49 | } 50 | public int getTotalActions() { 51 | return totalActions; 52 | } 53 | 54 | public long getTotal3BV() { 55 | return total3BV; 56 | } 57 | public long getTotal3BVSolved() { 58 | return total3BVSolved; 59 | } 60 | 61 | protected void setFailedToStart(int failedToStart) { 62 | this.failedToStart = failedToStart; 63 | } 64 | protected void setGamesToPlay(int gamesToPlay) { 65 | this.gamesToPlay = gamesToPlay; 66 | } 67 | protected void setGamesPlayed(int gamesPlayed) { 68 | this.gamesPlayed = gamesPlayed; 69 | } 70 | protected void setGamesWon(int gamesWon) { 71 | this.gamesWon = gamesWon; 72 | } 73 | protected void setNoGuessWins(int noGuessWins) { 74 | this.noGuessWins = noGuessWins; 75 | } 76 | protected void setTotalGuesses(int totalGuesses) { 77 | this.totalGuesses = totalGuesses; 78 | } 79 | protected void setFairness(double fairness) { 80 | this.fairness = fairness; 81 | } 82 | protected void setWinStreak(int winStreak) { 83 | this.winStreak = winStreak; 84 | } 85 | protected void setMastery(int mastery) { 86 | this.mastery = mastery; 87 | } 88 | protected void setTotalActions(int actions) { 89 | this.totalActions = actions; 90 | } 91 | protected void setTotal3BV(long total3bv) { 92 | total3BV = total3bv; 93 | } 94 | protected void setTotal3BVSolved(long total3bvSolved) { 95 | total3BVSolved = total3bvSolved; 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkEventMain.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | public class BulkEventMain { 4 | 5 | private int gamesToPlay; 6 | private int gamesPlayed; 7 | 8 | private long timeSoFar; 9 | private long estimatedTimeLeft; 10 | 11 | private BulkEventGame[] events; 12 | private boolean isFinished = false; 13 | 14 | public int getGamesToPlay() { 15 | return gamesToPlay; 16 | } 17 | public int getGamesPlayed() { 18 | return gamesPlayed; 19 | } 20 | 21 | protected void setGamesToPlay(int gamesToPlay) { 22 | this.gamesToPlay = gamesToPlay; 23 | } 24 | protected void setGamesPlayed(int gamesPlayed) { 25 | this.gamesPlayed = gamesPlayed; 26 | } 27 | public long getTimeSoFar() { 28 | return timeSoFar; 29 | } 30 | public long getEstimatedTimeLeft() { 31 | return estimatedTimeLeft; 32 | } 33 | protected void setTimeSoFar(long timeSoFar) { 34 | this.timeSoFar = timeSoFar; 35 | } 36 | protected void setEstimatedTimeLeft(long estimatedTimeLeft) { 37 | this.estimatedTimeLeft = estimatedTimeLeft; 38 | } 39 | public boolean isFinished() { 40 | return isFinished; 41 | } 42 | protected void setFinished(boolean isFinished) { 43 | this.isFinished = isFinished; 44 | } 45 | 46 | public BulkEventGame[] getGameEvents() { 47 | return events; 48 | } 49 | protected void setGameEvents(BulkEventGame[] events) { 50 | this.events = events; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkListener.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | public abstract class BulkListener { 4 | 5 | /** 6 | * This is run at regular intervals and should be used to provide any out put that is needed 7 | */ 8 | abstract public void intervalAction(BulkEventMain event); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkPlayer.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | import java.util.Random; 4 | 5 | import minesweeper.gamestate.GameFactory; 6 | import minesweeper.gamestate.GameStateModel; 7 | import minesweeper.settings.GameSettings; 8 | import minesweeper.settings.GameType; 9 | import minesweeper.solver.settings.SolverSettings; 10 | 11 | public class BulkPlayer extends BulkController { 12 | 13 | protected final GameType gameType; 14 | protected final GameSettings gameSettings; 15 | //private List preActions; 16 | 17 | /** 18 | * Use the bulk controller to play games from the beginning 19 | */ 20 | public BulkPlayer(Random seeder, int gamesToPlay, GameType gameType, GameSettings gameSettings, SolverSettings solverSettings, int workers) { 21 | super(seeder, gamesToPlay, solverSettings, workers); 22 | this.gameType = gameType; 23 | this.gameSettings = gameSettings; 24 | 25 | } 26 | 27 | public BulkPlayer(Random seeder, int gamesToPlay, GameType gameType, GameSettings gameSettings, SolverSettings solverSettings, int workers, int bufferPerWorker) { 28 | super(seeder, gamesToPlay, new SolverSettings[] {solverSettings}, workers, bufferPerWorker); 29 | this.gameType = gameType; 30 | this.gameSettings = gameSettings; 31 | } 32 | 33 | public BulkPlayer(Random seeder, int gamesToPlay, GameType gameType, GameSettings gameSettings, SolverSettings[] solverSettings, int workers, int bufferPerWorker) { 34 | super(seeder, gamesToPlay, solverSettings, workers, bufferPerWorker); 35 | this.gameType = gameType; 36 | this.gameSettings = gameSettings; 37 | } 38 | 39 | protected GameStateModel getGameState(long seed) {; 40 | 41 | GameStateModel gs = GameFactory.create(gameType, gameSettings, seed); 42 | 43 | return gs; 44 | 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkRequest.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | public class BulkRequest { 4 | 5 | protected final static BulkRequest WAIT = new BulkRequest() { 6 | { 7 | action = BulkAction.WAIT; 8 | } 9 | }; 10 | 11 | protected final static BulkRequest STOP = new BulkRequest() { 12 | { 13 | action = BulkAction.STOP; 14 | } 15 | }; 16 | 17 | public enum BulkAction { 18 | STOP, 19 | WAIT, 20 | RUN; 21 | } 22 | 23 | protected BulkAction action; 24 | protected int sequence; // the sequence number for this request 25 | protected int slot; // the slot the request is to be store in the buffer 26 | 27 | protected BulkRequestGame[] games; 28 | 29 | public BulkRequestGame[] getRequestGames( ) { 30 | return this.games; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkRequestGame.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import minesweeper.gamestate.GameStateModel; 6 | import minesweeper.solver.settings.SolverSettings; 7 | 8 | public class BulkRequestGame { 9 | 10 | protected SolverSettings solverSettings; 11 | protected GameStateModel gs; 12 | 13 | protected int guesses = 0; 14 | protected BigDecimal fairness = BigDecimal.ZERO; 15 | protected boolean startedOkay = true; 16 | 17 | public GameStateModel getGame( ) { 18 | return this.gs; 19 | } 20 | 21 | public int getGuesses() { 22 | return this.guesses; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/BulkRollout.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | import java.util.Random; 4 | 5 | import minesweeper.gamestate.GameStateModel; 6 | import minesweeper.solver.RolloutGenerator; 7 | import minesweeper.solver.settings.SolverSettings; 8 | import minesweeper.structure.Action; 9 | import minesweeper.structure.Location; 10 | 11 | public class BulkRollout extends BulkController { 12 | 13 | protected final RolloutGenerator generator; 14 | protected final Location safeTile; 15 | protected final Location startTile; 16 | 17 | /** 18 | * Use the bulk controller to play games from the beginning 19 | */ 20 | public BulkRollout(Random seeder, int gamesToPlay, RolloutGenerator generator, Location startTile, boolean safeStart, SolverSettings solverSettings, int workers) { 21 | super(seeder, gamesToPlay, solverSettings, workers); 22 | 23 | this.generator = generator; 24 | this.startTile = startTile; 25 | 26 | if (safeStart) { 27 | this.safeTile = startTile; 28 | } else { 29 | this.safeTile = null; 30 | } 31 | 32 | } 33 | 34 | 35 | protected GameStateModel getGameState(long seed) { 36 | 37 | GameStateModel gs = generator.generateGame(seed, safeTile); 38 | 39 | // play the start tile and return the game 40 | gs.doAction(new Action(startTile, Action.CLEAR)); 41 | 42 | return gs; 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/GamePostListener.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | /** 4 | * The "postAction" method is run after each game finishes 5 | */ 6 | public abstract class GamePostListener { 7 | 8 | /** 9 | * This is run after each game finishes 10 | */ 11 | abstract public void postAction(BulkRequest request); 12 | 13 | /** 14 | * Place the results you want to show here 15 | */ 16 | abstract public void postResults(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/GamePreListener.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | import minesweeper.gamestate.GameStateModel; 4 | 5 | public abstract class GamePreListener { 6 | 7 | /** 8 | * This is run before each game starts 9 | */ 10 | abstract public void preAction(GameStateModel game); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/bulk/StaticCounter.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.bulk; 2 | 3 | public class StaticCounter { 4 | 5 | public enum SCType { 6 | LONG_5050 (0, "Long 50/50"), 7 | RECURSIVE_SAFETY_CACHE_HIT (1, "Recursive Safety cache hit") 8 | ; 9 | 10 | final public String name; 11 | final private int index; 12 | 13 | private SCType(int index, String name) { 14 | this.index = index; 15 | this.name = name; 16 | } 17 | } 18 | 19 | private final static long[] counter = new long[SCType.values().length]; 20 | 21 | private StaticCounter() { 22 | } 23 | 24 | final static public void count(SCType type) { 25 | counter[type.index]++; 26 | } 27 | 28 | 29 | final static public long report(SCType type) { 30 | return counter[type.index]; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/coach/CoachModel.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.coach; 2 | 3 | public interface CoachModel { 4 | 5 | 6 | abstract public void clearScreen(); 7 | 8 | abstract public void writeLine(String text); 9 | 10 | abstract public void setOkay(); 11 | 12 | abstract public void setWarn(); 13 | 14 | abstract public void setError(); 15 | 16 | abstract public void kill(); 17 | 18 | abstract public boolean analyseFlags(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/coach/CoachSilent.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.coach; 2 | 3 | /** 4 | * A implementation of the CoachModel class which does nothing 5 | * @author David 6 | * 7 | */ 8 | public class CoachSilent implements CoachModel { 9 | 10 | 11 | @Override 12 | public void clearScreen() { 13 | } 14 | 15 | @Override 16 | public void writeLine(String text) { 17 | } 18 | 19 | @Override 20 | public void setOkay() { 21 | } 22 | 23 | @Override 24 | public void setWarn() { 25 | } 26 | 27 | @Override 28 | public void setError() { 29 | } 30 | 31 | @Override 32 | public void kill() { 33 | } 34 | 35 | @Override 36 | public boolean analyseFlags() { 37 | return false; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/CandidateLocation.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Comparator; 5 | 6 | import minesweeper.gamestate.MoveMethod; 7 | import minesweeper.structure.Action; 8 | import minesweeper.structure.Location; 9 | public class CandidateLocation extends Location { 10 | 11 | private final BigDecimal prob; 12 | private String description = ""; 13 | private final int adjSquares; 14 | private final int adjFlags; 15 | private final boolean dead; // Whether the tile is dead 16 | private final boolean deferGuessing; // Whether the tile is not a good idea to guess 17 | 18 | public CandidateLocation(int x, int y, BigDecimal prob, int adjSquares, int adjFlags) { 19 | this(x, y, prob, adjSquares, adjFlags, false, false); 20 | 21 | } 22 | 23 | public CandidateLocation(int x, int y, BigDecimal prob, int adjSquares, int adjFlags, boolean dead, boolean deferGuessing) { 24 | super(x,y); 25 | 26 | this.prob = prob; 27 | this.adjSquares = adjSquares; 28 | this.adjFlags = adjFlags; 29 | this.dead = dead; 30 | this.deferGuessing = deferGuessing; 31 | 32 | } 33 | 34 | public BigDecimal getProbability() { 35 | return this.prob; 36 | } 37 | 38 | public boolean isDead() { 39 | return this.dead; 40 | } 41 | 42 | public void setDescription(String desc) { 43 | this.description = desc; 44 | } 45 | 46 | public void appendDescription(String desc) { 47 | if (this.description != "") { 48 | this.description = this.description + " " + desc; 49 | } else { 50 | this.description = desc; 51 | } 52 | 53 | } 54 | 55 | public String getDescription() { 56 | return this.description; 57 | } 58 | 59 | public boolean getDeferGuessing() { 60 | return this.deferGuessing; 61 | } 62 | 63 | public Action buildAction(MoveMethod method) { 64 | 65 | String comment = description; 66 | 67 | if (prob.compareTo(BigDecimal.ZERO) == 0) { // the best move is to place a flag 68 | return new Action(this, Action.FLAG, method, comment, BigDecimal.ONE); 69 | } else { 70 | return new Action(this, Action.CLEAR, method, comment, prob); 71 | } 72 | 73 | } 74 | 75 | /** 76 | * This sorts by highest probability of not being a mine then the number of adjacent flags, unrevealed squares and finally Location order 77 | */ 78 | static public final Comparator SORT_BY_PROB_FLAG_FREE = new Comparator() { 79 | @Override 80 | public int compare(CandidateLocation o1, CandidateLocation o2) { 81 | 82 | int c = 0; 83 | 84 | c = -o1.prob.compareTo(o2.prob); // highest probability first 85 | if (c == 0) { 86 | if (o1.deferGuessing && !o2.deferGuessing) { 87 | c = -1; 88 | } else if (!o1.deferGuessing && o2.deferGuessing) { 89 | c = 1; 90 | } else { 91 | c = -(o1.adjFlags - o2.adjFlags); // highest number of flags 2nd 92 | if (c == 0) { 93 | c= o1.adjSquares - o2.adjSquares; // lowest adjacent free squares 94 | if (c == 0) { 95 | c = o1.sortOrder - o2.sortOrder; // location order 96 | } 97 | } 98 | } 99 | } 100 | 101 | return c; 102 | 103 | } 104 | }; 105 | 106 | /** 107 | * This sorts by highest probability of not being a mine then the number unrevealed squares (lowest first), then of adjacent flags (highest first) ,and finally Location order 108 | */ 109 | static public final Comparator SORT_BY_PROB_FREE_FLAG = new Comparator() { 110 | @Override 111 | public int compare(CandidateLocation o1, CandidateLocation o2) { 112 | 113 | int c = -o1.prob.compareTo(o2.prob); // highest probability first 114 | if (c == 0) { 115 | int a1, a2; 116 | if (o1.adjSquares == 0) { 117 | a1 = 9; 118 | } else { 119 | a1 = o1.adjSquares; 120 | } 121 | if (o2.adjSquares == 0) { 122 | a2 = 9; 123 | } else { 124 | a2 = o2.adjSquares; 125 | } 126 | c= a1 - a2; // lowest adjacent free squares (except zero treated as 9) 127 | if (c == 0) { 128 | c = -(o1.adjFlags - o2.adjFlags); // highest number of flags 129 | if (c == 0) { 130 | c = o1.sortOrder - o2.sortOrder; // location order 131 | } 132 | } 133 | } 134 | 135 | return c; 136 | 137 | } 138 | }; 139 | 140 | } 141 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/ChordLocation.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | 7 | import minesweeper.structure.Location; 8 | 9 | public class ChordLocation extends Location { 10 | 11 | private final int benefit; 12 | private final int cost; 13 | private final int exposedTiles; 14 | private final BigDecimal netBenefit; 15 | private final BigDecimal scale; 16 | private final List mines; 17 | 18 | public ChordLocation(int x, int y, int benefit, int cost, int exposedTiles, BigDecimal scale, List mines) { 19 | super(x, y); 20 | 21 | this.benefit = benefit; 22 | this.cost = cost; 23 | this.exposedTiles = exposedTiles; 24 | 25 | this.netBenefit = chordReward(benefit, cost).multiply(scale); 26 | //this.netBenefit = chordReward(benefit, cost); 27 | 28 | this.scale = scale; // probability of being a mine 29 | this.mines = mines; 30 | } 31 | 32 | public int getBenefit() { 33 | return this.benefit; 34 | } 35 | 36 | public int getCost() { 37 | return this.cost; 38 | } 39 | 40 | public BigDecimal getNetBenefit() { 41 | return this.netBenefit; 42 | } 43 | 44 | public int getExposedTileCount() { 45 | return this.exposedTiles; 46 | } 47 | 48 | public List getMines() { 49 | return this.mines; 50 | } 51 | 52 | public BigDecimal getScale() { 53 | return this.scale; 54 | } 55 | 56 | static public final BigDecimal chordReward(int benefit, int cost) { 57 | 58 | BigDecimal netBenefit; // benefit as a ratio of the cost 59 | 60 | /* 61 | if (cost == 0) { 62 | netBenefit = BigDecimal.valueOf(benefit); 63 | } else { 64 | netBenefit = BigDecimal.valueOf(benefit - cost).divide(BigDecimal.valueOf(cost), 10, RoundingMode.HALF_UP); // benefit as a ratio of the cost 65 | } 66 | */ 67 | 68 | /* 69 | if (cost == 0) { 70 | netBenefit = BigDecimal.valueOf(benefit); 71 | } else { 72 | netBenefit = BigDecimal.valueOf(benefit).divide(BigDecimal.valueOf(cost), 10, RoundingMode.HALF_UP); // benefit as a ratio of the cost 73 | } 74 | */ 75 | 76 | netBenefit = BigDecimal.valueOf(benefit - cost); // absolute benefit without regard for the cost 77 | 78 | return netBenefit; 79 | 80 | } 81 | 82 | static public final Comparator SORT_BY_BENEFIT_DESC = new Comparator() { 83 | @Override 84 | public int compare(ChordLocation o1, ChordLocation o2) { 85 | 86 | int c = o2.netBenefit.compareTo(o1.netBenefit); 87 | 88 | if (c==0) { 89 | c = o2.exposedTiles - o1.exposedTiles; 90 | } 91 | 92 | if (c==0) { 93 | c = o2.scale.compareTo(o1.scale); 94 | } 95 | 96 | return c; 97 | 98 | //if (o2.netBenefit == o1.netBenefit) { 99 | // return o2.exposedTiles - o1.exposedTiles; 100 | //} else { 101 | // return o2.netBenefit.compareTo(o1.netBenefit); 102 | //} 103 | 104 | } 105 | }; 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/EvaluatedLocation.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | 8 | import minesweeper.gamestate.MoveMethod; 9 | import minesweeper.structure.Action; 10 | import minesweeper.structure.Location; 11 | 12 | public class EvaluatedLocation extends Location { 13 | 14 | private final BigDecimal safeProbability; 15 | private BigDecimal weight; 16 | private final BigDecimal maxValueProgress; 17 | private String description = ""; 18 | private BigDecimal expectedClears; 19 | private final int fixedClears; //number of tiles which are clears regardless of what value is revealed 20 | private List emptyBoxes; 21 | private boolean pruned = false; 22 | private boolean deferGuessing = false; 23 | private Location dominatingLocation; // this tile is a better choice 24 | 25 | public EvaluatedLocation(int x, int y, BigDecimal safeProbability, BigDecimal weight, BigDecimal expectedClears, int fixedClears, 26 | List emptyBoxes, BigDecimal maxValueProgress) { 27 | super(x,y); 28 | 29 | this.safeProbability = safeProbability; 30 | this.weight = weight.setScale(8, RoundingMode.UP); // give a slight bump up, so those coming later have to be actually better 31 | this.expectedClears = expectedClears; 32 | this.fixedClears = fixedClears; 33 | this.maxValueProgress = maxValueProgress; 34 | this.emptyBoxes = emptyBoxes; 35 | 36 | } 37 | 38 | public BigDecimal getProbability() { 39 | return this.safeProbability; 40 | } 41 | 42 | public BigDecimal getWeighting() { 43 | return this.weight; 44 | } 45 | 46 | public BigDecimal getMaxValueProgress() { 47 | return maxValueProgress; 48 | } 49 | 50 | public List getEmptyBoxes() { 51 | return emptyBoxes; 52 | } 53 | 54 | 55 | public Action buildAction(MoveMethod method) { 56 | 57 | String comment = description; 58 | 59 | return new Action(this, Action.CLEAR, method, comment, safeProbability); 60 | 61 | } 62 | 63 | public void setPruned() { 64 | this.pruned = true; 65 | } 66 | 67 | public void setDeferGuessing(boolean deferGuessing) { 68 | this.deferGuessing = deferGuessing; 69 | } 70 | 71 | public boolean isDeferGuessing() { 72 | return this.deferGuessing; 73 | } 74 | 75 | public void setDominatingLocation(Location loc) { 76 | this.dominatingLocation = loc; 77 | } 78 | 79 | public Location getDominatingLocation() { 80 | return this.dominatingLocation; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | 86 | String prunedString; 87 | if (this.pruned) { 88 | prunedString = " ** Pruned"; 89 | } else { 90 | prunedString = ""; 91 | } 92 | 93 | return super.toString() + " Fixed clears is " + fixedClears + " expected clears is " + expectedClears.toPlainString() 94 | + ", final weight is " + weight + ", maximum tile value prob is " + maxValueProgress + ", defer guessing " + deferGuessing + prunedString; 95 | 96 | } 97 | 98 | /** 99 | * This sorts by ... 100 | */ 101 | static public final Comparator SORT_BY_WEIGHT = new Comparator() { 102 | @Override 103 | public int compare(EvaluatedLocation o1, EvaluatedLocation o2) { 104 | 105 | 106 | int c = 0; 107 | 108 | if (c == 0) { 109 | if (o1.deferGuessing && !o2.deferGuessing) { 110 | c = 1; 111 | } else if (!o1.deferGuessing && o2.deferGuessing) { 112 | c = -1; 113 | } 114 | } 115 | 116 | if (c == 0) { 117 | c = -o1.weight.compareTo(o2.weight); // tile with the highest weighting 118 | } 119 | 120 | if (c == 0) { 121 | c = -o1.expectedClears.compareTo(o2.expectedClears); // then highest expected number of clears 122 | } 123 | 124 | return c; 125 | 126 | } 127 | }; 128 | 129 | static public final Comparator SORT_BY_SAFETY_MINIMAX = new Comparator() { 130 | @Override 131 | public int compare(EvaluatedLocation o1, EvaluatedLocation o2) { 132 | 133 | 134 | int c = 0; 135 | 136 | c = -o1.safeProbability.compareTo(o2.safeProbability); // safest tiles 137 | 138 | if (c == 0) { 139 | c = o1.maxValueProgress.compareTo(o2.maxValueProgress); // then lowest max value ... the Minimax; 140 | } 141 | 142 | // go back to the weight option 143 | if (c == 0) { 144 | c = SORT_BY_WEIGHT.compare(o1, o2); 145 | } 146 | 147 | return c; 148 | 149 | } 150 | }; 151 | 152 | 153 | } 154 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/InformationLocation.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import minesweeper.solver.ProgressEvaluator; 9 | import minesweeper.solver.Solver; 10 | import minesweeper.structure.Location; 11 | 12 | public class InformationLocation extends Location { 13 | 14 | //public final static BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100); 15 | 16 | public class ByValue { 17 | 18 | public final int value; 19 | public final int clears; 20 | public final BigDecimal probability; 21 | 22 | private ByValue(int value, int clears, BigDecimal prob) { 23 | this.value = value; 24 | this.clears = clears; 25 | this.probability = prob; 26 | } 27 | 28 | } 29 | 30 | 31 | private BigDecimal safety; 32 | private BigDecimal expectedClears; 33 | private BigDecimal progressProbability; 34 | private BigDecimal weighting; 35 | private BigDecimal expectedSolutionSpaceReduction; 36 | private BigDecimal mTanzerRatio; 37 | private BigDecimal secondarySafety; 38 | 39 | private BigDecimal longTermSafety; 40 | //private BigDecimal poweredRatio; 41 | 42 | 43 | private List byValues; 44 | 45 | public InformationLocation(int x, int y) { 46 | super(x, y); 47 | } 48 | 49 | public void calculate() { 50 | 51 | BigDecimal expClears = BigDecimal.ZERO; 52 | BigDecimal progProb = BigDecimal.ZERO; 53 | BigDecimal ess = BigDecimal.ONE.subtract(this.safety); // expect solution space = p(mine) + sum[ P(n)*p(n) ] 54 | BigDecimal powerClears = BigDecimal.ZERO; 55 | 56 | 57 | if (byValues == null) { 58 | return; 59 | } 60 | 61 | for (ByValue bv: byValues) { 62 | 63 | //essr = essr.add(bv.probability.multiply(BigDecimal.ONE.subtract(bv.probability))); // sum of p(1-p) 64 | ess = ess.add(bv.probability.multiply(bv.probability)); // sum of p^2 65 | 66 | if (bv.clears != 0) { 67 | progProb = progProb.add(bv.probability); 68 | expClears = expClears.add(bv.probability.multiply(BigDecimal.valueOf(bv.clears))); 69 | //powerClears = powerClears.add(bv.probability.multiply(new BigDecimal(Math.pow(1.15d, bv.clears)))); 70 | } 71 | 72 | } 73 | 74 | this.expectedClears = expClears; 75 | this.progressProbability = progProb; 76 | this.expectedSolutionSpaceReduction = ess; 77 | 78 | BigDecimal bonus = BigDecimal.ONE.add(progressProbability.multiply(ProgressEvaluator.PROGRESS_VALUE)); 79 | 80 | this.weighting = this.safety.multiply(bonus); 81 | 82 | if (this.safety.compareTo(BigDecimal.ONE) != 0) { 83 | this.mTanzerRatio = this.progressProbability.divide(BigDecimal.ONE.subtract(safety), 4, RoundingMode.HALF_DOWN); 84 | } 85 | 86 | //if (this.prob.compareTo(BigDecimal.ONE) != 0) { 87 | // this.poweredRatio = powerClears.divide(BigDecimal.ONE.subtract(prob), 4, RoundingMode.HALF_DOWN); 88 | //} 89 | 90 | } 91 | 92 | public BigDecimal getSafety() { 93 | return this.safety; 94 | } 95 | 96 | public void setSafety(BigDecimal prob) { 97 | this.safety = prob; 98 | } 99 | 100 | public void setByValue(int value, int clears, BigDecimal prob) { 101 | if (byValues == null) { 102 | byValues = new ArrayList<>(); 103 | } 104 | 105 | byValues.add(new ByValue(value, clears, prob)); 106 | 107 | } 108 | 109 | public void setSecondarySafety(BigDecimal safety2) { 110 | this.secondarySafety = safety2; 111 | } 112 | 113 | public void setLongTermSafety(BigDecimal lts) { 114 | this.longTermSafety = lts; 115 | } 116 | 117 | public BigDecimal getSecondarySafety() { 118 | return this.secondarySafety; 119 | } 120 | 121 | public BigDecimal getLongTermSafety() { 122 | return this.longTermSafety; 123 | } 124 | 125 | public List getByValueData() { 126 | return this.byValues; 127 | } 128 | 129 | public BigDecimal getExpectedClears() { 130 | return expectedClears; 131 | } 132 | 133 | public BigDecimal getProgressProbability() { 134 | return progressProbability; 135 | } 136 | 137 | public BigDecimal getWeighting() { 138 | return weighting; 139 | } 140 | 141 | public BigDecimal getExpectedSolutionSpaceReduction() { 142 | return this.expectedSolutionSpaceReduction; 143 | } 144 | 145 | public BigDecimal getMTanzerRatio() { 146 | return this.mTanzerRatio; 147 | } 148 | 149 | //public BigDecimal getPoweredRatio() { 150 | // return this.poweredRatio; 151 | //} 152 | } 153 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/LinkedLocation.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import java.util.Comparator; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | import minesweeper.structure.Location; 9 | 10 | public class LinkedLocation extends Location { 11 | 12 | private Set partners = new HashSet<>(); 13 | 14 | private int links = 0; 15 | 16 | public LinkedLocation(int x, int y, List partner) { 17 | super(x, y); 18 | 19 | incrementLinks(partner); 20 | 21 | } 22 | 23 | public void incrementLinks(List partner) { 24 | 25 | for (Location p: partner) { 26 | if (partners.add(p)) { 27 | links++; 28 | } 29 | } 30 | } 31 | 32 | public int getLinksCount() { 33 | return links; 34 | } 35 | 36 | public Set getLinkedLocations() { 37 | return partners; 38 | } 39 | 40 | static public final Comparator SORT_BY_LINKS_DESC = new Comparator() { 41 | @Override 42 | public int compare(LinkedLocation o1, LinkedLocation o2) { 43 | return o2.links - o1.links; 44 | } 45 | }; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/LocationValue.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.constructs; 2 | 3 | import minesweeper.structure.Location; 4 | 5 | public class LocationValue extends Location { 6 | 7 | 8 | private final int value; 9 | 10 | public LocationValue(Location loc, int value) { 11 | super(loc.x, loc.y); 12 | this.value = value; 13 | } 14 | 15 | public int getValue() { 16 | return this.value; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/Square.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper.solver.constructs; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import minesweeper.structure.Location; 11 | 12 | /** 13 | * 14 | * @author David 15 | */ 16 | public class Square extends Location { 17 | 18 | private final List witnesses = new ArrayList<>(); 19 | 20 | private int webNum = 0; 21 | 22 | 23 | public Square(Location loc) { 24 | super(loc.x, loc.y); 25 | 26 | } 27 | 28 | public void addWitness(Witness wit) { 29 | witnesses.add(wit); 30 | } 31 | 32 | public List getWitnesses() { 33 | return witnesses; 34 | } 35 | 36 | 37 | public int getWebNum() { 38 | return webNum; 39 | } 40 | 41 | public void setWebNum(int webNum) { 42 | this.webNum = webNum; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/Witness.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper.solver.constructs; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Comparator; 9 | import java.util.List; 10 | 11 | import minesweeper.solver.Solver; 12 | import minesweeper.structure.Location; 13 | 14 | /** 15 | * 16 | * @author David 17 | */ 18 | public class Witness extends Location { 19 | 20 | private final int minesToFind; // the number of mines left to find 21 | 22 | private final int iterations; 23 | 24 | private int webNum = 0; 25 | 26 | private final List squares; 27 | 28 | private final List boxes = new ArrayList<>(); 29 | 30 | private boolean processed = false; 31 | 32 | public Witness(Location loc, int mines, List adjSqu) { 33 | super(loc.x, loc.y); 34 | 35 | this.minesToFind = mines; 36 | squares = adjSqu; 37 | 38 | this.iterations = Solver.combination(mines, squares.size()).intValue(); 39 | 40 | } 41 | 42 | public List getSquares() { 43 | return this.squares; 44 | } 45 | 46 | public int getMines() { 47 | return minesToFind; 48 | } 49 | 50 | public void addSquare(Square squ) { 51 | 52 | squares.add(squ); 53 | 54 | } 55 | 56 | public void addBox(Box box) { 57 | boxes.add(box); 58 | } 59 | 60 | public List getBoxes() { 61 | return this.boxes; 62 | } 63 | 64 | public int getWebNum() { 65 | return webNum; 66 | } 67 | 68 | public boolean isProcessed() { 69 | return this.processed; 70 | } 71 | public void setProcessed(boolean processed) { 72 | this.processed = processed; 73 | } 74 | 75 | 76 | public void setWebNum(int webNum) { 77 | this.webNum = webNum; 78 | } 79 | 80 | // if two witnesses have the same Squares around them they are equivalent 81 | public boolean equivalent(Witness wit) { 82 | 83 | // if the number of squares is different then they can't be equal 84 | if (squares.size() != wit.getSquares().size()) { 85 | return false; 86 | } 87 | 88 | // if the locations are too far apart they can't share the same squares 89 | if (Math.abs(wit.x - this.x) > 2 || Math.abs(wit.y - this.y) > 2) { 90 | return false; 91 | } 92 | 93 | for (Square l1: squares) { 94 | boolean found = false; 95 | for (Square l2: wit.getSquares()) { 96 | if (l2.equals(l1)) { 97 | found = true; 98 | break; 99 | } 100 | } 101 | if (!found) { 102 | return false; 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | public boolean overlap(Witness w) { 110 | 111 | // if the locations are too far apart they can't share any of the same squares 112 | if (Math.abs(w.x - this.x) > 2 || Math.abs(w.y - this.y) > 2) { 113 | return false; 114 | } 115 | 116 | boolean result = false; 117 | 118 | top: for (Square s: w.squares) { 119 | for (Square s1: this.squares) { 120 | if (s.equals(s1)) { 121 | result = true; 122 | break top; 123 | } 124 | } 125 | } 126 | 127 | return result; 128 | 129 | } 130 | 131 | /** 132 | * This sorts by the number of iterations around this witness descending 133 | */ 134 | static public final Comparator SORT_BY_ITERATIONS_DESC = new Comparator() { 135 | @Override 136 | public int compare(Witness o1, Witness o2) { 137 | 138 | return -(o1.iterations - o2.iterations); 139 | 140 | } 141 | }; 142 | 143 | } 144 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/constructs/WitnessData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package minesweeper.solver.constructs; 7 | 8 | import minesweeper.structure.Location; 9 | 10 | /** 11 | * 12 | * @author David 13 | */ 14 | public class WitnessData { 15 | 16 | public Location location; 17 | public boolean witnessRestFlag = true; 18 | public boolean witnessRestClear = true; 19 | public int witnessGood; 20 | public int currentFlags; 21 | public boolean alwaysSatisfied; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/iterator/Iterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper.solver.iterator; 6 | 7 | import minesweeper.structure.Location; 8 | 9 | /** 10 | * 11 | * @author David 12 | */ 13 | abstract public class Iterator { 14 | 15 | final int numberBalls; 16 | final int numberHoles; 17 | 18 | public Iterator(int n, int m) { 19 | 20 | this.numberBalls = n; 21 | this.numberHoles = m; 22 | 23 | } 24 | 25 | 26 | public int[] getSample(int start) { 27 | return null; 28 | } 29 | 30 | public int[] getSample() { 31 | return getSample(numberBalls - 1); 32 | } 33 | 34 | public int getBalls() { 35 | return numberBalls; 36 | } 37 | 38 | public int getHoles() { 39 | return numberHoles; 40 | } 41 | 42 | // if this is true then the checkSample logic can ignore this witness 43 | // This is used by the WitnessWebIterator since the IndependentWitnesses 44 | // must always be satisified. 45 | public boolean witnessAlwaysSatisfied(Location l) { 46 | return false; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/iterator/RandomIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper.solver.iterator; 6 | 7 | /** 8 | * 9 | * @author David 10 | */ 11 | public class RandomIterator extends Iterator { 12 | 13 | int max; 14 | int count=0; 15 | 16 | int[] shuffle; 17 | 18 | int[] sample; 19 | 20 | public RandomIterator(int n, int m, int max) { 21 | super(n,m); 22 | 23 | this.max = max; 24 | 25 | shuffle = new int[m]; 26 | 27 | for (int i=0; i < shuffle.length; i++) { 28 | shuffle[i] = i; 29 | } 30 | 31 | sample = new int[n]; 32 | 33 | } 34 | 35 | @Override 36 | public int[] getSample() { 37 | 38 | if (count >= max) { 39 | return null; 40 | } 41 | count++; 42 | 43 | int top = shuffle.length -1; 44 | 45 | // create a random sample 46 | for (int i=0; i < sample.length; i++) { 47 | 48 | int e = (int) Math.floor(Math.random()*top); 49 | 50 | sample[i] = shuffle[e]; 51 | 52 | // swap shuffle[e] to the top off the array 53 | shuffle[e] = shuffle[top]; 54 | shuffle[top] = sample[i]; 55 | 56 | // reduce top by 1 so, shuffle[e] can't be picked again 57 | top--; 58 | 59 | } 60 | 61 | return sample; 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/iterator/SequentialIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper.solver.iterator; 6 | 7 | /** 8 | * 9 | * @author David 10 | */ 11 | public class SequentialIterator extends Iterator { 12 | 13 | final private int[] sample; 14 | 15 | private boolean more = true; 16 | 17 | private int index; 18 | 19 | 20 | // a sequential iterator that puts n-balls in m-holes once in each possible way 21 | public SequentialIterator(int n, int m) { 22 | super(n,m); 23 | 24 | sample = new int[n]; 25 | 26 | index = n - 1; 27 | 28 | for (int i=0; i < sample.length; i++) { 29 | sample[i] = i; 30 | } 31 | 32 | // reduce the iterator by 1, since the first getSample() will increase it 33 | // by 1 again 34 | sample[index]--; 35 | 36 | } 37 | 38 | 39 | 40 | @Override 41 | public int[] getSample(int start) { 42 | 43 | if (!more) { 44 | System.err.println("trying to iterate after the end"); 45 | return null; 46 | } 47 | 48 | index = start; 49 | 50 | // add on one to the iterator 51 | sample[index]++; 52 | 53 | // if we have rolled off the end then move backwards until we can fit 54 | // the next iteration 55 | while (sample[index] >= numberHoles - numberBalls + 1 + index) { 56 | if (index == 0) { 57 | more = false; 58 | return null; 59 | } else { 60 | index--; 61 | sample[index]++; 62 | } 63 | } 64 | 65 | // roll forward 66 | while (index != numberBalls - 1) { 67 | index++; 68 | sample[index] = sample[index-1] + 1; 69 | } 70 | 71 | return sample; 72 | 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/settings/PlayStyle.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.settings; 2 | 3 | public enum PlayStyle { 4 | FLAGGED(false, false), 5 | NO_FLAG(true, false), 6 | EFFICIENCY(false, true), 7 | NO_FLAG_EFFICIENCY(true, true); 8 | 9 | public final boolean flagless; 10 | public final boolean efficiency; 11 | 12 | private PlayStyle(boolean flagless, boolean efficiency) { 13 | this.flagless = flagless; 14 | this.efficiency = efficiency; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/settings/SettingsFactory.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.settings; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class SettingsFactory { 6 | 7 | public enum Setting { 8 | NO_BRUTE_FORCE, 9 | TINY_ANALYSIS, 10 | SMALL_ANALYSIS, 11 | LARGE_ANALYSIS, 12 | VERY_LARGE_ANALYSIS, 13 | MAX_ANALYSIS; 14 | } 15 | 16 | final static public SolverSettings GetSettings(Setting setting) { 17 | 18 | if (setting == Setting.SMALL_ANALYSIS) { 19 | return smallAnalysis(); 20 | } else if (setting == Setting.TINY_ANALYSIS) { 21 | return tinyAnalysis(); 22 | } else if (setting == Setting.LARGE_ANALYSIS) { 23 | return largeAnalysis(); 24 | } else if (setting == Setting.NO_BRUTE_FORCE) { 25 | return noBruteForce(); 26 | } else if (setting == Setting.VERY_LARGE_ANALYSIS) { 27 | return veryLargeAnalysis(); 28 | } else if (setting == Setting.MAX_ANALYSIS) { 29 | return maxAnalysis(); 30 | } 31 | 32 | return smallAnalysis(); 33 | 34 | } 35 | 36 | 37 | private static SolverSettings noBruteForce() { 38 | 39 | SolverSettings settings = new SolverSettings(); 40 | 41 | settings.bruteForceMaxSolutions = 0; 42 | settings.bruteForceVariableSolutions = 0; 43 | settings.bruteForceMaxNodes = 0; 44 | settings.bruteForceTreeDepth = 10; 45 | settings.bruteForceMaxIterations = BigInteger.ZERO; 46 | 47 | return settings; 48 | }; 49 | 50 | private static SolverSettings tinyAnalysis() { 51 | 52 | SolverSettings settings = new SolverSettings(); 53 | 54 | settings.bruteForceMaxSolutions = 40; 55 | settings.bruteForceVariableSolutions = 15; 56 | settings.bruteForceMaxNodes = 150000; 57 | settings.bruteForceTreeDepth = 10; 58 | settings.bruteForceMaxIterations = new BigInteger("1000000"); // 5 million 59 | 60 | return settings; 61 | }; 62 | 63 | /** 64 | * Does trivial, Local and Probability Engine searches. 65 | * Does a small brute force search with a 400 solution deep analysis. 66 | * This is suitable for bulk runs. 67 | */ 68 | private static SolverSettings smallAnalysis() { 69 | 70 | SolverSettings settings = new SolverSettings(); 71 | 72 | settings.bruteForceMaxSolutions = 400; 73 | settings.bruteForceVariableSolutions = 250; 74 | settings.bruteForceMaxNodes = 300000; 75 | settings.bruteForceTreeDepth = 10; 76 | settings.bruteForceMaxIterations = new BigInteger("10000000"); // 10 million 77 | 78 | return settings; 79 | }; 80 | 81 | 82 | /** 83 | * Does trivial, Local and Probability Engine searches. 84 | * Does a large brute force search with a 4000 solution deep analysis. 85 | * This is probably not suitable for bulk runs. 86 | */ 87 | private static SolverSettings largeAnalysis() { 88 | 89 | SolverSettings settings = new SolverSettings(); 90 | 91 | settings.bruteForceMaxSolutions = 4000; 92 | settings.bruteForceVariableSolutions = 2000; 93 | settings.bruteForceMaxNodes = 20000000; // 20 million 94 | settings.bruteForceTreeDepth = 10; 95 | settings.bruteForceMaxIterations = new BigInteger("10000000"); // 10 million 96 | 97 | return settings; 98 | }; 99 | 100 | private static SolverSettings veryLargeAnalysis() { 101 | 102 | SolverSettings settings = new SolverSettings(); 103 | 104 | settings.bruteForceMaxSolutions = 20000; 105 | settings.bruteForceVariableSolutions = 10000; 106 | settings.bruteForceMaxNodes = 200000000; // 200 million 107 | settings.bruteForceTreeDepth = 3; 108 | settings.bruteForceMaxIterations = new BigInteger("50000000"); // 50 million 109 | 110 | return settings; 111 | }; 112 | 113 | private static SolverSettings maxAnalysis() { 114 | 115 | SolverSettings settings = new SolverSettings(); 116 | 117 | settings.bruteForceMaxSolutions = 200000; 118 | settings.bruteForceVariableSolutions = 100000; 119 | settings.bruteForceMaxNodes = 2000000000; // 2000 million 120 | settings.bruteForceTreeDepth = 3; 121 | settings.bruteForceMaxIterations = new BigInteger("500000000"); // 500 million 122 | 123 | return settings; 124 | }; 125 | 126 | } 127 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/BigDecimalCache.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class BigDecimalCache { 6 | 7 | private static BigDecimal[] cache = new BigDecimal[4000]; 8 | static { 9 | for (int i=0; i < cache.length; i++) { 10 | cache[i] = BigDecimal.valueOf(i); 11 | } 12 | } 13 | 14 | 15 | public static BigDecimal get(int i) { 16 | 17 | if (i < cache.length) { 18 | return cache[i]; 19 | } else { 20 | return BigDecimal.valueOf(i); 21 | } 22 | 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/Binomial.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | import java.math.BigInteger; 3 | 4 | public class Binomial { 5 | 6 | private final int max; 7 | private final PrimeSieve ps; 8 | 9 | private final BigInteger[][] binomialLookup; 10 | private final int lookupLimit; 11 | 12 | public Binomial(int max, int lookup) { 13 | 14 | this.max = max; 15 | 16 | ps = new PrimeSieve(this.max); 17 | 18 | if (lookup < 10) { 19 | lookup = 10; 20 | } 21 | this.lookupLimit = lookup; 22 | 23 | final int lookup2 = lookup / 2; 24 | 25 | binomialLookup = new BigInteger[lookup + 1][lookup2 + 1]; 26 | 27 | for (int total = 1; total <= lookup; total++) { 28 | for (int choose = 0; choose <= total / 2; choose++) { 29 | try { 30 | binomialLookup[total][choose] = generate(choose, total); 31 | //System.out.println("Binomial " + total + " choose " + choose + " is " + binomialLookup[total][choose]); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | 38 | } 39 | 40 | } 41 | 42 | 43 | public BigInteger generate(int k, int n) throws Exception { 44 | 45 | if (n == 0 && k == 0) { 46 | return BigInteger.ONE; 47 | } 48 | 49 | if (n < 1 || n > max) { 50 | throw new Exception("Binomial: 1 <= n and n <= max required, but n was " + n + " and max was " + max); 51 | } 52 | 53 | if (0 > k || k > n) { 54 | throw new Exception("Binomial: 0 <= k and k <= n required, but n was " + n + " and k was " + k); 55 | } 56 | 57 | int choose = Math.min(k, n-k); 58 | 59 | if (n <= lookupLimit && binomialLookup[n][choose] != null) { 60 | return binomialLookup[n][choose]; 61 | } else if (choose < 125) { 62 | return combination(choose, n); 63 | } else { 64 | return combinationLarge(choose, n); 65 | } 66 | 67 | } 68 | 69 | private static BigInteger combination(int mines, int squares) { 70 | 71 | BigInteger top = BigInteger.ONE; 72 | BigInteger bot = BigInteger.ONE; 73 | 74 | int range = Math.min(mines, squares - mines); 75 | 76 | // calculate the combination. 77 | for (int i = 0; i < range; i++) { 78 | top = top.multiply(BigInteger.valueOf(squares - i)); 79 | bot = bot.multiply(BigInteger.valueOf(i+1)); 80 | } 81 | 82 | BigInteger result = top.divide(bot); 83 | 84 | return result; 85 | 86 | } 87 | 88 | 89 | private BigInteger combinationLarge(int k, int n) throws Exception { 90 | 91 | if ((k == 0) || (k == n)) return BigInteger.ONE; 92 | 93 | int n2 = n / 2; 94 | 95 | if (k > n2) { 96 | k = n - k; 97 | } 98 | 99 | int nk = n - k; 100 | 101 | int rootN = (int) Math.floor(Math.sqrt(n)); 102 | 103 | BigInteger result = BigInteger.ONE; 104 | 105 | 106 | for (int prime : ps.getPrimesIterable(2, n)) { 107 | 108 | if (prime > nk) { 109 | result = result.multiply(BigInteger.valueOf(prime)); 110 | continue; 111 | } 112 | 113 | if (prime > n2) { 114 | continue; 115 | } 116 | 117 | if (prime > rootN) { 118 | if (n % prime < k % prime) { 119 | result = result.multiply(BigInteger.valueOf(prime)); 120 | } 121 | continue; 122 | } 123 | 124 | int r = 0, N = n, K = k, p = 1; 125 | 126 | while (N > 0) { 127 | r = (N % prime) < (K % prime + r) ? 1 : 0; 128 | if (r == 1) 129 | { 130 | p *= prime; 131 | } 132 | N /= prime; 133 | K /= prime; 134 | } 135 | if (p > 1) { 136 | result = result.multiply(BigInteger.valueOf(p)); 137 | } 138 | } 139 | 140 | return result; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/BinomialCache.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Arrays; 5 | 6 | public class BinomialCache { 7 | 8 | private class BinomialEntry implements Comparable { 9 | final private int n; 10 | final private int k; 11 | final private BigInteger bco; 12 | 13 | private long lastUsed; 14 | 15 | private BinomialEntry(int k, int n, BigInteger bco) { 16 | this.k = k; 17 | this.n = n; 18 | this.bco = bco; 19 | } 20 | 21 | @Override 22 | public int compareTo(BinomialEntry o) { 23 | if (lastUsed < o.lastUsed) { 24 | return -1; 25 | } else if (lastUsed > o.lastUsed) { 26 | return 1; 27 | } else { 28 | return 0; 29 | } 30 | } 31 | 32 | } 33 | private BinomialEntry[] cache; 34 | private int start = -1; 35 | private long useCount = 0; 36 | 37 | private int cacheHits = 0; 38 | private int cacheStored = 0; 39 | private int sumDepth = 0; 40 | private int nearMissHits = 0; 41 | private int fullCalc = 0; 42 | 43 | private final int cacheSize; 44 | private final int cacheFreshold; 45 | private final int compressRemoval; 46 | 47 | private final Binomial binomialEngine; 48 | 49 | public BinomialCache(int cacheSize, int cacheFreshold, Binomial binomialEngine) { 50 | 51 | this.cacheSize = cacheSize; 52 | this.cacheFreshold = cacheFreshold; 53 | this.binomialEngine = binomialEngine; 54 | this.compressRemoval = cacheSize / 2; 55 | 56 | this.cache = new BinomialEntry[this.cacheSize]; 57 | 58 | } 59 | 60 | public BigInteger getBinomial(int k, int n) { 61 | 62 | // if the binomial is below the size freshold then just go get it 63 | if (n <= cacheFreshold) { 64 | try { 65 | return binomialEngine.generate(k, n); 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | return BigInteger.ONE; 69 | } 70 | } 71 | 72 | useCount++; 73 | 74 | // if there are some details in the cache then search it 75 | BinomialEntry nearMissK = null; 76 | BinomialEntry nearMissN = null; 77 | if (start != -1) { 78 | int tally = 0; 79 | for (int i = start; i >= 0; i--) { 80 | BinomialEntry entry = cache[i]; 81 | tally++; 82 | if (entry.n == n && entry.k == k) { 83 | entry.lastUsed = useCount; 84 | cacheHits++; 85 | sumDepth = sumDepth + tally; 86 | 87 | //if (cacheHits % 250 == 0) { 88 | // Arrays.sort(cache, 0, start); 89 | //} 90 | 91 | return entry.bco; 92 | } 93 | if (entry.n == n && entry.k == k + 1) { 94 | nearMissK = entry; 95 | } 96 | if (entry.n == n + 1 && entry.k == k) { 97 | nearMissN = entry; 98 | } 99 | } 100 | } 101 | 102 | BigInteger b; 103 | if (nearMissK != null) { // one below a cached entry we can do quickly 104 | b = nearMissK.bco.multiply(BigInteger.valueOf(nearMissK.k)).divide(BigInteger.valueOf(nearMissK.n - nearMissK.k + 1)); 105 | nearMissHits++; 106 | 107 | } else if (nearMissN != null) { // one below a cached entry we can do quickly 108 | b = nearMissN.bco.multiply(BigInteger.valueOf(nearMissN.n - nearMissN.k)).divide(BigInteger.valueOf(nearMissN.n)); 109 | nearMissHits++; 110 | 111 | } else { // not in the cache, so generate it 112 | try { 113 | b = binomialEngine.generate(k, n); 114 | fullCalc++; 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | b = BigInteger.ONE; 118 | } 119 | } 120 | 121 | if (start == cacheSize - 1) { 122 | compressCache(); 123 | } 124 | //else if (useCount % 50 == 0) { 125 | // Arrays.sort(cache, 0, start); 126 | //} 127 | 128 | start++; 129 | BinomialEntry be = new BinomialEntry(k, n, b); 130 | be.lastUsed = useCount; 131 | cacheStored++; 132 | 133 | cache[start] = be; 134 | 135 | return b; 136 | } 137 | 138 | // remove the least used binomials from the cache 139 | private void compressCache() { 140 | 141 | //System.out.print(Thread.currentThread().getName() + " Cache compressing..."); 142 | 143 | Arrays.sort(cache); 144 | 145 | if (cache[0].lastUsed > cache[1].lastUsed) { 146 | System.out.println("Sort order wrong!"); 147 | } 148 | 149 | for (int i=0; i < this.cacheSize - this.compressRemoval; i++) { 150 | cache[i] = cache[i + this.compressRemoval]; 151 | } 152 | 153 | this.start = this.start - this.compressRemoval; 154 | //System.out.println("Cache compressed ..." + this.start); 155 | } 156 | 157 | public void showStats() { 158 | 159 | int avgDepth = 0; 160 | if (cacheHits != 0) { 161 | avgDepth = sumDepth / cacheHits; 162 | } 163 | 164 | 165 | System.out.println(Thread.currentThread().getName() + " Cache stored: " + cacheStored + ", cache Hits: " + cacheHits + ", Near Miss Hits: " + nearMissHits 166 | + ", Full Calculation: " + fullCalc + ", current entries: " + (start + 1) + ", avg depth: " + avgDepth); 167 | } 168 | 169 | 170 | } 171 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/Logger.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | 3 | public class Logger { 4 | 5 | /** 6 | * Nothing gets logged 7 | */ 8 | public final static Logger NO_LOGGING = new Logger(Level.NONE); 9 | 10 | public enum Level { 11 | DEBUG(1), 12 | INFO(2), 13 | WARN(3), 14 | ERROR(4), 15 | ALWAYS(90), 16 | NONE(99); 17 | 18 | private int value; 19 | private Level(int value) { 20 | this.value = value; 21 | } 22 | } 23 | 24 | private final String logName; 25 | private final Level logLevel; 26 | private final String prefix; 27 | 28 | public Logger(Level level) { 29 | this(level, ""); 30 | } 31 | 32 | public Logger(Level level, String logName) { 33 | this.logLevel = level; 34 | this.logName = logName; 35 | if (this.logName.isEmpty()) { 36 | this.prefix = " "; 37 | } else { 38 | this.prefix = " " + this.logName + " "; 39 | } 40 | 41 | } 42 | 43 | public void log(Level level, String format, Object... parms) { 44 | 45 | if (level.value < logLevel.value) { 46 | return; 47 | } 48 | 49 | String output; 50 | try { 51 | output = String.format(format, parms); 52 | 53 | } catch (Exception e) { // if it goes wrong show the unformated information 54 | StringBuilder sb = new StringBuilder(); 55 | sb.append(format); 56 | sb.append(" Parms:"); 57 | for (Object parm: parms) { 58 | sb.append("["); 59 | sb.append(parm); 60 | sb.append("]"); 61 | } 62 | output = sb.toString(); 63 | } 64 | 65 | System.out.println(level + prefix + output); 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/PrimeSieve.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | import java.util.Iterator; 3 | 4 | public class PrimeSieve { 5 | 6 | // iterator for prime numbers 7 | private class Primes implements Iterable, Iterator { 8 | 9 | private int index = 0; 10 | private int stop; 11 | private int nextPrime; 12 | 13 | private Primes(int start, int stop) { 14 | this.index = start; 15 | this.stop = stop; 16 | this.nextPrime = findNext(); 17 | } 18 | 19 | @Override 20 | public Iterator iterator() { 21 | return this; 22 | } 23 | 24 | @Override 25 | public boolean hasNext() { 26 | return (nextPrime != -1); 27 | } 28 | 29 | @Override 30 | public Integer next() { 31 | 32 | int result = nextPrime; 33 | nextPrime = findNext(); 34 | 35 | return result; 36 | } 37 | 38 | private int findNext() { 39 | 40 | int next = -1; 41 | while (index <= stop && next == -1) { 42 | if (!composite[index]) { 43 | next = index; 44 | } 45 | index++; 46 | } 47 | 48 | return next; 49 | 50 | } 51 | 52 | } 53 | 54 | 55 | private final boolean[] composite; 56 | private final int max; 57 | 58 | public PrimeSieve(int n) { 59 | 60 | if (n < 2) { 61 | max = 2; 62 | } else { 63 | max = n; 64 | } 65 | 66 | composite = new boolean[max + 1]; 67 | 68 | final int rootN = (int) Math.floor(Math.sqrt(n)); 69 | 70 | for (int i=2; i < rootN; i++) { 71 | 72 | // if this is a prime number (not composite) then sieve the array 73 | if (!composite[i]) { 74 | int index = i + i; 75 | while (index <= max) { 76 | composite[index] = true; 77 | index = index + i; 78 | } 79 | } 80 | } 81 | 82 | } 83 | 84 | 85 | 86 | public boolean isPrime(int n) throws Exception { 87 | if (n <= 1 || n > max) { 88 | throw new Exception("Test value " + n + " is out of range 2 - " + max); 89 | } 90 | 91 | return !composite[n]; 92 | } 93 | 94 | protected Iterable getPrimesIterable(int start, int stop) throws Exception { 95 | 96 | if (start > stop) { 97 | throw new Exception("start " + start + " must be <= to stop " + stop); 98 | } 99 | if (start <= 1 || start > max) { 100 | throw new Exception("Start value " + start + " is out of range 2 - " + max); 101 | } 102 | if (stop <= 1 || stop > max) { 103 | throw new Exception("Stop value " + stop + " is out of range 2 - " + max); 104 | } 105 | 106 | return new Primes(start, stop); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/ProgressMonitor.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | 3 | public class ProgressMonitor { 4 | 5 | private String step; 6 | private int maxProgress; 7 | private int progress; 8 | 9 | public void SetMaxProgress(String step, int max) { 10 | this.step = step; 11 | this.maxProgress = max; 12 | this.progress = 0; 13 | } 14 | 15 | public int getMaxProgress() { 16 | return this.maxProgress; 17 | } 18 | 19 | public void setProgress(int progress) { 20 | this.progress = progress; 21 | } 22 | 23 | public int getProgress() { 24 | return this.progress; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /MineSweeperSolver/src/minesweeper/solver/utility/Timer.java: -------------------------------------------------------------------------------- 1 | package minesweeper.solver.utility; 2 | 3 | import java.text.DecimalFormat; 4 | import java.text.NumberFormat; 5 | 6 | public class Timer { 7 | 8 | private final static NumberFormat FORMAT = new DecimalFormat("###0.000"); 9 | 10 | private long start; 11 | private boolean running = false; 12 | private long duration = 0; 13 | private final String label; 14 | 15 | public Timer(String label) { 16 | 17 | this.label = label; 18 | 19 | } 20 | 21 | public Timer start() { 22 | if (running) { // if already started then ignore 23 | return this; 24 | } 25 | start = System.nanoTime(); 26 | running = true; 27 | return this; 28 | } 29 | 30 | public Timer stop() { 31 | if (!running) { // if not running then ignore 32 | return this; 33 | } 34 | running = false; 35 | duration = duration + System.nanoTime() - start; 36 | return this; 37 | } 38 | 39 | /** 40 | * Return the duration in milliseconds 41 | * @return 42 | */ 43 | public double getDuration() { 44 | 45 | long result; 46 | 47 | if (running) { 48 | result = duration + System.nanoTime() - start; 49 | } else { 50 | result = duration; 51 | } 52 | 53 | double milli = (double) result / 1000000d; 54 | 55 | return milli; 56 | } 57 | 58 | static public String humanReadable(long ms) { 59 | 60 | long milliseconds = ms % 1000; 61 | long rem = (ms - milliseconds) / 1000; 62 | long seconds = rem % 60; 63 | rem = (rem - seconds) / 60; 64 | long minutes = rem % 60; 65 | long hours = (rem - minutes) /60; 66 | 67 | String result; 68 | if (hours > 0) { 69 | result = hours(hours) + " " + minutes(minutes); 70 | } else if (minutes > 0) { 71 | result = minutes(minutes) + " " + seconds(seconds); 72 | } else if (seconds > 0){ 73 | result = seconds(seconds); 74 | } else { 75 | result = "< 1 second"; 76 | } 77 | 78 | return result; 79 | } 80 | 81 | static private String hours(long hours) { 82 | if (hours == 1) { 83 | return "1 hour"; 84 | } else { 85 | return hours + " hours"; 86 | } 87 | } 88 | 89 | static private String minutes(long minutes) { 90 | 91 | if (minutes == 0) { 92 | return ""; 93 | } else if (minutes == 1) { 94 | return "1 minute"; 95 | } else { 96 | return minutes + " minutes"; 97 | } 98 | } 99 | 100 | static private String seconds(long seconds) { 101 | 102 | if (seconds == 0) { 103 | return ""; 104 | } else if (seconds == 1) { 105 | return "1 second"; 106 | } else { 107 | return seconds + " seconds"; 108 | } 109 | } 110 | 111 | @Override 112 | public String toString() { 113 | return label + " " + FORMAT.format(getDuration()) + " milliseconds"; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /Minesweeper/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Minesweeper/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /build/ 3 | -------------------------------------------------------------------------------- /Minesweeper/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Minesweeper 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.xtext.ui.shared.xtextBuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.xtext.ui.shared.xtextNature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /Minesweeper/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /Minesweeper/build.fxbuild: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/Animator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper; 6 | 7 | import javafx.application.Platform; 8 | 9 | /** 10 | * 11 | * @author David 12 | */ 13 | public class Animator implements Runnable{ 14 | 15 | private Thread animator; 16 | 17 | private ScreenController scon; 18 | 19 | private boolean stopped = false; 20 | private long gameTime = 0; 21 | 22 | public Animator(ScreenController scon) { 23 | 24 | this.scon = scon; 25 | 26 | // start animating the display pane - see the run method 27 | animator = new Thread(this, "Animator"); 28 | 29 | } 30 | 31 | public void start() { 32 | 33 | animator.start(); 34 | 35 | } 36 | 37 | public void stop() { 38 | 39 | stopped = true; 40 | 41 | } 42 | 43 | 44 | @Override 45 | public void run() { 46 | 47 | long timeDiff, sleep, timeDelay; 48 | 49 | timeDelay = 50; 50 | 51 | gameTime = System.currentTimeMillis(); 52 | 53 | while (!stopped) { 54 | 55 | Platform.runLater(new Runnable() { 56 | @Override public void run() { 57 | scon.updateTime(); 58 | scon.moveCheck(); 59 | scon.highlightMove(); 60 | } 61 | }); 62 | 63 | 64 | // calculate how long the work took 65 | timeDiff = System.currentTimeMillis() - gameTime; 66 | 67 | // pause for the ramainder of timeDelay 68 | sleep = timeDelay - timeDiff; 69 | 70 | if (sleep < 0) { 71 | sleep = 2; 72 | } 73 | 74 | try { 75 | Thread.sleep(sleep); 76 | } catch (InterruptedException e) { 77 | System.out.println("interrupted"); 78 | } 79 | 80 | gameTime += timeDelay; 81 | 82 | } 83 | 84 | System.out.println("Animator thread stopped"); 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/Custom.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 47 | 48 | -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/Graphics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper; 6 | 7 | import javafx.scene.image.Image; 8 | 9 | /** 10 | * 11 | * @author David 12 | */ 13 | public class Graphics { 14 | 15 | public static final double SIZE = 50; 16 | 17 | 18 | private static final Image button; 19 | private static final Image mineBang; 20 | private static final Image flag; 21 | private static final Image[] number = new Image[9]; 22 | private static final Image mine; 23 | 24 | 25 | static { 26 | 27 | button = clean(new Image(Graphics.class.getResource("resources/ms_button.png").toExternalForm(), SIZE, SIZE, true, true)); 28 | mineBang = clean(new Image(Graphics.class.getResource("resources/ms_mine_bang.png").toExternalForm(), SIZE, SIZE, true, true)); 29 | flag = clean(new Image(Graphics.class.getResource("resources/ms_flag.png").toExternalForm(), SIZE, SIZE, true, true)); 30 | mine = clean(new Image(Graphics.class.getResource("resources/ms_mine.png").toExternalForm(), SIZE, SIZE, true, true)); 31 | 32 | number[0] = clean(new Image(Graphics.class.getResource("resources/ms_zero.png").toExternalForm(), SIZE, SIZE, true, true)); 33 | number[1] = clean(new Image(Graphics.class.getResource("resources/ms_one.png").toExternalForm(), SIZE, SIZE, true, true)); 34 | number[2] = clean(new Image(Graphics.class.getResource("resources/ms_two.png").toExternalForm(), SIZE, SIZE, true, true)); 35 | number[3] = clean(new Image(Graphics.class.getResource("resources/ms_three.png").toExternalForm(), SIZE, SIZE, true, true)); 36 | number[4] = clean(new Image(Graphics.class.getResource("resources/ms_four.png").toExternalForm(), SIZE, SIZE, true, true)); 37 | number[5] = clean(new Image(Graphics.class.getResource("resources/ms_five.png").toExternalForm(), SIZE, SIZE, true, true)); 38 | number[6] = clean(new Image(Graphics.class.getResource("resources/ms_six.png").toExternalForm(), SIZE, SIZE, true, true)); 39 | number[7] = clean(new Image(Graphics.class.getResource("resources/ms_seven.png").toExternalForm(), SIZE, SIZE, true, true)); 40 | number[8] = clean(new Image(Graphics.class.getResource("resources/ms_eight.png").toExternalForm(), SIZE, SIZE, true, true)); 41 | 42 | 43 | } 44 | 45 | static public Image getNumber(int c) { 46 | 47 | return number[c]; 48 | 49 | } 50 | 51 | static public Image getMineBang() { 52 | 53 | return mineBang; 54 | 55 | } 56 | 57 | static public Image getMine() { 58 | 59 | return mine; 60 | 61 | } 62 | 63 | static public Image getFlag() { 64 | 65 | return flag; 66 | 67 | } 68 | 69 | static public Image getButton() { 70 | 71 | return button; 72 | 73 | } 74 | 75 | // in case we want to do some image manipulation 76 | static private Image clean(Image image) { 77 | 78 | return image; 79 | 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/Rotator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package minesweeper; 6 | 7 | import javafx.scene.Node; 8 | 9 | /** 10 | * 11 | * @author David 12 | */ 13 | public class Rotator implements Runnable { 14 | 15 | private Thread rotator; 16 | 17 | Node object; 18 | 19 | 20 | public Rotator(Node object) { 21 | 22 | this.object = object; 23 | 24 | rotator = new Thread(this, "Rotator"); 25 | 26 | } 27 | 28 | public void start() { 29 | 30 | rotator.start(); 31 | 32 | } 33 | 34 | @Override 35 | public void run() { 36 | 37 | for (int i=0; i < 360; i=i+20) { 38 | 39 | this.object.setRotate(i); 40 | 41 | try { 42 | Thread.sleep(20); 43 | } catch (InterruptedException e) { 44 | System.out.println("interrupted"); 45 | } 46 | 47 | 48 | } 49 | 50 | this.object.setRotate(0); 51 | 52 | } 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/Screen.css: -------------------------------------------------------------------------------- 1 | /* 2 | Document : Screen 3 | Created on : 29-Nov-2013, 15:48:32 4 | Author : David 5 | Description: 6 | Purpose of the stylesheet follows. 7 | */ 8 | 9 | .root { 10 | 11 | -fx-background-color: linear-gradient(GREEN, DARKGREEN); 12 | 13 | -fx-background-color: linear-gradient(#61a2b1, #2A5058); 14 | } 15 | 16 | .menu-bar { 17 | 18 | -fx-background-color: linear-gradient(#61a2b1, #2A5058); 19 | 20 | } 21 | 22 | 23 | #myPane { 24 | 25 | -fx-border-color: black; 26 | -fx-border-width: 1; 27 | -fx-border-style: solid outside; 28 | 29 | } 30 | 31 | #scoreLabel { 32 | 33 | -fx-border-color: black; 34 | -fx-border-width: 1; 35 | -fx-border-style: solid outside; 36 | -fx-border-radius: 15; 37 | 38 | } 39 | 40 | #timeLabel { 41 | 42 | -fx-border-color: black; 43 | -fx-border-width: 1; 44 | -fx-border-style: solid outside; 45 | -fx-border-radius: 15; 46 | 47 | } -------------------------------------------------------------------------------- /Minesweeper/src/minesweeper/bulk/BulkResults.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |